1/***************************************************************************** 2 * Firma: Hehner Reus Systems GmbH 3 * Projekt: com.cerno.basis 4 * Datei: Event.js 5 * Erstellung: 07.03.2016 6 * Verwendung: Hilfsfunktionen und erweitertes Event-Handling. 7 * 8 * $Id: Event.js,v 1.21 2026/03/31 08:29:53 lpo Exp $ 9 * $Author: lpo $ 10 *****************************************************************************/ 11 12/** 13 * Enumeration für unsere eigenen Events. 14 */ 15var HREventNames = { 16 /* 17 * Ein Ajax-Request wurde erfolgreich aufgerufen. 18 */ 19 ajaxload: 'ajax-load', 20 21 /* 22 * Der Wert eines Formelements hat sich geändert. 23 */ 24 formValueChanged: 'HRFormValueChanged', 25 26 /* 27 * Es wurde eine Eingabe in ein Formelement gemacht. 28 */ 29 formInputGiven: 'HRFormInputGiven', 30 31 /* 32 * Eine geschlossene Aufklappbox ist kurz davor sich zu öffnen / ein Reiter wurde angeklickt. 33 */ 34 show: 'show', 35 36 /* 37 * Eine geöffnete Aufklappbox ist kurz davor sich zu schließen. 38 */ 39 hide: 'hide', 40 41 /* 42 * Eine Aufklappbox ist aufgefahren / zugefahren. 43 */ 44 resize: 'resize', 45 46 /* 47 * Falls alle WPEs einer bestimmten Consent-Item-Art geladen werden dürfen. Das 48 * Programhandle der entsprechenden Art wird mit Bindestrich hinter den Eventnamen gesetzt. 49 * 50 * Beispiel: 'unlock-PGMHDL_GoogleMaps'. 51 */ 52 unlock: 'unlock', 53 54 /* 55 * Ein Event, dass Google-Maps dazu veranlasst sich zu initialisieren. 56 */ 57 loadGoogleMaps: 'load-google-maps', 58 59 /* 60 * Wird gefeuert, wenn der Consent-Manager geschlossen wurde. Das bedeutet 61 * insbesondere, dass der Nutzer seine Auswahl getroffen hat. 62 */ 63 consentManagerClosed: 'consent-manager-closed', 64 65 /* 66 * Wird gefeuert, wenn der Aufklapper geöffnet wurde. 67 */ 68 collapsableOpen: 'collapsable-open', 69 70 /* 71 * Wird gefeuert, wenn der Aufklapper geschlossen wurde. 72 */ 73 collapsableClose: 'collapsable-close', 74 75 /* 76 * Wird gefeuert, wenn der Aufklapper bereit ist. 77 */ 78 collapsableReady: 'collapsable-ready', 79 80 /* 81 * Wird gefeuert, wenn in einem Edit-In-Place-Eingabefeld ein neuer Wert übernommen wurde. 82 */ 83 editInPlaceSaveEdit: 'edit-in-place-save-edit', 84 85 /* 86 * Wird gefeuert, wenn die Navigation eingeblendet wird. 87 */ 88 showNavigation: 'show-navigation', 89 90 /* 91 * Wird gefeuert, wenn die Navigation ausgeblendet wird. 92 */ 93 hideNavigation: 'hide-navigation' 94} 95 96/** 97 * Event-Managers für eingebaute (resize, mousemove, etc.) aber auch eigene Events. 98 * Ermöglicht erweiterte Notifications, die über das "normale" Event bubbling 99 * von eingebauten Events hinausgehen. 100 * So können z.B. Events an Kindelemente aber auch beliebige ander Elemente 101 * weitergegeben werden, sofern sie sich zuvor nur registriert haben. 102 */ 103function HREventManagerClass() 104{ 105 this.RegisteredEvents = new Array(); 106 107 this.RegisteredBodyEvents = new Array(); 108} 109 110HREventManagerClass.prototype = 111{ 112 /** 113 * Fügt einen neuen Event-Handler hinzu. 114 * 115 * @param elem Das Element, welches den neuen / die neuen Listener bekommt. 116 * @param eventNames Der Bezeichner des Events, welches am Element hinzugefügt werden 117 * soll. Hier können auch kommasepariert mehrere Events übergeben werden. 118 * @param eventFunc Die Funktion, die beim Auslösen des Events aufgerufen wird. Sie 119 * erhält das aktuelle Event als Funktionsparameter. 120 * @param callConfig Konfigurationsobjekt für weitere Einstellungen: 121 * <ul> 122 * <li>unique: falls true, wird der Listener in Bezug auf das Element und den Elementnamen nur einmalig ergänzt. Default: false.</li> 123 * <li>once: falls true, wird der Listener nur einmal genutzt und dann sofort wieder entfernt. Default: false.</li> 124 * <li>useStandard: Verwendet die Standardfunktion "HRAddEventListener" zum Ergänzen des Events.</li> 125 * </ul> 126 */ 127 addListener : function(elem, eventNames, eventFunc, callConfig) 128 { 129 try 130 { 131 if (elem && eventNames && eventFunc) 132 { 133 var config = {useStandard: true, notifiedByChildren: true, notifiedBySelf: true, notifiedByParents: false, notifiedByAll: false}; 134 if (callConfig != null && typeof(callConfig) == "object") 135 { 136 for (prop in callConfig) 137 { 138 config[prop] = callConfig[prop]; 139 } 140 } 141 142 eventNamesArray = eventNames.split(','); 143 for (let i = 0; i < eventNamesArray.length; i++) 144 { 145 const eventName = eventNamesArray[i].trim(); 146 if (eventName) 147 { 148 if (!this.RegisteredEvents[eventName]) 149 this.RegisteredEvents[eventName] = new Array(); 150 else 151 { 152 // 153 // Einmalige Listener: Der Listener wird nur an das Element gehängt, wenn dort 154 // nicht schon ein Listener des selben Namens hinterlegt wurde. 155 // 156 if (eventName && config.unique) 157 { 158 var registeredEvents = this.RegisteredEvents[eventName]; 159 for (var j = 0; j < registeredEvents.length; j++) 160 { 161 if (registeredEvents[j] && registeredEvents[j].Element && registeredEvents[j].Element === elem) 162 return; 163 } 164 } 165 } 166 167 this.RegisteredEvents[eventName].push(new HREventEntry(elem, eventName, eventFunc, config)); 168 169 if (config.useStandard && typeof(HRDOM.addEventListener) == "function") 170 { 171 let domConfig = {}; 172 if (config.once) 173 domConfig.once = true; 174 175 HRDOM.addEventListener(elem, eventName, eventFunc, domConfig); 176 } 177 } 178 } 179 } 180 } 181 catch (except) 182 { 183 if (typeof(HRLogError) == 'function') 184 HRLogError(except); 185 } 186 }, 187 188 /** 189 * Löst einen Event "zu Fuß" aus. 190 * 191 * @param callConfig Optionale Call-Konfigurationen.
192 * detail: Über dieses Objekt können dem Event weitere Informationen mitgegeben werden. 193 */ 194 dispatchEvent : function(sender, event, callConfig) 195 { 196 try 197 { 198 var config = {bubbles: true, useStandard: true, notifySelf: true, notifyChildren: false, notifyParents: true, notifyAll: false}; 199 if (callConfig != null && typeof(callConfig) == "object") 200 { 201 for (prop in callConfig) 202 { 203 config[prop] = callConfig[prop]; 204 } 205 } 206 var eventObj = null; 207 if (typeof(event) == "string") 208 { 209 var bubbleEvent = false; 210 if (config.notifyParents || config.bubbles) 211 { 212 bubbleEvent = true; 213 } 214 try 215 { 216 eventObj = new CustomEvent(event, {"bubbles": bubbleEvent, "cancelable": true, "detail": config.detail}); 217 } 218 catch (e) 219 { 220 // Unter IE bis Edge ist new Event() noch nicht möglich: 221 if (typeof(document.createEvent) == "function") 222 { 223 eventObj = document.createEvent("Event"); 224 eventObj.initEvent(event, bubbleEvent, true); 225 } 226 else 227 { 228 // Wenn alle Stricke reissen, versuchen wir es mit einem selbstgebastelten Event-Objekt 229 eventObj = {type : event, bubbles : bubbleEvent, cancelable : true, detail: config.detail}; 230 } 231 } 232 } 233 else if (typeof(event) == "object") 234 { 235 eventObj = event; 236 } 237 if (eventObj != null && typeof(eventObj.type) == "string" && typeof(sender) == "object") 238 { 239 if (config.useStandard) 240 { 241 sender.dispatchEvent(eventObj); 242 } 243 if (this.RegisteredEvents[eventObj.type]) 244 { 245 for (var i = 0; i < this.RegisteredEvents[eventObj.type].length; i++) 246 { 247 if (typeof(this.RegisteredEvents[eventObj.type][i]) == "object") 248 { 249 if (config.notifySelf == true && this.RegisteredEvents[eventObj.type][i].Config.notifiedBySelf && sender == this.RegisteredEvents[eventObj.type][i].Element) 250 { 251 this.RegisteredEvents[eventObj.type][i].EventFunction(eventObj, sender); 252 } 253 else if (eventObj.bubbles == true && this.RegisteredEvents[eventObj.type][i].Config.notifiedByChildren && HRIsParentElement(sender, this.RegisteredEvents[eventObj.type][i].Element)) 254 { 255 this.RegisteredEvents[eventObj.type][i].EventFunction(eventObj, sender); 256 } 257 else if (config.notifyChildren == true && this.RegisteredEvents[eventObj.type][i].Config.notifiedByParents && HRIsParentElement(this.RegisteredEvents[eventObj.type][i].Element, sender)) 258 { 259 this.RegisteredEvents[eventObj.type][i].EventFunction(eventObj, sender); 260 } 261 else if (config.notifyAll && this.RegisteredEvents[eventObj.type][i].Config.notifiedByAll) 262 { 263 this.RegisteredEvents[eventObj.type][i].EventFunction(eventObj, sender); 264 } 265 266 // Falls das Event nur ein einziges mal genutzt werden, soll, aus der Event-Schlange entfernen. 267 const registeredEventConfig = this.RegisteredEvents[eventObj.type][i].Config; 268 if (registeredEventConfig && registeredEventConfig.once) 269 { 270 this.RegisteredEvents[eventObj.type].splice(i, 1); 271 i--; 272 } 273 } 274 } 275 } 276 } 277 } 278 catch (except) 279 { 280 if (typeof(HRLogError) == 'function') 281 HRLogError(except); 282 } 283 }, 284 285 /** 286 * Registriert einen Event-Handler auf dem Body. 287 * 288 * HTML-Elemente, die mit einem Event-Handler verknüpft werden sollen, müssen das data-Attribut 289 * <pre> 290 * data-action-<event-Name>="<identifier>" 291 * </pre> 292 * erhalten. 293 * Beispiel: <div data-action-click="device"></div> 294 * 295 * Sollte auf die identifizierende Zeichenkette verzichtet werden, muss am Element zumindest eine ID hinterlegt werden. 296 * Beispiel: <div id="meine_id" data-action-click></div> 297 * 298 * Anmerkung: Warum nicht das sendende Element direkt übergeben? Durch Ajax-Calls wird das sendende Element überschrieben. 299 * Um auch in solchen Fällen das Element korrekt zu identifizieren benötigt es die Zeichenkette des data-action-Attributs. 300 * 301 * @param identifier Die identifizierende Zeichenkette. 302 * @param eventNames Der Bezeichner des Events, welches am Element hinzugefügt werden 303 * soll. Hier können auch kommasepariert mehrere Events übergeben werden. 304 * @param eventFunc Die Funktion, die beim Auslösen des Body-Events aufgerufen wird. Sie 305 * erhält das aktuelle Event als Funktionsparameter. 306 */
307 addBodyListener : function(identifier, eventNames, eventFunc) 308 { 309 try 310 { 311 if (identifier && eventNames && eventFunc) 312 { 313 eventNames.split(',').forEach(eventName => { 314 if (eventName) 315 { 316 if (!this.RegisteredBodyEvents[eventName]) 317 { 318 this.RegisteredBodyEvents[eventName] = new Array(); 319 320 if (typeof(HRAddEventListener) === 'function') 321 HRAddEventListener(window.document.body, eventName, this); 322 } 323 this.RegisteredBodyEvents[eventName].push(new HREventEntry(identifier, eventName, eventFunc, null)); 324 } 325 }); 326 } 327 } 328 catch (except) 329 { 330 if (typeof(HRLogError) === 'function') 331 HRLogError(except); 332 } 333 }, 334 335 addClickListener : function(identifier, eventFunc) 336 { 337 this.addBodyListener(identifier, 'click', eventFunc); 338 }, 339 340 addChangeListener : function(identifier, eventFunc) 341 { 342 this.addBodyListener(identifier, 'change', eventFunc); 343 }, 344 345 addInputListener : function(identifier, eventFunc) 346 { 347 this.addBodyListener(identifier, 'input', eventFunc); 348 }, 349 350 addKeydownListener : function(identifier, eventFunc) 351 { 352 this.addBodyListener(identifier, 'keydown', eventFunc); 353 }, 354 355 addKeyupListener : function(identifier, eventFunc) 356 { 357 this.addBodyListener(identifier, 'keyup', eventFunc); 358 }, 359 360 addMousedownListener : function(identifier, eventFunc) 361 { 362 this.addBodyListener(identifier, 'mousedown', eventFunc); 363 }, 364 365 /** 366 * Generischer Handler für die manuelle Event-Delegation der auf dem Body registrierten Events. 367 * 368 * @param event Das zu behandelnde Event. 369 */ 370 handleEvent : function(event) 371 { 372 // Aus Liste der registrierten Events auf diejenigen einschränken, welche den Type des Events unterstützen. 373 const bodyEvents = this.RegisteredBodyEvents[event.type]; 374 for (let i = 0; i < bodyEvents.length; i++) 375 { 376 if (event.target) 377 { 378 // Dasjenige Element über den Identifizierer heraussuchen, auf dem die Event-Funktion aufgerufen werden soll. 379 let sender = event.target.closest(`[data-action-${event.type}="${bodyEvents[i].Element}"]`); 380 381 // Wurde das Element nicht gefunden, prüfen wir, ob es sich um eine ID handelt. 382 if (!sender) 383 sender = event.target.closest(`#${bodyEvents[i].Element}`); 384 if (sender) 385 { 386 bodyEvents[i].EventFunction(event, sender); 387 break; 388 } 389 } 390 } 391 } 392} 393 394/** 395 * Ein Eintrag in der EventRegistry des EventManagers. 396 * 397 * @param Element Das Element, auf dem das Event ausgelöst wird. Hier kann insbesondere auch eine 398 * das Element identifizierende Zeichenkette übergeben werden. Dies entspricht entweder dem Wert des 399 * data-Attributes data-action-<event-Name> oder die ID des Elements. 400 */ 401function HREventEntry(elem, eventName, eventFunc, config) 402{ 403 this.Element = elem; 404 this.EventName = eventName; 405 this.EventFunction = eventFunc; 406 this.Config = config; 407} 408 409/** 410 * Das eine EventManager-Objekt das zu verwenden ist. 411 */ 412var HREventManager = window.HREventManager = (typeof(window.HREventManager) == 'undefined' ? new HREventManagerClass() : window.HREventManager);
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.