Range.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777
  1. /**
  2. * Range.js
  3. *
  4. * Copyright, Moxiecode Systems AB
  5. * Released under LGPL License.
  6. *
  7. * License: http://www.tinymce.com/license
  8. * Contributing: http://www.tinymce.com/contributing
  9. */
  10. define("tinymce/dom/Range", [
  11. "tinymce/util/Tools"
  12. ], function(Tools) {
  13. // Range constructor
  14. function Range(dom) {
  15. var self = this,
  16. doc = dom.doc,
  17. EXTRACT = 0,
  18. CLONE = 1,
  19. DELETE = 2,
  20. TRUE = true,
  21. FALSE = false,
  22. START_OFFSET = 'startOffset',
  23. START_CONTAINER = 'startContainer',
  24. END_CONTAINER = 'endContainer',
  25. END_OFFSET = 'endOffset',
  26. extend = Tools.extend,
  27. nodeIndex = dom.nodeIndex;
  28. function createDocumentFragment() {
  29. return doc.createDocumentFragment();
  30. }
  31. function setStart(n, o) {
  32. _setEndPoint(TRUE, n, o);
  33. }
  34. function setEnd(n, o) {
  35. _setEndPoint(FALSE, n, o);
  36. }
  37. function setStartBefore(n) {
  38. setStart(n.parentNode, nodeIndex(n));
  39. }
  40. function setStartAfter(n) {
  41. setStart(n.parentNode, nodeIndex(n) + 1);
  42. }
  43. function setEndBefore(n) {
  44. setEnd(n.parentNode, nodeIndex(n));
  45. }
  46. function setEndAfter(n) {
  47. setEnd(n.parentNode, nodeIndex(n) + 1);
  48. }
  49. function collapse(ts) {
  50. if (ts) {
  51. self[END_CONTAINER] = self[START_CONTAINER];
  52. self[END_OFFSET] = self[START_OFFSET];
  53. } else {
  54. self[START_CONTAINER] = self[END_CONTAINER];
  55. self[START_OFFSET] = self[END_OFFSET];
  56. }
  57. self.collapsed = TRUE;
  58. }
  59. function selectNode(n) {
  60. setStartBefore(n);
  61. setEndAfter(n);
  62. }
  63. function selectNodeContents(n) {
  64. setStart(n, 0);
  65. setEnd(n, n.nodeType === 1 ? n.childNodes.length : n.nodeValue.length);
  66. }
  67. function compareBoundaryPoints(h, r) {
  68. var sc = self[START_CONTAINER], so = self[START_OFFSET], ec = self[END_CONTAINER], eo = self[END_OFFSET],
  69. rsc = r.startContainer, rso = r.startOffset, rec = r.endContainer, reo = r.endOffset;
  70. // Check START_TO_START
  71. if (h === 0) {
  72. return _compareBoundaryPoints(sc, so, rsc, rso);
  73. }
  74. // Check START_TO_END
  75. if (h === 1) {
  76. return _compareBoundaryPoints(ec, eo, rsc, rso);
  77. }
  78. // Check END_TO_END
  79. if (h === 2) {
  80. return _compareBoundaryPoints(ec, eo, rec, reo);
  81. }
  82. // Check END_TO_START
  83. if (h === 3) {
  84. return _compareBoundaryPoints(sc, so, rec, reo);
  85. }
  86. }
  87. function deleteContents() {
  88. _traverse(DELETE);
  89. }
  90. function extractContents() {
  91. return _traverse(EXTRACT);
  92. }
  93. function cloneContents() {
  94. return _traverse(CLONE);
  95. }
  96. function insertNode(n) {
  97. var startContainer = this[START_CONTAINER],
  98. startOffset = this[START_OFFSET], nn, o;
  99. // Node is TEXT_NODE or CDATA
  100. if ((startContainer.nodeType === 3 || startContainer.nodeType === 4) && startContainer.nodeValue) {
  101. if (!startOffset) {
  102. // At the start of text
  103. startContainer.parentNode.insertBefore(n, startContainer);
  104. } else if (startOffset >= startContainer.nodeValue.length) {
  105. // At the end of text
  106. dom.insertAfter(n, startContainer);
  107. } else {
  108. // Middle, need to split
  109. nn = startContainer.splitText(startOffset);
  110. startContainer.parentNode.insertBefore(n, nn);
  111. }
  112. } else {
  113. // Insert element node
  114. if (startContainer.childNodes.length > 0) {
  115. o = startContainer.childNodes[startOffset];
  116. }
  117. if (o) {
  118. startContainer.insertBefore(n, o);
  119. } else {
  120. if (startContainer.nodeType == 3) {
  121. dom.insertAfter(n, startContainer);
  122. } else {
  123. startContainer.appendChild(n);
  124. }
  125. }
  126. }
  127. }
  128. function surroundContents(n) {
  129. var f = self.extractContents();
  130. self.insertNode(n);
  131. n.appendChild(f);
  132. self.selectNode(n);
  133. }
  134. function cloneRange() {
  135. return extend(new Range(dom), {
  136. startContainer: self[START_CONTAINER],
  137. startOffset: self[START_OFFSET],
  138. endContainer: self[END_CONTAINER],
  139. endOffset: self[END_OFFSET],
  140. collapsed: self.collapsed,
  141. commonAncestorContainer: self.commonAncestorContainer
  142. });
  143. }
  144. // Private methods
  145. function _getSelectedNode(container, offset) {
  146. var child;
  147. if (container.nodeType == 3 /* TEXT_NODE */) {
  148. return container;
  149. }
  150. if (offset < 0) {
  151. return container;
  152. }
  153. child = container.firstChild;
  154. while (child && offset > 0) {
  155. --offset;
  156. child = child.nextSibling;
  157. }
  158. if (child) {
  159. return child;
  160. }
  161. return container;
  162. }
  163. function _isCollapsed() {
  164. return (self[START_CONTAINER] == self[END_CONTAINER] && self[START_OFFSET] == self[END_OFFSET]);
  165. }
  166. function _compareBoundaryPoints(containerA, offsetA, containerB, offsetB) {
  167. var c, offsetC, n, cmnRoot, childA, childB;
  168. // In the first case the boundary-points have the same container. A is before B
  169. // if its offset is less than the offset of B, A is equal to B if its offset is
  170. // equal to the offset of B, and A is after B if its offset is greater than the
  171. // offset of B.
  172. if (containerA == containerB) {
  173. if (offsetA == offsetB) {
  174. return 0; // equal
  175. }
  176. if (offsetA < offsetB) {
  177. return -1; // before
  178. }
  179. return 1; // after
  180. }
  181. // In the second case a child node C of the container of A is an ancestor
  182. // container of B. In this case, A is before B if the offset of A is less than or
  183. // equal to the index of the child node C and A is after B otherwise.
  184. c = containerB;
  185. while (c && c.parentNode != containerA) {
  186. c = c.parentNode;
  187. }
  188. if (c) {
  189. offsetC = 0;
  190. n = containerA.firstChild;
  191. while (n != c && offsetC < offsetA) {
  192. offsetC++;
  193. n = n.nextSibling;
  194. }
  195. if (offsetA <= offsetC) {
  196. return -1; // before
  197. }
  198. return 1; // after
  199. }
  200. // In the third case a child node C of the container of B is an ancestor container
  201. // of A. In this case, A is before B if the index of the child node C is less than
  202. // the offset of B and A is after B otherwise.
  203. c = containerA;
  204. while (c && c.parentNode != containerB) {
  205. c = c.parentNode;
  206. }
  207. if (c) {
  208. offsetC = 0;
  209. n = containerB.firstChild;
  210. while (n != c && offsetC < offsetB) {
  211. offsetC++;
  212. n = n.nextSibling;
  213. }
  214. if (offsetC < offsetB) {
  215. return -1; // before
  216. }
  217. return 1; // after
  218. }
  219. // In the fourth case, none of three other cases hold: the containers of A and B
  220. // are siblings or descendants of sibling nodes. In this case, A is before B if
  221. // the container of A is before the container of B in a pre-order traversal of the
  222. // Ranges' context tree and A is after B otherwise.
  223. cmnRoot = dom.findCommonAncestor(containerA, containerB);
  224. childA = containerA;
  225. while (childA && childA.parentNode != cmnRoot) {
  226. childA = childA.parentNode;
  227. }
  228. if (!childA) {
  229. childA = cmnRoot;
  230. }
  231. childB = containerB;
  232. while (childB && childB.parentNode != cmnRoot) {
  233. childB = childB.parentNode;
  234. }
  235. if (!childB) {
  236. childB = cmnRoot;
  237. }
  238. if (childA == childB) {
  239. return 0; // equal
  240. }
  241. n = cmnRoot.firstChild;
  242. while (n) {
  243. if (n == childA) {
  244. return -1; // before
  245. }
  246. if (n == childB) {
  247. return 1; // after
  248. }
  249. n = n.nextSibling;
  250. }
  251. }
  252. function _setEndPoint(st, n, o) {
  253. var ec, sc;
  254. if (st) {
  255. self[START_CONTAINER] = n;
  256. self[START_OFFSET] = o;
  257. } else {
  258. self[END_CONTAINER] = n;
  259. self[END_OFFSET] = o;
  260. }
  261. // If one boundary-point of a Range is set to have a root container
  262. // other than the current one for the Range, the Range is collapsed to
  263. // the new position. This enforces the restriction that both boundary-
  264. // points of a Range must have the same root container.
  265. ec = self[END_CONTAINER];
  266. while (ec.parentNode) {
  267. ec = ec.parentNode;
  268. }
  269. sc = self[START_CONTAINER];
  270. while (sc.parentNode) {
  271. sc = sc.parentNode;
  272. }
  273. if (sc == ec) {
  274. // The start position of a Range is guaranteed to never be after the
  275. // end position. To enforce this restriction, if the start is set to
  276. // be at a position after the end, the Range is collapsed to that
  277. // position.
  278. if (_compareBoundaryPoints(self[START_CONTAINER], self[START_OFFSET], self[END_CONTAINER], self[END_OFFSET]) > 0) {
  279. self.collapse(st);
  280. }
  281. } else {
  282. self.collapse(st);
  283. }
  284. self.collapsed = _isCollapsed();
  285. self.commonAncestorContainer = dom.findCommonAncestor(self[START_CONTAINER], self[END_CONTAINER]);
  286. }
  287. function _traverse(how) {
  288. var c, endContainerDepth = 0, startContainerDepth = 0, p, depthDiff, startNode, endNode, sp, ep;
  289. if (self[START_CONTAINER] == self[END_CONTAINER]) {
  290. return _traverseSameContainer(how);
  291. }
  292. for (c = self[END_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {
  293. if (p == self[START_CONTAINER]) {
  294. return _traverseCommonStartContainer(c, how);
  295. }
  296. ++endContainerDepth;
  297. }
  298. for (c = self[START_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {
  299. if (p == self[END_CONTAINER]) {
  300. return _traverseCommonEndContainer(c, how);
  301. }
  302. ++startContainerDepth;
  303. }
  304. depthDiff = startContainerDepth - endContainerDepth;
  305. startNode = self[START_CONTAINER];
  306. while (depthDiff > 0) {
  307. startNode = startNode.parentNode;
  308. depthDiff--;
  309. }
  310. endNode = self[END_CONTAINER];
  311. while (depthDiff < 0) {
  312. endNode = endNode.parentNode;
  313. depthDiff++;
  314. }
  315. // ascend the ancestor hierarchy until we have a common parent.
  316. for (sp = startNode.parentNode, ep = endNode.parentNode; sp != ep; sp = sp.parentNode, ep = ep.parentNode) {
  317. startNode = sp;
  318. endNode = ep;
  319. }
  320. return _traverseCommonAncestors(startNode, endNode, how);
  321. }
  322. function _traverseSameContainer(how) {
  323. var frag, s, sub, n, cnt, sibling, xferNode, start, len;
  324. if (how != DELETE) {
  325. frag = createDocumentFragment();
  326. }
  327. // If selection is empty, just return the fragment
  328. if (self[START_OFFSET] == self[END_OFFSET]) {
  329. return frag;
  330. }
  331. // Text node needs special case handling
  332. if (self[START_CONTAINER].nodeType == 3 /* TEXT_NODE */) {
  333. // get the substring
  334. s = self[START_CONTAINER].nodeValue;
  335. sub = s.substring(self[START_OFFSET], self[END_OFFSET]);
  336. // set the original text node to its new value
  337. if (how != CLONE) {
  338. n = self[START_CONTAINER];
  339. start = self[START_OFFSET];
  340. len = self[END_OFFSET] - self[START_OFFSET];
  341. if (start === 0 && len >= n.nodeValue.length - 1) {
  342. n.parentNode.removeChild(n);
  343. } else {
  344. n.deleteData(start, len);
  345. }
  346. // Nothing is partially selected, so collapse to start point
  347. self.collapse(TRUE);
  348. }
  349. if (how == DELETE) {
  350. return;
  351. }
  352. if (sub.length > 0) {
  353. frag.appendChild(doc.createTextNode(sub));
  354. }
  355. return frag;
  356. }
  357. // Copy nodes between the start/end offsets.
  358. n = _getSelectedNode(self[START_CONTAINER], self[START_OFFSET]);
  359. cnt = self[END_OFFSET] - self[START_OFFSET];
  360. while (n && cnt > 0) {
  361. sibling = n.nextSibling;
  362. xferNode = _traverseFullySelected(n, how);
  363. if (frag) {
  364. frag.appendChild(xferNode);
  365. }
  366. --cnt;
  367. n = sibling;
  368. }
  369. // Nothing is partially selected, so collapse to start point
  370. if (how != CLONE) {
  371. self.collapse(TRUE);
  372. }
  373. return frag;
  374. }
  375. function _traverseCommonStartContainer(endAncestor, how) {
  376. var frag, n, endIdx, cnt, sibling, xferNode;
  377. if (how != DELETE) {
  378. frag = createDocumentFragment();
  379. }
  380. n = _traverseRightBoundary(endAncestor, how);
  381. if (frag) {
  382. frag.appendChild(n);
  383. }
  384. endIdx = nodeIndex(endAncestor);
  385. cnt = endIdx - self[START_OFFSET];
  386. if (cnt <= 0) {
  387. // Collapse to just before the endAncestor, which
  388. // is partially selected.
  389. if (how != CLONE) {
  390. self.setEndBefore(endAncestor);
  391. self.collapse(FALSE);
  392. }
  393. return frag;
  394. }
  395. n = endAncestor.previousSibling;
  396. while (cnt > 0) {
  397. sibling = n.previousSibling;
  398. xferNode = _traverseFullySelected(n, how);
  399. if (frag) {
  400. frag.insertBefore(xferNode, frag.firstChild);
  401. }
  402. --cnt;
  403. n = sibling;
  404. }
  405. // Collapse to just before the endAncestor, which
  406. // is partially selected.
  407. if (how != CLONE) {
  408. self.setEndBefore(endAncestor);
  409. self.collapse(FALSE);
  410. }
  411. return frag;
  412. }
  413. function _traverseCommonEndContainer(startAncestor, how) {
  414. var frag, startIdx, n, cnt, sibling, xferNode;
  415. if (how != DELETE) {
  416. frag = createDocumentFragment();
  417. }
  418. n = _traverseLeftBoundary(startAncestor, how);
  419. if (frag) {
  420. frag.appendChild(n);
  421. }
  422. startIdx = nodeIndex(startAncestor);
  423. ++startIdx; // Because we already traversed it
  424. cnt = self[END_OFFSET] - startIdx;
  425. n = startAncestor.nextSibling;
  426. while (n && cnt > 0) {
  427. sibling = n.nextSibling;
  428. xferNode = _traverseFullySelected(n, how);
  429. if (frag) {
  430. frag.appendChild(xferNode);
  431. }
  432. --cnt;
  433. n = sibling;
  434. }
  435. if (how != CLONE) {
  436. self.setStartAfter(startAncestor);
  437. self.collapse(TRUE);
  438. }
  439. return frag;
  440. }
  441. function _traverseCommonAncestors(startAncestor, endAncestor, how) {
  442. var n, frag, startOffset, endOffset, cnt, sibling, nextSibling;
  443. if (how != DELETE) {
  444. frag = createDocumentFragment();
  445. }
  446. n = _traverseLeftBoundary(startAncestor, how);
  447. if (frag) {
  448. frag.appendChild(n);
  449. }
  450. startOffset = nodeIndex(startAncestor);
  451. endOffset = nodeIndex(endAncestor);
  452. ++startOffset;
  453. cnt = endOffset - startOffset;
  454. sibling = startAncestor.nextSibling;
  455. while (cnt > 0) {
  456. nextSibling = sibling.nextSibling;
  457. n = _traverseFullySelected(sibling, how);
  458. if (frag) {
  459. frag.appendChild(n);
  460. }
  461. sibling = nextSibling;
  462. --cnt;
  463. }
  464. n = _traverseRightBoundary(endAncestor, how);
  465. if (frag) {
  466. frag.appendChild(n);
  467. }
  468. if (how != CLONE) {
  469. self.setStartAfter(startAncestor);
  470. self.collapse(TRUE);
  471. }
  472. return frag;
  473. }
  474. function _traverseRightBoundary(root, how) {
  475. var next = _getSelectedNode(self[END_CONTAINER], self[END_OFFSET] - 1), parent, clonedParent;
  476. var prevSibling, clonedChild, clonedGrandParent, isFullySelected = next != self[END_CONTAINER];
  477. if (next == root) {
  478. return _traverseNode(next, isFullySelected, FALSE, how);
  479. }
  480. parent = next.parentNode;
  481. clonedParent = _traverseNode(parent, FALSE, FALSE, how);
  482. while (parent) {
  483. while (next) {
  484. prevSibling = next.previousSibling;
  485. clonedChild = _traverseNode(next, isFullySelected, FALSE, how);
  486. if (how != DELETE) {
  487. clonedParent.insertBefore(clonedChild, clonedParent.firstChild);
  488. }
  489. isFullySelected = TRUE;
  490. next = prevSibling;
  491. }
  492. if (parent == root) {
  493. return clonedParent;
  494. }
  495. next = parent.previousSibling;
  496. parent = parent.parentNode;
  497. clonedGrandParent = _traverseNode(parent, FALSE, FALSE, how);
  498. if (how != DELETE) {
  499. clonedGrandParent.appendChild(clonedParent);
  500. }
  501. clonedParent = clonedGrandParent;
  502. }
  503. }
  504. function _traverseLeftBoundary(root, how) {
  505. var next = _getSelectedNode(self[START_CONTAINER], self[START_OFFSET]), isFullySelected = next != self[START_CONTAINER];
  506. var parent, clonedParent, nextSibling, clonedChild, clonedGrandParent;
  507. if (next == root) {
  508. return _traverseNode(next, isFullySelected, TRUE, how);
  509. }
  510. parent = next.parentNode;
  511. clonedParent = _traverseNode(parent, FALSE, TRUE, how);
  512. while (parent) {
  513. while (next) {
  514. nextSibling = next.nextSibling;
  515. clonedChild = _traverseNode(next, isFullySelected, TRUE, how);
  516. if (how != DELETE) {
  517. clonedParent.appendChild(clonedChild);
  518. }
  519. isFullySelected = TRUE;
  520. next = nextSibling;
  521. }
  522. if (parent == root) {
  523. return clonedParent;
  524. }
  525. next = parent.nextSibling;
  526. parent = parent.parentNode;
  527. clonedGrandParent = _traverseNode(parent, FALSE, TRUE, how);
  528. if (how != DELETE) {
  529. clonedGrandParent.appendChild(clonedParent);
  530. }
  531. clonedParent = clonedGrandParent;
  532. }
  533. }
  534. function _traverseNode(n, isFullySelected, isLeft, how) {
  535. var txtValue, newNodeValue, oldNodeValue, offset, newNode;
  536. if (isFullySelected) {
  537. return _traverseFullySelected(n, how);
  538. }
  539. if (n.nodeType == 3 /* TEXT_NODE */) {
  540. txtValue = n.nodeValue;
  541. if (isLeft) {
  542. offset = self[START_OFFSET];
  543. newNodeValue = txtValue.substring(offset);
  544. oldNodeValue = txtValue.substring(0, offset);
  545. } else {
  546. offset = self[END_OFFSET];
  547. newNodeValue = txtValue.substring(0, offset);
  548. oldNodeValue = txtValue.substring(offset);
  549. }
  550. if (how != CLONE) {
  551. n.nodeValue = oldNodeValue;
  552. }
  553. if (how == DELETE) {
  554. return;
  555. }
  556. newNode = dom.clone(n, FALSE);
  557. newNode.nodeValue = newNodeValue;
  558. return newNode;
  559. }
  560. if (how == DELETE) {
  561. return;
  562. }
  563. return dom.clone(n, FALSE);
  564. }
  565. function _traverseFullySelected(n, how) {
  566. if (how != DELETE) {
  567. return how == CLONE ? dom.clone(n, TRUE) : n;
  568. }
  569. n.parentNode.removeChild(n);
  570. }
  571. function toStringIE() {
  572. return dom.create('body', null, cloneContents()).outerText;
  573. }
  574. extend(self, {
  575. // Inital states
  576. startContainer: doc,
  577. startOffset: 0,
  578. endContainer: doc,
  579. endOffset: 0,
  580. collapsed: TRUE,
  581. commonAncestorContainer: doc,
  582. // Range constants
  583. START_TO_START: 0,
  584. START_TO_END: 1,
  585. END_TO_END: 2,
  586. END_TO_START: 3,
  587. // Public methods
  588. setStart: setStart,
  589. setEnd: setEnd,
  590. setStartBefore: setStartBefore,
  591. setStartAfter: setStartAfter,
  592. setEndBefore: setEndBefore,
  593. setEndAfter: setEndAfter,
  594. collapse: collapse,
  595. selectNode: selectNode,
  596. selectNodeContents: selectNodeContents,
  597. compareBoundaryPoints: compareBoundaryPoints,
  598. deleteContents: deleteContents,
  599. extractContents: extractContents,
  600. cloneContents: cloneContents,
  601. insertNode: insertNode,
  602. surroundContents: surroundContents,
  603. cloneRange: cloneRange,
  604. toStringIE: toStringIE
  605. });
  606. return self;
  607. }
  608. // Older IE versions doesn't let you override toString by it's constructor so we have to stick it in the prototype
  609. Range.prototype.toString = function() {
  610. return this.toStringIE();
  611. };
  612. return Range;
  613. });