]> git.sur5r.net Git - bacula/bacula/blob - gui/baculum/debian/missing-sources/framework/Web/Javascripts/source/tinymce-405/classes/dom/TridentSelection.js
baculum: Add missing-sources directory in debian metadata structure
[bacula/bacula] / gui / baculum / debian / missing-sources / framework / Web / Javascripts / source / tinymce-405 / classes / dom / TridentSelection.js
1 /**
2  * TridentSelection.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 /**
12  * Selection class for old explorer versions. This one fakes the
13  * native selection object available on modern browsers.
14  *
15  * @class tinymce.dom.TridentSelection
16  */
17 define("tinymce/dom/TridentSelection", [], function() {
18         function Selection(selection) {
19                 var self = this, dom = selection.dom, FALSE = false;
20
21                 function getPosition(rng, start) {
22                         var checkRng, startIndex = 0, endIndex, inside,
23                                 children, child, offset, index, position = -1, parent;
24
25                         // Setup test range, collapse it and get the parent
26                         checkRng = rng.duplicate();
27                         checkRng.collapse(start);
28                         parent = checkRng.parentElement();
29
30                         // Check if the selection is within the right document
31                         if (parent.ownerDocument !== selection.dom.doc) {
32                                 return;
33                         }
34
35                         // IE will report non editable elements as it's parent so look for an editable one
36                         while (parent.contentEditable === "false") {
37                                 parent = parent.parentNode;
38                         }
39
40                         // If parent doesn't have any children then return that we are inside the element
41                         if (!parent.hasChildNodes()) {
42                                 return {node: parent, inside: 1};
43                         }
44
45                         // Setup node list and endIndex
46                         children = parent.children;
47                         endIndex = children.length - 1;
48
49                         // Perform a binary search for the position
50                         while (startIndex <= endIndex) {
51                                 index = Math.floor((startIndex + endIndex) / 2);
52
53                                 // Move selection to node and compare the ranges
54                                 child = children[index];
55                                 checkRng.moveToElementText(child);
56                                 position = checkRng.compareEndPoints(start ? 'StartToStart' : 'EndToEnd', rng);
57
58                                 // Before/after or an exact match
59                                 if (position > 0) {
60                                         endIndex = index - 1;
61                                 } else if (position < 0) {
62                                         startIndex = index + 1;
63                                 } else {
64                                         return {node: child};
65                                 }
66                         }
67
68                         // Check if child position is before or we didn't find a position
69                         if (position < 0) {
70                                 // No element child was found use the parent element and the offset inside that
71                                 if (!child) {
72                                         checkRng.moveToElementText(parent);
73                                         checkRng.collapse(true);
74                                         child = parent;
75                                         inside = true;
76                                 } else {
77                                         checkRng.collapse(false);
78                                 }
79
80                                 // Walk character by character in text node until we hit the selected range endpoint,
81                                 // hit the end of document or parent isn't the right one
82                                 // We need to walk char by char since rng.text or rng.htmlText will trim line endings
83                                 offset = 0;
84                                 while (checkRng.compareEndPoints(start ? 'StartToStart' : 'StartToEnd', rng) !== 0) {
85                                         if (checkRng.move('character', 1) === 0 || parent != checkRng.parentElement()) {
86                                                 break;
87                                         }
88
89                                         offset++;
90                                 }
91                         } else {
92                                 // Child position is after the selection endpoint
93                                 checkRng.collapse(true);
94
95                                 // Walk character by character in text node until we hit the selected range endpoint, hit
96                                 // the end of document or parent isn't the right one
97                                 offset = 0;
98                                 while (checkRng.compareEndPoints(start ? 'StartToStart' : 'StartToEnd', rng) !== 0) {
99                                         if (checkRng.move('character', -1) === 0 || parent != checkRng.parentElement()) {
100                                                 break;
101                                         }
102
103                                         offset++;
104                                 }
105                         }
106
107                         return {node: child, position: position, offset: offset, inside: inside};
108                 }
109
110                 // Returns a W3C DOM compatible range object by using the IE Range API
111                 function getRange() {
112                         var ieRange = selection.getRng(), domRange = dom.createRng(), element, collapsed, tmpRange, element2, bookmark;
113
114                         // If selection is outside the current document just return an empty range
115                         element = ieRange.item ? ieRange.item(0) : ieRange.parentElement();
116                         if (element.ownerDocument != dom.doc) {
117                                 return domRange;
118                         }
119
120                         collapsed = selection.isCollapsed();
121
122                         // Handle control selection
123                         if (ieRange.item) {
124                                 domRange.setStart(element.parentNode, dom.nodeIndex(element));
125                                 domRange.setEnd(domRange.startContainer, domRange.startOffset + 1);
126
127                                 return domRange;
128                         }
129
130                         function findEndPoint(start) {
131                                 var endPoint = getPosition(ieRange, start), container, offset, textNodeOffset = 0, sibling, undef, nodeValue;
132
133                                 container = endPoint.node;
134                                 offset = endPoint.offset;
135
136                                 if (endPoint.inside && !container.hasChildNodes()) {
137                                         domRange[start ? 'setStart' : 'setEnd'](container, 0);
138                                         return;
139                                 }
140
141                                 if (offset === undef) {
142                                         domRange[start ? 'setStartBefore' : 'setEndAfter'](container);
143                                         return;
144                                 }
145
146                                 if (endPoint.position < 0) {
147                                         sibling = endPoint.inside ? container.firstChild : container.nextSibling;
148
149                                         if (!sibling) {
150                                                 domRange[start ? 'setStartAfter' : 'setEndAfter'](container);
151                                                 return;
152                                         }
153
154                                         if (!offset) {
155                                                 if (sibling.nodeType == 3) {
156                                                         domRange[start ? 'setStart' : 'setEnd'](sibling, 0);
157                                                 } else {
158                                                         domRange[start ? 'setStartBefore' : 'setEndBefore'](sibling);
159                                                 }
160
161                                                 return;
162                                         }
163
164                                         // Find the text node and offset
165                                         while (sibling) {
166                                                 nodeValue = sibling.nodeValue;
167                                                 textNodeOffset += nodeValue.length;
168
169                                                 // We are at or passed the position we where looking for
170                                                 if (textNodeOffset >= offset) {
171                                                         container = sibling;
172                                                         textNodeOffset -= offset;
173                                                         textNodeOffset = nodeValue.length - textNodeOffset;
174                                                         break;
175                                                 }
176
177                                                 sibling = sibling.nextSibling;
178                                         }
179                                 } else {
180                                         // Find the text node and offset
181                                         sibling = container.previousSibling;
182
183                                         if (!sibling) {
184                                                 return domRange[start ? 'setStartBefore' : 'setEndBefore'](container);
185                                         }
186
187                                         // If there isn't any text to loop then use the first position
188                                         if (!offset) {
189                                                 if (container.nodeType == 3) {
190                                                         domRange[start ? 'setStart' : 'setEnd'](sibling, container.nodeValue.length);
191                                                 } else {
192                                                         domRange[start ? 'setStartAfter' : 'setEndAfter'](sibling);
193                                                 }
194
195                                                 return;
196                                         }
197
198                                         while (sibling) {
199                                                 textNodeOffset += sibling.nodeValue.length;
200
201                                                 // We are at or passed the position we where looking for
202                                                 if (textNodeOffset >= offset) {
203                                                         container = sibling;
204                                                         textNodeOffset -= offset;
205                                                         break;
206                                                 }
207
208                                                 sibling = sibling.previousSibling;
209                                         }
210                                 }
211
212                                 domRange[start ? 'setStart' : 'setEnd'](container, textNodeOffset);
213                         }
214
215                         try {
216                                 // Find start point
217                                 findEndPoint(true);
218
219                                 // Find end point if needed
220                                 if (!collapsed) {
221                                         findEndPoint();
222                                 }
223                         } catch (ex) {
224                                 // IE has a nasty bug where text nodes might throw "invalid argument" when you
225                                 // access the nodeValue or other properties of text nodes. This seems to happend when
226                                 // text nodes are split into two nodes by a delete/backspace call. So lets detect it and try to fix it.
227                                 if (ex.number == -2147024809) {
228                                         // Get the current selection
229                                         bookmark = self.getBookmark(2);
230
231                                         // Get start element
232                                         tmpRange = ieRange.duplicate();
233                                         tmpRange.collapse(true);
234                                         element = tmpRange.parentElement();
235
236                                         // Get end element
237                                         if (!collapsed) {
238                                                 tmpRange = ieRange.duplicate();
239                                                 tmpRange.collapse(false);
240                                                 element2 = tmpRange.parentElement();
241                                                 element2.innerHTML = element2.innerHTML;
242                                         }
243
244                                         // Remove the broken elements
245                                         element.innerHTML = element.innerHTML;
246
247                                         // Restore the selection
248                                         self.moveToBookmark(bookmark);
249
250                                         // Since the range has moved we need to re-get it
251                                         ieRange = selection.getRng();
252
253                                         // Find start point
254                                         findEndPoint(true);
255
256                                         // Find end point if needed
257                                         if (!collapsed) {
258                                                 findEndPoint();
259                                         }
260                                 } else {
261                                         throw ex; // Throw other errors
262                                 }
263                         }
264
265                         return domRange;
266                 }
267
268                 this.getBookmark = function(type) {
269                         var rng = selection.getRng(), bookmark = {};
270
271                         function getIndexes(node) {
272                                 var parent, root, children, i, indexes = [];
273
274                                 parent = node.parentNode;
275                                 root = dom.getRoot().parentNode;
276
277                                 while (parent != root && parent.nodeType !== 9) {
278                                         children = parent.children;
279
280                                         i = children.length;
281                                         while (i--) {
282                                                 if (node === children[i]) {
283                                                         indexes.push(i);
284                                                         break;
285                                                 }
286                                         }
287
288                                         node = parent;
289                                         parent = parent.parentNode;
290                                 }
291
292                                 return indexes;
293                         }
294
295                         function getBookmarkEndPoint(start) {
296                                 var position;
297
298                                 position = getPosition(rng, start);
299                                 if (position) {
300                                         return {
301                                                 position: position.position,
302                                                 offset: position.offset,
303                                                 indexes: getIndexes(position.node),
304                                                 inside: position.inside
305                                         };
306                                 }
307                         }
308
309                         // Non ubstructive bookmark
310                         if (type === 2) {
311                                 // Handle text selection
312                                 if (!rng.item) {
313                                         bookmark.start = getBookmarkEndPoint(true);
314
315                                         if (!selection.isCollapsed()) {
316                                                 bookmark.end = getBookmarkEndPoint();
317                                         }
318                                 } else {
319                                         bookmark.start = {ctrl: true, indexes: getIndexes(rng.item(0))};
320                                 }
321                         }
322
323                         return bookmark;
324                 };
325
326                 this.moveToBookmark = function(bookmark) {
327                         var rng, body = dom.doc.body;
328
329                         function resolveIndexes(indexes) {
330                                 var node, i, idx, children;
331
332                                 node = dom.getRoot();
333                                 for (i = indexes.length - 1; i >= 0; i--) {
334                                         children = node.children;
335                                         idx = indexes[i];
336
337                                         if (idx <= children.length - 1) {
338                                                 node = children[idx];
339                                         }
340                                 }
341
342                                 return node;
343                         }
344
345                         function setBookmarkEndPoint(start) {
346                                 var endPoint = bookmark[start ? 'start' : 'end'], moveLeft, moveRng, undef, offset;
347
348                                 if (endPoint) {
349                                         moveLeft = endPoint.position > 0;
350
351                                         moveRng = body.createTextRange();
352                                         moveRng.moveToElementText(resolveIndexes(endPoint.indexes));
353
354                                         offset = endPoint.offset;
355                                         if (offset !== undef) {
356                                                 moveRng.collapse(endPoint.inside || moveLeft);
357                                                 moveRng.moveStart('character', moveLeft ? -offset : offset);
358                                         } else {
359                                                 moveRng.collapse(start);
360                                         }
361
362                                         rng.setEndPoint(start ? 'StartToStart' : 'EndToStart', moveRng);
363
364                                         if (start) {
365                                                 rng.collapse(true);
366                                         }
367                                 }
368                         }
369
370                         if (bookmark.start) {
371                                 if (bookmark.start.ctrl) {
372                                         rng = body.createControlRange();
373                                         rng.addElement(resolveIndexes(bookmark.start.indexes));
374                                         rng.select();
375                                 } else {
376                                         rng = body.createTextRange();
377                                         setBookmarkEndPoint(true);
378                                         setBookmarkEndPoint();
379                                         rng.select();
380                                 }
381                         }
382                 };
383
384                 this.addRange = function(rng) {
385                         var ieRng, ctrlRng, startContainer, startOffset, endContainer, endOffset, sibling,
386                                 doc = selection.dom.doc, body = doc.body, nativeRng, ctrlElm;
387
388                         function setEndPoint(start) {
389                                 var container, offset, marker, tmpRng, nodes;
390
391                                 marker = dom.create('a');
392                                 container = start ? startContainer : endContainer;
393                                 offset = start ? startOffset : endOffset;
394                                 tmpRng = ieRng.duplicate();
395
396                                 if (container == doc || container == doc.documentElement) {
397                                         container = body;
398                                         offset = 0;
399                                 }
400
401                                 if (container.nodeType == 3) {
402                                         container.parentNode.insertBefore(marker, container);
403                                         tmpRng.moveToElementText(marker);
404                                         tmpRng.moveStart('character', offset);
405                                         dom.remove(marker);
406                                         ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng);
407                                 } else {
408                                         nodes = container.childNodes;
409
410                                         if (nodes.length) {
411                                                 if (offset >= nodes.length) {
412                                                         dom.insertAfter(marker, nodes[nodes.length - 1]);
413                                                 } else {
414                                                         container.insertBefore(marker, nodes[offset]);
415                                                 }
416
417                                                 tmpRng.moveToElementText(marker);
418                                         } else if (container.canHaveHTML) {
419                                                 // Empty node selection for example <div>|</div>
420                                                 // Setting innerHTML with a span marker then remove that marker seems to keep empty block elements open
421                                                 container.innerHTML = '<span>&#xFEFF;</span>';
422                                                 marker = container.firstChild;
423                                                 tmpRng.moveToElementText(marker);
424                                                 tmpRng.collapse(FALSE); // Collapse false works better than true for some odd reason
425                                         }
426
427                                         ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng);
428                                         dom.remove(marker);
429                                 }
430                         }
431
432                         // Setup some shorter versions
433                         startContainer = rng.startContainer;
434                         startOffset = rng.startOffset;
435                         endContainer = rng.endContainer;
436                         endOffset = rng.endOffset;
437                         ieRng = body.createTextRange();
438
439                         // If single element selection then try making a control selection out of it
440                         if (startContainer == endContainer && startContainer.nodeType == 1) {
441                                 // Trick to place the caret inside an empty block element like <p></p>
442                                 if (startOffset == endOffset && !startContainer.hasChildNodes()) {
443                                         if (startContainer.canHaveHTML) {
444                                                 // Check if previous sibling is an empty block if it is then we need to render it
445                                                 // IE would otherwise move the caret into the sibling instead of the empty startContainer see: #5236
446                                                 // Example this: <p></p><p>|</p> would become this: <p>|</p><p></p>
447                                                 sibling = startContainer.previousSibling;
448                                                 if (sibling && !sibling.hasChildNodes() && dom.isBlock(sibling)) {
449                                                         sibling.innerHTML = '&#xFEFF;';
450                                                 } else {
451                                                         sibling = null;
452                                                 }
453
454                                                 startContainer.innerHTML = '<span>&#xFEFF;</span><span>&#xFEFF;</span>';
455                                                 ieRng.moveToElementText(startContainer.lastChild);
456                                                 ieRng.select();
457                                                 dom.doc.selection.clear();
458                                                 startContainer.innerHTML = '';
459
460                                                 if (sibling) {
461                                                         sibling.innerHTML = '';
462                                                 }
463                                                 return;
464                                         } else {
465                                                 startOffset = dom.nodeIndex(startContainer);
466                                                 startContainer = startContainer.parentNode;
467                                         }
468                                 }
469
470                                 if (startOffset == endOffset - 1) {
471                                         try {
472                                                 ctrlElm = startContainer.childNodes[startOffset];
473                                                 ctrlRng = body.createControlRange();
474                                                 ctrlRng.addElement(ctrlElm);
475                                                 ctrlRng.select();
476
477                                                 // Check if the range produced is on the correct element and is a control range
478                                                 // On IE 8 it will select the parent contentEditable container if you select an inner element see: #5398
479                                                 nativeRng = selection.getRng();
480                                                 if (nativeRng.item && ctrlElm === nativeRng.item(0)) {
481                                                         return;
482                                                 }
483                                         } catch (ex) {
484                                                 // Ignore
485                                         }
486                                 }
487                         }
488
489                         // Set start/end point of selection
490                         setEndPoint(true);
491                         setEndPoint();
492
493                         // Select the new range and scroll it into view
494                         ieRng.select();
495                 };
496
497                 // Expose range method
498                 this.getRangeAt = getRange;
499         }
500
501         return Selection;
502 });