mirror of
https://github.com/Lastorder-DC/rhymix.git
synced 2026-01-19 03:09:55 +09:00
17223554 : xquared upgrade to 0.7
git-svn-id: http://xe-core.googlecode.com/svn/sandbox@4968 201d5d3c-b55e-5fd7-737f-ddc643e51545
This commit is contained in:
parent
5956e254e7
commit
7c3b336e41
59 changed files with 34562 additions and 8454 deletions
329
modules/editor/skins/xquared/javascripts/DomTree.js
Normal file
329
modules/editor/skins/xquared/javascripts/DomTree.js
Normal file
|
|
@ -0,0 +1,329 @@
|
|||
/**
|
||||
* @requires Xquared.js
|
||||
*/
|
||||
xq.DomTree = xq.Class(/** @lends xq.DomTree.prototype */{
|
||||
/**
|
||||
* Provides various tree operations.
|
||||
*
|
||||
* TODO: Add specs
|
||||
*
|
||||
* @constructs
|
||||
*/
|
||||
initialize: function() {
|
||||
xq.addToFinalizeQueue(this);
|
||||
|
||||
this._blockTags = ["DIV", "DD", "LI", "ADDRESS", "CAPTION", "DT", "H1", "H2", "H3", "H4", "H5", "H6", "HR", "P", "BODY", "BLOCKQUOTE", "PRE", "PARAM", "DL", "OL", "UL", "TABLE", "THEAD", "TBODY", "TR", "TH", "TD"];
|
||||
this._blockContainerTags = ["DIV", "DD", "LI", "BODY", "BLOCKQUOTE", "UL", "OL", "DL", "TABLE", "THEAD", "TBODY", "TR", "TH", "TD"];
|
||||
this._listContainerTags = ["OL", "UL", "DL"];
|
||||
this._tableCellTags = ["TH", "TD"];
|
||||
this._blockOnlyContainerTags = ["BODY", "BLOCKQUOTE", "UL", "OL", "DL", "TABLE", "THEAD", "TBODY", "TR"];
|
||||
this._atomicTags = ["IMG", "OBJECT", "PARAM", "BR", "HR"];
|
||||
},
|
||||
|
||||
getBlockTags: function() {
|
||||
return this._blockTags;
|
||||
},
|
||||
|
||||
/**
|
||||
* Find common ancestor(parent) and his immediate children(left and right).<br />
|
||||
*<br />
|
||||
* A --- B -+- C -+- D -+- E<br />
|
||||
* |<br />
|
||||
* +- F -+- G<br />
|
||||
*<br />
|
||||
* For example:<br />
|
||||
* > findCommonAncestorAndImmediateChildrenOf("E", "G")<br />
|
||||
*<br />
|
||||
* will return<br />
|
||||
*<br />
|
||||
* > {parent:"B", left:"C", right:"F"}
|
||||
*/
|
||||
findCommonAncestorAndImmediateChildrenOf: function(left, right) {
|
||||
if(left.parentNode === right.parentNode) {
|
||||
return {
|
||||
left:left,
|
||||
right:right,
|
||||
parent:left.parentNode
|
||||
};
|
||||
} else {
|
||||
var parentsOfLeft = this.collectParentsOf(left, true);
|
||||
var parentsOfRight = this.collectParentsOf(right, true);
|
||||
var ca = this.getCommonAncestor(parentsOfLeft, parentsOfRight);
|
||||
|
||||
var leftAncestor = parentsOfLeft.find(function(node) {return node.parentNode === ca});
|
||||
var rightAncestor = parentsOfRight.find(function(node) {return node.parentNode === ca});
|
||||
|
||||
return {
|
||||
left:leftAncestor,
|
||||
right:rightAncestor,
|
||||
parent:ca
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Find leaves at edge.<br />
|
||||
*<br />
|
||||
* A --- B -+- C -+- D -+- E<br />
|
||||
* |<br />
|
||||
* +- F -+- G<br />
|
||||
*<br />
|
||||
* For example:<br />
|
||||
* > getLeavesAtEdge("A")<br />
|
||||
*<br />
|
||||
* will return<br />
|
||||
*<br />
|
||||
* > ["E", "G"]
|
||||
*/
|
||||
getLeavesAtEdge: function(element) {
|
||||
if(!element.hasChildNodes()) return [null, null];
|
||||
|
||||
var findLeft = function(el) {
|
||||
for (var i = 0; i < el.childNodes.length; i++) {
|
||||
if (el.childNodes[i].nodeType === 1 && this.isBlock(el.childNodes[i])) return findLeft(el.childNodes[i]);
|
||||
}
|
||||
return el;
|
||||
}.bind(this);
|
||||
|
||||
var findRight=function(el) {
|
||||
for (var i = el.childNodes.length; i--;) {
|
||||
if (el.childNodes[i].nodeType === 1 && this.isBlock(el.childNodes[i])) return findRight(el.childNodes[i]);
|
||||
}
|
||||
return el;
|
||||
}.bind(this);
|
||||
|
||||
var left = findLeft(element);
|
||||
var right = findRight(element);
|
||||
|
||||
return [left === element ? null : left, right === element ? null : right];
|
||||
},
|
||||
|
||||
getCommonAncestor: function(parents1, parents2) {
|
||||
for(var i = 0; i < parents1.length; i++) {
|
||||
for(var j = 0; j < parents2.length; j++) {
|
||||
if(parents1[i] === parents2[j]) return parents1[i];
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
collectParentsOf: function(node, includeSelf, exitCondition) {
|
||||
var parents = [];
|
||||
if(includeSelf) parents.push(node);
|
||||
|
||||
while((node = node.parentNode) && (node.nodeName !== "HTML") && !(typeof exitCondition === "function" && exitCondition(node))) parents.push(node);
|
||||
return parents;
|
||||
},
|
||||
|
||||
isDescendantOf: function(parent, child) {
|
||||
if(parent.length > 0) {
|
||||
for(var i = 0; i < parent.length; i++) {
|
||||
if(this.isDescendantOf(parent[i], child)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if(parent === child) return false;
|
||||
|
||||
while (child = child.parentNode)
|
||||
if (child === parent) return true;
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Perform tree walking (foreward)
|
||||
*/
|
||||
walkForward: function(node) {
|
||||
var target = node.firstChild;
|
||||
if(target) return target;
|
||||
|
||||
// intentional assignment for micro performance turing
|
||||
if(target = node.nextSibling) return target;
|
||||
|
||||
while(node = node.parentNode) {
|
||||
// intentional assignment for micro performance turing
|
||||
if(target = node.nextSibling) return target;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Perform tree walking (backward)
|
||||
*/
|
||||
walkBackward: function(node) {
|
||||
if(node.previousSibling) {
|
||||
node = node.previousSibling;
|
||||
while(node.hasChildNodes()) {node = node.lastChild;}
|
||||
return node;
|
||||
}
|
||||
|
||||
return node.parentNode;
|
||||
},
|
||||
|
||||
/**
|
||||
* Perform tree walking (to next siblings)
|
||||
*/
|
||||
walkNext: function(node) {return node.nextSibling},
|
||||
|
||||
/**
|
||||
* Perform tree walking (to next siblings)
|
||||
*/
|
||||
walkPrev: function(node) {return node.previousSibling},
|
||||
|
||||
/**
|
||||
* Returns true if target is followed by start
|
||||
*/
|
||||
checkTargetForward: function(start, target) {
|
||||
return this._check(start, this.walkForward, target);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if start is followed by target
|
||||
*/
|
||||
checkTargetBackward: function(start, target) {
|
||||
return this._check(start, this.walkBackward, target);
|
||||
},
|
||||
|
||||
findForward: function(start, condition, exitCondition) {
|
||||
return this._find(start, this.walkForward, condition, exitCondition);
|
||||
},
|
||||
|
||||
findBackward: function(start, condition, exitCondition) {
|
||||
return this._find(start, this.walkBackward, condition, exitCondition);
|
||||
},
|
||||
|
||||
_check: function(start, direction, target) {
|
||||
if(start === target) return false;
|
||||
|
||||
while(start = direction(start)) {
|
||||
if(start === target) return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
_find: function(start, direction, condition, exitCondition) {
|
||||
while(start = direction(start)) {
|
||||
if(exitCondition && exitCondition(start)) return null;
|
||||
if(condition(start)) return start;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Walks Forward through DOM tree from start to end, and collects all nodes that matches with a filter.
|
||||
* If no filter provided, it just collects all nodes.
|
||||
*
|
||||
* @param {Element} start Starting element.
|
||||
* @param {Element} end Ending element.
|
||||
* @param {Function} filter A filter function.
|
||||
*/
|
||||
collectNodesBetween: function(start, end, filter) {
|
||||
if(start === end) return [start, end].findAll(filter || function() {return true});
|
||||
|
||||
var nodes = this.collectForward(start, function(node) {return node === end}, filter);
|
||||
if(
|
||||
start !== end &&
|
||||
typeof filter === "function" &&
|
||||
filter(end)
|
||||
) nodes.push(end);
|
||||
|
||||
return nodes;
|
||||
},
|
||||
|
||||
collectForward: function(start, exitCondition, filter) {
|
||||
return this.collect(start, this.walkForward, exitCondition, filter);
|
||||
},
|
||||
|
||||
collectBackward: function(start, exitCondition, filter) {
|
||||
return this.collect(start, this.walkBackward, exitCondition, filter);
|
||||
},
|
||||
|
||||
collectNext: function(start, exitCondition, filter) {
|
||||
return this.collect(start, this.walkNext, exitCondition, filter);
|
||||
},
|
||||
|
||||
collectPrev: function(start, exitCondition, filter) {
|
||||
return this.collect(start, this.walkPrev, exitCondition, filter);
|
||||
},
|
||||
|
||||
collect: function(start, next, exitCondition, filter) {
|
||||
var nodes = [start];
|
||||
|
||||
while(true) {
|
||||
start = next(start);
|
||||
if(
|
||||
(start === null) ||
|
||||
(typeof exitCondition === "function" && exitCondition(start))
|
||||
) break;
|
||||
|
||||
nodes.push(start);
|
||||
}
|
||||
|
||||
return (typeof filter === "function") ? nodes.findAll(filter) : nodes;
|
||||
},
|
||||
|
||||
hasBlocks: function(element) {
|
||||
var nodes = element.childNodes;
|
||||
for(var i = 0; i < nodes.length; i++) {
|
||||
if(this.isBlock(nodes[i])) return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
hasMixedContents: function(element) {
|
||||
if(!this.isBlock(element)) return false;
|
||||
if(!this.isBlockContainer(element)) return false;
|
||||
|
||||
var hasTextOrInline = false;
|
||||
var hasBlock = false;
|
||||
for(var i = 0; i < element.childNodes.length; i++) {
|
||||
var node = element.childNodes[i];
|
||||
if(!hasTextOrInline && this.isTextOrInlineNode(node)) hasTextOrInline = true;
|
||||
if(!hasBlock && this.isBlock(node)) hasBlock = true;
|
||||
|
||||
if(hasTextOrInline && hasBlock) break;
|
||||
}
|
||||
if(!hasTextOrInline || !hasBlock) return false;
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
isBlockOnlyContainer: function(element) {
|
||||
if(!element) return false;
|
||||
return this._blockOnlyContainerTags.indexOf(typeof element === 'string' ? element : element.nodeName) !== -1;
|
||||
},
|
||||
|
||||
isTableCell: function(element) {
|
||||
if(!element) return false;
|
||||
return this._tableCellTags.indexOf(typeof element === 'string' ? element : element.nodeName) !== -1;
|
||||
},
|
||||
|
||||
isBlockContainer: function(element) {
|
||||
if(!element) return false;
|
||||
return this._blockContainerTags.indexOf(typeof element === 'string' ? element : element.nodeName) !== -1;
|
||||
},
|
||||
|
||||
isHeading: function(element) {
|
||||
if(!element) return false;
|
||||
return (typeof element === 'string' ? element : element.nodeName).match(/H\d/);
|
||||
},
|
||||
|
||||
isBlock: function(element) {
|
||||
if(!element) return false;
|
||||
return this._blockTags.indexOf(typeof element === 'string' ? element : element.nodeName) !== -1;
|
||||
},
|
||||
|
||||
isAtomic: function(element) {
|
||||
if(!element) return false;
|
||||
return this._atomicTags.indexOf(typeof element === 'string' ? element : element.nodeName) !== -1;
|
||||
},
|
||||
|
||||
isListContainer: function(element) {
|
||||
if(!element) return false;
|
||||
return this._listContainerTags.indexOf(typeof element === 'string' ? element : element.nodeName) !== -1;
|
||||
},
|
||||
|
||||
isTextOrInlineNode: function(node) {
|
||||
return node && (node.nodeType === 3 || !this.isBlock(node));
|
||||
}
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue