Ticket #3304: 3304_8.patch
File 3304_8.patch, 32.1 KB (added by , 15 years ago) |
---|
-
_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( 'S9', null ); 991 var bookmark = range.createBookmark(); 992 range.enlarge( CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS ); 993 994 assert.areSame( document.getElementById( '_EnlargeP15' ), range.startContainer.$, 'range.startContainer' ); 995 assert.areSame( 0, range.startOffset, 'range.startOffset' ); 996 assert.areSame( document.getElementById( '_EnlargeP15' ), 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 1002 test_enlarge_block1 : function() 1003 { 1004 var range = getRange( 'S5', null ); 1005 range.enlarge( CKEDITOR.ENLARGE_BLOCK_CONTENTS ); 1006 1007 assert.areSame( document.getElementById( '_EnlargeP11' ), range.startContainer.$, 'range.startContainer' ); 1008 assert.areSame( 0, range.startOffset, 'range.startOffset' ); 1009 assert.areSame( document.getElementById( '_EnlargeP11'), range.endContainer.$, 'range.endContainer' ); 1010 assert.areSame( 5, range.endOffset, 'range.endOffset' ); 1011 assert.isFalse( range.collapsed, 'range.collapsed' ); 1012 }, 1013 1014 test_enlarge_block2 : function() 1015 { 1016 var range = getRange( 'S10', null ); 1017 var bookmark = range.createBookmark(); 1018 range.enlarge( CKEDITOR.ENLARGE_BLOCK_CONTENTS ); 1019 1020 assert.areSame( document.getElementById( '_EnlargeP16' ), range.startContainer.$, 'range.startContainer' ); 1021 assert.areSame( 0, range.startOffset, 'range.startOffset' ); 1022 assert.areSame( document.getElementById( '_EnlargeP16'), range.endContainer.$, 'range.endContainer' ); 1023 assert.areSame( 5, range.endOffset, 'range.endOffset' ); 1024 assert.isFalse( range.collapsed, 'range.collapsed' ); 1025 range.moveToBookmark( bookmark ); 1026 }, 1027 1028 test_enlarge_block3 : function() 1029 { 1030 var range = getRange( 'S6', null ); 1031 range.enlarge( CKEDITOR.ENLARGE_BLOCK_CONTENTS ); 1032 1033 assert.areSame( document.getElementById( '_EnlargeP12' ), range.startContainer.$, 'range.startContainer' ); 1034 assert.areSame( 0, range.startOffset, 'range.startOffset' ); 1035 assert.areSame( document.getElementById( '_EnlargeP12'), range.endContainer.$, 'range.endContainer' ); 1036 assert.areSame( 2, range.endOffset, 'range.endOffset' ); 1037 assert.isFalse( range.collapsed, 'range.collapsed' ); 1038 }, 1039 1040 test_enlarge_block4 : function() 1041 { 1042 var range = getRange( 'S7', null ); 1043 range.enlarge( CKEDITOR.ENLARGE_BLOCK_CONTENTS ); 1044 1045 assert.areSame( document.getElementById( '_EnlargeP13' ), range.startContainer.$, 'range.startContainer' ); 1046 assert.areSame( 0, range.startOffset, 'range.startOffset' ); 1047 assert.areSame( document.getElementById( '_EnlargeP13'), range.endContainer.$, 'range.endContainer' ); 1048 assert.areSame( 2, range.endOffset, 'range.endOffset' ); 1049 assert.isFalse( range.collapsed, 'range.collapsed' ); 1050 }, 1051 1052 test_enlarge_block5 : function() 1053 { 1054 var range = getRange( 'S8', null ); 1055 range.enlarge( CKEDITOR.ENLARGE_BLOCK_CONTENTS ); 1056 1057 assert.areSame( document.getElementById( '_EnlargeP14' ), range.startContainer.$, 'range.startContainer' ); 1058 assert.areSame( 0, range.startOffset, 'range.startOffset' ); 1059 assert.isTrue( range.collapsed, 'range.collapsed' ); 1060 }, 1061 933 1062 test_deleteContents_W3C_1 : function() 934 1063 { 935 1064 // W3C DOM Range Specs - Section 2.6 - Example 1 … … 1970 2099 <p id="_EnlargeP4">Test <i id="_EnlargeI4"> Enlarge</i></p> 1971 2100 <p id="_EnlargeP5">Test <i id="_EnlargeI5">Enlarge</i></p> 1972 2101 <p id="_EnlargeP6">Test <i id="_EnlargeI6"><b></b>Enlarge</i></p> 2102 <p id="_EnlargeP7">Test <span id="S1"></span>List<br/ >Item Enlarge</p> 2103 <p id="_EnlargeP8">Test <span id="S2"></span>List<span id="E2"></span> <br /><br />Item Enlarge</p> 2104 <p id="_EnlargeP9">Test List <br /><span id="S3"></span><br />Item Enlarge</p> 2105 <p id="_EnlargeP10">Test List <br /><br />Item<span id="S4"></span> Enlarge</p> 2106 <p id="_EnlargeP11">Test <strong>Block<span id="S5"></span></strong><br /><br />Enlarge</p> 2107 <div id="_EnlargeP12">Test<span id="S6"></span> Block <div>Enlarge</div></div> 2108 <div>Test <div id="_EnlargeP13">Blo<span id="S7"></span>ck</div> Enlarge</div> 2109 <p id="_EnlargeP14"><span id="S8"></span></p> 2110 <p id="_EnlargeP15">Test <span id="S9"></span>List<br/ >Item Enlarge</p> 2111 <p id="_EnlargeP16">Test <strong>Block<span id="S10"></span></strong><br /><br />Enlarge</p> 1973 2112 </div> 1974 2113 <script type="text/javascript"> 1975 2114 //<![CDATA[ -
_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 and 100 // raise an event. 101 if( this.textNode.type == CKEDITOR.NODE_TEXT 102 && 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 ) 119 { 120 this.offset--; 121 return cursorStep.call( this ); 122 } 123 else if( currentTextNode 124 && this.offset < currentTextNode.getLength() - 1 ) 103 125 { 104 126 this.offset++; 105 127 return cursorStep.call( this ); … … 104 126 this.offset++; 105 127 return cursorStep.call( this ); 106 128 } 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 ) ) 129 else 113 130 { 114 data = this._.walker.forward( 115 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 ); 116 137 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 }, 138 // Stop searching if we're need full word match OR 139 // already reach document end. 140 if ( this._.matchWord && !currentTextNode 141 ||this._.walker._.end ) 142 break; 128 143 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--; 137 return cursorStep.call( this ); 144 // Marking as match character boundaries. 145 if( !currentTextNode 146 && checkCharactersBoundary( this._.walker.current ) ) 147 this._.matchBoundary = true; 148 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; 138 156 } 139 157 140 // Start of text node -> use dom walker to get the previous text node.141 var data = null;142 while ( !data143 || ( data.node && data.node.type != CKEDITOR.NODE_TEXT ) )144 {145 data = this._.walker.reverse( guardDomWalkerNonEmptyTextNode );146 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;151 }152 this.textNode = data.node;153 this.offset = data.node.length - 1;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/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
101 101 node = null; 102 102 } 103 103 else 104 node = node.getPreviousSourceNode( true, type, guard ); 104 node = ( guard ( node ) === false ) ? 105 null : node.getPreviousSourceNode( true, type, guard ); 105 106 } 106 107 else 107 108 { … … 114 115 node = null; 115 116 } 116 117 else 117 node = range.startContainer.getNextSourceNode( true, type, guard ); 118 node = ( guard ( range.startContainer ) === false ) ? 119 null : range.startContainer.getNextSourceNode( true, type, guard ) ; 118 120 } 119 121 } 120 122 … … 123 125 this.current = node; 124 126 125 127 if ( !this.evaluator || this.evaluator( node ) !== false ) 126 return node; 128 { 129 if ( !breakOnFalse ) 130 return node; 131 } 127 132 else if ( breakOnFalse && this.evaluator ) 128 133 return false; 129 134 … … 134 139 return this.current = null; 135 140 } 136 141 137 //function iterateToLast( rtl )138 //{139 //var node, last = null;142 function iterateToLast( rtl ) 143 { 144 var node, last = null; 140 145 141 //while ( node = iterate.call( this, rtl ) )142 //last = node;146 while ( node = iterate.call( this, rtl ) ) 147 last = node; 143 148 144 //return last;145 //}149 return last; 150 } 146 151 147 152 CKEDITOR.dom.walker = CKEDITOR.tools.createClass( 148 153 { … … 177 182 * it's to be considered into the walk or not. If not provided, all 178 183 * matched nodes are considered good. 179 184 * If the function returns "false" the node is ignored. 180 * @name CKEDITOR. pluginDefinition.prototype.evaluator185 * @name CKEDITOR.dom.walker.prototype.evaluator 181 186 * @property 182 187 * @type Function 183 188 */ … … 189 194 * entering and exiting nodes, as well as for the matched nodes. 190 195 * If this function returns "false", the walking ends and no more 191 196 * nodes are evaluated. 192 * @name CKEDITOR. pluginDefinition.prototype.guard197 * @name CKEDITOR.dom.walker.prototype.guard 193 198 * @property 194 199 * @type Function 195 200 */ … … 214 219 // createOnNodes : function( startNode, endNode, startInclusive, endInclusive ) 215 220 // { 216 221 // var range = new CKEDITOR.dom.range(); 217 // range.setStartAt( startNode, startInclusive ? CKEDITOR.POSITION_BEFORE_START : CKEDITOR.POSITION_AFTER_END ) ; 218 222 // if ( startNode ) 223 // range.setStartAt( startNode, startInclusive ? CKEDITOR.POSITION_BEFORE_START : CKEDITOR.POSITION_AFTER_END ) ; 224 // else 225 // range.setStartAt( startNode.getDocument().getBody(), CKEDITOR.POSITION_AFTER_START ) ; 226 // 219 227 // if ( endNode ) 220 228 // range.setEndAt( endNode, endInclusive ? CKEDITOR.POSITION_AFTER_END : CKEDITOR.POSITION_BEFORE_START ) ; 221 229 // else … … 220 228 // range.setEndAt( endNode, endInclusive ? CKEDITOR.POSITION_AFTER_END : CKEDITOR.POSITION_BEFORE_START ) ; 221 229 // else 222 230 // range.setEndAt( startNode.getDocument().getBody(), CKEDITOR.POSITION_BEFORE_END ) ; 223 231 // 224 232 // return new CKEDITOR.dom.walker( range ); 225 233 // } 226 234 // }, 227 235 // 228 236 proto : 229 237 { 230 238 /** … … 274 282 checkBackward : function() 275 283 { 276 284 return iterate.call( this, true, true ) !== false; 277 } 285 }, 278 286 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. 287 /** 288 * Executes a full walk forward (to the right), until no more nodes 289 * are available, returning the last valid node. 290 * @returns {CKEDITOR.dom.node} The last node at the right or null 291 * if no valid nodes are available. 292 */ 293 lastForward : function() 294 { 295 return iterateToLast.call( this ); 296 }, 281 297 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 // }, 298 /** 299 * Executes a full walk backwards (to the left), until no more nodes 300 * are available, returning the last valid node. 301 * @returns {CKEDITOR.dom.node} The last node at the left or null 302 * if no valid nodes are available. 303 */ 304 lastBackward : function() 305 { 306 return iterateToLast.call( this, true ); 307 } 292 308 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 309 } 304 310 }); 311 312 /* 313 * Anything whose display computed style is block, list-item, table, 314 * table-row-group, table-header-group, table-footer-group, table-row, 315 * table-column-group, table-column, table-cell, table-caption, or whose node 316 * name is hr, br (when enterMode is br only) is a block boundary. 317 */ 318 var blockBoundaryDisplayMatch = 319 { 320 block : 1, 321 'list-item' : 1, 322 table : 1, 323 'table-row-group' : 1, 324 'table-header-group' : 1, 325 'table-footer-group' : 1, 326 'table-row' : 1, 327 'table-column-group' : 1, 328 'table-column' : 1, 329 'table-cell' : 1, 330 'table-caption' : 1 331 }, 332 blockBoundaryNodeNameMatch = { hr : 1 }; 333 334 CKEDITOR.dom.element.prototype.isBlockBoundary = function( customNodeNames ) 335 { 336 var nodeNameMatches = CKEDITOR.tools.extend( {}, 337 blockBoundaryNodeNameMatch, customNodeNames || {} ); 338 339 return blockBoundaryDisplayMatch[ this.getComputedStyle( 'display' ) ] || 340 nodeNameMatches[ this.getName() ]; 341 }; 342 343 CKEDITOR.dom.walker.blockBoundary = function( customNodeNames ) 344 { 345 return function( node , type ) 346 { 347 return ! ( node.type == CKEDITOR.NODE_ELEMENT 348 && node.isBlockBoundary( customNodeNames ) ); 349 }; 350 }; 351 352 CKEDITOR.dom.walker.listItemBoundary = function() 353 { 354 return this.blockBoundary( { br : 1 } ); 355 }; 356 305 357 })();