Browse code

tinyMCE '2' is now 2.1.2 - Seems tinyMCE '2' was actually 3.0.9 (my fault for not checking), so '2' is now 2.1.2, and '3' is now 3.4.8... eFiction 3.4.3 used tinyMCE 2.1.0, and 3.5 upgraded that to 3.0.9.

Clarissa Walker authored on 2026/07/28 07:20:33
Showing 1 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,626 @@
1
+/**
2
+ * $Id$
3
+ *
4
+ * @author Moxiecode
5
+ * @copyright Copyright � 2004-2007, Moxiecode Systems AB, All rights reserved.
6
+ */
7
+
8
+/**
9
+ * Constructs a Selection instance and binds it to the specificed TinyMCE editor control.
10
+ *
11
+ * @param {TinyMCE_Control} inst TinyMCE editor control instance.
12
+ * @constructor
13
+ * @member TinyMCE_Selection
14
+ */
15
+function TinyMCE_Selection(inst) {
16
+	this.instance = inst;
17
+};
18
+
19
+/**#@+
20
+ * @member TinyMCE_Selection
21
+ */
22
+TinyMCE_Selection.prototype = {
23
+	/**#@+
24
+	 * @method
25
+	 */
26
+
27
+	/**
28
+	 * Returns the selected HTML code.
29
+	 *
30
+	 * @return Selected HTML contents.
31
+	 * @type string
32
+	 */
33
+	getSelectedHTML : function() {
34
+		var inst = this.instance, e, r = this.getRng(), h;
35
+
36
+		if (!r)
37
+			return null;
38
+
39
+		e = document.createElement("body");
40
+
41
+		if (r.cloneContents)
42
+			e.appendChild(r.cloneContents());
43
+		else if (typeof(r.item) != 'undefined' || typeof(r.htmlText) != 'undefined')
44
+			e.innerHTML = r.item ? r.item(0).outerHTML : r.htmlText;
45
+		else
46
+			e.innerHTML = r.toString(); // Failed, use text for now
47
+
48
+		h = tinyMCE._cleanupHTML(inst, inst.contentDocument, inst.settings, e, e, false, true, false);
49
+
50
+		// When editing always use fonts internaly
51
+		//if (tinyMCE.getParam("convert_fonts_to_spans"))
52
+		//	tinyMCE.convertSpansToFonts(inst.getDoc());
53
+
54
+		return h;
55
+	},
56
+
57
+	/**
58
+	 * Returns the selected text.
59
+	 *
60
+	 * @return Selected text contents.
61
+	 * @type string
62
+	 */
63
+	getSelectedText : function() {
64
+		var inst = this.instance, d, r, s, t;
65
+
66
+		if (tinyMCE.isIE) {
67
+			d = inst.getDoc();
68
+
69
+			if (d.selection.type == "Text") {
70
+				r = d.selection.createRange();
71
+				t = r.text;
72
+			} else
73
+				t = '';
74
+		} else {
75
+			s = this.getSel();
76
+
77
+			if (s && s.toString)
78
+				t = s.toString();
79
+			else
80
+				t = '';
81
+		}
82
+
83
+		return t;
84
+	},
85
+
86
+	/**
87
+	 * Returns a selection bookmark that can be restored later with moveToBookmark.
88
+	 * This acts much like the one MSIE has built in but this one is persistent if between DOM
89
+	 * tree rewritings. The simple mode enables a quicker and non persistent bookmark.
90
+	 *
91
+	 * @param {boolean} simple If this is set to true, the selection bookmark will not me dom persistent.
92
+	 * @return Selection bookmark that can be restored later with moveToBookmark.
93
+	 * @type TinyMCE_Bookmark
94
+	 */
95
+	getBookmark : function(simple) {
96
+		var inst = this.instance, rng = this.getRng(), doc = inst.getDoc(), b = inst.getBody();
97
+		var trng, sx, sy, xx = -999999999, vp = inst.getViewPort();
98
+		var sp, le, s, e, nl, i, si, ei, w;
99
+
100
+		sx = vp.left;
101
+		sy = vp.top;
102
+
103
+		if (simple)
104
+			return {rng : rng, scrollX : sx, scrollY : sy};
105
+
106
+		if (tinyMCE.isRealIE) {
107
+			if (rng.item) {
108
+				e = rng.item(0);
109
+
110
+				nl = b.getElementsByTagName(e.nodeName);
111
+				for (i=0; i<nl.length; i++) {
112
+					if (e == nl[i]) {
113
+						sp = i;
114
+						break;
115
+					}
116
+				}
117
+
118
+				return {
119
+					tag : e.nodeName,
120
+					index : sp,
121
+					scrollX : sx,
122
+					scrollY : sy
123
+				};
124
+			} else {
125
+				trng = doc.body.createTextRange();
126
+				trng.moveToElementText(inst.getBody());
127
+				trng.collapse(true);
128
+				bp = Math.abs(trng.move('character', xx));
129
+
130
+				trng = rng.duplicate();
131
+				trng.collapse(true);
132
+				sp = Math.abs(trng.move('character', xx));
133
+
134
+				trng = rng.duplicate();
135
+				trng.collapse(false);
136
+				le = Math.abs(trng.move('character', xx)) - sp;
137
+
138
+				return {
139
+					start : sp - bp,
140
+					length : le,
141
+					scrollX : sx,
142
+					scrollY : sy
143
+				};
144
+			}
145
+		} else {
146
+			s = this.getSel();
147
+			e = this.getFocusElement();
148
+
149
+			if (!s)
150
+				return null;
151
+
152
+			if (e && e.nodeName == 'IMG') {
153
+				/*nl = b.getElementsByTagName('IMG');
154
+				for (i=0; i<nl.length; i++) {
155
+					if (e == nl[i]) {
156
+						sp = i;
157
+						break;
158
+					}
159
+				}*/
160
+
161
+				return {
162
+					start : -1,
163
+					end : -1,
164
+					index : sp,
165
+					scrollX : sx,
166
+					scrollY : sy
167
+				};
168
+			}
169
+
170
+			// Caret or selection
171
+			if (s.anchorNode == s.focusNode && s.anchorOffset == s.focusOffset) {
172
+				e = this._getPosText(b, s.anchorNode, s.focusNode);
173
+
174
+				if (!e)
175
+					return {scrollX : sx, scrollY : sy};
176
+
177
+				return {
178
+					start : e.start + s.anchorOffset,
179
+					end : e.end + s.focusOffset,
180
+					scrollX : sx,
181
+					scrollY : sy
182
+				};
183
+			} else {
184
+				e = this._getPosText(b, rng.startContainer, rng.endContainer);
185
+
186
+				if (!e)
187
+					return {scrollX : sx, scrollY : sy};
188
+
189
+				return {
190
+					start : e.start + rng.startOffset,
191
+					end : e.end + rng.endOffset,
192
+					scrollX : sx,
193
+					scrollY : sy
194
+				};
195
+			}
196
+		}
197
+
198
+		return null;
199
+	},
200
+
201
+	/**
202
+	 * Restores the selection to the specified bookmark.
203
+	 *
204
+	 * @param {TinyMCE_Bookmark} bookmark Bookmark to restore selection from.
205
+	 * @return true/false if it was successful or not.
206
+	 * @type boolean
207
+	 */
208
+	moveToBookmark : function(bookmark) {
209
+		var inst = this.instance, rng, nl, i, ex, b = inst.getBody(), sd;
210
+		var doc = inst.getDoc(), win = inst.getWin(), sel = this.getSel();
211
+
212
+		if (!bookmark)
213
+			return false;
214
+
215
+		if (tinyMCE.isSafari && bookmark.rng) {
216
+			sel.setBaseAndExtent(bookmark.rng.startContainer, bookmark.rng.startOffset, bookmark.rng.endContainer, bookmark.rng.endOffset);
217
+			return true;
218
+		}
219
+
220
+		if (tinyMCE.isRealIE) {
221
+			if (bookmark.rng) {
222
+				try {
223
+					bookmark.rng.select();
224
+				} catch (ex) {
225
+					// Ignore
226
+				}
227
+
228
+				return true;
229
+			}
230
+
231
+			win.focus();
232
+
233
+			if (bookmark.tag) {
234
+				rng = b.createControlRange();
235
+
236
+				nl = b.getElementsByTagName(bookmark.tag);
237
+
238
+				if (nl.length > bookmark.index) {
239
+					try {
240
+						rng.addElement(nl[bookmark.index]);
241
+					} catch (ex) {
242
+						// Might be thrown if the node no longer exists
243
+					}
244
+				}
245
+			} else {
246
+				// Try/catch needed since this operation breaks when TinyMCE is placed in hidden divs/tabs
247
+				try {
248
+					// Incorrect bookmark
249
+					if (bookmark.start < 0)
250
+						return true;
251
+
252
+					rng = inst.getSel().createRange();
253
+					rng.moveToElementText(inst.getBody());
254
+					rng.collapse(true);
255
+					rng.moveStart('character', bookmark.start);
256
+					rng.moveEnd('character', bookmark.length);
257
+				} catch (ex) {
258
+					return true;
259
+				}
260
+			}
261
+
262
+			rng.select();
263
+
264
+			win.scrollTo(bookmark.scrollX, bookmark.scrollY);
265
+			return true;
266
+		}
267
+
268
+		if (tinyMCE.isGecko || tinyMCE.isOpera) {
269
+			if (!sel)
270
+				return false;
271
+
272
+			if (bookmark.rng) {
273
+				sel.removeAllRanges();
274
+				sel.addRange(bookmark.rng);
275
+			}
276
+
277
+			if (bookmark.start != -1 && bookmark.end != -1) {
278
+				try {
279
+					sd = this._getTextPos(b, bookmark.start, bookmark.end);
280
+					rng = doc.createRange();
281
+					rng.setStart(sd.startNode, sd.startOffset);
282
+					rng.setEnd(sd.endNode, sd.endOffset);
283
+					sel.removeAllRanges();
284
+					sel.addRange(rng);
285
+
286
+					if (!tinyMCE.isOpera)
287
+						win.focus();
288
+				} catch (ex) {
289
+					// Ignore
290
+				}
291
+			}
292
+
293
+			/*
294
+			if (typeof(bookmark.index) != 'undefined') {
295
+				tinyMCE.selectElements(b, 'IMG', function (n) {
296
+					if (bookmark.index-- == 0) {
297
+						// Select image in Gecko here
298
+					}
299
+
300
+					return false;
301
+				});
302
+			}
303
+			*/
304
+
305
+			win.scrollTo(bookmark.scrollX, bookmark.scrollY);
306
+			return true;
307
+		}
308
+
309
+		return false;
310
+	},
311
+
312
+	/**
313
+	 * Returns the character start and end position of a start and end node. This is Gecko specific logic.
314
+	 *
315
+	 * @param {Element} r Root element to loop through.
316
+	 * @param {Node} sn Start node to get char position of.
317
+	 * @param {Node} en End node to get char position of.
318
+	 * @return Data container with start/end properties.
319
+	 * @type Object
320
+	 * @private
321
+	 */
322
+	_getPosText : function(r, sn, en) {
323
+		var w = document.createTreeWalker(r, NodeFilter.SHOW_TEXT, null, false), n, p = 0, d = {};
324
+
325
+		while ((n = w.nextNode()) != null) {
326
+			if (n == sn)
327
+				d.start = p;
328
+
329
+			if (n == en) {
330
+				d.end = p;
331
+				return d;
332
+			}
333
+
334
+			p += n.nodeValue ? n.nodeValue.length : 0;
335
+		}
336
+
337
+		return null;
338
+	},
339
+
340
+	/**
341
+	 * Returns the node and offset of start and end character positions. This is Gecko specific logic.
342
+	 *
343
+	 * @param {Element} r Root element to loop through.
344
+	 * @param {int} sp Start character position.
345
+	 * @param {Node} ep End character position.
346
+	 * @return Data container with startNode/startOffset/endNode/endOffset properties.
347
+	 * @type Object
348
+	 * @private
349
+	 */
350
+	_getTextPos : function(r, sp, ep) {
351
+		var w = document.createTreeWalker(r, NodeFilter.SHOW_TEXT, null, false), n, p = 0, d = {};
352
+
353
+		while ((n = w.nextNode()) != null) {
354
+			p += n.nodeValue ? n.nodeValue.length : 0;
355
+
356
+			if (p >= sp && !d.startNode) {
357
+				d.startNode = n;
358
+				d.startOffset = sp - (p - n.nodeValue.length);
359
+			}
360
+
361
+			if (p >= ep) {
362
+				d.endNode = n;
363
+				d.endOffset = ep - (p - n.nodeValue.length);
364
+
365
+				return d;
366
+			}
367
+		}
368
+
369
+		return null;
370
+	},
371
+
372
+	/**
373
+	 * Selects the specified node.
374
+	 *
375
+	 * @param {HTMLNode} node Node object to move selection to.
376
+	 * @param {boolean} collapse True/false if it will be collasped.
377
+	 * @param {boolean} select_text_node True/false if the text contents should be selected or not.
378
+	 * @param {boolean} to_start True/false if the collapse should be to start or end of range.
379
+	 */
380
+	selectNode : function(node, collapse, select_text_node, to_start) {
381
+		var inst = this.instance, sel, rng, nodes;
382
+
383
+		if (!node)
384
+			return;
385
+
386
+		if (typeof(collapse) == "undefined")
387
+			collapse = true;
388
+
389
+		if (typeof(select_text_node) == "undefined")
390
+			select_text_node = false;
391
+
392
+		if (typeof(to_start) == "undefined")
393
+			to_start = true;
394
+
395
+		if (inst.settings.auto_resize)
396
+			inst.resizeToContent();
397
+
398
+		if (tinyMCE.isRealIE) {
399
+			rng = inst.getDoc().body.createTextRange();
400
+
401
+			try {
402
+				rng.moveToElementText(node);
403
+
404
+				if (collapse)
405
+					rng.collapse(to_start);
406
+
407
+				rng.select();
408
+			} catch (e) {
409
+				// Throws illigal agrument in MSIE some times
410
+			}
411
+		} else {
412
+			sel = this.getSel();
413
+
414
+			if (!sel)
415
+				return;
416
+
417
+			if (tinyMCE.isSafari) {
418
+				sel.setBaseAndExtent(node, 0, node, node.innerText.length);
419
+
420
+				if (collapse) {
421
+					if (to_start)
422
+						sel.collapseToStart();
423
+					else
424
+						sel.collapseToEnd();
425
+				}
426
+
427
+				this.scrollToNode(node);
428
+
429
+				return;
430
+			}
431
+
432
+			rng = inst.getDoc().createRange();
433
+
434
+			if (select_text_node) {
435
+				// Find first textnode in tree
436
+				nodes = tinyMCE.getNodeTree(node, [], 3);
437
+				if (nodes.length > 0)
438
+					rng.selectNodeContents(nodes[0]);
439
+				else
440
+					rng.selectNodeContents(node);
441
+			} else
442
+				rng.selectNode(node);
443
+
444
+			if (collapse) {
445
+				// Special treatment of textnode collapse
446
+				if (!to_start && node.nodeType == 3) {
447
+					rng.setStart(node, node.nodeValue.length);
448
+					rng.setEnd(node, node.nodeValue.length);
449
+				} else
450
+					rng.collapse(to_start);
451
+			}
452
+
453
+			sel.removeAllRanges();
454
+			sel.addRange(rng);
455
+		}
456
+
457
+		this.scrollToNode(node);
458
+
459
+		// Set selected element
460
+		tinyMCE.selectedElement = null;
461
+		if (node.nodeType == 1)
462
+			tinyMCE.selectedElement = node;
463
+	},
464
+
465
+	/**
466
+	 * Scrolls to the specified node location.
467
+	 *
468
+	 * @param {HTMLNode} node Node to scroll to.
469
+	 */
470
+	scrollToNode : function(node) {
471
+		var inst = this.instance, w = inst.getWin(), vp = inst.getViewPort(), pos = tinyMCE.getAbsPosition(node), cvp, p, cwin;
472
+
473
+		// Only scroll if out of visible area
474
+		if (pos.absLeft < vp.left || pos.absLeft > vp.left + vp.width || pos.absTop < vp.top || pos.absTop > vp.top + (vp.height-25))
475
+			w.scrollTo(pos.absLeft, pos.absTop - vp.height + 25);
476
+
477
+		// Scroll container window
478
+		if (inst.settings.auto_resize) {
479
+			cwin = inst.getContainerWin();
480
+			cvp = tinyMCE.getViewPort(cwin);
481
+			p = this.getAbsPosition(node);
482
+
483
+			if (p.absLeft < cvp.left || p.absLeft > cvp.left + cvp.width || p.absTop < cvp.top || p.absTop > cvp.top + cvp.height)
484
+				cwin.scrollTo(p.absLeft, p.absTop - cvp.height + 25);
485
+		}
486
+	},
487
+
488
+	/**
489
+	 * Returns a global absolute position calculating both the container iframe location and the position within.
490
+	 *
491
+	 * @param {HTMLNode} n node.
492
+	 */
493
+	getAbsPosition : function(n) {
494
+		var pos = tinyMCE.getAbsPosition(n), ipos = tinyMCE.getAbsPosition(this.instance.iframeElement);
495
+
496
+		return {
497
+			absLeft : ipos.absLeft + pos.absLeft,
498
+			absTop : ipos.absTop + pos.absTop
499
+		};
500
+	},
501
+
502
+	/**
503
+	 * Returns the browsers selection instance.
504
+	 *
505
+	 * @return Browser selection instance.
506
+	 * @type DOMSelection
507
+	 */
508
+	getSel : function() {
509
+		var inst = this.instance;
510
+
511
+		if (tinyMCE.isRealIE)
512
+			return inst.getDoc().selection;
513
+
514
+		return inst.contentWindow.getSelection();
515
+	},
516
+
517
+	/**
518
+	 * Returns the browsers selections first range instance.
519
+	 *
520
+	 * @return Browsers selections first range instance.
521
+	 * @type DOMRange
522
+	 */
523
+	getRng : function() {
524
+		var s = this.getSel();
525
+
526
+		if (s == null)
527
+			return null;
528
+
529
+		if (tinyMCE.isRealIE)
530
+			return s.createRange();
531
+
532
+		if (tinyMCE.isSafari && !s.getRangeAt)
533
+			return '' + window.getSelection();
534
+
535
+		if (s.rangeCount > 0)
536
+			return s.getRangeAt(0);
537
+
538
+		return null;
539
+	},
540
+
541
+	/**
542
+	 * Returns true/false if the current selection is collapsed or not.
543
+	 *
544
+	 * @return {bool} true/false if the current selection is collapsed or not.
545
+	 */
546
+	isCollapsed : function() {
547
+		var r = this.getRng();
548
+
549
+		if (r.item)
550
+			return false;
551
+
552
+		return r.boundingWidth == 0 || this.getSel().isCollapsed;
553
+	},
554
+
555
+	/**
556
+	 * Collapses the current selection to start or end.
557
+	 *
558
+	 * @param {bool} b true - to start, false - to end.
559
+	 */
560
+	collapse : function(b) {
561
+		var r = this.getRng(), s = this.getSel();
562
+
563
+		if (r.select) {
564
+			r.collapse(b);
565
+			r.select();
566
+		} else {
567
+			if (b)
568
+				s.collapseToStart();
569
+			else
570
+				s.collapseToEnd();
571
+		}
572
+	},
573
+
574
+	/**
575
+	 * Returns the currently selected/focused element.
576
+	 *
577
+	 * @return Currently selected element.
578
+	 * @type HTMLElement
579
+	 */
580
+	getFocusElement : function() {
581
+		var inst = this.instance, doc, rng, sel, elm;
582
+
583
+		if (tinyMCE.isRealIE) {
584
+			doc = inst.getDoc();
585
+			rng = doc.selection.createRange();
586
+
587
+	//		if (rng.collapse)
588
+	//			rng.collapse(true);
589
+
590
+			elm = rng.item ? rng.item(0) : rng.parentElement();
591
+		} else {
592
+			if (!tinyMCE.isSafari && inst.isHidden())
593
+				return inst.getBody();
594
+
595
+			sel = this.getSel();
596
+			rng = this.getRng();
597
+
598
+			if (!sel || !rng)
599
+				return null;
600
+
601
+			elm = rng.commonAncestorContainer;
602
+			//elm = (sel && sel.anchorNode) ? sel.anchorNode : null;
603
+
604
+			// Handle selection a image or other control like element such as anchors
605
+			if (!rng.collapsed) {
606
+				// Is selection small
607
+				if (rng.startContainer == rng.endContainer) {
608
+					if (rng.startOffset - rng.endOffset < 2) {
609
+						if (rng.startContainer.hasChildNodes())
610
+							elm = rng.startContainer.childNodes[rng.startOffset];
611
+					}
612
+				}
613
+			}
614
+
615
+			// Get the element parent of the node
616
+			elm = tinyMCE.getParentElement(elm);
617
+
618
+			//if (tinyMCE.selectedElement != null && tinyMCE.selectedElement.nodeName.toLowerCase() == "img")
619
+			//	elm = tinyMCE.selectedElement;
620
+		}
621
+
622
+		return elm;
623
+	}
624
+
625
+	/**#@-*/
626
+};