1 /**
  2  * list Utils
  3  * @author yiminghe@gmail.com
  4  */
  5 KISSY.add('editor/plugin/listUtils/index', function (S, Editor) {
  6     var listNodeNames = {ol:1, ul:1},
  7         Node = S.Node,
  8         DOM = S.DOM,
  9         UA = S.UA,
 10         list = {
 11             /*
 12              * Convert a DOM list tree into a data structure that is easier to
 13              * manipulate. This operation should be non-intrusive in the sense that it
 14              * does not change the DOM tree, with the exception that it may add some
 15              * markers to the list item nodes when database is specified.
 16              * 扁平化处理,深度遍历,利用 indent 和顺序来表示一棵树
 17              */
 18             listToArray:function (listNode, database, baseArray, baseIndentLevel, grandparentNode) {
 19                 if (!listNodeNames[ listNode.nodeName() ]) {
 20                     return [];
 21                 }
 22                 if (!baseIndentLevel)
 23                     baseIndentLevel = 0;
 24                 if (!baseArray) {
 25                     baseArray = [];
 26                 }
 27                 // Iterate over all list items to and look for inner lists.
 28                 for (var i = 0, count = listNode[0].childNodes.length;
 29                      i < count; i++) {
 30                     var listItem = new Node(listNode[0].childNodes[i]);
 31 
 32                     // It may be a text node or some funny stuff.
 33                     if (listItem.nodeName() != 'li') {
 34                         continue;
 35                     }
 36                     var itemObj = { 'parent':listNode,
 37                         indent:baseIndentLevel,
 38                         element:listItem, contents:[] };
 39                     if (!grandparentNode) {
 40                         itemObj.grandparent = listNode.parent();
 41                         if (itemObj.grandparent && itemObj.grandparent.nodeName() == 'li')
 42                             itemObj.grandparent = itemObj.grandparent.parent();
 43                     }
 44                     else {
 45                         itemObj.grandparent = grandparentNode;
 46                     }
 47                     if (database) {
 48                         listItem._4e_setMarker(database, 'listarray_index', baseArray.length, undefined);
 49                     }
 50                     baseArray.push(itemObj);
 51 
 52                     for (var j = 0, itemChildCount = listItem[0].childNodes.length, child;
 53                          j < itemChildCount; j++) {
 54                         child = new Node(listItem[0].childNodes[j]);
 55                         if (child[0].nodeType == DOM.ELEMENT_NODE &&
 56                             listNodeNames[ child.nodeName() ]) {
 57                             // Note the recursion here, it pushes inner list items with
 58                             // +1 indentation in the correct order.
 59                             list.listToArray(child, database, baseArray,
 60                                 baseIndentLevel + 1, itemObj.grandparent);
 61                         } else {
 62                             itemObj.contents.push(child);
 63                         }
 64                     }
 65                 }
 66                 return baseArray;
 67             },
 68 
 69             // Convert our internal representation of a list back to a DOM forest.
 70             //根据包含indent属性的元素数组来生成树
 71             arrayToList:function (listArray, database, baseIndex, paragraphMode) {
 72                 if (!baseIndex) {
 73                     baseIndex = 0;
 74                 }
 75                 if (!listArray || listArray.length < baseIndex + 1) {
 76                     return null;
 77                 }
 78                 var doc = listArray[ baseIndex ].parent[0].ownerDocument,
 79                     retval = doc.createDocumentFragment(),
 80                     rootNode = null,
 81                     currentIndex = baseIndex,
 82                     indentLevel = Math.max(listArray[ baseIndex ].indent, 0),
 83                     currentListItem = null;
 84                 //,paragraphName = paragraphMode;
 85 
 86                 while (true) {
 87                     var item = listArray[ currentIndex ];
 88                     if (item.indent == indentLevel) {
 89                         if (!rootNode
 90                             ||
 91                             //用于替换标签,ul->ol ,ol->ul
 92                             listArray[ currentIndex ].parent.nodeName() != rootNode.nodeName()) {
 93                             rootNode = listArray[ currentIndex ].parent.clone(false);
 94                             retval.appendChild(rootNode[0]);
 95                         }
 96                         currentListItem = rootNode[0].appendChild(item.element.clone(false)[0]);
 97                         for (var i = 0; i < item.contents.length; i++) {
 98                             currentListItem.appendChild(item.contents[i].clone(true)[0]);
 99                         }
100                         currentIndex++;
101                     } else if (item.indent == Math.max(indentLevel, 0) + 1) {
102                         //进入一个li里面,里面的嵌套li递归构造父亲ul/ol
103                         var listData = list.arrayToList(listArray, null,
104                             currentIndex, paragraphMode);
105                         currentListItem.appendChild(listData.listNode);
106                         currentIndex = listData.nextIndex;
107                     } else if (item.indent == -1 && !baseIndex &&
108                         item.grandparent) {
109 
110                         if (listNodeNames[ item.grandparent.nodeName() ]) {
111                             currentListItem = item.element.clone(false)[0];
112                         } else {
113                             // Create completely new blocks here, attributes are dropped.
114                             //为什么要把属性去掉???#3857
115                             if (item.grandparent.nodeName() != 'td') {
116                                 currentListItem = doc.createElement(paragraphMode);
117                                 item.element._4e_copyAttributes(new Node(currentListItem));
118                             }
119                             else
120                                 currentListItem = doc.createDocumentFragment();
121                         }
122 
123                         for (i = 0; i < item.contents.length; i++) {
124                             var ic = item.contents[i].clone(true);
125                             //如果是list中,应该只退出ul,保留margin-left
126                             if (currentListItem.nodeType == DOM.DOCUMENT_FRAGMENT_NODE) {
127                                 item.element._4e_copyAttributes(new Node(ic));
128                             }
129                             currentListItem.appendChild(ic[0]);
130                         }
131 
132                         if (currentListItem.nodeType == DOM.DOCUMENT_FRAGMENT_NODE
133                             && currentIndex != listArray.length - 1) {
134                             if (currentListItem.lastChild
135                                 && currentListItem.lastChild.nodeType == DOM.ELEMENT_NODE
136                                 && currentListItem.lastChild.getAttribute('type') == '_moz') {
137                                 DOM._4e_remove(currentListItem.lastChild);
138                             }
139                             DOM._4e_appendBogus(currentListItem);
140                         }
141 
142                         if (currentListItem.nodeType == DOM.ELEMENT_NODE &&
143                             DOM.nodeName(currentListItem) == paragraphMode &&
144                             currentListItem.firstChild) {
145                             DOM._4e_trim(currentListItem);
146                             var firstChild = currentListItem.firstChild;
147                             if (firstChild.nodeType == DOM.ELEMENT_NODE &&
148                                 DOM._4e_isBlockBoundary(firstChild)) {
149                                 var tmp = doc.createDocumentFragment();
150                                 DOM._4e_moveChildren(currentListItem, tmp);
151                                 currentListItem = tmp;
152                             }
153                         }
154 
155                         var currentListItemName = DOM.nodeName(currentListItem);
156                         if (!UA['ie'] && ( currentListItemName == 'div' ||
157                             currentListItemName == 'p' )) {
158                             DOM._4e_appendBogus(currentListItem);
159                         }
160                         retval.appendChild(currentListItem);
161                         rootNode = null;
162                         currentIndex++;
163                     }
164                     else {
165                         return null;
166                     }
167                     if (listArray.length <= currentIndex ||
168                         Math.max(listArray[ currentIndex ].indent, 0) < indentLevel)
169                         break;
170                 }
171 
172                 // Clear marker attributes for the new list tree made of cloned nodes, if any.
173                 if (database) {
174                     var currentNode = new Node(retval.firstChild);
175                     while (currentNode && currentNode[0]) {
176                         if (currentNode[0].nodeType == DOM.ELEMENT_NODE) {
177                             currentNode._4e_clearMarkers(database, true);
178                         }
179                         currentNode = currentNode._4e_nextSourceNode();
180                     }
181                 }
182 
183                 return { listNode:retval, nextIndex:currentIndex };
184             }
185         };
186 
187     return list;
188 }, {
189     requires:['editor']
190 });