Changeset 6495
- Timestamp:
- 02/25/11 14:03:28 (2 years ago)
- Location:
- CKEditor/branches/features/v4-domiterator
- Files:
-
- 3 edited
- 1 copied
-
. (copied) (copied from CKEditor/trunk)
-
_source/core/dom/range.js (modified) (6 diffs)
-
_source/core/tools.js (modified) (1 diff)
-
_source/plugins/domiterator/plugin.js (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
CKEditor/branches/features/v4-domiterator/_source/core/dom/range.js
r6461 r6495 1272 1272 case CKEDITOR.ENLARGE_BLOCK_CONTENTS: 1273 1273 case CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS: 1274 case CKEDITOR.ENLARGE_BLOCKS: 1275 case CKEDITOR.ENLARGE_LIST_ITEMS: 1274 1276 1275 1277 // Enlarging the start boundary. … … 1282 1284 1283 1285 var walker = new CKEDITOR.dom.walker( walkerRange ), 1286 toSkip = CKEDITOR.dom.walker.whitespaces( 1 ), 1284 1287 blockBoundary, // The node on which the enlarging should stop. 1285 1288 tailBr, // In case BR as block boundary. 1286 1289 notBlockBoundary = CKEDITOR.dom.walker.blockBoundary( 1287 ( unit == CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS ) ? { br : 1 } : null ),1290 ( unit == CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS || unit == CKEDITOR.ENLARGE_LIST_ITEMS ) ? { br : 1 } : null ), 1288 1291 // Record the encountered 'blockBoundary' for later use. 1289 1292 boundaryGuard = function( node ) … … 1313 1316 // or at the start of block (<p>[text...), by comparing the document position 1314 1317 // with 'enlargeable' node. 1315 this.setStartAt( 1316 blockBoundary, 1317 !blockBoundary.is( 'br' ) && 1318 var fromInside = !blockBoundary.is( 'br' ) && 1318 1319 ( !enlargeable && this.checkStartOfBlock() 1319 || enlargeable && blockBoundary.contains( enlargeable ) ) ? 1320 CKEDITOR.POSITION_AFTER_START : 1321 CKEDITOR.POSITION_AFTER_END ); 1320 || enlargeable && blockBoundary.contains( enlargeable ) ); 1321 1322 if ( ( unit == CKEDITOR.ENLARGE_BLOCKS || unit == CKEDITOR.ENLARGE_LIST_ITEMS ) && fromInside ) 1323 { 1324 while ( ! ( blockBoundary.is( 'body' ) || blockBoundary.getPrevious( toSkip ) ) ) 1325 blockBoundary = blockBoundary.getParent(); 1326 this.setStartBefore( blockBoundary ); 1327 } 1328 else 1329 { 1330 this.setStartAt( blockBoundary, fromInside ? 1331 CKEDITOR.POSITION_AFTER_START : 1332 CKEDITOR.POSITION_AFTER_END ); 1333 } 1322 1334 1323 1335 // Enlarging the end boundary. … … 1328 1340 1329 1341 // tailBrGuard only used for on range end. 1330 walker.guard = ( unit == CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS ) ?1342 walker.guard = ( unit == CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS || unit == CKEDITOR.ENLARGE_LIST_ITEMS ) ? 1331 1343 tailBrGuard : boundaryGuard; 1332 1344 blockBoundary = null; … … 1340 1352 // Close the range either before the found block start (text]<p>...</p>) or at the block end (...text]</p>) 1341 1353 // by comparing the document position with 'enlargeable' node. 1342 this.setEndAt( 1343 blockBoundary, 1344 ( !enlargeable && this.checkEndOfBlock() 1345 || enlargeable && blockBoundary.contains( enlargeable ) ) ? 1346 CKEDITOR.POSITION_BEFORE_END : 1347 CKEDITOR.POSITION_BEFORE_START ); 1354 fromInside = ( !enlargeable && this.checkEndOfBlock() 1355 || enlargeable && blockBoundary.contains( enlargeable ) ); 1356 1357 if ( ( unit == CKEDITOR.ENLARGE_BLOCKS || unit == CKEDITOR.ENLARGE_LIST_ITEMS ) && fromInside ) 1358 { 1359 while ( ! ( blockBoundary.is( 'body' ) || blockBoundary.getNext( toSkip ) ) ) 1360 blockBoundary = blockBoundary.getParent(); 1361 this.setEndAfter( blockBoundary ); 1362 } 1363 else 1364 { 1365 this.setEndAt( 1366 blockBoundary, fromInside ? 1367 CKEDITOR.POSITION_BEFORE_END : 1368 CKEDITOR.POSITION_BEFORE_START ); 1369 } 1370 1348 1371 // We must include the <br> at the end of range if there's 1349 1372 // one and we're expanding list item contents … … 2020 2043 CKEDITOR.ENLARGE_BLOCK_CONTENTS = 2; 2021 2044 CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS = 3; 2045 CKEDITOR.ENLARGE_BLOCKS = 4; 2046 CKEDITOR.ENLARGE_LIST_ITEMS = 5; 2022 2047 2023 2048 // Check boundary types. -
CKEditor/branches/features/v4-domiterator/_source/core/tools.js
r6348 r6495 221 221 }, 222 222 223 convertToList : function( list ) 224 { 225 var array; 226 if ( CKEDITOR.tools.isArray( list ) ) 227 { 228 array = list; 229 list = {}; 230 for ( var i = 0; i < array.length; i++ ) 231 list[ array[ i ] ] = 1; 232 } 233 return list; 234 }, 235 223 236 /** 224 237 * Whether the object contains no properties of it's own. -
CKEditor/branches/features/v4-domiterator/_source/plugins/domiterator/plugin.js
r6368 r6495 15 15 * @name CKEDITOR.dom.iterator 16 16 */ 17 function iterator( range )17 function iterator( range, blocklist ) 18 18 { 19 if ( arguments.length < 1 ) 20 return; 19 this.range = range; 20 this.enlargeBr = 1; 21 blocklist = CKEDITOR.tools.convertToList( blocklist ) || {}; 21 22 22 this.range = range;23 this.forceBrBreak = 0;24 23 25 // Whether include <br>s into the enlarged range.(#3730). 26 this.enlargeBr = 1; 27 this.enforceRealBlocks = 0; 24 var doc = range.document, 25 walker, 26 current, 27 insidePre; // pre-formatted context indicator. 28 28 29 this._ || ( this._ = {} ); 30 } 29 this.getNextParagraph = function( fixBlockTag ) 30 { 31 if ( !walker ) 32 { 33 var walkerRange = range.clone(); 34 walkerRange.enlarge( this.enlargeBr ? CKEDITOR.ENLARGE_BLOCKS : CKEDITOR.ENLARGE_LIST_ITEMS ); 35 walkerRange.trim(); 31 36 32 var beginWhitespaceRegex = /^[\r\n\t ]+$/, 33 // Ignore bookmark nodes.(#3783) 34 bookmarkGuard = CKEDITOR.dom.walker.bookmark( false, true ); 37 walker = new CKEDITOR.dom.walker( walkerRange ); 35 38 36 // Get a reference for the next element, bookmark nodes are skipped. 37 function getNextSourceNode( node, startFromSibling, lastNode ) 38 { 39 var next = node.getNextSourceNode( startFromSibling, null, lastNode ); 40 while ( !bookmarkGuard( next ) ) 41 next = next.getNextSourceNode( startFromSibling, null, lastNode ); 42 return next; 43 } 39 var blocker = walkerRange.endContainer.getChild( walkerRange.endOffset - 1 ) || walkerRange.endContainer.getPreviousSourceNode( 1 ); 40 walker.evaluator = function( node ) 41 { 42 if ( nonBookmark( node ) && nonWhitespaces( node ) ) 43 { 44 var position = node.getPosition( blocker ); 44 45 45 iterator.prototype = { 46 getNextParagraph : function( blockTag ) 47 { 48 // The block element to be returned. 49 var block; 46 // Considers only the nodes that are within the range. 47 return ! ( position && !( ( position & CKEDITOR.POSITION_PRECEDING + CKEDITOR.POSITION_IS_CONTAINED ) 48 && position ^ CKEDITOR.POSITION_PRECEDING + CKEDITOR.POSITION_CONTAINS ) ); 49 } 50 else 51 return false; 52 }; 50 53 51 // The range object used to identify the paragraph contents. 52 var range; 54 walker.guard = function( node, walkOut ) 55 { 56 if ( node.type == CKEDITOR.NODE_ELEMENT && node.is( 'pre' ) ) 57 insidePre = !walkOut; 58 }; 59 } 53 60 54 // Indicats that the current element in the loop is the last one. 55 var isLast; 61 current = current == undefined ? walker.next() : current; 56 62 57 // Indicate at least one of the range boundaries is inside a preformat block. 58 var touchPre; 63 if ( !current ) 64 { 65 walker.reset(); 66 return null; 67 } 59 68 60 // Instructs to cleanup remaining BRs. 61 var removePreviousBr, removeLastBr; 69 var path, block, checkRange, brToRemove, fixedBlock, lastChild; 70 do 71 { 72 // Block encountered. 73 if ( current.type == CKEDITOR.NODE_ELEMENT && current.isBlockBoundary( !this.enlargeBr && { br : 1 } ) ) 74 { 75 if ( current.getName() in blocklist ) 76 block = current; 77 } 78 // At the beginning inside of a block. 79 else 80 { 81 path = new CKEDITOR.dom.elementPath( current ); 82 block = path.block; 62 83 63 // This is the first iteration. Let's initialize it. 64 if ( !this._.lastNode ) 65 { 66 range = this.range.clone(); 84 // TODO: We should fix CKEDITOR.dom.elementPath instead. 67 85 68 // Shrink the range to exclude harmful "noises" (#4087, #4450, #5435). 69 range.shrink( CKEDITOR.NODE_ELEMENT, true ); 86 // Negate any shared (pseudo) blocks that contain more than one block. 87 // e.g. <li>^pseudo<p>paragraph</p></li> 88 if ( block ) 89 { 90 if ( block.getName() in pathBlockExclusion ) 91 block = null; 92 else 93 { 94 checkRange = new CKEDITOR.dom.range( doc ); 95 checkRange.setStartBefore( current ); 96 checkRange.enlarge( insidePre || this.enlargeBr ? 97 CKEDITOR.ENLARGE_BLOCK_CONTENTS : CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS ); 98 var atBlockStart = checkRange.checkStartOfBlock( block ), 99 atBlockEnd = checkRange.checkEndOfBlock( block ); 100 if ( ! ( atBlockStart && atBlockEnd ) ) 101 { 102 if ( !this.enlargeBr ) 103 { 104 fixedBlock = block.clone(); 105 brToRemove = brHandler( checkRange ); 106 checkRange.extractContents().appendTo( fixedBlock ); 107 if ( atBlockStart ) 108 fixedBlock.insertBefore( block ); 109 else if ( atBlockEnd ) 110 fixedBlock.insertAfter( block ); 111 else 112 fixedBlock.insertBefore( checkRange.splitBlock().nextBlock ); 113 brToRemove.remove(); 114 block = fixedBlock; 115 } 116 else 117 block = null; 118 } 119 } 120 } 70 121 71 touchPre = range.endContainer.hasAscendant( 'pre', true ) 72 || range.startContainer.hasAscendant( 'pre', true ); 73 74 range.enlarge( this.forceBrBreak && !touchPre || !this.enlargeBr ? 75 CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS : CKEDITOR.ENLARGE_BLOCK_CONTENTS ); 76 77 var walker = new CKEDITOR.dom.walker( range ), 78 ignoreBookmarkTextEvaluator = CKEDITOR.dom.walker.bookmark( true, true ); 79 // Avoid anchor inside bookmark inner text. 80 walker.evaluator = ignoreBookmarkTextEvaluator; 81 this._.nextNode = walker.next(); 82 // TODO: It's better to have walker.reset() used here. 83 walker = new CKEDITOR.dom.walker( range ); 84 walker.evaluator = ignoreBookmarkTextEvaluator; 85 var lastNode = walker.previous(); 86 this._.lastNode = lastNode.getNextSourceNode( true ); 87 88 // We may have an empty text node at the end of block due to [3770]. 89 // If that node is the lastNode, it would cause our logic to leak to the 90 // next block.(#3887) 91 if ( this._.lastNode && 92 this._.lastNode.type == CKEDITOR.NODE_TEXT && 93 !CKEDITOR.tools.trim( this._.lastNode.getText() ) && 94 this._.lastNode.getParent().isBlockBoundary() ) 95 { 96 var testRange = new CKEDITOR.dom.range( range.document ); 97 testRange.moveToPosition( this._.lastNode, CKEDITOR.POSITION_AFTER_END ); 98 if ( testRange.checkEndOfBlock() ) 122 if ( !block ) 99 123 { 100 var path = new CKEDITOR.dom.elementPath( testRange.endContainer ); 101 var lastBlock = path.block || path.blockLimit; 102 this._.lastNode = lastBlock.getNextSourceNode( true ); 124 checkRange = new CKEDITOR.dom.range( doc ); 125 checkRange.setStartBefore( current ); 126 if ( !this.enlargeBr ) 127 { 128 fixedBlock = doc.createElement( fixBlockTag || 'p' ); 129 checkRange.enlarge( CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS ); 130 brToRemove = brHandler( checkRange ); 131 checkRange.extractContents().appendTo( fixedBlock ); 132 checkRange.insertNode( fixedBlock ); 133 brToRemove.remove(); 134 block = fixedBlock; 135 } 136 else 137 { 138 block = checkRange.fixBlock( 1, fixBlockTag || 'p' ); 139 } 103 140 } 104 141 } 142 } 143 while ( !block && ( current = walker.next() ) ); 105 144 106 // Probably the document end is reached, we need a marker node. 107 if ( !this._.lastNode ) 108 { 109 this._.lastNode = this._.docEndMarker = range.document.createText( '' ); 110 this._.lastNode.insertAfter( lastNode ); 111 } 145 if ( block ) 146 { 147 // Get a reference for the next element. This is important because the 148 // above block can be removed or changed, so we can rely on it for the 149 // next interation. 112 150 113 // Let's reuse this variable.114 range = null;115 }151 current = block; 152 while ( current.type == CKEDITOR.NODE_ELEMENT && ( lastChild = current.getLast() ) ) 153 current = lastChild; 116 154 117 var currentNode = this._.nextNode; 118 lastNode = this._.lastNode; 119 120 this._.nextNode = null; 121 while ( currentNode ) 122 { 123 // closeRange indicates that a paragraph boundary has been found, 124 // so the range can be closed. 125 var closeRange = 0, 126 parentPre = currentNode.hasAscendant( 'pre' ); 127 128 // includeNode indicates that the current node is good to be part 129 // of the range. By default, any non-element node is ok for it. 130 var includeNode = ( currentNode.type != CKEDITOR.NODE_ELEMENT ), 131 continueFromSibling = 0; 132 133 // If it is an element node, let's check if it can be part of the 134 // range. 135 if ( !includeNode ) 136 { 137 var nodeName = currentNode.getName(); 138 139 if ( currentNode.isBlockBoundary( this.forceBrBreak && 140 !parentPre && { br : 1 } ) ) 141 { 142 // <br> boundaries must be part of the range. It will 143 // happen only if ForceBrBreak. 144 if ( nodeName == 'br' ) 145 includeNode = 1; 146 else if ( !range && !currentNode.getChildCount() && nodeName != 'hr' ) 147 { 148 // If we have found an empty block, and haven't started 149 // the range yet, it means we must return this block. 150 block = currentNode; 151 isLast = currentNode.equals( lastNode ); 152 break; 153 } 154 155 // The range must finish right before the boundary, 156 // including possibly skipped empty spaces. (#1603) 157 if ( range ) 158 { 159 range.setEndAt( currentNode, CKEDITOR.POSITION_BEFORE_START ); 160 161 // The found boundary must be set as the next one at this 162 // point. (#1717) 163 if ( nodeName != 'br' ) 164 this._.nextNode = currentNode; 165 } 166 167 closeRange = 1; 168 } 169 else 170 { 171 // If we have child nodes, let's check them. 172 if ( currentNode.getFirst() ) 173 { 174 // If we don't have a range yet, let's start it. 175 if ( !range ) 176 { 177 range = new CKEDITOR.dom.range( this.range.document ); 178 range.setStartAt( currentNode, CKEDITOR.POSITION_BEFORE_START ); 179 } 180 181 currentNode = currentNode.getFirst(); 182 continue; 183 } 184 includeNode = 1; 185 } 186 } 187 else if ( currentNode.type == CKEDITOR.NODE_TEXT ) 188 { 189 // Ignore normal whitespaces (i.e. not including or 190 // other unicode whitespaces) before/after a block node. 191 if ( beginWhitespaceRegex.test( currentNode.getText() ) ) 192 includeNode = 0; 193 } 194 195 // The current node is good to be part of the range and we are 196 // starting a new range, initialize it first. 197 if ( includeNode && !range ) 198 { 199 range = new CKEDITOR.dom.range( this.range.document ); 200 range.setStartAt( currentNode, CKEDITOR.POSITION_BEFORE_START ); 201 } 202 203 // The last node has been found. 204 isLast = ( ( !closeRange || includeNode ) && currentNode.equals( lastNode ) ); 205 206 // If we are in an element boundary, let's check if it is time 207 // to close the range, otherwise we include the parent within it. 208 if ( range && !closeRange ) 209 { 210 while ( !currentNode.getNext( bookmarkGuard ) && !isLast ) 211 { 212 var parentNode = currentNode.getParent(); 213 214 if ( parentNode.isBlockBoundary( this.forceBrBreak 215 && !parentPre && { br : 1 } ) ) 216 { 217 closeRange = 1; 218 isLast = isLast || ( parentNode.equals( lastNode) ); 219 break; 220 } 221 222 currentNode = parentNode; 223 includeNode = 1; 224 isLast = ( currentNode.equals( lastNode ) ); 225 continueFromSibling = 1; 226 } 227 } 228 229 // Now finally include the node. 230 if ( includeNode ) 231 range.setEndAt( currentNode, CKEDITOR.POSITION_AFTER_END ); 232 233 currentNode = getNextSourceNode ( currentNode, continueFromSibling, lastNode ); 234 isLast = !currentNode; 235 236 // We have found a block boundary. Let's close the range and move out of the 237 // loop. 238 if ( isLast || ( closeRange && range ) ) 239 break; 240 } 241 242 // Now, based on the processed range, look for (or create) the block to be returned. 243 if ( !block ) 244 { 245 // If no range has been found, this is the end. 246 if ( !range ) 247 { 248 this._.docEndMarker && this._.docEndMarker.remove(); 249 this._.nextNode = null; 250 return null; 251 } 252 253 var startPath = new CKEDITOR.dom.elementPath( range.startContainer ); 254 var startBlockLimit = startPath.blockLimit, 255 checkLimits = { div : 1, th : 1, td : 1 }; 256 block = startPath.block; 257 258 if ( !block 259 && !this.enforceRealBlocks 260 && checkLimits[ startBlockLimit.getName() ] 261 && range.checkStartOfBlock() 262 && range.checkEndOfBlock() ) 263 block = startBlockLimit; 264 else if ( !block || ( this.enforceRealBlocks && block.getName() == 'li' ) ) 265 { 266 // Create the fixed block. 267 block = this.range.document.createElement( blockTag || 'p' ); 268 269 // Move the contents of the temporary range to the fixed block. 270 range.extractContents().appendTo( block ); 271 block.trim(); 272 273 // Insert the fixed block into the DOM. 274 range.insertNode( block ); 275 276 removePreviousBr = removeLastBr = true; 277 } 278 else if ( block.getName() != 'li' ) 279 { 280 // If the range doesn't includes the entire contents of the 281 // block, we must split it, isolating the range in a dedicated 282 // block. 283 if ( !range.checkStartOfBlock() || !range.checkEndOfBlock() ) 284 { 285 // The resulting block will be a clone of the current one. 286 block = block.clone( false ); 287 288 // Extract the range contents, moving it to the new block. 289 range.extractContents().appendTo( block ); 290 block.trim(); 291 292 // Split the block. At this point, the range will be in the 293 // right position for our intents. 294 var splitInfo = range.splitBlock(); 295 296 removePreviousBr = !splitInfo.wasStartOfBlock; 297 removeLastBr = !splitInfo.wasEndOfBlock; 298 299 // Insert the new block into the DOM. 300 range.insertNode( block ); 301 } 302 } 303 else if ( !isLast ) 304 { 305 // LIs are returned as is, with all their children (due to the 306 // nested lists). But, the next node is the node right after 307 // the current range, which could be an <li> child (nested 308 // lists) or the next sibling <li>. 309 310 this._.nextNode = ( block.equals( lastNode ) ? null : getNextSourceNode( range.getBoundaryNodes().endNode, 1, lastNode ) ); 311 } 312 } 313 314 if ( removePreviousBr ) 315 { 316 var previousSibling = block.getPrevious(); 317 if ( previousSibling && previousSibling.type == CKEDITOR.NODE_ELEMENT ) 318 { 319 if ( previousSibling.getName() == 'br' ) 320 previousSibling.remove(); 321 else if ( previousSibling.getLast() && previousSibling.getLast().$.nodeName.toLowerCase() == 'br' ) 322 previousSibling.getLast().remove(); 323 } 324 } 325 326 if ( removeLastBr ) 327 { 328 var lastChild = block.getLast(); 329 if ( lastChild && lastChild.type == CKEDITOR.NODE_ELEMENT && lastChild.getName() == 'br' ) 330 { 331 // Take care not to remove the block expanding <br> in non-IE browsers. 332 if ( CKEDITOR.env.ie 333 || lastChild.getPrevious( bookmarkGuard ) 334 || lastChild.getNext( bookmarkGuard ) ) 335 lastChild.remove(); 336 } 337 } 338 339 // Get a reference for the next element. This is important because the 340 // above block can be removed or changed, so we can rely on it for the 341 // next interation. 342 if ( !this._.nextNode ) 343 { 344 this._.nextNode = ( isLast || block.equals( lastNode ) ) ? null : 345 getNextSourceNode( block, 1, lastNode ); 155 walker.current = current; 156 current = walker.next(); 346 157 } 347 158 348 159 return block; 349 160 } 161 } 162 163 CKEDITOR.dom.range.prototype.createIterator = function( blockList ) 164 { 165 return new iterator( this, blockList ); 350 166 }; 351 167 352 CKEDITOR.dom.range.prototype.createIterator = function()168 function brHandler( range ) 353 169 { 354 return new iterator( this ); 355 }; 170 var startBoundary = range.getTouchedStartNode(), 171 endBoundary = range.getTouchedEndNode(), 172 toRemove = [ startBoundary.getPrevious(), startBoundary, endBoundary ]; 173 174 for ( var i = 0; i < toRemove.length; i++ ) 175 { 176 var node = toRemove[ i ]; 177 if ( !( node && node.type == CKEDITOR.NODE_ELEMENT && node.is( 'br' ) ) ) 178 toRemove.splice( i, 1 ); 179 } 180 181 return { 182 remove : function() 183 { 184 for ( var i = 0; i < toRemove.length; i++ ) 185 toRemove[ i ].remove(); 186 } 187 } 188 } 189 190 var nonWhitespaces = CKEDITOR.dom.walker.whitespaces( 1 ), 191 nonBookmark = CKEDITOR.dom.walker.bookmark( 0, 1 ); 192 193 var pathBlockExclusion = { address:1,blockquote:1,dl:1,dt:1,dd:1,li:1 }; 194 356 195 })();
Note: See TracChangeset
for help on using the changeset viewer.
