| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777 |
- /**
- * Range.js
- *
- * Copyright, Moxiecode Systems AB
- * Released under LGPL License.
- *
- * License: http://www.tinymce.com/license
- * Contributing: http://www.tinymce.com/contributing
- */
- define("tinymce/dom/Range", [
- "tinymce/util/Tools"
- ], function(Tools) {
- // Range constructor
- function Range(dom) {
- var self = 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 = Tools.extend,
- nodeIndex = dom.nodeIndex;
- function createDocumentFragment() {
- return doc.createDocumentFragment();
- }
- 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) {
- self[END_CONTAINER] = self[START_CONTAINER];
- self[END_OFFSET] = self[START_OFFSET];
- } else {
- self[START_CONTAINER] = self[END_CONTAINER];
- self[START_OFFSET] = self[END_OFFSET];
- }
- self.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 = self[START_CONTAINER], so = self[START_OFFSET], ec = self[END_CONTAINER], eo = self[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 {
- if (startContainer.nodeType == 3) {
- dom.insertAfter(n, startContainer);
- } else {
- startContainer.appendChild(n);
- }
- }
- }
- }
- function surroundContents(n) {
- var f = self.extractContents();
- self.insertNode(n);
- n.appendChild(f);
- self.selectNode(n);
- }
- function cloneRange() {
- return extend(new Range(dom), {
- startContainer: self[START_CONTAINER],
- startOffset: self[START_OFFSET],
- endContainer: self[END_CONTAINER],
- endOffset: self[END_OFFSET],
- collapsed: self.collapsed,
- commonAncestorContainer: self.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 (self[START_CONTAINER] == self[END_CONTAINER] && self[START_OFFSET] == self[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) {
- self[START_CONTAINER] = n;
- self[START_OFFSET] = o;
- } else {
- self[END_CONTAINER] = n;
- self[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 = self[END_CONTAINER];
- while (ec.parentNode) {
- ec = ec.parentNode;
- }
- sc = self[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(self[START_CONTAINER], self[START_OFFSET], self[END_CONTAINER], self[END_OFFSET]) > 0) {
- self.collapse(st);
- }
- } else {
- self.collapse(st);
- }
- self.collapsed = _isCollapsed();
- self.commonAncestorContainer = dom.findCommonAncestor(self[START_CONTAINER], self[END_CONTAINER]);
- }
- function _traverse(how) {
- var c, endContainerDepth = 0, startContainerDepth = 0, p, depthDiff, startNode, endNode, sp, ep;
- if (self[START_CONTAINER] == self[END_CONTAINER]) {
- return _traverseSameContainer(how);
- }
- for (c = self[END_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {
- if (p == self[START_CONTAINER]) {
- return _traverseCommonStartContainer(c, how);
- }
- ++endContainerDepth;
- }
- for (c = self[START_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {
- if (p == self[END_CONTAINER]) {
- return _traverseCommonEndContainer(c, how);
- }
- ++startContainerDepth;
- }
- depthDiff = startContainerDepth - endContainerDepth;
- startNode = self[START_CONTAINER];
- while (depthDiff > 0) {
- startNode = startNode.parentNode;
- depthDiff--;
- }
- endNode = self[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, start, len;
- if (how != DELETE) {
- frag = createDocumentFragment();
- }
- // If selection is empty, just return the fragment
- if (self[START_OFFSET] == self[END_OFFSET]) {
- return frag;
- }
- // Text node needs special case handling
- if (self[START_CONTAINER].nodeType == 3 /* TEXT_NODE */) {
- // get the substring
- s = self[START_CONTAINER].nodeValue;
- sub = s.substring(self[START_OFFSET], self[END_OFFSET]);
- // set the original text node to its new value
- if (how != CLONE) {
- n = self[START_CONTAINER];
- start = self[START_OFFSET];
- len = self[END_OFFSET] - self[START_OFFSET];
- if (start === 0 && len >= n.nodeValue.length - 1) {
- n.parentNode.removeChild(n);
- } else {
- n.deleteData(start, len);
- }
- // Nothing is partially selected, so collapse to start point
- self.collapse(TRUE);
- }
- if (how == DELETE) {
- return;
- }
- if (sub.length > 0) {
- frag.appendChild(doc.createTextNode(sub));
- }
- return frag;
- }
- // Copy nodes between the start/end offsets.
- n = _getSelectedNode(self[START_CONTAINER], self[START_OFFSET]);
- cnt = self[END_OFFSET] - self[START_OFFSET];
- while (n && 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) {
- self.collapse(TRUE);
- }
- return frag;
- }
- function _traverseCommonStartContainer(endAncestor, how) {
- var frag, n, endIdx, cnt, sibling, xferNode;
- if (how != DELETE) {
- frag = createDocumentFragment();
- }
- n = _traverseRightBoundary(endAncestor, how);
- if (frag) {
- frag.appendChild(n);
- }
- endIdx = nodeIndex(endAncestor);
- cnt = endIdx - self[START_OFFSET];
- if (cnt <= 0) {
- // Collapse to just before the endAncestor, which
- // is partially selected.
- if (how != CLONE) {
- self.setEndBefore(endAncestor);
- self.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) {
- self.setEndBefore(endAncestor);
- self.collapse(FALSE);
- }
- return frag;
- }
- function _traverseCommonEndContainer(startAncestor, how) {
- var frag, startIdx, n, cnt, sibling, xferNode;
- if (how != DELETE) {
- frag = createDocumentFragment();
- }
- n = _traverseLeftBoundary(startAncestor, how);
- if (frag) {
- frag.appendChild(n);
- }
- startIdx = nodeIndex(startAncestor);
- ++startIdx; // Because we already traversed it
- cnt = self[END_OFFSET] - startIdx;
- n = startAncestor.nextSibling;
- while (n && cnt > 0) {
- sibling = n.nextSibling;
- xferNode = _traverseFullySelected(n, how);
- if (frag) {
- frag.appendChild(xferNode);
- }
- --cnt;
- n = sibling;
- }
- if (how != CLONE) {
- self.setStartAfter(startAncestor);
- self.collapse(TRUE);
- }
- return frag;
- }
- function _traverseCommonAncestors(startAncestor, endAncestor, how) {
- var n, frag, startOffset, endOffset, cnt, sibling, nextSibling;
- if (how != DELETE) {
- frag = createDocumentFragment();
- }
- n = _traverseLeftBoundary(startAncestor, how);
- if (frag) {
- frag.appendChild(n);
- }
- 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) {
- self.setStartAfter(startAncestor);
- self.collapse(TRUE);
- }
- return frag;
- }
- function _traverseRightBoundary(root, how) {
- var next = _getSelectedNode(self[END_CONTAINER], self[END_OFFSET] - 1), parent, clonedParent;
- var prevSibling, clonedChild, clonedGrandParent, isFullySelected = next != self[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(self[START_CONTAINER], self[START_OFFSET]), isFullySelected = next != self[START_CONTAINER];
- var 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 = self[START_OFFSET];
- newNodeValue = txtValue.substring(offset);
- oldNodeValue = txtValue.substring(0, offset);
- } else {
- offset = self[END_OFFSET];
- newNodeValue = txtValue.substring(0, offset);
- oldNodeValue = txtValue.substring(offset);
- }
- if (how != CLONE) {
- n.nodeValue = oldNodeValue;
- }
- if (how == DELETE) {
- return;
- }
- newNode = dom.clone(n, FALSE);
- newNode.nodeValue = newNodeValue;
- return newNode;
- }
- if (how == DELETE) {
- return;
- }
- return dom.clone(n, FALSE);
- }
- function _traverseFullySelected(n, how) {
- if (how != DELETE) {
- return how == CLONE ? dom.clone(n, TRUE) : n;
- }
- n.parentNode.removeChild(n);
- }
- function toStringIE() {
- return dom.create('body', null, cloneContents()).outerText;
- }
- extend(self, {
- // 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,
- toStringIE: toStringIE
- });
- return self;
- }
- // Older IE versions doesn't let you override toString by it's constructor so we have to stick it in the prototype
- Range.prototype.toString = function() {
- return this.toStringIE();
- };
- return Range;
- });
|