1 /** 2 * modified from ckeditor ,dom iterator implementation using walker and nextSourceNode 3 * @author yiminghe@gmail.com 4 */ 5 /* 6 Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. 7 For licensing, see LICENSE.html or http://ckeditor.com/license 8 */ 9 KISSY.add("editor/core/domIterator", function (S) { 10 var TRUE = true, 11 FALSE = false, 12 NULL = null, 13 Editor = S.Editor, 14 UA = S.UA, 15 Walker = Editor.Walker, 16 KERange = Editor.Range, 17 KER = Editor.RANGE, 18 ElementPath = Editor.ElementPath, 19 Node = S.Node, 20 DOM = S.DOM; 21 22 /** 23 * @constructor 24 * @param range {KISSY.Editor.Range} 25 */ 26 function Iterator(range) { 27 if (arguments.length < 1) 28 return; 29 var self = this; 30 self.range = range; 31 self.forceBrBreak = FALSE; 32 33 // Whether include <br>s into the enlarged range.(#3730). 34 self.enlargeBr = TRUE; 35 self.enforceRealBlocks = FALSE; 36 37 self._ || ( self._ = {} ); 38 } 39 40 var beginWhitespaceRegex = /^[\r\n\t ]*$/;///^[\r\n\t ]+$/,//+:*??不匹配空串 41 42 S.augment(Iterator, { 43 //奇怪点: 44 //<ul> 45 // <li> 46 // x 47 // </li> 48 // <li> 49 // y 50 // </li> 51 // </ul> 52 //会返回两次 li,li,而不是一次 ul , 53 // 可能只是返回包含文字的段落概念? 54 getNextParagraph:function (blockTag) { 55 // The block element to be returned. 56 var block, self = this; 57 58 // The range object used to identify the paragraph contents. 59 var range; 60 61 // Indicats that the current element in the loop is the last one. 62 var isLast; 63 64 // Instructs to cleanup remaining BRs. 65 var removePreviousBr, removeLastBr; 66 67 // self is the first iteration. Let's initialize it. 68 if (!self._.lastNode) { 69 range = self.range.clone(); 70 71 // 2010-09-30 shrink 72 // 3.4.2 新增, 73 // Shrink the range to exclude harmful "noises" (#4087, #4450, #5435). 74 range.shrink(KER.SHRINK_ELEMENT, TRUE); 75 76 range.enlarge(self.forceBrBreak || !self.enlargeBr ? 77 KER.ENLARGE_LIST_ITEM_CONTENTS : KER.ENLARGE_BLOCK_CONTENTS); 78 79 var walker = new Walker(range), 80 ignoreBookmarkTextEvaluator = Walker.bookmark(TRUE, TRUE); 81 // Avoid anchor inside bookmark inner text. 82 walker.evaluator = ignoreBookmarkTextEvaluator; 83 self._.nextNode = walker.next(); 84 // TODO: It's better to have walker.reset() used here. 85 walker = new Walker(range); 86 walker.evaluator = ignoreBookmarkTextEvaluator; 87 var lastNode = walker.previous(); 88 self._.lastNode = lastNode._4e_nextSourceNode(TRUE); 89 90 // We may have an empty text node at the end of block due to [3770]. 91 // If that node is the lastNode, it would cause our logic to leak to the 92 // next block.(#3887) 93 if (self._.lastNode && 94 self._.lastNode[0].nodeType == DOM.TEXT_NODE && 95 !S.trim(self._.lastNode[0].nodeValue) && 96 self._.lastNode.parent()._4e_isBlockBoundary()) { 97 var testRange = new KERange(range.document); 98 testRange.moveToPosition(self._.lastNode, KER.POSITION_AFTER_END); 99 if (testRange.checkEndOfBlock()) { 100 var path = new ElementPath(testRange.endContainer); 101 var lastBlock = path.block || path.blockLimit; 102 self._.lastNode = lastBlock._4e_nextSourceNode(TRUE); 103 } 104 } 105 106 // Probably the document end is reached, we need a marker node. 107 if (!self._.lastNode) { 108 self._.lastNode = self._.docEndMarker = new Node(range.document.createTextNode('')); 109 DOM.insertAfter(self._.lastNode[0], lastNode[0]); 110 } 111 112 // Let's reuse self variable. 113 range = NULL; 114 } 115 116 var currentNode = self._.nextNode; 117 lastNode = self._.lastNode; 118 119 self._.nextNode = NULL; 120 while (currentNode) { 121 // closeRange indicates that a paragraph boundary has been found, 122 // so the range can be closed. 123 var closeRange = FALSE; 124 125 // includeNode indicates that the current node is good to be part 126 // of the range. By default, any non-element node is ok for it. 127 var includeNode = ( currentNode[0].nodeType != DOM.ELEMENT_NODE ), 128 continueFromSibling = FALSE; 129 130 // If it is an element node, let's check if it can be part of the 131 // range. 132 if (!includeNode) { 133 var nodeName = currentNode.nodeName(); 134 135 if (currentNode._4e_isBlockBoundary(self.forceBrBreak && { br:1 })) { 136 // <br> boundaries must be part of the range. It will 137 // happen only if ForceBrBreak. 138 if (nodeName == 'br') 139 includeNode = TRUE; 140 else if (!range && !currentNode[0].childNodes.length && nodeName != 'hr') { 141 // If we have found an empty block, and haven't started 142 // the range yet, it means we must return self block. 143 block = currentNode; 144 isLast = currentNode.equals(lastNode); 145 break; 146 } 147 148 // The range must finish right before the boundary, 149 // including possibly skipped empty spaces. (#1603) 150 if (range) { 151 range.setEndAt(currentNode, KER.POSITION_BEFORE_START); 152 153 // The found boundary must be set as the next one at self 154 // point. (#1717) 155 if (nodeName != 'br') 156 self._.nextNode = currentNode; 157 } 158 159 closeRange = TRUE; 160 } else { 161 // If we have child nodes, let's check them. 162 if (currentNode[0].firstChild) { 163 // If we don't have a range yet, let's start it. 164 if (!range) { 165 range = new KERange(self.range.document); 166 range.setStartAt(currentNode, KER.POSITION_BEFORE_START); 167 } 168 169 currentNode = new Node(currentNode[0].firstChild); 170 continue; 171 } 172 includeNode = TRUE; 173 } 174 } 175 else if (currentNode[0].nodeType == DOM.TEXT_NODE) { 176 // Ignore normal whitespaces (i.e. not including or 177 // other unicode whitespaces) before/after a block node. 178 if (beginWhitespaceRegex.test(currentNode[0].nodeValue)) 179 includeNode = FALSE; 180 } 181 182 // The current node is good to be part of the range and we are 183 // starting a new range, initialize it first. 184 if (includeNode && !range) { 185 range = new KERange(self.range.document); 186 range.setStartAt(currentNode, KER.POSITION_BEFORE_START); 187 } 188 189 // The last node has been found. 190 isLast = ( !closeRange || includeNode ) && currentNode.equals(lastNode); 191 192 // If we are in an element boundary, let's check if it is time 193 // to close the range, otherwise we include the parent within it. 194 if (range && !closeRange) { 195 while (!currentNode[0].nextSibling && !isLast) { 196 var parentNode = currentNode.parent(); 197 198 if (parentNode._4e_isBlockBoundary(self.forceBrBreak && { br:1 })) { 199 closeRange = TRUE; 200 isLast = isLast || parentNode.equals(lastNode); 201 break; 202 } 203 204 currentNode = parentNode; 205 includeNode = TRUE; 206 isLast = currentNode.equals(lastNode); 207 continueFromSibling = TRUE; 208 } 209 } 210 211 // Now finally include the node. 212 if (includeNode) 213 range.setEndAt(currentNode, KER.POSITION_AFTER_END); 214 215 currentNode = currentNode._4e_nextSourceNode(continueFromSibling, NULL, lastNode); 216 isLast = !currentNode; 217 218 // We have found a block boundary. Let's close the range and move out of the 219 // loop. 220 if (isLast || ( closeRange && range )) 221 break; 222 } 223 224 // Now, based on the processed range, look for (or create) the block to be returned. 225 if (!block) { 226 // If no range has been found, self is the end. 227 if (!range) { 228 self._.docEndMarker && self._.docEndMarker._4e_remove(); 229 self._.nextNode = NULL; 230 return NULL; 231 } 232 233 var startPath = new ElementPath(range.startContainer); 234 var startBlockLimit = startPath.blockLimit, 235 checkLimits = { div:1, th:1, td:1 }; 236 block = startPath.block; 237 238 if ((!block || !block[0]) 239 && !self.enforceRealBlocks 240 && checkLimits[ startBlockLimit.nodeName() ] 241 && range.checkStartOfBlock() 242 && range.checkEndOfBlock()) 243 block = startBlockLimit; 244 else if (!block || ( self.enforceRealBlocks && block.nodeName() == 'li' )) { 245 // Create the fixed block. 246 block = new Node(self.range.document.createElement(blockTag || 'p')); 247 // Move the contents of the temporary range to the fixed block. 248 block[0].appendChild(range.extractContents()); 249 block._4e_trim(); 250 // Insert the fixed block into the DOM. 251 range.insertNode(block); 252 removePreviousBr = removeLastBr = TRUE; 253 } 254 else if (block.nodeName() != 'li') { 255 // If the range doesn't includes the entire contents of the 256 // block, we must split it, isolating the range in a dedicated 257 // block. 258 if (!range.checkStartOfBlock() || !range.checkEndOfBlock()) { 259 // The resulting block will be a clone of the current one. 260 block = block.clone(FALSE); 261 262 // Extract the range contents, moving it to the new block. 263 block[0].appendChild(range.extractContents()); 264 block._4e_trim(); 265 266 // Split the block. At self point, the range will be in the 267 // right position for our intents. 268 var splitInfo = range.splitBlock(); 269 270 removePreviousBr = !splitInfo.wasStartOfBlock; 271 removeLastBr = !splitInfo.wasEndOfBlock; 272 273 // Insert the new block into the DOM. 274 range.insertNode(block); 275 } 276 } 277 else if (!isLast) { 278 // LIs are returned as is, with all their children (due to the 279 // nested lists). But, the next node is the node right after 280 // the current range, which could be an <li> child (nested 281 // lists) or the next sibling <li>. 282 283 self._.nextNode = ( block.equals(lastNode) ? NULL : 284 range.getBoundaryNodes().endNode._4e_nextSourceNode(TRUE, NULL, lastNode) ); 285 } 286 } 287 288 if (removePreviousBr) { 289 var previousSibling = new Node(block[0].previousSibling); 290 if (previousSibling[0] && previousSibling[0].nodeType == DOM.ELEMENT_NODE) { 291 if (previousSibling.nodeName() == 'br') 292 previousSibling._4e_remove(); 293 else if (previousSibling[0].lastChild && DOM.nodeName(previousSibling[0].lastChild) == 'br') 294 DOM._4e_remove(previousSibling[0].lastChild); 295 } 296 } 297 298 if (removeLastBr) { 299 // Ignore bookmark nodes.(#3783) 300 var bookmarkGuard = Walker.bookmark(FALSE, TRUE); 301 302 var lastChild = new Node(block[0].lastChild); 303 if (lastChild[0] && lastChild[0].nodeType == DOM.ELEMENT_NODE && lastChild.nodeName() == 'br') { 304 // Take care not to remove the block expanding <br> in non-IE browsers. 305 if (UA['ie'] 306 || lastChild.prev(bookmarkGuard, 1) 307 || lastChild.next(bookmarkGuard, 1)) 308 lastChild.remove(); 309 } 310 } 311 312 // Get a reference for the next element. self is important because the 313 // above block can be removed or changed, so we can rely on it for the 314 // next interation. 315 if (!self._.nextNode) { 316 self._.nextNode = ( isLast || block.equals(lastNode) ) ? NULL : 317 block._4e_nextSourceNode(TRUE, NULL, lastNode); 318 } 319 320 return block; 321 } 322 }); 323 324 KERange.prototype.createIterator = function () { 325 return new Iterator(this); 326 }; 327 328 return Iterator; 329 }, { 330 requires:['./base', './range', './elementPath', './walker'] 331 }); 332