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