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