Ticket #3367: 3367.patch
File 3367.patch, 14.2 KB (added by , 16 years ago) |
---|
-
_source/core/dom/range.js
1405 1405 }; 1406 1406 }, 1407 1407 1408 // Calls to this function may produce changes to the DOM. The range may 1409 // be updated to reflect such changes. 1408 1410 checkStartOfBlock : function() 1409 1411 { 1410 1412 var startContainer = this.startContainer, 1411 startOffset = this.startOffset, 1412 startNode, 1413 startInclusive; 1413 startOffset = this.startOffset; 1414 1414 1415 if ( startOffset ) 1415 // If the starting node is a text node, and non-empty before the offset, 1416 // then we're surely not at the start of block. 1417 if ( startOffset && startContainer.type == CKEDITOR.NODE_TEXT ) 1416 1418 { 1417 // If the starting node is a text node, and non-empty before the offset, 1418 // then we're surely not at the start of block. 1419 if ( startContainer.type == CKEDITOR.NODE_TEXT ) 1420 { 1421 var textBefore = CKEDITOR.tools.ltrim( startContainer.substring( 0, startOffset ) ); 1422 if ( textBefore.length ) 1423 return false; 1424 } 1425 else 1426 { 1427 startNode = startContainer.getChild( startOffset - 1 ); 1428 startInclusive = true; 1429 } 1419 var textBefore = CKEDITOR.tools.ltrim( startContainer.substring( 0, startOffset ) ); 1420 if ( textBefore.length ) 1421 return false; 1430 1422 } 1431 1423 1432 if ( !startNode ) 1433 startNode = startContainer; 1424 // Antecipate the trim() call here, so the walker will not make 1425 // changes to the DOM, which would not get reflected into this 1426 // range otherwise. 1427 this.trim(); 1434 1428 1435 var path = new CKEDITOR.dom.elementPath( startNode ), 1436 walker = new CKEDITOR.dom.walker( startNode, ( path.block || path.blockLimit ) ); 1437 1438 if ( ( path.block && startNode.equals( path.block ) ) 1439 || ( !path.block && startNode.equals( path.blockLimit ) ) ) 1440 { 1441 return true; 1442 } 1443 1444 walker.startInclusive = startInclusive; 1445 walker.evaluator = getCheckStartEndBlockEvalFunction( true ); 1429 // We need to grab the block element holding the start boundary, so 1430 // let's use an element path for it. 1431 var path = new CKEDITOR.dom.elementPath( this.startContainer ); 1446 1432 1433 // Creates a range starting at the block start until the range start. 1434 var walkerRange = this.clone(); 1435 walkerRange.collapse( true ); 1436 walkerRange.setStartAt( path.block || path.blockLimit, CKEDITOR.POSITION_AFTER_START ); 1437 1438 var walker = new CKEDITOR.dom.walker( walkerRange ); 1439 walker.evaluator = getCheckStartEndBlockEvalFunction( true ); 1440 1447 1441 return walker.checkBackward(); 1448 1442 }, 1449 1443 1450 1444 checkEndOfBlock : function() 1451 1445 { 1452 1446 var endContainer = this.endContainer, 1453 endOffset = this.endOffset, 1454 startNode, 1455 startInclusive; 1447 endOffset = this.endOffset; 1456 1448 1457 1449 // If the ending node is a text node, and non-empty after the offset, 1458 1450 // then we're surely not at the end of block. … … 1462 1454 if ( textAfter.length ) 1463 1455 return false; 1464 1456 } 1465 else1466 {1467 startNode = endContainer.getChild( endOffset );1468 startInclusive = !!startNode;1469 }1470 1457 1471 if ( !startNode ) 1472 startNode = endContainer; 1458 // Antecipate the trim() call here, so the walker will not make 1459 // changes to the DOM, which would not get reflected into this 1460 // range otherwise. 1461 this.trim(); 1473 1462 1474 var path = new CKEDITOR.dom.elementPath( startNode ), 1475 walker = new CKEDITOR.dom.walker( startNode, ( path.block || path.blockLimit ) ); 1476 1477 if ( ( path.block && startNode.equals( path.block ) ) 1478 || ( !path.block && startNode.equals( path.blockLimit ) ) ) 1479 { 1480 return true; 1481 } 1482 1483 walker.startInclusive = startInclusive; 1484 walker.evaluator = getCheckStartEndBlockEvalFunction( false ); 1463 // We need to grab the block element holding the start boundary, so 1464 // let's use an element path for it. 1465 var path = new CKEDITOR.dom.elementPath( this.endContainer ); 1485 1466 1467 // Creates a range starting at the block start until the range start. 1468 var walkerRange = this.clone(); 1469 walkerRange.collapse( false ); 1470 walkerRange.setEndAt( path.block || path.blockLimit, CKEDITOR.POSITION_BEFORE_END ); 1471 1472 var walker = new CKEDITOR.dom.walker( walkerRange ); 1473 walker.evaluator = getCheckStartEndBlockEvalFunction( false ); 1474 1486 1475 return walker.checkForward(); 1487 1476 }, 1488 1477 -
_source/core/dom/walker.js
8 8 // This function is to be called under a "walker" instance scope. 9 9 function iterate( rtl, breakOnFalse ) 10 10 { 11 // Return null if we have reached the end. 11 12 if ( this._.end ) 12 13 return null; 13 14 14 15 var node, 16 range = this.range, 17 guard, 18 userGuard = this.guard, 15 19 type = this.type, 16 20 getSourceNodeFn = ( rtl ? 'getPreviousSourceNode' : 'getNextSourceNode' ); 17 21 18 var guard = this.guard, 19 endNode = this.endNode; 22 // This is the first call. Initialize it. 23 if ( !this._.start ) 24 { 25 this._.start = 1; 26 27 // Trim text nodes and optmize the range boundaries. DOM changes 28 // may happen at this point. 29 range.trim(); 20 30 21 if ( endNode ) 31 // A collapsed range must return null at first call. 32 if ( range.collapsed ) 33 { 34 this.end(); 35 return null; 36 } 37 } 38 39 // Create the LTR guard function, if necessary. 40 if ( !rtl && !this._.guardLTR ) 22 41 { 23 if ( guard ) 42 // Gets the node that stops the walker when going LTR. 43 var blockerLTR = range.endContainer; 44 blockerLTR = blockerLTR.getChild( range.endOffset ) || blockerLTR.getNextSourceNode( true ); 45 46 this._.guardLTR = function( node ) 24 47 { 25 var originalGuard; 26 guard = function( node ) 27 { 28 if ( node.equals( endNode ) ) 29 return false; 48 return ( ( !blockerLTR || !node.equals( blockerLTR ) ) 49 && ( node.type != CKEDITOR.NODE_ELEMENT || node.name != 'body' ) ); 50 } 51 } 52 53 // Create the RTL guard function, if necessary. 54 if ( rtl && !this._.guardRTL ) 55 { 56 // Gets the node that stops the walker when going LTR. 57 var blockerRTL = range.startContainer; 58 blockerRTL = ( range.startOffset > 0 ) ? blockerRTL.getChild( range.startOffset - 1 ) : blockerRTL.getPreviousSourceNode( true ); 59 60 this._.guardRTL = function( node ) 61 { 62 return ( ( !blockerRTL || !node.equals( blockerRTL ) ) 63 && ( node.type != CKEDITOR.NODE_ELEMENT || node.name != 'body' ) ); 64 } 65 } 66 67 // Define which guard function to use. 68 stopGuard = rtl ? this._.guardRTL : this._.guardLTR; 30 69 31 return originalGuard( node ); 32 } 70 // Make the user defined guard function participate in the process, 71 // otherwise simply use the boundary guard. 72 if ( userGuard ) 73 { 74 guard = function( node ) 75 { 76 if ( stopGuard( node ) === false ) 77 return false; 78 79 return userGuard( node ); 33 80 } 34 else35 guard = endNode;36 81 } 82 else 83 guard = stopGuard; 37 84 38 85 if ( this.current ) 39 86 node = this.current[ getSourceNodeFn ]( false, type, guard ); 40 else if ( this.startInclusive )87 else 41 88 { 42 node = this.startNode; 43 if ( this.guard && this.guard( node ) === false ) 44 node = null; 89 // Get the first node to be returned. 90 91 if ( rtl ) 92 { 93 node = range.endContainer; 94 node = ( range.endOffset > 0 ) ? node.getChild( range.endOffset - 1 ) : node.getPreviousSourceNode( true, type, guard ); 95 } 96 else 97 { 98 node = range.startContainer; 99 node = node.getChild( range.startOffset ) || node.getNextSourceNode( true, type, guard ); 100 } 45 101 } 46 else47 node = this.startNode[ getSourceNodeFn ]( true, type, guard );48 102 49 103 while ( node && !this._.end ) 50 104 { 51 105 this.current = node; 52 106 53 if ( node == this.endNode && !this.endInclusive )54 break;55 56 107 if ( !this.evaluator || this.evaluator( node ) !== false ) 57 108 return node; 58 109 else if ( breakOnFalse && this.evaluator ) … … 65 116 return this.current = null; 66 117 } 67 118 68 function iterateToLast( rtl )69 {70 var node, last = null;119 // function iterateToLast( rtl ) 120 // { 121 // var node, last = null; 71 122 72 while ( node = iterate.call( this, rtl ) )73 last = node;123 // while ( node = iterate.call( this, rtl ) ) 124 // last = node; 74 125 75 return last;76 }126 // return last; 127 // } 77 128 78 129 CKEDITOR.dom.walker = CKEDITOR.tools.createClass( 79 130 { 80 131 /** 81 * Utility class to "walk" inside a DOM tree starting from a specific 82 * node. Each step in the walk can be preciselly controlled. 132 * Utility class to "walk" the DOM inside a range boundaries. If 133 * necessary, partially included nodes (text nodes) are broken to 134 * reflect the boundaries limits, so DOM and range changes may happen. 135 * Outside changes to the range may break the walker. 136 * 137 * The walker may return nodes that are not totaly included into the 138 * range boundaires. Let's take the following range representation, 139 * where the square brackets indicate the boundaries: 140 * 141 * [<p>Some <b>sample] text</b> 142 * 143 * While walking forward into the above range, the following nodes are 144 * returned: <p>, "Some ", <b> and "sample". Going 145 * backwards instead we have: "sample" and "Some ". So note that the 146 * walker always returns nodes when "entering" them, but not when 147 * "leaving" them. The guard function is instead called both when 148 * entering and leaving nodes. 149 * 83 150 * @constructor 84 * @param {CKEDITOR.dom.node} startNode The node from wich the walk 85 * will start. 86 * @param {CKEDITOR.dom.node} [endNode] The last node to be considered 87 * in the walk. No more nodes are retrieved after touching or 88 * passing it. 151 * @param {CKEDITOR.dom.range} range The range within which walk. 89 152 */ 90 $ : function( startNode, endNode )153 $ : function( range ) 91 154 { 92 /** 93 * The node from which start walking. 94 * @type {CKEDITOR.dom.node} 95 */ 96 this.startNode = startNode; 155 this.range = range; 97 156 98 157 /** 99 * The end boundary node of the walk.100 * @type {CKEDITOR.dom.node}101 */102 this.endNode = endNode;103 104 /**105 * Indicates that the start node is to be included in the walk.106 * @name CKEDITOR.pluginDefinition.prototype.startInclusive107 * @property108 * @type Boolean109 * @default false110 */111 // this.startInclusive = false;112 113 /**114 * Indicates that the end node is to be included in the walk.115 * @name CKEDITOR.pluginDefinition.prototype.endInclusive116 * @property117 * @type Boolean118 * @default false119 */120 // this.endInclusive = false;121 122 /**123 158 * A function executed for every matched node, to check whether 124 159 * it's to be considered into the walk or not. If not provided, all 125 160 * matched nodes are considered good. … … 146 181 this._ = {}; 147 182 }, 148 183 184 // statics : 185 // { 186 // /* Creates a CKEDITOR.dom.walker instance to walk inside DOM boundaries set by nodes. 187 // * @param {CKEDITOR.dom.node} startNode The node from wich the walk 188 // * will start. 189 // * @param {CKEDITOR.dom.node} [endNode] The last node to be considered 190 // * in the walk. No more nodes are retrieved after touching or 191 // * passing it. If not provided, the walker stops at the 192 // * <body> closing boundary. 193 // * @returns {CKEDITOR.dom.walker} A DOM walker for the nodes between the 194 // * provided nodes. 195 // */ 196 // createOnNodes : function( startNode, endNode, startInclusive, endInclusive ) 197 // { 198 // var range = new CKEDITOR.dom.range(); 199 // range.setStartAt( startNode, startInclusive ? CKEDITOR.POSITION_BEFORE_START : CKEDITOR.POSITION_AFTER_END ) ; 200 201 // if ( endNode ) 202 // range.setEndAt( endNode, endInclusive ? CKEDITOR.POSITION_AFTER_END : CKEDITOR.POSITION_BEFORE_START ) ; 203 // else 204 // range.setEndAt( startNode.getDocument().getBody(), CKEDITOR.POSITION_BEFORE_END ) ; 205 206 // return new CKEDITOR.dom.walker( range ); 207 // } 208 // }, 209 149 210 proto : 150 211 { 151 212 /** … … 166 227 { 167 228 return iterate.call( this ); 168 229 }, 169 230 170 231 /** 171 232 * Retrieves the previous node (at left). 172 233 * @returns {CKEDITOR.dom.node} The previous node or null if no more … … 178 239 }, 179 240 180 241 /** 181 * Executes a full walk forward (to the right), until no more nodes182 * are available, returning the last valid node.183 * @returns {CKEDITOR.dom.node} The last node at the right or null184 * if no valid nodes are available.185 */186 lastForward : function()187 {188 return iterateToLast.call( this );189 },190 191 /**192 * Executes a full walk backwards (to the left), until no more nodes193 * are available, returning the last valid node.194 * @returns {CKEDITOR.dom.node} The last node at the left or null195 * if no valid nodes are available.196 */197 lastBackward : function()198 {199 return iterateToLast.call( this, true );200 },201 202 /**203 242 * Check all nodes at right, executing the evaluation fuction. 204 243 * @returns {Boolean} "false" if the evaluator function returned 205 244 * "false" for any of the matched nodes. Otherwise "true". … … 218 257 { 219 258 return iterate.call( this, true, true ) !== false; 220 259 } 260 261 // The following features have been originally included in the implementation, 262 // but they are not used anywhere in the code, so they got commented out. 263 264 // /** 265 // * Executes a full walk forward (to the right), until no more nodes 266 // * are available, returning the last valid node. 267 // * @returns {CKEDITOR.dom.node} The last node at the right or null 268 // * if no valid nodes are available. 269 // */ 270 // lastForward : function() 271 // { 272 // return iterateToLast.call( this ); 273 // }, 274 275 // /** 276 // * Executes a full walk backwards (to the left), until no more nodes 277 // * are available, returning the last valid node. 278 // * @returns {CKEDITOR.dom.node} The last node at the left or null 279 // * if no valid nodes are available. 280 // */ 281 // lastBackward : function() 282 // { 283 // return iterateToLast.call( this, true ); 284 // } 221 285 } 222 286 }); 223 287 })();