%PDF- %PDF-
| Direktori : /var/www/crm/include/javascript/tiny_mce/classes/dom/ |
| Current File : /var/www/crm/include/javascript/tiny_mce/classes/dom/Range.js |
/**
* Range.js
*
* Copyright 2009, Moxiecode Systems AB
* Released under LGPL License.
*
* License: http://tinymce.moxiecode.com/license
* Contributing: http://tinymce.moxiecode.com/contributing
*/
(function(ns) {
// Range constructor
function Range(dom) {
var t = this,
doc = dom.doc,
EXTRACT = 0,
CLONE = 1,
DELETE = 2,
TRUE = true,
FALSE = false,
START_OFFSET = 'startOffset',
START_CONTAINER = 'startContainer',
END_CONTAINER = 'endContainer',
END_OFFSET = 'endOffset',
extend = tinymce.extend,
nodeIndex = dom.nodeIndex;
extend(t, {
// Inital states
startContainer : doc,
startOffset : 0,
endContainer : doc,
endOffset : 0,
collapsed : TRUE,
commonAncestorContainer : doc,
// Range constants
START_TO_START : 0,
START_TO_END : 1,
END_TO_END : 2,
END_TO_START : 3,
// Public methods
setStart : setStart,
setEnd : setEnd,
setStartBefore : setStartBefore,
setStartAfter : setStartAfter,
setEndBefore : setEndBefore,
setEndAfter : setEndAfter,
collapse : collapse,
selectNode : selectNode,
selectNodeContents : selectNodeContents,
compareBoundaryPoints : compareBoundaryPoints,
deleteContents : deleteContents,
extractContents : extractContents,
cloneContents : cloneContents,
insertNode : insertNode,
surroundContents : surroundContents,
cloneRange : cloneRange
});
function setStart(n, o) {
_setEndPoint(TRUE, n, o);
};
function setEnd(n, o) {
_setEndPoint(FALSE, n, o);
};
function setStartBefore(n) {
setStart(n.parentNode, nodeIndex(n));
};
function setStartAfter(n) {
setStart(n.parentNode, nodeIndex(n) + 1);
};
function setEndBefore(n) {
setEnd(n.parentNode, nodeIndex(n));
};
function setEndAfter(n) {
setEnd(n.parentNode, nodeIndex(n) + 1);
};
function collapse(ts) {
if (ts) {
t[END_CONTAINER] = t[START_CONTAINER];
t[END_OFFSET] = t[START_OFFSET];
} else {
t[START_CONTAINER] = t[END_CONTAINER];
t[START_OFFSET] = t[END_OFFSET];
}
t.collapsed = TRUE;
};
function selectNode(n) {
setStartBefore(n);
setEndAfter(n);
};
function selectNodeContents(n) {
setStart(n, 0);
setEnd(n, n.nodeType === 1 ? n.childNodes.length : n.nodeValue.length);
};
function compareBoundaryPoints(h, r) {
var sc = t[START_CONTAINER], so = t[START_OFFSET], ec = t[END_CONTAINER], eo = t[END_OFFSET],
rsc = r.startContainer, rso = r.startOffset, rec = r.endContainer, reo = r.endOffset;
// Check START_TO_START
if (h === 0)
return _compareBoundaryPoints(sc, so, rsc, rso);
// Check START_TO_END
if (h === 1)
return _compareBoundaryPoints(ec, eo, rsc, rso);
// Check END_TO_END
if (h === 2)
return _compareBoundaryPoints(ec, eo, rec, reo);
// Check END_TO_START
if (h === 3)
return _compareBoundaryPoints(sc, so, rec, reo);
};
function deleteContents() {
_traverse(DELETE);
};
function extractContents() {
return _traverse(EXTRACT);
};
function cloneContents() {
return _traverse(CLONE);
};
function insertNode(n) {
var startContainer = this[START_CONTAINER],
startOffset = this[START_OFFSET], nn, o;
// Node is TEXT_NODE or CDATA
if ((startContainer.nodeType === 3 || startContainer.nodeType === 4) && startContainer.nodeValue) {
if (!startOffset) {
// At the start of text
startContainer.parentNode.insertBefore(n, startContainer);
} else if (startOffset >= startContainer.nodeValue.length) {
// At the end of text
dom.insertAfter(n, startContainer);
} else {
// Middle, need to split
nn = startContainer.splitText(startOffset);
startContainer.parentNode.insertBefore(n, nn);
}
} else {
// Insert element node
if (startContainer.childNodes.length > 0)
o = startContainer.childNodes[startOffset];
if (o)
startContainer.insertBefore(n, o);
else
startContainer.appendChild(n);
}
};
function surroundContents(n) {
var f = t.extractContents();
t.insertNode(n);
n.appendChild(f);
t.selectNode(n);
};
function cloneRange() {
return extend(new Range(dom), {
startContainer : t[START_CONTAINER],
startOffset : t[START_OFFSET],
endContainer : t[END_CONTAINER],
endOffset : t[END_OFFSET],
collapsed : t.collapsed,
commonAncestorContainer : t.commonAncestorContainer
});
};
// Private methods
function _getSelectedNode(container, offset) {
var child;
if (container.nodeType == 3 /* TEXT_NODE */)
return container;
if (offset < 0)
return container;
child = container.firstChild;
while (child && offset > 0) {
--offset;
child = child.nextSibling;
}
if (child)
return child;
return container;
};
function _isCollapsed() {
return (t[START_CONTAINER] == t[END_CONTAINER] && t[START_OFFSET] == t[END_OFFSET]);
};
function _compareBoundaryPoints(containerA, offsetA, containerB, offsetB) {
var c, offsetC, n, cmnRoot, childA, childB;
// In the first case the boundary-points have the same container. A is before B
// if its offset is less than the offset of B, A is equal to B if its offset is
// equal to the offset of B, and A is after B if its offset is greater than the
// offset of B.
if (containerA == containerB) {
if (offsetA == offsetB)
return 0; // equal
if (offsetA < offsetB)
return -1; // before
return 1; // after
}
// In the second case a child node C of the container of A is an ancestor
// container of B. In this case, A is before B if the offset of A is less than or
// equal to the index of the child node C and A is after B otherwise.
c = containerB;
while (c && c.parentNode != containerA)
c = c.parentNode;
if (c) {
offsetC = 0;
n = containerA.firstChild;
while (n != c && offsetC < offsetA) {
offsetC++;
n = n.nextSibling;
}
if (offsetA <= offsetC)
return -1; // before
return 1; // after
}
// In the third case a child node C of the container of B is an ancestor container
// of A. In this case, A is before B if the index of the child node C is less than
// the offset of B and A is after B otherwise.
c = containerA;
while (c && c.parentNode != containerB) {
c = c.parentNode;
}
if (c) {
offsetC = 0;
n = containerB.firstChild;
while (n != c && offsetC < offsetB) {
offsetC++;
n = n.nextSibling;
}
if (offsetC < offsetB)
return -1; // before
return 1; // after
}
// In the fourth case, none of three other cases hold: the containers of A and B
// are siblings or descendants of sibling nodes. In this case, A is before B if
// the container of A is before the container of B in a pre-order traversal of the
// Ranges' context tree and A is after B otherwise.
cmnRoot = dom.findCommonAncestor(containerA, containerB);
childA = containerA;
while (childA && childA.parentNode != cmnRoot)
childA = childA.parentNode;
if (!childA)
childA = cmnRoot;
childB = containerB;
while (childB && childB.parentNode != cmnRoot)
childB = childB.parentNode;
if (!childB)
childB = cmnRoot;
if (childA == childB)
return 0; // equal
n = cmnRoot.firstChild;
while (n) {
if (n == childA)
return -1; // before
if (n == childB)
return 1; // after
n = n.nextSibling;
}
};
function _setEndPoint(st, n, o) {
var ec, sc;
if (st) {
t[START_CONTAINER] = n;
t[START_OFFSET] = o;
} else {
t[END_CONTAINER] = n;
t[END_OFFSET] = o;
}
// If one boundary-point of a Range is set to have a root container
// other than the current one for the Range, the Range is collapsed to
// the new position. This enforces the restriction that both boundary-
// points of a Range must have the same root container.
ec = t[END_CONTAINER];
while (ec.parentNode)
ec = ec.parentNode;
sc = t[START_CONTAINER];
while (sc.parentNode)
sc = sc.parentNode;
if (sc == ec) {
// The start position of a Range is guaranteed to never be after the
// end position. To enforce this restriction, if the start is set to
// be at a position after the end, the Range is collapsed to that
// position.
if (_compareBoundaryPoints(t[START_CONTAINER], t[START_OFFSET], t[END_CONTAINER], t[END_OFFSET]) > 0)
t.collapse(st);
} else
t.collapse(st);
t.collapsed = _isCollapsed();
t.commonAncestorContainer = dom.findCommonAncestor(t[START_CONTAINER], t[END_CONTAINER]);
};
function _traverse(how) {
var c, endContainerDepth = 0, startContainerDepth = 0, p, depthDiff, startNode, endNode, sp, ep;
if (t[START_CONTAINER] == t[END_CONTAINER])
return _traverseSameContainer(how);
for (c = t[END_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {
if (p == t[START_CONTAINER])
return _traverseCommonStartContainer(c, how);
++endContainerDepth;
}
for (c = t[START_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {
if (p == t[END_CONTAINER])
return _traverseCommonEndContainer(c, how);
++startContainerDepth;
}
depthDiff = startContainerDepth - endContainerDepth;
startNode = t[START_CONTAINER];
while (depthDiff > 0) {
startNode = startNode.parentNode;
depthDiff--;
}
endNode = t[END_CONTAINER];
while (depthDiff < 0) {
endNode = endNode.parentNode;
depthDiff++;
}
// ascend the ancestor hierarchy until we have a common parent.
for (sp = startNode.parentNode, ep = endNode.parentNode; sp != ep; sp = sp.parentNode, ep = ep.parentNode) {
startNode = sp;
endNode = ep;
}
return _traverseCommonAncestors(startNode, endNode, how);
};
function _traverseSameContainer(how) {
var frag, s, sub, n, cnt, sibling, xferNode;
if (how != DELETE)
frag = doc.createDocumentFragment();
// If selection is empty, just return the fragment
if (t[START_OFFSET] == t[END_OFFSET])
return frag;
// Text node needs special case handling
if (t[START_CONTAINER].nodeType == 3 /* TEXT_NODE */) {
// get the substring
s = t[START_CONTAINER].nodeValue;
sub = s.substring(t[START_OFFSET], t[END_OFFSET]);
// set the original text node to its new value
if (how != CLONE) {
t[START_CONTAINER].deleteData(t[START_OFFSET], t[END_OFFSET] - t[START_OFFSET]);
// Nothing is partially selected, so collapse to start point
t.collapse(TRUE);
}
if (how == DELETE)
return;
frag.appendChild(doc.createTextNode(sub));
return frag;
}
// Copy nodes between the start/end offsets.
n = _getSelectedNode(t[START_CONTAINER], t[START_OFFSET]);
cnt = t[END_OFFSET] - t[START_OFFSET];
while (cnt > 0) {
sibling = n.nextSibling;
xferNode = _traverseFullySelected(n, how);
if (frag)
frag.appendChild( xferNode );
--cnt;
n = sibling;
}
// Nothing is partially selected, so collapse to start point
if (how != CLONE)
t.collapse(TRUE);
return frag;
};
function _traverseCommonStartContainer(endAncestor, how) {
var frag, n, endIdx, cnt, sibling, xferNode;
if (how != DELETE)
frag = doc.createDocumentFragment();
n = _traverseRightBoundary(endAncestor, how);
if (frag)
frag.appendChild(n);
endIdx = nodeIndex(endAncestor);
cnt = endIdx - t[START_OFFSET];
if (cnt <= 0) {
// Collapse to just before the endAncestor, which
// is partially selected.
if (how != CLONE) {
t.setEndBefore(endAncestor);
t.collapse(FALSE);
}
return frag;
}
n = endAncestor.previousSibling;
while (cnt > 0) {
sibling = n.previousSibling;
xferNode = _traverseFullySelected(n, how);
if (frag)
frag.insertBefore(xferNode, frag.firstChild);
--cnt;
n = sibling;
}
// Collapse to just before the endAncestor, which
// is partially selected.
if (how != CLONE) {
t.setEndBefore(endAncestor);
t.collapse(FALSE);
}
return frag;
};
function _traverseCommonEndContainer(startAncestor, how) {
var frag, startIdx, n, cnt, sibling, xferNode;
if (how != DELETE)
frag = doc.createDocumentFragment();
n = _traverseLeftBoundary(startAncestor, how);
if (frag)
frag.appendChild(n);
startIdx = nodeIndex(startAncestor);
++startIdx; // Because we already traversed it
cnt = t[END_OFFSET] - startIdx;
n = startAncestor.nextSibling;
while (cnt > 0) {
sibling = n.nextSibling;
xferNode = _traverseFullySelected(n, how);
if (frag)
frag.appendChild(xferNode);
--cnt;
n = sibling;
}
if (how != CLONE) {
t.setStartAfter(startAncestor);
t.collapse(TRUE);
}
return frag;
};
function _traverseCommonAncestors(startAncestor, endAncestor, how) {
var n, frag, commonParent, startOffset, endOffset, cnt, sibling, nextSibling;
if (how != DELETE)
frag = doc.createDocumentFragment();
n = _traverseLeftBoundary(startAncestor, how);
if (frag)
frag.appendChild(n);
commonParent = startAncestor.parentNode;
startOffset = nodeIndex(startAncestor);
endOffset = nodeIndex(endAncestor);
++startOffset;
cnt = endOffset - startOffset;
sibling = startAncestor.nextSibling;
while (cnt > 0) {
nextSibling = sibling.nextSibling;
n = _traverseFullySelected(sibling, how);
if (frag)
frag.appendChild(n);
sibling = nextSibling;
--cnt;
}
n = _traverseRightBoundary(endAncestor, how);
if (frag)
frag.appendChild(n);
if (how != CLONE) {
t.setStartAfter(startAncestor);
t.collapse(TRUE);
}
return frag;
};
function _traverseRightBoundary(root, how) {
var next = _getSelectedNode(t[END_CONTAINER], t[END_OFFSET] - 1), parent, clonedParent, prevSibling, clonedChild, clonedGrandParent, isFullySelected = next != t[END_CONTAINER];
if (next == root)
return _traverseNode(next, isFullySelected, FALSE, how);
parent = next.parentNode;
clonedParent = _traverseNode(parent, FALSE, FALSE, how);
while (parent) {
while (next) {
prevSibling = next.previousSibling;
clonedChild = _traverseNode(next, isFullySelected, FALSE, how);
if (how != DELETE)
clonedParent.insertBefore(clonedChild, clonedParent.firstChild);
isFullySelected = TRUE;
next = prevSibling;
}
if (parent == root)
return clonedParent;
next = parent.previousSibling;
parent = parent.parentNode;
clonedGrandParent = _traverseNode(parent, FALSE, FALSE, how);
if (how != DELETE)
clonedGrandParent.appendChild(clonedParent);
clonedParent = clonedGrandParent;
}
};
function _traverseLeftBoundary(root, how) {
var next = _getSelectedNode(t[START_CONTAINER], t[START_OFFSET]), isFullySelected = next != t[START_CONTAINER], parent, clonedParent, nextSibling, clonedChild, clonedGrandParent;
if (next == root)
return _traverseNode(next, isFullySelected, TRUE, how);
parent = next.parentNode;
clonedParent = _traverseNode(parent, FALSE, TRUE, how);
while (parent) {
while (next) {
nextSibling = next.nextSibling;
clonedChild = _traverseNode(next, isFullySelected, TRUE, how);
if (how != DELETE)
clonedParent.appendChild(clonedChild);
isFullySelected = TRUE;
next = nextSibling;
}
if (parent == root)
return clonedParent;
next = parent.nextSibling;
parent = parent.parentNode;
clonedGrandParent = _traverseNode(parent, FALSE, TRUE, how);
if (how != DELETE)
clonedGrandParent.appendChild(clonedParent);
clonedParent = clonedGrandParent;
}
};
function _traverseNode(n, isFullySelected, isLeft, how) {
var txtValue, newNodeValue, oldNodeValue, offset, newNode;
if (isFullySelected)
return _traverseFullySelected(n, how);
if (n.nodeType == 3 /* TEXT_NODE */) {
txtValue = n.nodeValue;
if (isLeft) {
offset = t[START_OFFSET];
newNodeValue = txtValue.substring(offset);
oldNodeValue = txtValue.substring(0, offset);
} else {
offset = t[END_OFFSET];
newNodeValue = txtValue.substring(0, offset);
oldNodeValue = txtValue.substring(offset);
}
if (how != CLONE)
n.nodeValue = oldNodeValue;
if (how == DELETE)
return;
newNode = n.cloneNode(FALSE);
newNode.nodeValue = newNodeValue;
return newNode;
}
if (how == DELETE)
return;
return n.cloneNode(FALSE);
};
function _traverseFullySelected(n, how) {
if (how != DELETE)
return how == CLONE ? n.cloneNode(TRUE) : n;
n.parentNode.removeChild(n);
};
};
ns.Range = Range;
})(tinymce.dom);