text: ﻿/*=======================domIterator.js===========================*/
  /*
  Copyright 2012, KISSY UI Library v1.40dev
  MIT Licensed
  build time: Aug 27 10:38
  */
  /**
   * modified from ckeditor ,dom iterator implementation using walker and nextSourceNode
   * @author yiminghe@gmail.com
   */
  /*
   Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
   For licensing, see LICENSE.html or http://ckeditor.com/license
   */
  KISSY.add("editor/core/domIterator", function (S) {
      var TRUE = true,
          FALSE = false,
          NULL = null,
          Editor = S.Editor,
          UA = S.UA,
          Walker = Editor.Walker,
          KERange = Editor.Range,
          KER = Editor.RANGE,
          ElementPath = Editor.ElementPath,
          Node = S.Node,
          DOM = S.DOM;
  
      /**
       * @constructor
       * @param range {KISSY.Editor.Range}
       */
      function Iterator(range) {
          if (arguments.length < 1)
              return;
          var self = this;
          self.range = range;
          self.forceBrBreak = FALSE;
  
          // Whether include <br>s into the enlarged range.(#3730).
          self.enlargeBr = TRUE;
          self.enforceRealBlocks = FALSE;
  
          self._ || ( self._ = {} );
      }
  
      var beginWhitespaceRegex = /^[\r\n\t ]*$/;///^[\r\n\t ]+$/,//+:*??不匹配空串
  
      S.augment(Iterator, {
          //奇怪点：
          //<ul>
          // <li>
          // x
          // </li>
          // <li>
          // y
          // </li>
          // </ul>
          //会返回两次 li,li,而不是一次 ul ，
          // 可能只是返回包含文字的段落概念？
          getNextParagraph:function (blockTag) {
              // The block element to be returned.
              var block, self = this;
  
              // The range object used to identify the paragraph contents.
              var range;
  
              // Indicats that the current element in the loop is the last one.
              var isLast;
  
              // Instructs to cleanup remaining BRs.
              var removePreviousBr, removeLastBr;
  
              // self is the first iteration. Let's initialize it.
              if (!self._.lastNode) {
                  range = self.range.clone();
  
                  // 2010-09-30 shrink
                  // 3.4.2 新增，
                  // Shrink the range to exclude harmful "noises" (#4087, #4450, #5435).
                  range.shrink(KER.SHRINK_ELEMENT, TRUE);
  
                  range.enlarge(self.forceBrBreak || !self.enlargeBr ?
                      KER.ENLARGE_LIST_ITEM_CONTENTS : KER.ENLARGE_BLOCK_CONTENTS);
  
                  var walker = new Walker(range),
                      ignoreBookmarkTextEvaluator = Walker.bookmark(TRUE, TRUE);
                  // Avoid anchor inside bookmark inner text.
                  walker.evaluator = ignoreBookmarkTextEvaluator;
                  self._.nextNode = walker.next();
                  // TODO: It's better to have walker.reset() used here.
                  walker = new Walker(range);
                  walker.evaluator = ignoreBookmarkTextEvaluator;
                  var lastNode = walker.previous();
                  self._.lastNode = lastNode._4e_nextSourceNode(TRUE);
  
                  // We may have an empty text node at the end of block due to [3770].
                  // If that node is the lastNode, it would cause our logic to leak to the
                  // next block.(#3887)
                  if (self._.lastNode &&
                      self._.lastNode[0].nodeType == DOM.NodeType.TEXT_NODE &&
                      !S.trim(self._.lastNode[0].nodeValue) &&
                      self._.lastNode.parent()._4e_isBlockBoundary()) {
                      var testRange = new KERange(range.document);
                      testRange.moveToPosition(self._.lastNode, KER.POSITION_AFTER_END);
                      if (testRange.checkEndOfBlock()) {
                          var path = new ElementPath(testRange.endContainer);
                          var lastBlock = path.block || path.blockLimit;
                          self._.lastNode = lastBlock._4e_nextSourceNode(TRUE);
                      }
                  }
  
                  // Probably the document end is reached, we need a marker node.
                  if (!self._.lastNode) {
                      self._.lastNode = self._.docEndMarker = new Node(range.document.createTextNode(''));
                      DOM.insertAfter(self._.lastNode[0], lastNode[0]);
                  }
  
                  // Let's reuse self variable.
                  range = NULL;
              }
  
              var currentNode = self._.nextNode;
              lastNode = self._.lastNode;
  
              self._.nextNode = NULL;
              while (currentNode) {
                  // closeRange indicates that a paragraph boundary has been found,
                  // so the range can be closed.
                  var closeRange = FALSE;
  
                  // includeNode indicates that the current node is good to be part
                  // of the range. By default, any non-element node is ok for it.
                  var includeNode = ( currentNode[0].nodeType != DOM.NodeType.ELEMENT_NODE ),
                      continueFromSibling = FALSE;
  
                  // If it is an element node, let's check if it can be part of the
                  // range.
                  if (!includeNode) {
                      var nodeName = currentNode.nodeName();
  
                      if (currentNode._4e_isBlockBoundary(self.forceBrBreak && { br:1 })) {
                          // <br> boundaries must be part of the range. It will
                          // happen only if ForceBrBreak.
                          if (nodeName == 'br')
                              includeNode = TRUE;
                          else if (!range && !currentNode[0].childNodes.length && nodeName != 'hr') {
                              // If we have found an empty block, and haven't started
                              // the range yet, it means we must return self block.
                              block = currentNode;
                              isLast = currentNode.equals(lastNode);
                              break;
                          }
  
                          // The range must finish right before the boundary,
                          // including possibly skipped empty spaces. (#1603)
                          if (range) {
                              range.setEndAt(currentNode, KER.POSITION_BEFORE_START);
  
                              // The found boundary must be set as the next one at self
                              // point. (#1717)
                              if (nodeName != 'br')
                                  self._.nextNode = currentNode;
                          }
  
                          closeRange = TRUE;
                      } else {
                          // If we have child nodes, let's check them.
                          if (currentNode[0].firstChild) {
                              // If we don't have a range yet, let's start it.
                              if (!range) {
                                  range = new KERange(self.range.document);
                                  range.setStartAt(currentNode, KER.POSITION_BEFORE_START);
                              }
  
                              currentNode = new Node(currentNode[0].firstChild);
                              continue;
                          }
                          includeNode = TRUE;
                      }
                  }
                  else if (currentNode[0].nodeType == DOM.NodeType.TEXT_NODE) {
                      // Ignore normal whitespaces (i.e. not including &nbsp; or
                      // other unicode whitespaces) before/after a block node.
                      if (beginWhitespaceRegex.test(currentNode[0].nodeValue))
                          includeNode = FALSE;
                  }
  
                  // The current node is good to be part of the range and we are
                  // starting a new range, initialize it first.
                  if (includeNode && !range) {
                      range = new KERange(self.range.document);
                      range.setStartAt(currentNode, KER.POSITION_BEFORE_START);
                  }
  
                  // The last node has been found.
                  isLast = ( !closeRange || includeNode ) && currentNode.equals(lastNode);
  
                  // If we are in an element boundary, let's check if it is time
                  // to close the range, otherwise we include the parent within it.
                  if (range && !closeRange) {
                      while (!currentNode[0].nextSibling && !isLast) {
                          var parentNode = currentNode.parent();
  
                          if (parentNode._4e_isBlockBoundary(self.forceBrBreak && { br:1 })) {
                              closeRange = TRUE;
                              isLast = isLast || parentNode.equals(lastNode);
                              break;
                          }
  
                          currentNode = parentNode;
                          includeNode = TRUE;
                          isLast = currentNode.equals(lastNode);
                          continueFromSibling = TRUE;
                      }
                  }
  
                  // Now finally include the node.
                  if (includeNode)
                      range.setEndAt(currentNode, KER.POSITION_AFTER_END);
  
                  currentNode = currentNode._4e_nextSourceNode(continueFromSibling, NULL, lastNode);
                  isLast = !currentNode;
  
                  // We have found a block boundary. Let's close the range and move out of the
                  // loop.
                  if (isLast || ( closeRange && range ))
                      break;
              }
  
              // Now, based on the processed range, look for (or create) the block to be returned.
              if (!block) {
                  // If no range has been found, self is the end.
                  if (!range) {
                      self._.docEndMarker && self._.docEndMarker._4e_remove();
                      self._.nextNode = NULL;
                      return NULL;
                  }
  
                  var startPath = new ElementPath(range.startContainer);
                  var startBlockLimit = startPath.blockLimit,
                      checkLimits = { div:1, th:1, td:1 };
                  block = startPath.block;
  
                  if ((!block || !block[0])
                      && !self.enforceRealBlocks
                      && checkLimits[ startBlockLimit.nodeName() ]
                      && range.checkStartOfBlock()
                      && range.checkEndOfBlock())
                      block = startBlockLimit;
                  else if (!block || ( self.enforceRealBlocks && block.nodeName() == 'li' )) {
                      // Create the fixed block.
                      block = new Node(self.range.document.createElement(blockTag || 'p'));
                      // Move the contents of the temporary range to the fixed block.
                      block[0].appendChild(range.extractContents());
                      block._4e_trim();
                      // Insert the fixed block into the DOM.
                      range.insertNode(block);
                      removePreviousBr = removeLastBr = TRUE;
                  }
                  else if (block.nodeName() != 'li') {
                      // If the range doesn't includes the entire contents of the
                      // block, we must split it, isolating the range in a dedicated
                      // block.
                      if (!range.checkStartOfBlock() || !range.checkEndOfBlock()) {
                          // The resulting block will be a clone of the current one.
                          block = block.clone(FALSE);
  
                          // Extract the range contents, moving it to the new block.
                          block[0].appendChild(range.extractContents());
                          block._4e_trim();
  
                          // Split the block. At self point, the range will be in the
                          // right position for our intents.
                          var splitInfo = range.splitBlock();
  
                          removePreviousBr = !splitInfo.wasStartOfBlock;
                          removeLastBr = !splitInfo.wasEndOfBlock;
  
                          // Insert the new block into the DOM.
                          range.insertNode(block);
                      }
                  }
                  else if (!isLast) {
                      // LIs are returned as is, with all their children (due to the
                      // nested lists). But, the next node is the node right after
                      // the current range, which could be an <li> child (nested
                      // lists) or the next sibling <li>.
  
                      self._.nextNode = ( block.equals(lastNode) ? NULL :
                          range.getBoundaryNodes().endNode._4e_nextSourceNode(TRUE, NULL, lastNode) );
                  }
              }
  
              if (removePreviousBr) {
                  var previousSibling = new Node(block[0].previousSibling);
                  if (previousSibling[0] && previousSibling[0].nodeType == DOM.NodeType.ELEMENT_NODE) {
                      if (previousSibling.nodeName() == 'br')
                          previousSibling._4e_remove();
                      else if (previousSibling[0].lastChild && DOM.nodeName(previousSibling[0].lastChild) == 'br')
                          DOM._4e_remove(previousSibling[0].lastChild);
                  }
              }
  
              if (removeLastBr) {
                  // Ignore bookmark nodes.(#3783)
                  var bookmarkGuard = Walker.bookmark(FALSE, TRUE);
  
                  var lastChild = new Node(block[0].lastChild);
                  if (lastChild[0] && lastChild[0].nodeType == DOM.NodeType.ELEMENT_NODE && lastChild.nodeName() == 'br') {
                      // Take care not to remove the block expanding <br> in non-IE browsers.
                      if (UA['ie']
                          || lastChild.prev(bookmarkGuard, 1)
                          || lastChild.next(bookmarkGuard, 1))
                          lastChild.remove();
                  }
              }
  
              // Get a reference for the next element. self is important because the
              // above block can be removed or changed, so we can rely on it for the
              // next interation.
              if (!self._.nextNode) {
                  self._.nextNode = ( isLast || block.equals(lastNode) ) ? NULL :
                      block._4e_nextSourceNode(TRUE, NULL, lastNode);
              }
  
              return block;
          }
      });
  
      KERange.prototype.createIterator = function () {
          return new Iterator(this);
      };
  
      return Iterator;
  }, {
      requires:['./base', './range', './elementPath', './walker']
  });
  
  