Ticket #3304: 3304_7.patch
File 3304_7.patch, 30.6 KB (added by , 14 years ago) |
---|
-
_source/plugins/domiterator/plugin.js
314 314 // above block can be removed or changed, so we can rely on it for the 315 315 // next interation. 316 316 if ( !this._.nextNode ) 317 { 317 318 this._.nextNode = ( isLast || block.equals( lastNode ) ) ? null : 318 319 getNextSourceNode( block, lastNode, true ); 320 } 319 321 320 322 return block; 321 323 } -
_source/plugins/find/dialogs/find.js
5 5 6 6 (function() 7 7 { 8 // Element tag names which prevent characters counting. 9 var characterBoundaryElementsEnum = 8 function guardDomWalkerNonEmptyTextNode( node ) 10 9 { 11 address :1, blockquote :1, dl :1, h1 :1, h2 :1, h3 :1, 12 h4 :1, h5 :1, h6 :1, p :1, pre :1, li :1, dt :1, de :1, div :1, td:1, th:1 13 }; 10 return ( node.type == CKEDITOR.NODE_TEXT && node.getLength() > 0 ) 11 } 14 12 15 var guardDomWalkerNonEmptyTextNode = function( evt ) 13 /** 14 * Elements which break characters been considered as sequence. 15 */ 16 function checkCharactersBoundary ( node ) 16 17 { 17 if ( evt.data.to && evt.data.to.type == CKEDITOR.NODE_TEXT 18 && evt.data.to.$.length > 0 ) 19 this.stop(); 20 CKEDITOR.dom.domWalker.blockBoundary( { br : 1 } ).call( this, evt ); 21 }; 22 18 var dtd = CKEDITOR.dtd; 19 return node.isBlockBoundary( 20 CKEDITOR.tools.extend( {}, dtd.$empty, dtd.$nonEditable ) ); 21 } 23 22 24 23 /** 25 24 * Get the cursor object which represent both current character and it's dom … … 30 29 var obj = { 31 30 textNode : this.textNode, 32 31 offset : this.offset, 33 character : this.textNode ? this.textNode.getText().charAt( this.offset ) : null, 32 character : this.textNode ? 33 this.textNode.getText().charAt( this.offset ) : null, 34 34 hitMatchBoundary : this._.matchBoundary 35 35 }; 36 36 return obj; … … 75 75 * Iterator which walk through document char by char. 76 76 * @param {Object} start 77 77 * @param {Number} offset 78 * @param {Boolean} isStrict 78 79 */ 79 var characterWalker = function( start, offset)80 var characterWalker = function( range , matchWord ) 80 81 { 81 var isCursor = typeof( start.textNode ) !== 'undefined'; 82 this.textNode = isCursor ? start.textNode : start; 83 this.offset = isCursor ? start.offset : offset; 82 var walker = 83 new CKEDITOR.dom.walker( range ); 84 walker[ matchWord ? 'guard' : 'evaluator' ] = 85 guardDomWalkerNonEmptyTextNode; 86 walker.breakOnFalse = true; 87 84 88 this._ = { 85 walker : new CKEDITOR.dom.domWalker( this.textNode ), 89 matchWord : matchWord, 90 walker : walker, 86 91 matchBoundary : false 87 92 }; 88 93 }; … … 90 95 characterWalker.prototype = { 91 96 next : function() 92 97 { 98 return this.move(); 99 }, 100 101 back : function() 102 { 103 return this.move( true ); 104 }, 105 106 move : function( rtl ) 107 { 108 var currentTextNode = this.textNode; 93 109 // Already at the end of document, no more character available. 94 if( !this.textNode)110 if( currentTextNode === null ) 95 111 return cursorStep.call( this ); 96 112 97 113 this._.matchBoundary = false; … … 96 112 97 113 this._.matchBoundary = false; 98 114 99 // If there are more characters in the text node, get it and100 // raise an event.101 if( this.textNode.type == CKEDITOR.NODE_TEXT102 && this.offset < this.textNode.getLength() - 1)115 // There are more characters in the text node, step forward. 116 if( currentTextNode 117 && rtl 118 && this.offset > 0 ) 103 119 { 104 this.offset ++;120 this.offset--; 105 121 return cursorStep.call( this ); 106 122 } 107 108 // If we are at the end of the text node, use dom walker to get 109 // the next text node. 110 var data = null; 111 while ( !data || ( data.node && data.node.type != 112 CKEDITOR.NODE_TEXT ) ) 123 else if( currentTextNode 124 && this.offset < currentTextNode.getLength() - 1 ) 113 125 { 114 data = this._.walker.forward( 115 guardDomWalkerNonEmptyTextNode ); 116 117 // Block boundary? BR? Document boundary? 118 if ( !data.node 119 || ( data.node.type !== CKEDITOR.NODE_TEXT 120 && data.node.getName() in 121 characterBoundaryElementsEnum ) ) 122 this._.matchBoundary = true; 123 } 124 this.textNode = data.node; 125 this.offset = 0; 126 return cursorStep.call( this ); 127 }, 128 129 back : function() 130 { 131 this._.matchBoundary = false; 132 133 // More characters -> decrement offset and return. 134 if ( this.textNode.type == CKEDITOR.NODE_TEXT && this.offset > 0 ) 135 { 136 this.offset--; 126 this.offset++; 137 127 return cursorStep.call( this ); 138 128 } 139 140 // Start of text node -> use dom walker to get the previous text node. 141 var data = null; 142 while ( !data 143 || ( data.node && data.node.type != CKEDITOR.NODE_TEXT ) ) 129 else 144 130 { 145 data = this._.walker.reverse( guardDomWalkerNonEmptyTextNode ); 131 currentTextNode = null; 132 // At the end of the text node, walking foward for the next. 133 while ( !currentTextNode ) 134 { 135 currentTextNode = 136 this._.walker[ rtl ? 'previous' : 'next' ].call( this._.walker ); 146 137 147 // Block boundary? BR? Document boundary? 148 if ( !data.node || ( data.node.type !== CKEDITOR.NODE_TEXT && 149 data.node.getName() in characterBoundaryElementsEnum ) ) 150 this._.matchBoundary = true; 138 // Stop searching if we're need full word match. 139 if ( this._.matchWord && !currentTextNode ) 140 break; 141 // Marking as match boundaries. 142 if( currentTextNode === false 143 && checkCharactersBoundary( this._.walker.current ) ) 144 this._.matchBoundary = true; 145 146 // Already reach document end. 147 if ( this._.walker._.end ) 148 break; 149 } 150 // Found a fresh text node. 151 this.textNode = currentTextNode; 152 if ( currentTextNode ) 153 this.offset = rtl ? currentTextNode.getLength() - 1 : 0; 154 else 155 this.offset = 0; 151 156 } 152 this.textNode = data.node; 153 this.offset = data.node.length - 1; 157 154 158 return cursorStep.call( this ); 155 159 } 160 156 161 }; 157 162 158 163 /** … … 218 223 // node. 219 224 if ( endNode.getLength() < 1 ) 220 225 { 221 while ( ( endNode = endNode.getPreviousSourceNode() ) && !( endNode.type == CKEDITOR.NODE_TEXT && endNode.getLength() > 0 ) ) 226 while ( ( endNode = endNode.getPreviousSourceNode() ) 227 && !( endNode.type == CKEDITOR.NODE_TEXT 228 && endNode.getLength() > 0 ) ) 222 229 { /*jsl:pass*/ } 223 230 224 231 endIndex = endNode.getLength(); … … 224 231 endIndex = endNode.getLength(); 225 232 } 226 233 227 var cursor = new characterWalker( startNode, startIndex ); 228 this._.cursors = [ cursor ]; 229 if ( !( cursor.textNode.equals( endNode ) && cursor.offset == endIndex - 1 ) ) 234 // Rebuild the character range. 235 var cursor, 236 walker = new characterWalker( 237 getRangeAfterCursor( 238 ( { textNode: startNode, offset : startIndex } ), 239 true ) ); 240 this._.cursors = []; 241 do 230 242 { 231 do 232 { 233 cursor = new characterWalker( cursor ); 234 cursor.next(); 235 this._.cursors.push( cursor ); 236 } 237 while ( !( cursor.textNode.equals( endNode ) && cursor.offset == endIndex - 1 ) ); 243 cursor = walker.next(); 244 this._.cursors.push( cursor ); 238 245 } 239 246 while ( !( cursor.textNode.equals( endNode ) 247 && cursor.offset == endIndex - 1 ) ); 240 248 this._.rangeLength = this._.cursors.length; 241 249 }, 242 250 … … 338 346 return cursors[ cursors.length - 1 ].character; 339 347 }, 340 348 341 getNext Range : function( maxLength )349 getNextCharacterRange : function( maxLength ) 342 350 { 343 var cursors = this._.cursors; 344 if ( cursors.length < 1 ) 351 var lastCursor, 352 cursors = this._.cursors; 353 if ( !( lastCursor = cursors[ cursors.length - 1 ] ) ) 345 354 return null; 346 347 var next = new characterWalker( cursors[ cursors.length - 1 ] ); 348 return new characterRange( next, maxLength ); 355 return new characterRange( 356 new characterWalker( 357 getRangeAfterCursor( lastCursor ) ), 358 maxLength ); 349 359 }, 350 360 351 361 getCursors : function() 352 362 { 353 363 return this._.cursors; … … 354 364 } 355 365 }; 356 366 367 368 // The remaining document range after the character cursor. 369 function getRangeAfterCursor( cursor , inclusive ) 370 { 371 var range = new CKEDITOR.dom.range(); 372 range.setStart( cursor.textNode, 373 ( inclusive ? cursor.offset : cursor.offset + 1 ) ); 374 range.setEndAt( editor.document.getBody(), 375 CKEDITOR.POSITION_BEFORE_END ); 376 return range; 377 } 378 379 // The document range before the character cursor. 380 function getRangeBeforeCursor( cursor ) 381 { 382 var range = new CKEDITOR.dom.range(); 383 range.setStartAt( editor.document.getBody(), 384 CKEDITOR.POSITION_AFTER_START ); 385 range.setEnd( cursor.textNode, cursor.offset ); 386 return range; 387 } 388 357 389 var KMP_NOMATCH = 0, 358 390 KMP_ADVANCED = 1, 359 391 KMP_MATCHED = 2; … … 430 462 }; 431 463 432 464 var finder = { 433 s tartCursor : null,434 range : null,465 searchRange : null, 466 matchRange : null, 435 467 find : function( pattern, matchCase, matchWord, matchCyclic ) 436 468 { 437 if( !this.range ) 438 this.range = new characterRange( new characterWalker( this.startCursor ), pattern.length ); 469 if( !this.matchRange ) 470 this.matchRange = 471 new characterRange( 472 new characterWalker( this.searchRange ), 473 pattern.length ); 439 474 else 440 475 { 441 this. range.removeHighlight();442 this. range = this.range.getNextRange( pattern.length );476 this.matchRange.removeHighlight(); 477 this.matchRange = this.matchRange.getNextCharacterRange( pattern.length ); 443 478 } 444 479 445 480 var matcher = new kmpMatcher( pattern, !matchCase ), … … 448 483 449 484 while ( character !== null ) 450 485 { 451 this. range.moveNext();452 while ( ( character = this. range.getEndCharacter() ) )486 this.matchRange.moveNext(); 487 while ( ( character = this.matchRange.getEndCharacter() ) ) 453 488 { 454 489 matchState = matcher.feedCharacter( character ); 455 490 if ( matchState == KMP_MATCHED ) … … 454 489 matchState = matcher.feedCharacter( character ); 455 490 if ( matchState == KMP_MATCHED ) 456 491 break; 457 if ( this. range.moveNext().hitMatchBoundary )492 if ( this.matchRange.moveNext().hitMatchBoundary ) 458 493 matcher.reset(); 459 494 } 460 495 … … 462 497 { 463 498 if ( matchWord ) 464 499 { 465 var cursors = this. range.getCursors(),500 var cursors = this.matchRange.getCursors(), 466 501 tail = cursors[ cursors.length - 1 ], 467 head = cursors[ 0 ], 468 headWalker = new characterWalker( head ), 469 tailWalker = new characterWalker( tail ); 470 502 head = cursors[ 0 ]; 503 headWalker = 504 new characterWalker( 505 getRangeBeforeCursor( head ), 506 true ), 507 tailWalker = 508 new characterWalker( 509 getRangeAfterCursor( tail ), 510 true ); 471 511 if ( ! ( isWordSeparator( 472 512 headWalker.back().character ) 473 513 && isWordSeparator( … … 475 515 continue; 476 516 } 477 517 478 this. range.setMatched();518 this.matchRange.setMatched(); 479 519 return true; 480 520 } 481 521 } … … 480 520 } 481 521 } 482 522 483 this.range.clearMatched(); 484 485 // clear current session and restart from beginning 523 this.matchRange.clearMatched(); 524 this.matchRange.removeHighlight(); 525 // Clear current session and restart with the default search 526 // range. 486 527 if ( matchCyclic ) 487 528 { 488 this.s tartCursor = getDefaultStartCursor();489 this. range = null;529 this.searchRange = getSearchRange( true ); 530 this.matchRange = null; 490 531 } 491 532 492 533 return false; … … 530 571 } 531 572 532 573 /** 533 * Get cursor that indicate search begin with, receive from user574 * The range in which find/replace happened, receive from user 534 575 * selection prior. 535 576 */ 536 function getS tartCursor()577 function getSearchRange( isDefault ) 537 578 { 538 var sel = editor.getSelection(); 539 if ( sel ) 579 var searchRange, 580 sel = editor.getSelection(), 581 body = editor.document.getBody(); 582 if ( sel && !isDefault ) 540 583 { 541 var lastRange = sel.getRanges()[ sel.getRanges().length - 1 ]; 542 return { 543 textNode : lastRange.getBoundaryNodes().endNode, 544 offset : lastRange.endContainer.type === 545 CKEDITOR.NODE_ELEMENT ? 546 0 : lastRange.endOffset 547 }; 584 searchRange = sel.getRanges()[ 0 ].clone(); 585 searchRange.collapse( true ); 548 586 } 549 587 else 550 return getDefaultStartCursor(); 588 { 589 searchRange = new CKEDITOR.dom.range(); 590 searchRange.setStartAt( body, CKEDITOR.POSITION_AFTER_START ); 591 } 592 searchRange.setEndAt( body, CKEDITOR.POSITION_BEFORE_END ); 593 return searchRange; 551 594 } 552 595 553 596 return { … … 785 828 onShow : function() 786 829 { 787 830 // Establish initial searching start position. 788 finder.s tartCursor = getStartCursor.call( this);831 finder.searchRange = getSearchRange(); 789 832 790 833 if ( startupPage == 'replace' ) 791 834 this.getContentElement( 'replace', 'txtFindReplace' ).focus(); … … 794 837 }, 795 838 onHide : function() 796 839 { 797 if ( finder. range && finder.range.isMatched() )840 if ( finder.matchRange && finder.matchRange.isMatched() ) 798 841 { 799 finder. range.removeHighlight();842 finder.matchRange.removeHighlight(); 800 843 editor.focus(); 801 844 editor.getSelection().selectRanges( 802 [ finder. range.toDomRange() ] );845 [ finder.matchRange.toDomRange() ] ); 803 846 } 804 847 805 848 // Clear current session before dialog close 806 delete finder. range;849 delete finder.matchRange; 807 850 } 808 851 }; 809 852 }; -
_source/tests/core/dom/range.html
21 21 22 22 var doc = new CKEDITOR.dom.document( document ); 23 23 24 var getRange = function( startId, endId ) 25 { 26 var range = new CKEDITOR.dom.range( CKEDITOR.document ); 27 range.moveToBookmark( { startNode : startId, endNode : endId, serializable : true } ); 28 return range; 29 }; 30 24 31 return tests = { 25 32 test__constructor : function() 26 33 { … … 930 937 assert.isFalse( range.collapsed, 'range.collapsed' ); 931 938 }, 932 939 940 test_enlarge_list1 : function() 941 { 942 var range = getRange( 'S1', null ); 943 range.enlarge( CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS ); 944 945 assert.areSame( document.getElementById( '_EnlargeP7' ), range.startContainer.$, 'range.startContainer' ); 946 assert.areSame( 0, range.startOffset, 'range.startOffset' ); 947 assert.areSame( document.getElementById( '_EnlargeP7' ), range.endContainer.$, 'range.endContainer' ); 948 assert.areSame( 3, range.endOffset, 'range.endOffset' ); 949 assert.isFalse( range.collapsed, 'range.collapsed' ); 950 }, 951 952 test_enlarge_list2 : function() 953 { 954 var range = getRange( 'S2', 'E2' ); 955 range.enlarge( CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS ); 956 957 assert.areSame( document.getElementById( '_EnlargeP8' ), range.startContainer.$, 'range.startContainer' ); 958 assert.areSame( 0, range.startOffset, 'range.startOffset' ); 959 assert.areSame( document.getElementById( '_EnlargeP8' ), range.endContainer.$, 'range.endContainer' ); 960 assert.areSame( 4, range.endOffset, 'range.endOffset' ); 961 assert.isFalse( range.collapsed, 'range.collapsed' ); 962 }, 963 964 test_enlarge_list3 : function() 965 { 966 var range = getRange( 'S3', null ); 967 range.enlarge( CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS ); 968 969 assert.areSame( document.getElementById( '_EnlargeP9' ), range.startContainer.$, 'range.startContainer' ); 970 assert.areSame( 2, range.startOffset, 'range.startOffset' ); 971 assert.areSame( document.getElementById( '_EnlargeP9' ), range.endContainer.$, 'range.endContainer' ); 972 assert.areSame( 3, range.endOffset, 'range.endOffset' ); 973 assert.isFalse( range.collapsed, 'range.collapsed' ); 974 }, 975 976 test_enlarge_list4 : function() 977 { 978 var range = getRange( 'S4', null ); 979 range.enlarge( CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS ); 980 981 assert.areSame( document.getElementById( '_EnlargeP10' ), range.startContainer.$, 'range.startContainer' ); 982 assert.areSame( 3, range.startOffset, 'range.startOffset' ); 983 assert.areSame( document.getElementById( '_EnlargeP10' ), range.endContainer.$, 'range.endContainer' ); 984 assert.areSame( 5, range.endOffset, 'range.endOffset' ); 985 assert.isFalse( range.collapsed, 'range.collapsed' ); 986 }, 987 988 test_enlarge_list5 : function() 989 { 990 var range = getRange( 'S1', null ); 991 var bookmark = range.createBookmark(); 992 range.enlarge( CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS ); 993 994 assert.areSame( document.getElementById( '_EnlargeP7' ), range.startContainer.$, 'range.startContainer' ); 995 assert.areSame( 0, range.startOffset, 'range.startOffset' ); 996 assert.areSame( document.getElementById( '_EnlargeP7' ), range.endContainer.$, 'range.endContainer' ); 997 assert.areSame( 4, range.endOffset, 'range.endOffset' ); 998 assert.isFalse( range.collapsed, 'range.collapsed' ); 999 range.moveToBookmark( bookmark ); 1000 }, 1001 933 1002 test_deleteContents_W3C_1 : function() 934 1003 { 935 1004 // W3C DOM Range Specs - Section 2.6 - Example 1 … … 1823 1892 assert.isFalse( range.checkEndOfBlock() ); 1824 1893 }, 1825 1894 1895 test_checkEndOfBlock9 : function() 1896 { 1897 var range = getRange( 'S5', null ); 1898 assert.isFalse( range.checkEndOfBlock() ); 1899 }, 1900 1901 test_checkEndOfBlock10 : function() 1902 { 1903 var range = getRange( 'S6', null ); 1904 assert.isTrue( range.checkEndOfBlock() ); 1905 }, 1906 1907 test_checkEndOfBlock11 : function() 1908 { 1909 var range = getRange( 'S7', null ); 1910 assert.areSame( range.checkEndOfBlock(), !CKEDITOR.env.ie, 'range.checkBlock()' ); 1911 }, 1912 1913 test_checkEndOfBlock12 : function() 1914 { 1915 var range = getRange( 'S8', null ); 1916 assert.isFalse( range.checkEndOfBlock() ); 1917 }, 1918 1919 test_checkEndOfBlock13 : function() 1920 { 1921 var range = getRange( 'S9', null ); 1922 assert.isFalse( range.checkEndOfBlock() ); 1923 }, 1924 1826 1925 ///////////// 1827 1926 1828 1927 setUp : function() … … 1970 2069 <p id="_EnlargeP4">Test <i id="_EnlargeI4"> Enlarge</i></p> 1971 2070 <p id="_EnlargeP5">Test <i id="_EnlargeI5">Enlarge</i></p> 1972 2071 <p id="_EnlargeP6">Test <i id="_EnlargeI6"><b></b>Enlarge</i></p> 2072 <p id="_EnlargeP7">Test <span id="S1"></span>List<br/ >Item Enlarge</p> 2073 <p id="_EnlargeP8">Test <span id="S2"></span>List<span id="E2"></span> <br /><br />Item Enlarge</p> 2074 <p id="_EnlargeP9">Test List <br /><span id="S3"></span><br />Item Enlarge</p> 2075 <p id="_EnlargeP10">Test List <br /><br />Item<span id="S4"></span> Enlarge</p> 2076 <p>Test Check<span id="S5"></span><br />End</p> 2077 <p>Test Check<br />End<span id="S6"></span></p> 2078 <p>Test Check End<span id="S7"></span><br /></p> 2079 <p>Test Check End<span id="S8"></span><br /><br /></p> 2080 <p>Test Check<span id="S9"></span> End</p> 1973 2081 </div> 1974 2082 <script type="text/javascript"> 1975 2083 //<![CDATA[ -
_source/core/dom/range.js
263 263 // check(Start|End)OfBlock. 264 264 function getCheckStartEndBlockEvalFunction( isStart ) 265 265 { 266 var hadBr = false; 266 267 return function( node ) 267 268 { 268 var hadBr = false;269 270 269 if ( node.type == CKEDITOR.NODE_TEXT ) 271 270 { 272 271 // If there's any visible text, then we're not at the start. … … 279 278 // at the start. 280 279 if ( !inlineChildReqElements[ node.getName() ] ) 281 280 { 282 // If we're working at the end-of-block, forgive the first <br />. 283 if ( !isStart && node.getName() == 'br' && !hadBr ) 281 // If we're working at the end-of-block, forgive the first <br /> in non-IE 282 // browsers. 283 if ( !isStart && !CKEDITOR.env.ie && node.getName() == 'br' && !hadBr ) 284 284 hadBr = true; 285 285 else 286 286 return false; … … 1087 1087 1088 1088 case CKEDITOR.ENLARGE_BLOCK_CONTENTS: 1089 1089 case CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS: 1090 // DFS backward to get the block/list item boundary at or before the start.1091 1090 1092 // Get the boundaries nodes. 1093 var startNode = this.getTouchedStartNode(), 1094 endNode = this.getTouchedEndNode(); 1095 1096 if ( startNode.type == CKEDITOR.NODE_ELEMENT && startNode.isBlockBoundary() ) 1097 { 1098 this.setStartAt( startNode, 1099 CKEDITOR.dtd.$empty[ startNode.getName() ] ? 1100 CKEDITOR.POSITION_AFTER_END : 1101 CKEDITOR.POSITION_AFTER_START ); 1102 } 1103 else 1104 { 1105 // Get the function used to check the enlaarging limits. 1106 var guardFunction = ( unit == CKEDITOR.ENLARGE_BLOCK_CONTENTS ? 1107 CKEDITOR.dom.domWalker.blockBoundary() : 1108 CKEDITOR.dom.domWalker.listItemBoundary() ); 1109 1110 // Create the DOM walker, which will traverse the DOM. 1111 var walker = new CKEDITOR.dom.domWalker( startNode ); 1112 1113 // Go walk in reverse sense. 1114 var data = walker.reverse( guardFunction ); 1115 1116 var boundaryEvent = data.events.shift(); 1091 // Enlarging the start boundary. 1092 var walkerRange = new CKEDITOR.dom.range( this.document ); 1093 walkerRange.setStartAt( 1094 this.document.getBody(), CKEDITOR.POSITION_AFTER_START ); 1095 walkerRange.setEnd( this.startContainer, this.startOffset ); 1096 var walker = new CKEDITOR.dom.walker( walkerRange ), 1097 1098 guard = CKEDITOR.dom.walker.blockBoundary( 1099 ( unit == CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS ) ? { br : 1 } : null ), 1100 tailBr, 1101 listGuard = function( node ) 1102 { 1103 var result = guard( node ); 1104 if ( !result && node.is && node.is( 'br' ) ) 1105 tailBr = node; 1106 return result; 1107 }, 1108 enlargeable; 1109 walker.guard = guard; 1110 1111 if ( ( enlargeable = walker.lastBackward() ) ) 1112 this.setStartAt( 1113 enlargeable, CKEDITOR.POSITION_BEFORE_START ); 1117 1114 1118 this.setStartBefore( boundaryEvent.from ); 1119 } 1120 1121 if ( endNode.type == CKEDITOR.NODE_ELEMENT && endNode.isBlockBoundary() ) 1122 { 1123 this.setEndAt( endNode, 1124 CKEDITOR.dtd.$empty[ endNode.getName() ] ? 1125 CKEDITOR.POSITION_BEFORE_START : 1126 CKEDITOR.POSITION_BEFORE_END ); 1127 } 1128 else 1129 { 1130 // DFS forward to get the block/list item boundary at or before the end. 1131 walker.setNode( endNode ); 1132 data = walker.forward( guardFunction ); 1133 boundaryEvent = data.events.shift(); 1134 1135 this.setEndAfter( boundaryEvent.from ); 1136 } 1115 // Enlarging the end boundary. 1116 walkerRange = this.clone(); 1117 walkerRange.collapse(); 1118 walkerRange.setEndAt( 1119 this.document.getBody(), CKEDITOR.POSITION_BEFORE_END ); 1120 walker = new CKEDITOR.dom.walker( walkerRange ); 1121 walker.guard = ( unit == CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS ) ? 1122 listGuard : guard; 1123 if ( ( enlargeable = walker.lastForward() ) ) 1124 this.setEndAfter( enlargeable ); 1125 // We must include the <br> at the end of range if there's 1126 // one and we're expanding list item contents 1127 if ( tailBr ) 1128 this.setEndAfter( tailBr ); 1137 1129 } 1138 1130 }, 1139 1131 … … 1518 1510 if ( this.collapsed || container.type != CKEDITOR.NODE_ELEMENT ) 1519 1511 return container ; 1520 1512 1521 return container.getChild [ this.endOffset - 1 ]|| container ;1513 return container.getChild( this.endOffset - 1 ) || container ; 1522 1514 } 1523 1515 }; 1524 1516 })(); -
_source/core/dom/walker.js
6 6 (function() 7 7 { 8 8 // This function is to be called under a "walker" instance scope. 9 function iterate( rtl, breakOnFalse )9 function iterate( rtl, forceBreakOnFalse ) 10 10 { 11 11 // Return null if we have reached the end. 12 12 if ( this._.end ) … … 17 17 guard, 18 18 userGuard = this.guard, 19 19 type = this.type, 20 breakOnFalse = forceBreakOnFalse || this._.breakOnFalse; 20 21 getSourceNodeFn = ( rtl ? 'getPreviousSourceNode' : 'getNextSourceNode' ); 21 22 22 23 // This is the first call. Initialize it. … … 134 135 return this.current = null; 135 136 } 136 137 137 //function iterateToLast( rtl )138 //{139 //var node, last = null;138 function iterateToLast( rtl ) 139 { 140 var node, last = null; 140 141 141 //while ( node = iterate.call( this, rtl ) )142 //last = node;142 while ( node = iterate.call( this, rtl ) ) 143 last = node; 143 144 144 //return last;145 //}145 return last; 146 } 146 147 147 148 CKEDITOR.dom.walker = CKEDITOR.tools.createClass( 148 149 { … … 177 178 * it's to be considered into the walk or not. If not provided, all 178 179 * matched nodes are considered good. 179 180 * If the function returns "false" the node is ignored. 180 * @name CKEDITOR. pluginDefinition.prototype.evaluator181 * @name CKEDITOR.dom.walker.prototype.evaluator 181 182 * @property 182 183 * @type Function 183 184 */ … … 189 190 * entering and exiting nodes, as well as for the matched nodes. 190 191 * If this function returns "false", the walking ends and no more 191 192 * nodes are evaluated. 192 * @name CKEDITOR. pluginDefinition.prototype.guard193 * @name CKEDITOR.dom.walker.prototype.guard 193 194 * @property 194 195 * @type Function 195 196 */ … … 194 195 * @type Function 195 196 */ 196 197 // this.guard = null; 197 198 199 /** 200 * Whether the iteration return 'false' when the specified 201 * {@link CKEDITOR.dom.walker.prototype.evaluator} failed. 202 *@name CKEDITOR.dom.walker.prototype.breakOnFalse 203 *@property 204 *@type Boolean 205 */ 206 // this.breakOnFalse = false; 207 198 208 /** @private */ 199 209 this._ = {}; 200 210 }, 201 211 // 202 212 // statics : 203 213 // { 204 214 // /* Creates a CKEDITOR.dom.walker instance to walk inside DOM boundaries set by nodes. … … 214 224 // createOnNodes : function( startNode, endNode, startInclusive, endInclusive ) 215 225 // { 216 226 // var range = new CKEDITOR.dom.range(); 217 // range.setStartAt( startNode, startInclusive ? CKEDITOR.POSITION_BEFORE_START : CKEDITOR.POSITION_AFTER_END ) ; 218 227 // if ( startNode ) 228 // range.setStartAt( startNode, startInclusive ? CKEDITOR.POSITION_BEFORE_START : CKEDITOR.POSITION_AFTER_END ) ; 229 // else 230 // range.setStartAt( startNode.getDocument().getBody(), CKEDITOR.POSITION_AFTER_START ) ; 231 // 219 232 // if ( endNode ) 220 233 // range.setEndAt( endNode, endInclusive ? CKEDITOR.POSITION_AFTER_END : CKEDITOR.POSITION_BEFORE_START ) ; 221 234 // else … … 220 233 // range.setEndAt( endNode, endInclusive ? CKEDITOR.POSITION_AFTER_END : CKEDITOR.POSITION_BEFORE_START ) ; 221 234 // else 222 235 // range.setEndAt( startNode.getDocument().getBody(), CKEDITOR.POSITION_BEFORE_END ) ; 223 236 // 224 237 // return new CKEDITOR.dom.walker( range ); 225 238 // } 226 239 // }, 227 240 // 228 241 proto : 229 242 { 230 243 /** … … 263 276 */ 264 277 checkForward : function() 265 278 { 266 return iterate.call( this, false, true ) !== false; 279 var last; 280 while ( ( last = iterate.call( this, false, true ) ) ) 281 {} 282 return last !== false; 267 283 }, 268 284 269 285 /** … … 273 289 */ 274 290 checkBackward : function() 275 291 { 276 return iterate.call( this, true, true ) !== false; 277 } 292 var last; 293 while ( ( last = iterate.call( this, true, true ) ) ) 294 {} 295 return last !== false; 296 }, 278 297 279 // The following features have been originally included in the implementation, 280 // but they are not used anywhere in the code, so they got commented out. 298 /** 299 * Executes a full walk forward (to the right), until no more nodes 300 * are available, returning the last valid node. 301 * @returns {CKEDITOR.dom.node} The last node at the right or null 302 * if no valid nodes are available. 303 */ 304 lastForward : function() 305 { 306 return iterateToLast.call( this ); 307 }, 281 308 282 ///**283 // * Executes a full walk forward (to the right), until no more nodes284 //* are available, returning the last valid node.285 // * @returns {CKEDITOR.dom.node} The last node at the right or null286 //* if no valid nodes are available.287 //*/288 // lastForward : function()289 //{290 // return iterateToLast.call( this);291 // }, 309 /** 310 * Executes a full walk backwards (to the left), until no more nodes 311 * are available, returning the last valid node. 312 * @returns {CKEDITOR.dom.node} The last node at the left or null 313 * if no valid nodes are available. 314 */ 315 lastBackward : function() 316 { 317 return iterateToLast.call( this, true ); 318 } 292 319 293 // /**294 // * Executes a full walk backwards (to the left), until no more nodes295 // * are available, returning the last valid node.296 // * @returns {CKEDITOR.dom.node} The last node at the left or null297 // * if no valid nodes are available.298 // */299 // lastBackward : function()300 // {301 // return iterateToLast.call( this, true );302 // }303 320 } 304 321 }); 322 323 /* 324 * Anything whose display computed style is block, list-item, table, 325 * table-row-group, table-header-group, table-footer-group, table-row, 326 * table-column-group, table-column, table-cell, table-caption, or whose node 327 * name is hr, br (when enterMode is br only) is a block boundary. 328 */ 329 var blockBoundaryDisplayMatch = 330 { 331 block : 1, 332 'list-item' : 1, 333 table : 1, 334 'table-row-group' : 1, 335 'table-header-group' : 1, 336 'table-footer-group' : 1, 337 'table-row' : 1, 338 'table-column-group' : 1, 339 'table-column' : 1, 340 'table-cell' : 1, 341 'table-caption' : 1 342 }, 343 blockBoundaryNodeNameMatch = { hr : 1 }; 344 345 CKEDITOR.dom.element.prototype.isBlockBoundary = function( customNodeNames ) 346 { 347 var nodeNameMatches = CKEDITOR.tools.extend( 348 blockBoundaryNodeNameMatch, customNodeNames || {} ); 349 350 return blockBoundaryDisplayMatch[ this.getComputedStyle( 'display' ) ] || 351 nodeNameMatches[ this.getName() ]; 352 }; 353 354 CKEDITOR.dom.walker.blockBoundary = function( customNodeNames ) 355 { 356 return function( node , type ) 357 { 358 return ! ( node.type == CKEDITOR.NODE_ELEMENT 359 && node.isBlockBoundary( customNodeNames ) ); 360 }; 361 }; 362 363 CKEDITOR.dom.walker.listItemBoundary = function() 364 { 365 return this.blockBoundary( { br : 1 } ); 366 }; 367 305 368 })();