1 /**
  2  * modified from ckeditor ,elementPath represents element's tree path from body
  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/elementPath", function (S) {
 10     var Editor = S.Editor,
 11         DOM = S.DOM,
 12         dtd = Editor.XHTML_DTD,
 13         TRUE = true,
 14         FALSE = false,
 15         NULL = null,
 16         // Elements that may be considered the "Block boundary" in an element path.
 17         pathBlockElements = {
 18             address:1,
 19             blockquote:1,
 20             dl:1,
 21             h1:1,
 22             h2:1,
 23             h3:1,
 24             h4:1,
 25             h5:1,
 26             h6:1,
 27             p:1,
 28             pre:1,
 29             li:1,
 30             dt:1,
 31             dd:1
 32         },
 33         // Elements that may be considered the "Block limit" in an element path.
 34         // 特别注意:不带 p 元素
 35         pathBlockLimitElements = {
 36             body:1,
 37             div:1,
 38             table:1,
 39             tbody:1,
 40             tr:1,
 41             td:1,
 42             th:1,
 43             caption:1,
 44             form:1
 45         },
 46         // Check if an element contains any block element.
 47         checkHasBlock = function (element) {
 48             var childNodes = element[0].childNodes;
 49             for (var i = 0, count = childNodes.length; i < count; i++) {
 50                 var child = childNodes[i];
 51                 if (child.nodeType == DOM.ELEMENT_NODE
 52                     && dtd.$block[ child.nodeName.toLowerCase() ])
 53                     return TRUE;
 54             }
 55             return FALSE;
 56         };
 57 
 58     /**
 59      * @constructor
 60      * @param lastNode {NodeList}
 61      */
 62     function ElementPath(lastNode) {
 63         var self = this,
 64             block = NULL,
 65             blockLimit = NULL,
 66             elements = [],
 67             e = lastNode;
 68 
 69         while (e) {
 70             if (e[0].nodeType == DOM.ELEMENT_NODE) {
 71                 if (!this.lastElement)
 72                     this.lastElement = e;
 73 
 74                 var elementName = e.nodeName();
 75 
 76                 if (!blockLimit) {
 77                     if (!block && pathBlockElements[ elementName ]) {
 78                         block = e;
 79                     }
 80                     if (pathBlockLimitElements[ elementName ]) {
 81                         // DIV is considered the Block, if no block is available (#525)
 82                         // and if it doesn't contain other blocks.
 83                         if (!block && elementName == 'div' && !checkHasBlock(e))
 84                             block = e;
 85                         else
 86                             blockLimit = e;
 87                     }
 88                 }
 89 
 90                 elements.push(e);
 91                 if (elementName == 'body') {
 92                     break;
 93                 }
 94             }
 95             e = e.parent();
 96         }
 97 
 98         self.block = block;
 99         self.blockLimit = blockLimit;
100         self.elements = elements;
101     }
102 
103     ElementPath.prototype = {
104         /**
105          * Compares this element path with another one.
106          * @param otherPath ElementPath The elementPath object to be
107          * compared with this one.
108          * @return {Boolean} "TRUE" if the paths are equal, containing the same
109          * number of elements and the same elements in the same order.
110          */
111         compare:function (otherPath) {
112             var thisElements = this.elements;
113             var otherElements = otherPath && otherPath.elements;
114 
115             if (!otherElements || thisElements.length != otherElements.length)
116                 return FALSE;
117 
118             for (var i = 0; i < thisElements.length; i++) {
119                 if (!DOM.equals(thisElements[ i ], otherElements[ i ]))
120                     return FALSE;
121             }
122 
123             return TRUE;
124         },
125 
126         contains:function (tagNames) {
127             var elements = this.elements;
128             for (var i = 0; i < elements.length; i++) {
129                 if (elements[ i ].nodeName() in tagNames)
130                     return elements[ i ];
131             }
132             return NULL;
133         },
134         toString:function () {
135             var elements = this.elements, i, elNames = [];
136             for (i = 0; i < elements.length; i++) {
137                 elNames.push(elements[i].nodeName());
138             }
139             return elNames.toString();
140         }
141     };
142     Editor.ElementPath = ElementPath;
143 
144     return ElementPath;
145 }, {
146     requires:['./base', './dom']
147 });
148