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.