PageSourceSearch

https://www.semor.cz/administrace/assets/js/selectionmenu.js

js semor.cz collected 2026-10-04 13:26:28 UTC 10,847 bytes, 342 lines download raw bytes

1/*
2SelectionMenu 1.1
3http://github.com/molily/selectionmenu
4by molily ([email protected], http://molily.de/)
5
6EN: SelectionMenu displays a context menu when the user selects some text on the page
7DE: SelectionMenu blendet ein Kontextmenü beim Markieren von Text ein
8
9EN: License: Public Domain
10EN: You're allowed to copy, distribute and change the code without restrictions
11
12DE: Lizenz: Public Domain
13DE: Kopieren, Verteilen und Aendern ohne Einschraenkungen erlaubt
14*/
15
16// EN: Create a private scope using an anonymous function,
17// EN: save the return value in a global variable.
18// DE: Erzeuge einen privaten Scope durch eine anonyme Funktion,
19// DE: speichere den Rückgabwert in einer globalen Variable
20var SelectionMenu = (function (window, document) {
21
22	// EN: The menu element which is inserted when selecting text
23	// DE: Das Menü-Element, welche beim Markieren eingefügt wird
24	var span = null;
25
26	// EN: Shared private helper functions
27	// DE: Geteilte private Helferfunktionen
28
29	function addEvent (obj, type, fn) {
30		// EN: Feature dection DOM Events / Microsoft
31		// DE: Fähigkeitenweiche DOM Events / Microsoft
32		if (obj.addEventListener) {
33			obj.addEventListener(type, fn, false);
34		} else if (obj.attachEvent) {
35			obj.attachEvent('on' + type, function () {
36				return fn.call(obj, window.event);
37			});
38		}
39	}
40
41	// EN: Publish addEvent as a static method
42	// EN: (attach it to the constructor object)
43	// DE: Mache addEvent als statische Methode öffentlich
44	// DE: (hefte die Methode an den Konstruktor, der zurückgegeben wird)
45	SelectionMenu.addEvent = addEvent;
46
47	function getSelection () {
48		// EN: Feature dection HTML5 / Microsoft
49		// DE: Fähigkeitenweiche HTML5 / Microsoft
50		if (window.getSelection) {
51			return window.getSelection();
52		} else if (document.selection && document.selection.createRange) {
53			return document.selection.createRange();
54		} else {
55			// EN: No browser support available for the required features
56			// DE: Keine Browser-Unterstützung für die benötigten Features
57			return false;
58		}
59	}
60
61	function getSelectedText (selection) {
62		// EN: Feature detection HTML5 / Microsoft
63		// DE: Fähigkeitenweiche HTML5 / Microsoft
64		return selection.toString ? selection.toString() : selection.text;
65	}
66
67
68	function contains (a, b) {
69		// EN: Feature detection DOM Core / Microsoft
70		// DE: Fähigkeitenweiche DOM Core / Microsoft
71		return a.compareDocumentPosition ? !!(a.compareDocumentPosition(b) & 16) : a.contains(b);
72	}
73
74	function mouseOnMenu (e) {
75		// Greife auf das Zielelement des Ereignisses zu
76		// EN: Feature detection DOM Events / Microsoft
77		// DE: Fähigkeitenweiche DOM Events / Microsoft
78		var target = e.target || e.srcElement;
79		// Ist das Zielelement das Menü oder darin enthalten?
80		return target == span || contains(span, target);
81	}
82
83	// EN: Main constructor function
84	// DE: Konstruktorfunktion
85	function SelectionMenu (options) {
86		var instance = this;
87
88		// EN: Copy members from the options object to the instance
89		// DE: Kopiere Einstellungen aus dem options-Objekt herüber zur Instanz
90		instance.id = options.id || 'selection-menu';
91		instance.menuHTML = options.menuHTML;
92		instance.minimalSelection = options.minimalSelection || 5;
93		instance.container = options.container;
94		instance.handler = options.handler;
95
96		// EN: Initialisation
97		// DE: Initialisiere
98		instance.create();
99		instance.setupEvents();
100	}
101
102	SelectionMenu.prototype = {
103
104		create : function () {
105			var instance = this;
106
107			// EN: Create the menu container if necessary
108			// DE: Erzeuge den Menü-Container, sofern noch nicht passiert
109			if (span) {
110				return;
111			}
112
113			span = document.createElement('span');
114			span.id = instance.id;
115		},
116
117		setupEvents : function () {
118
119			var instance = this;
120			var container = instance.container;
121
122			// EN: Hide the menu on mouse down
123			// DE: Verstecke beim Mousedown
124			addEvent(container, 'mousedown', function (e) {
125				instance.hide(e);
126			});
127
128			// EN: Insert the menu on mouseup given some text is selected
129			// DE: Füge das Menü beim Mouseup ein, wenn Text ausgewählt wurde
130			addEvent(container, 'mouseup', function (e) {
131				instance.insert(e);
132
133				// EN: After a delay, check if the text was deselected
134				// DE: Prüfe nach einer Verzögerung, ob die Auswahl damit aufgehoben wurde
135				window.setTimeout(function () {
136					instance.hideIfNoSelection();
137				}, 0);
138
139			});
140
141			instance.setupMenuEvents();
142		},
143
144		setupMenuEvents : function () {
145			var instance = this;
146
147			// EN: Register the handler for clicks on the menu
148			// DE: Registiere Handlerfunktion für den Klick auf das Menü
149			addEvent(span, 'click', function (e) {
150				instance.handler.call(instance, e);
151				return false;
152			});
153
154			// EN: Prevent IE to select the text of the menu
155			// DE: Verhindere das Markieren des Menüs im IE
156			span.unselectable = true;
157		},
158
159		hide : function (e) {
160			// EN: Abort if an event object was passed and the click hit the menu itself
161			// Breche ab, wenn Event-Objekt übergeben wurde und der Klick beim Menü passierte
162			if (e && mouseOnMenu(e)) {
163				return;
164			}
165			// EN: Is the element attached to the DOM tree?
166			// DE: Ist das Element in den DOM-Baum gehängt?
167			var parent = span.parentNode;
168			if (parent) {
169				// EN: Remove the element from DOM (the element object remains
170				// EN: in memory and will be reused later)
171				// DE: Entferne das element aus dem DOM-Baum (Element bleibt im Speicher erhalten
172				// DE: und wird später wiederverwendet)
173				parent.removeChild(span);
174			}
175		},
176
177		hideIfNoSelection : function () {
178			var instance = this;
179			var selection = getSelection();
180			if (!selection) {
181				return;
182			}
183			var selectedText = getSelectedText(selection);
184			if (!selectedText.length) {
185				instance.hide();
186			}
187		},
188
189		insert : function (e) {
190			var instance = this;
191
192			// EN: Abort if the mouse event occured at the menu itself
193			// DE: Breche ab, wenn das Mausereignis beim Menü passierte
194			if (mouseOnMenu(e)) {
195				return;
196			}
197
198			// EN: Get a Selection object or a TextRange (IE)
199			// DE: Hole Selection bzw. TextRange (IE)
200			var selection = getSelection();
201			if (!selection) {
202				// EN: No browser support available for the required features
203				// DE: Keine Browser-Unterstützung für die benötigten Features
204				return;
205			}
206
207			// EN: Get the selected text
208			// DE: Hole markierten Text
209			var selectedText = getSelectedText(selection);
210			instance.selectedText = selectedText;
211
212			// EN: Abort if the selected text is too short
213			// DE: Breche ab, wenn der markierte Text zu kurz ist
214			if (selectedText.length < instance.minimalSelection) {
215				instance.hide(e);
216				return;
217			}
218
219			// EN : Feature detection DOM Range / Microsoft
220			// DE: Fähigkeitenweiche DOM Range / Microsoft
221			if (selection.getRangeAt) {
222
223				// EN: W3C DOM Range approach
224				// DE: Lösungsansatz mit W3C DOM Range
225
226				// EN: Get the first Range of the current Selection
227				// DE: Hole Range, die zur Selection gehört
228				var range = selection.getRangeAt(0);
229
230				// EN: Get the start and end nodes of the selection
231				// DE: Hole Start- und Endknoten der Auswahl
232				var startNode = range.startContainer;
233				var endNode = range.endContainer;
234
235				if (!(startNode && endNode && startNode.compareDocumentPosition)) {
236					// EN: Abort if we got bogus values or we can't compare their document position
237					// DE: Breche ab, wenn die Knoten nicht brauchbar sind
238					return;
239				}
240
241				// EN: If the start node succeeds the end node in the DOM tree, flip them
242				// DE: Wenn von hinten nach vorne markiert wurde, drehe Start und Ende um
243				if (startNode.compareDocumentPosition(endNode) & 2) {
244					startNode = endNode;
245					endNode = range.startContainer;
246				}
247
248				// EN: Get the end offset
249				// DE: Hole End-Offset
250				var endOffset = range.endOffset;
251
252				// EN: If the end node is an element, use its last text node as the end offset
253				// DE: Falls der Endknoten ein Element ist, nehme das Ende des letzten Textknoten
254				if (endNode.nodeType == 1) {
255					endNode = endNode.lastChild;
256					if (!endNode || endNode.nodeType != 3) {
257						return;
258					}
259					endOffset = endNode.data.length;
260				}
261
262				// EN: Create a new empty Range
263				// DE: Erzeuge neue, leere Range
264				var newRange = document.createRange();
265
266				// EN: Move the beginning of the new Range to the end of the selection
267				// DE: Verschiebe Anfang der neuen Range an das Ende der Auswahl
268				newRange.setStart(endNode, endOffset);
269
270				// EN: Fill the menu span
271				// DE: Befülle das Menü-span
272				span.innerHTML = instance.menuHTML;
273
274				// EN: Inject the span element into the new Range
275				// DE: Füge das span-Element in die neue Range ein
276				newRange.insertNode(span);
277
278				// EN: Adjust the selection by removing and adding the range.
279				// EN: This prevents the selection of the menu text.
280				// DE: Korrigiere Auswahl, verhindere das Markieren des Menüs
281				if (selection.removeRange) {
282					selection.removeRange(range);
283				} else {
284					selection.removeAllRanges();
285				}
286				selection.addRange(range);
287
288			} else if (selection.duplicate) {
289
290				// EN: Microsoft TextRange approach
291				// DE: Lösungsansatz mit Microsoft TextRanges
292
293				// EN: Create a copy the the TextRange
294				// DE: Kopiere TextRange
295				var newRange = selection.duplicate();
296
297				// EN: Move the start of the new range to the end of the selection
298				// DE: Verschiebe den Anfang der neuen Range an das Ende der Auswahl
299				newRange.setEndPoint('StartToEnd', selection);
300
301				// EN: Fill the menu span
302				// DE: Befülle das Menü-span
303				span.innerHTML = instance.menuHTML;
304
305				// EN: Insert the span into the new range
306				// DE: Fülle die neue Range mit dem span
307				newRange.pasteHTML(span.outerHTML);
308
309				// EN: Restore the selection so that the original text is selected
310				// EN: and not the menu
311				// DE: Korrigiere Auswahl und setze sie auf die ursprüngliche Auswahl zurück,
312				// DE: sodass das Menü nicht selektiert ist
313				selection.select();
314
315				// EN: Since we're using outerHTML to insert the span element,
316				// EN: we have to restore the span reference and the event handling
317				// DE: Da das Befüllen nicht über das DOM, sondern über serialisierten HTML-Code erfolgt,
318				// DE: stelle die Referenz und das Event-Handling wieder her
319				span = document.getElementById(id);
320				instance.setupMenuEvents();
321
322			} else {
323				// EN: No browser support available for the required features
324				// DE: Keine Browser-Unterstützung für die benötigten Features
325				return;
326			}
327
328			// EN: Menu positioning
329			// DE: Positioniere Menü
330			instance.position();
331		},
332
333		position : function () {
334			span.style.marginTop = -(span.offsetHeight + 5) + 'px';
335		}
336	};
337
338	// EN: Return the constructor function
339	// DE: Gib den Konstruktor zurück
340	return SelectionMenu;
341
342})(window, document);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.