1 /** 2 * Copyright (C) 2012-2013 KO GmbH <copyright@kogmbh.com> 3 * 4 * @licstart 5 * This file is part of WebODF. 6 * 7 * WebODF is free software: you can redistribute it and/or modify it 8 * under the terms of the GNU Affero General Public License (GNU AGPL) 9 * as published by the Free Software Foundation, either version 3 of 10 * the License, or (at your option) any later version. 11 * 12 * WebODF is distributed in the hope that it will be useful, but 13 * WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU Affero General Public License for more details. 16 * 17 * You should have received a copy of the GNU Affero General Public License 18 * along with WebODF. If not, see <http://www.gnu.org/licenses/>. 19 * @licend 20 * 21 * @source: http://www.webodf.org/ 22 * @source: https://github.com/kogmbh/WebODF/ 23 */ 24 25 /*global Node, runtime, core, odf, ops*/ 26 27 /** 28 * @constructor 29 * @implements {core.PositionFilter} 30 */ 31 ops.TextPositionFilter = function TextPositionFilter() { 32 "use strict"; 33 var odfUtils = odf.OdfUtils, 34 ELEMENT_NODE = Node.ELEMENT_NODE, 35 TEXT_NODE = Node.TEXT_NODE, 36 /**@const*/FILTER_ACCEPT = core.PositionFilter.FilterResult.FILTER_ACCEPT, 37 /**@const*/FILTER_REJECT = core.PositionFilter.FilterResult.FILTER_REJECT; 38 39 /** 40 * @param {!Node} container 41 * @param {?Node} leftNode 42 * @param {?Node} rightNode 43 * @return {!core.PositionFilter.FilterResult} 44 */ 45 function checkLeftRight(container, leftNode, rightNode) { 46 var r, firstPos, rightOfChar; 47 // accept if there is a character immediately to the left 48 if (leftNode) { 49 if (odfUtils.isInlineRoot(leftNode) && odfUtils.isGroupingElement(rightNode)) { 50 // Move first position after inline root inside trailing grouping element (part 1) 51 // Disallow positions to the right of an inline root (like an annotation) and 52 // to the left of a grouping element (like an annotation highlight span) 53 return FILTER_REJECT; 54 } 55 r = odfUtils.lookLeftForCharacter(leftNode); 56 if (r === 1) {// non-whitespace character or a character element 57 return FILTER_ACCEPT; 58 } 59 if (r === 2 && (odfUtils.scanRightForAnyCharacter(rightNode) 60 || odfUtils.scanRightForAnyCharacter(odfUtils.nextNode(container)))) { 61 // significant whitespace is ok, if not in trailing whitesp 62 return FILTER_ACCEPT; 63 } 64 } else { 65 // Note, cant use OdfUtils.previousNode here as that function automatically dives to the previous 66 // elements first child (if it has one) 67 if (odfUtils.isInlineRoot(container.previousSibling) && odfUtils.isGroupingElement(container)) { 68 // Move first position after inline root inside trailing grouping element (part 2) 69 // Allow the first position inside the first grouping element trailing an annotation 70 return FILTER_ACCEPT; 71 } 72 } 73 // at this point, we know that the position is not directly to the 74 // right of a significant character or element. so the position is 75 // only acceptable if it is the first in an empty p or h or if it 76 // is to the left of the first significant character or element. 77 78 // accept if this is the first position in p or h and there is no 79 // character in the p or h 80 firstPos = leftNode === null && odfUtils.isParagraph(container); 81 rightOfChar = odfUtils.lookRightForCharacter(rightNode); 82 if (firstPos) { 83 if (rightOfChar) { 84 return FILTER_ACCEPT; 85 } 86 // position is first position in empty paragraph 87 return odfUtils.scanRightForAnyCharacter(rightNode) ? FILTER_REJECT : FILTER_ACCEPT; 88 } 89 // if not directly to the right of a character, reject 90 if (!rightOfChar) { 91 return FILTER_REJECT; 92 } 93 // accept if there is no character to the left 94 leftNode = leftNode || odfUtils.previousNode(container); 95 return odfUtils.scanLeftForAnyCharacter(leftNode) ? FILTER_REJECT : FILTER_ACCEPT; 96 } 97 98 /** 99 * @param {!core.PositionIterator} iterator 100 * @return {!core.PositionFilter.FilterResult} 101 */ 102 this.acceptPosition = function (iterator) { 103 var container = iterator.container(), 104 nodeType = container.nodeType, 105 /**@type{number}*/ 106 offset, 107 /**@type{string}*/ 108 text, 109 /**@type{string}*/ 110 leftChar, 111 /**@type{string}*/ 112 rightChar, 113 leftNode, 114 rightNode, 115 r; 116 117 if (nodeType !== ELEMENT_NODE && nodeType !== TEXT_NODE) { 118 return FILTER_REJECT; 119 } 120 if (nodeType === TEXT_NODE) { 121 // In a PositionIterator, the offset in a text node is never 122 // equal to the length of the text node. 123 offset = iterator.unfilteredDomOffset(); 124 text = container.data; 125 runtime.assert(offset !== text.length, "Unexpected offset."); 126 if (offset > 0) { 127 // The cursor may be placed to the right of a non-whitespace 128 // character. 129 leftChar = /**@type{string}*/(text[offset - 1]); 130 if (!odfUtils.isODFWhitespace(leftChar)) { 131 return FILTER_ACCEPT; 132 } 133 // A whitespace to the left is ok, if 134 // * there is a non-whitespace character to the right and 135 // that is the first non-whitespace character or character 136 // element or 137 // * there is not another whitespace character in front of 138 // it. 139 if (offset > 1) { 140 leftChar = /**@type{string}*/(text[offset - 2]); 141 if (!odfUtils.isODFWhitespace(leftChar)) { 142 r = FILTER_ACCEPT; 143 } else if (!odfUtils.isODFWhitespace(text.substr(0, offset))) { 144 // check if this can be leading paragraph space 145 return FILTER_REJECT; 146 } 147 } else { 148 // check if there is a non-whitespace character or 149 // character element (other than text:s) in a preceding node 150 leftNode = odfUtils.previousNode(container); 151 if (odfUtils.scanLeftForNonSpace(leftNode)) { 152 r = FILTER_ACCEPT; 153 } 154 } 155 if (r === FILTER_ACCEPT) { 156 return odfUtils.isTrailingWhitespace( 157 /**@type{!Text}*/(container), offset) 158 ? FILTER_REJECT : FILTER_ACCEPT; 159 } 160 rightChar = /**@type{string}*/(text[offset]); 161 if (odfUtils.isODFWhitespace(rightChar)) { 162 return FILTER_REJECT; 163 } 164 return odfUtils.scanLeftForAnyCharacter(odfUtils.previousNode(container)) 165 ? FILTER_REJECT : FILTER_ACCEPT; 166 } 167 leftNode = iterator.leftNode(); 168 rightNode = container; 169 container = /**@type{!Node}*/(container.parentNode); 170 r = checkLeftRight(container, leftNode, rightNode); 171 } else if (!odfUtils.isGroupingElement(container)) { 172 r = FILTER_REJECT; 173 } else { 174 leftNode = iterator.leftNode(); 175 rightNode = iterator.rightNode(); 176 r = checkLeftRight(container, leftNode, rightNode); 177 } 178 return r; 179 }; 180 }; 181