]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/javascript/tiny_mce/classes/dom/RangeUtils.js
Release 6.5.0
[Github/sugarcrm.git] / include / javascript / tiny_mce / classes / dom / RangeUtils.js
1 /**
2  * Range.js
3  *
4  * Copyright 2009, Moxiecode Systems AB
5  * Released under LGPL License.
6  *
7  * License: http://tinymce.moxiecode.com/license
8  * Contributing: http://tinymce.moxiecode.com/contributing
9  */
10
11 (function(tinymce) {
12         tinymce.dom.RangeUtils = function(dom) {
13                 var INVISIBLE_CHAR = '\uFEFF';
14
15                 /**
16                  * Walks the specified range like object and executes the callback for each sibling collection it finds.
17                  *
18                  * @param {Object} rng Range like object.
19                  * @param {function} callback Callback function to execute for each sibling collection.
20                  */
21                 this.walk = function(rng, callback) {
22                         var startContainer = rng.startContainer,
23                                 startOffset = rng.startOffset,
24                                 endContainer = rng.endContainer,
25                                 endOffset = rng.endOffset,
26                                 ancestor, startPoint,
27                                 endPoint, node, parent, siblings, nodes;
28
29                         // Handle table cell selection the table plugin enables
30                         // you to fake select table cells and perform formatting actions on them
31                         nodes = dom.select('td.mceSelected,th.mceSelected');
32                         if (nodes.length > 0) {
33                                 tinymce.each(nodes, function(node) {
34                                         callback([node]);
35                                 });
36
37                                 return;
38                         }
39
40                         /**
41                          * Collects siblings
42                          *
43                          * @private
44                          * @param {Node} node Node to collect siblings from.
45                          * @param {String} name Name of the sibling to check for.
46                          * @return {Array} Array of collected siblings.
47                          */
48                         function collectSiblings(node, name, end_node) {
49                                 var siblings = [];
50
51                                 for (; node && node != end_node; node = node[name])
52                                         siblings.push(node);
53
54                                 return siblings;
55                         };
56
57                         /**
58                          * Find an end point this is the node just before the common ancestor root.
59                          *
60                          * @private
61                          * @param {Node} node Node to start at.
62                          * @param {Node} root Root/ancestor element to stop just before.
63                          * @return {Node} Node just before the root element.
64                          */
65                         function findEndPoint(node, root) {
66                                 do {
67                                         if (node.parentNode == root)
68                                                 return node;
69
70                                         node = node.parentNode;
71                                 } while(node);
72                         };
73
74                         function walkBoundary(start_node, end_node, next) {
75                                 var siblingName = next ? 'nextSibling' : 'previousSibling';
76
77                                 for (node = start_node, parent = node.parentNode; node && node != end_node; node = parent) {
78                                         parent = node.parentNode;
79                                         siblings = collectSiblings(node == start_node ? node : node[siblingName], siblingName);
80
81                                         if (siblings.length) {
82                                                 if (!next)
83                                                         siblings.reverse();
84
85                                                 callback(siblings);
86                                         }
87                                 }
88                         };
89
90                         // If index based start position then resolve it
91                         if (startContainer.nodeType == 1 && startContainer.hasChildNodes())
92                                 startContainer = startContainer.childNodes[startOffset];
93
94                         // If index based end position then resolve it
95                         if (endContainer.nodeType == 1 && endContainer.hasChildNodes())
96                                 endContainer = endContainer.childNodes[Math.min(endOffset - 1, endContainer.childNodes.length - 1)];
97
98                         // Find common ancestor and end points
99                         ancestor = dom.findCommonAncestor(startContainer, endContainer);
100
101                         // Same container
102                         if (startContainer == endContainer)
103                                 return callback([startContainer]);
104
105                         // Process left side
106                         for (node = startContainer; node; node = node.parentNode) {
107                                 if (node == endContainer)
108                                         return walkBoundary(startContainer, ancestor, true);
109
110                                 if (node == ancestor)
111                                         break;
112                         }
113
114                         // Process right side
115                         for (node = endContainer; node; node = node.parentNode) {
116                                 if (node == startContainer)
117                                         return walkBoundary(endContainer, ancestor);
118
119                                 if (node == ancestor)
120                                         break;
121                         }
122
123                         // Find start/end point
124                         startPoint = findEndPoint(startContainer, ancestor) || startContainer;
125                         endPoint = findEndPoint(endContainer, ancestor) || endContainer;
126
127                         // Walk left leaf
128                         walkBoundary(startContainer, startPoint, true);
129
130                         // Walk the middle from start to end point
131                         siblings = collectSiblings(
132                                 startPoint == startContainer ? startPoint : startPoint.nextSibling,
133                                 'nextSibling',
134                                 endPoint == endContainer ? endPoint.nextSibling : endPoint
135                         );
136
137                         if (siblings.length)
138                                 callback(siblings);
139
140                         // Walk right leaf
141                         walkBoundary(endContainer, endPoint);
142                 };
143
144                 /**
145                  * Splits the specified range at it's start/end points.
146                  *
147                  * @param {Range/RangeObject} rng Range to split.
148                  * @return {Object} Range position object.
149                  */
150 /*              this.split = function(rng) {
151                         var startContainer = rng.startContainer,
152                                 startOffset = rng.startOffset,
153                                 endContainer = rng.endContainer,
154                                 endOffset = rng.endOffset;
155
156                         function splitText(node, offset) {
157                                 if (offset == node.nodeValue.length)
158                                         node.appendData(INVISIBLE_CHAR);
159
160                                 node = node.splitText(offset);
161
162                                 if (node.nodeValue === INVISIBLE_CHAR)
163                                         node.nodeValue = '';
164
165                                 return node;
166                         };
167
168                         // Handle single text node
169                         if (startContainer == endContainer) {
170                                 if (startContainer.nodeType == 3) {
171                                         if (startOffset != 0)
172                                                 startContainer = endContainer = splitText(startContainer, startOffset);
173
174                                         if (endOffset - startOffset != startContainer.nodeValue.length)
175                                                 splitText(startContainer, endOffset - startOffset);
176                                 }
177                         } else {
178                                 // Split startContainer text node if needed
179                                 if (startContainer.nodeType == 3 && startOffset != 0) {
180                                         startContainer = splitText(startContainer, startOffset);
181                                         startOffset = 0;
182                                 }
183
184                                 // Split endContainer text node if needed
185                                 if (endContainer.nodeType == 3 && endOffset != endContainer.nodeValue.length) {
186                                         endContainer = splitText(endContainer, endOffset).previousSibling;
187                                         endOffset = endContainer.nodeValue.length;
188                                 }
189                         }
190
191                         return {
192                                 startContainer : startContainer,
193                                 startOffset : startOffset,
194                                 endContainer : endContainer,
195                                 endOffset : endOffset
196                         };
197                 };
198 */
199         };
200
201         /**
202          * Compares two ranges and checks if they are equal.
203          *
204          * @static
205          * @param {DOMRange} rng1 First range to compare.
206          * @param {DOMRange} rng2 First range to compare.
207          * @return {Boolean} true/false if the ranges are equal.
208          */
209         tinymce.dom.RangeUtils.compareRanges = function(rng1, rng2) {
210                 if (rng1 && rng2) {
211                         // Compare native IE ranges
212                         if (rng1.item || rng1.duplicate) {
213                                 // Both are control ranges and the selected element matches
214                                 if (rng1.item && rng2.item && rng1.item(0) === rng2.item(0))
215                                         return true;
216
217                                 // Both are text ranges and the range matches
218                                 if (rng1.isEqual && rng2.isEqual && rng2.isEqual(rng1))
219                                         return true;
220                         } else {
221                                 // Compare w3c ranges
222                                 return rng1.startContainer == rng2.startContainer && rng1.startOffset == rng2.startOffset;
223                         }
224                 }
225
226                 return false;
227         };
228 })(tinymce);