PageSourceSearch

https://www.bergbahn-heidelberg.de/en/static/js/HRSysLib/messagehandlers/messagehandler.js?nc=1788947158158

js bergbahn-heidelberg.de collected 2026-09-24 18:57:19 UTC 37,699 bytes, 922 lines download raw bytes

1/*****************************************************************************
2// Firma:        Hehner Reus Systems GmbH
3// Projekt:      com.cerno.basis
4// Datei:        messagehandler.js
5// Erstellung:   05.10.2022
6// Verwendung:   Dieses Objekt verwaltet das Messagehandling auf der Seite.
7/**************************************************************************** */
8
9"use strict";
10
11/**
12 * Die möglichen Level einer Nachricht.
13 */
14const HRMessageLevel = {
15   /*
16    * "Nur" eine Information.
17    */
18   Info: "info",
19   
20   /*
21    * Eine Warnung. Keine Aktion des Nutzers zwingend notwendig.
22    */
23   Warning: "warning",
24   
25   /*
26    * Eine Fehlerausgabe. Hier muss eine Korrektur erfolgen.
27    */
28   Error: "error"
29}
30
31/**
32 * Die möglichen Arten der im JSON übergebenen Quellen der Nachricht.
33 */
34const SourceTypes = {
35   /*
36    * Nachricht aus unbekannter Quelle.
37    */
38   Unknown: "unknown",
39   
40   /*
41    * Die Nachricht ist an einem Attribut aufgeschlagen. In diesem Fall wird versucht in der Oberfläche 
42    * ein Eingabefeld zu finden, welches mit diesem Attribut verknüpft ist und bezüglich dessen Feldrahmen
43    * die Nachricht ausgegeben werden kann.
44    */
45   Attribut: "attribut",
46   
47   /*
48    * Die Nachricht ist von einer Anwendung ausgelöst worden. Es wird der Hashcode der entsprechenden Instanz
49    * übergeben und versucht die Nachricht unterhalb des div-Elements mit dem data-Attribut "data-instance-id"
50    * anzuzeigen.
51    */
52   Anwendung: "anwendung"
53}
54
55/**
56 * Die möglichen Darstellungsformen der Nachrichten.
57 */
58const HRMessageDisplayTypes = {
59   /*
60    * Nachrichten werden als p-Tags angelegt. Zur Verwendung dieser Darstellungsform muss über das jeweilige
61    * Konfigurationsobjekt zwingend ein parent-Element angegeben werden, unter welchem die Nachricht 
62    * erzeugt werden soll.
63    */
64   Inline: "inline",
65   
66   /*
67    * Nachrichten werden als Toast-Notification angezeigt. 
68    */
69   Toast: "toast",
70   
71   /*
72    * Nachrichten werden in einer i-Box angezeigt. 
73    */
74   IBox: "ibox",
75   
76   /*
77    * Nachrichten werden im alert-Dialog des Browsers dargestellt. 
78    */
79   Alert: "alert",
80   
81   /*
82    * Nachrichten werden als Tooltip angezeigt. Zur Verwendung dieser Darstellungsform muss über das jeweilige
83    * Konfigurationsobjekt zwingend ein parent-Element angegeben werden, an dem das Tooltip erzeugt werden soll.
84    */
85   Tooltip: "tooltip"
86}
87
88var HRDefaultTextTypeError = {
89   RequiredCheckFailed: 'Bitte füllen Sie alle Pflichtfelder aus.'
90}
91
92/**
93 * Steuert das Anzeigen einer Nachricht. Die konkrete Darstellungsform (siehe HRMessageDisplayTypes) 
94 */
95class HRMessageHandler
96{
97   /*
98    * Die Standard-Darstellungsform von Nachrichten.
99    */
100   defaultDisplayType = HRMessageDisplayTypes.Inline;
101   
102   /*
103    * Schalter für den Debug-Modus: Hier werden diverse Konsolenausgaben gemacht.  
104    * Dazu einfach in der createDefaultHandler true übergeben.
105    */
106   #debugMode = false;
107   
108   /*
109    * Die Standard-Darstellungsform von Formularfeldern.
110    */
111   defaultFieldDisplayType = HRMessageDisplayTypes.Inline;
112   
113   /*
114    * Das HTML-Element, unter welches ohne Angabe einer parent-Konfiguraton die Nachrichten abgelegt werden.
115    */
116   defaultParentElement;
117   
118   /*
119    * In dieser Liste werden alle Nachrichten abgelegt. Ein Tupel besteht aus einer ID, die einem Hashcode bestehend aus diversen Nachrichteninfos entspricht, und 
120    * einem Nachrichtenobjekt. Letzteres hängt vom Nachrichtenskin ab und kann z.B. eine Instanz der Toast-Lib sein oder das frisch erzeugte DOM-Element im Inline-Fall.
121    */
122   messages = new Map();
123   
124   /*
125    * Verwaltende Instanz für die Darstellung von Toast-Notifications.
126    */
127   #toastFactory;
128   
129   /**
130    * Neuer Messagehandler. Es kann eine Standard-Darstellungsform übergeben werden. Eine Abweichung dieses Standards kann
131    * im konkreten Nachrichtenaufruf durch abweichende Angabe der displayType-Konfigurationseinstellung erreicht werden.
132    * 
133    * @param displayType Zum Default abweichende Darstellungsform, siehe HRMessageDisplayTypes für alle Möglichkeiten.
134    * @param debugMode Schalter für den Debug-Modus: Hier werden diverse Konsolenausgaben gemacht.
135    */
136   constructor(displayType, debugMode)
137   {
138      if (displayType)
139         this.defaultDisplayType = displayType;
140      
141      this.#debugMode = debugMode;
142   }
143   
144   /**
145    * Erzeugt den Default-Messagehandler unterhalb des window-Objektes.
146    * 
147    * @param debugMode Im Developer-Modus werden diverse Debugging-Ausgaben auf der Konsole getätigt.
148    */
149   static createDefaultHandler(debugMode)
150   {
151      if (!window.HRMessageHandler || !(window.HRMessageHandler instanceof HRMessageHandler))
152         window.HRMessageHandler = new HRMessageHandler(null, debugMode);
153   }
154   
155   /**
156    * Anzeigen einer Nachricht abhängig von der gewählten Darstellungsform.
157    * 
158    * @param txt Die anzuzeigende Textnachricht.
159    * @param messageLevel Das Message-Level (info, warn, error).
160    * @param config [optional] Konfiguration für erweiterte Anzeigemöglichkeiten.
161    * <ul>
162    *    <li>displayType: Zum Default abweichende Darstellungsform, siehe HRMessageDisplayTypes für alle Möglichkeiten. Default: null.</li>
163    *    <li>parent: Ein Element oder ein Selektor, relativ zu dem die Nachricht angezeigt werden soll. Default: null. Wir zeigen die Nachricht also 
164    *        relativ zum window an.</li>
165    *    <li>type: Über diese Angabe können mehrere Nachrichten einem gewisse
165n Nachrichtentyp zugehörig gruppiert werden (z.B. validation-error).
166    *        Die Nachricht erhält dann ein data-Attribut "data-message-type" mit dem übergebenen Wert und eine entsprechende CSS-Klasse. Default: null.</li>
167    *    <li>duration: Nach dieser Zeit in ms wird die Nachricht wieder ausgeblendet, sofern der displayType eine solche Eigenschaft unterstützt
168    *        (Toasts etwa unterstützen die Eigenschaft, Alerts hingegen nicht). Default: 1000 [ms].</li>
169    *    <li>processingFunction: Ist eine grundsätzlich andere Anzeige gewünscht, so kann hier eine abweichende verarbeitende Funktion hinterlegt werden. 
170    *        Diese hat eine höhere Priorität als die Default-Darstellungsform und die über dieses Konfigurationsobjekt gesetzte abweichende Darstellungsform. Default: null.</li>
171    *    <li>className: Eine zusätzliche CSS-Klasse, die am p-Tag der Nachricht befestigt wird. Default: ''.</li>
172    *    <li>id: Eine abweichende ID, die an dem p-Tag der Nachricht hinterlegt werden kann. Default: ''.</li>
173    *    <li>markParentElement: Falls true, wird das parent-Element mit dem message-Level als Css-Klasse markiert. Default: false.</li>
174    * </ul>
175    */
176   #showMessage(txt, messageLevel, config)
177   {
178      try
179      {
180         config = config || {};
181         
182         // Wurde eine vollkommen abweichende darstellende Funktion übergeben, so hat diese absoluten Vorrang.
183         if (config.processingFunction)
184            config.processingFunction(txt, config);
185         else
186         {
187            // Wurde keine alternative Darstellungsform übergeben, verwenden wir die Default-Darstellungsform.
188            let displayType = this.defaultDisplayType;
189            if (config.displayType && config.displayType.length > 0)
190               displayType = config.displayType;
191            
192            // Wurde ein Default-Parent gesetzt übernehmen wir diesen.
193            if (!config.parent && this.defaultParentElement)
194               config.parent = this.defaultParentElement;
195            
196            // Optionale Markierung am parent-Element vornehmen.
197            if (config.parent)
198            {
199               // Falls der Parent als Zeichenkette übergeben wurde, gehen wir davon aus, dass ein Selektor übergeben wurde.
200               if (typeof(config.parent) === 'string')
201                  config.parent = document.querySelector(config.parent);
202               
203               // Parent wird mit CSS-Klasse bestückt, Formularfelder wiederum werden an anderer Stelle behandelt.
204               if (config.markParentElement && !config.parent.hasAttribute('data-field-frame'))
205               {
206                  if (messageLevel === HRMessageLevel.Error)
207                     addCSSClass(config.parent, 'error-parent');
208                  else if (messageLevel === HRMessageLevel.Warning)
209                     addCSSClass(config.parent, 'warning-parent');
210                  else
211                     addCSSClass(config.parent, 'info-parent');
212               }
213            }
214            
215            if (txt && typeof(txt) === 'string')
216            {
217               // Nachricht abhängig vom Display-Type schicken.
218               if (displayType === HRMessageDisplayTypes.Inline)
219                  this.#showMessageInline(txt, messageLevel, config);
220               else if (displayType === HRMessageDisplayTypes.Tooltip)
221                  this.#showMessageTooltip(txt, messageLevel, config);
222               else if (displayType === HRMessageDisplayTypes.IBox && typeof(HROpenNoteInIBox) === 'function')
223                  HROpenNoteInIBox(txt, messageLevel.toLowerCase());
224               else if (displayType === HRMessageDisplayTypes.Alert)
225                  alert(txt);
226               else
227               {
228                  // Der Default ist das Anzeigen als Toast.
229                  this.#showMessageToast(txt, messageLevel, false, config);
230                  
231                  // Unbekannte Darstellungsform gewählt?
232                  if (displayType !== HRMessageDisplayTypes.Toast && typeof(HRLogError) === 'function')
233                     HRLogError(new Error('Die Darstellungsform "' + displayType + '" ist unbekannt.'));
234               }
235               
236               // Zusätzliche typspezifische Fehlermeldung senden. Diese dient oft der Gruppierung mehrerer Fehler.
237               if (config.type && messageLevel === HRMessageLevel.Error && displayType !== HRMessageDisplayTypes.Toast)
238               {
239                  if (config.type === 'RequiredCheckFailed')
240                  {
241                     // Falls es sich um einen typabhängigen Fehler handelt, soll man 
242                     // per Klick zu diesem Fehler scrollen können. Diese Meldung soll 
243                     // nur einmal pro Typ erzeugt werden.
244                     this.#showMessageToast(HRDefaultTextTypeError.RequiredCheckFailed, HRMessageLevel.Error, true, {
245                        closeOnClick: false,
246                        scrollOnClick: `[data-message-type="${config.type.toLowerCase()}"]`,
247                        messageType: config.type
248                     });
249                  }
250               }
251            }
252         }
253      }
254      catch (e)
255      {
256         if (typeof(HRLogError) === 'function')
257            HRLogError(e);
258      }
259   }
260   
261   /**
262    * Anzeigen einer Nachricht in der Inline-Darstellung.
263    * 
264    * @see #showMessage(txt, messageLevel, config)
265    */
266   #showMessageInline(txt, messageLevel, config)
267   {
268      // Ein parent-Element, an welches die Nachricht gehängt wird, ist Minimalanforderung.
269      if (config.parent)
270      {
271         // Einen Hashcode aus diversen Nachrichteninformationen ermitteln. Dieser soll die Nachricht eindeutig identifizieren.
272         const messageHashcode = this.hashCodeMessage(txt, messageLevel, config.type, config.parent);
273         if (config.parent.hasAttribute('data-field-frame')) // Nachricht an einem Formularfeld.
274         {
275            let messageContainerCssClassname;
276            if (messageLevel === HRMessageLevel.Error)
277               messageContainerCssClassname = 'formElementErrorTextHint';
278            else if (messageLevel === HRMessageLevel.Warning)
279               messageContainerCssClassname = 'formElementWarnTextHint';
280            else
281               messageContainerCssClassname = 'formElementInfoTextHint';
282            
283            // Zunächst, wenn nötig, das Container-Element für alle Nachrichten erzeugen.
284            let messageContainer = config.parent.getElementsByClassName(messageContainerCssClassname)[0];
285            if (!messageContainer)
286            {
287               messageContainer = document.createElement('div');
288               messageContainer.className = messageContainerCssClassname;
289               config.parent.appendChild(messageContainer);
290            }
291            
292            // Die Nachricht selbst als Span in den Container legen.
293            const messageElement = document.createElement('span');
294            messageElement.textContent = txt;
295            messageElement.className = messageLevel + (config.className ? ' ' + config.className : '');
296            if (config.id)
297               messageElement.id = config.id;
298            if (config.type)
299               messageElement.dataset.messageType = config.type.trim().toLowerCase();
300            messageElement.dataset.message = '';
301            messageElement.dataset.messageId = messageHashcode;
302            messageContainer.appendChild(messageElement);
303            
304            // Nachricht noch in die verwaltende Hashmap legen.
305            this.messages.set(messageHashcode, messageElement);
306         }
307         else // Allgemeine Nachricht unterhalb eines parent-HTML-Elements.
308         {
309            // Im allgemeinen ist das ein Container-Element nur für diese Nachrichten.
310            const messageElement = document.createElement('p');
311            messageElement.innerHTML = txt;
312            if (config.id)
313               messageElement.id = config.id;
314            messageElement.className = messageLevel + (config.className ? ' ' + config.className : '');
315            if (config.type)
316               messageElement.dataset.messageType = config.type.trim().toLowerCase();
317            messageElement.dataset.message = '';
318            config.parent.appendChild(messageElement);
319            
320            // Nachricht noch in die verwaltende Hashmap legen.
321            this.messages.set(messageHashcode, messageElement);
322         }
323      }
324      else
325      {
326         if (typeof(HRLogError) === 'function')
327            HRLogError(new Error('Eine Inline-Darstellung ohne ein parent-Element, an welches man den Fehler hängen kann, wird nicht unterstützt.'));
328      }
329   }
330   
331   /**
332    * Entfernt alle Nachrichten am Feld.
333    * 
334    * @param field Das betreffende Formularfeld.
335    */
336   removeFieldMessagesIfExist(field)
337   {
338      this.removeFieldErrorsIfExist(field);
339      this.removeFieldWarningsIfExist(field);
340   }
341   
342   /**
343    * Entfernt alle Fehlermeldungen am Feld.
344    * 
345    * @param field Das betreffende Formularfeld.
346    */
347   removeFieldErrorsIfExist(field)
348   {
349      try
350      {
351         if (field && field.hasAttribute('data-error'))
352         {
353            if (field.parentElement && field.parentElement.hasAttribute('data-field-frame'))
354               removeCSSClass(field.parentElement, 'formElementError');
355               
356            const errorDiv = field.parentElement.getElementsByClassName("formElementErrorTextHint")[0];
357            if (errorDiv)
358            {
359               // Fehler wieder aus der zentralen messages-Verwaltung austragen.
360               Array.from(errorDiv.querySelectorAll('.error')).forEach((error) => {
361                  if (error.dataset.messageId && !this.messages.delete(parseInt(error.dataset.messageId)) && this.messages.length > 0 && typeof(HRLogError) === 'function')
362                     HRLogError(new Error(`Fehler beim Entfernen einer Nachricht (message-id: ${parseInt(error.dataset.messageId)}). Message-Objekt konnte nicht in messages-Verwaltung gefunden werden.`));
363                  
364                  // Den Fehler am Feld selbst nun löschen.
365                  error.remove();
366                  
367                  // Gibt es eine Nachricht, die Nachrichten des selben Typs (z.B. required-Verletzungen) gruppiert?
368                  // Diese Nachricht muss dann auch noch entfernt werden.
369                  this.messages.forEach((obj, id) => {
370                     if (obj && typeof(HRToast) === 'function' && obj instanceof HRToast && error.hasAttribute('data-message-type') && obj.m_MessageType === error.dataset.messageType) // aktuell an dieser Stelle nur für Toasts implementiert, kann aber bel. erweitert werden.
371                     {
372                        // Wenn es keine weiteren Fehler mehr zu diesem Nachrichtentyp gibt, kann schließlich auch das gruppierende Toast geschlossen werden. 
373                        if (document.querySelectorAll(`.error[data-message-type="${obj.m_MessageType}"]`).length <= 0)
374                           obj.close();
375                     }
376                  });
377               });
378               
379               // Schließlich noch das die Fehler gruppierende div entfernen.
380               errorDiv.remove();
381            }
382            
383            // Die data-Markierung noch entfernen.
384            field.removeAttribute('data-error');
385         }
386      }
387      catch (e)
388      {
389         if (typeof(HRLogError) === 'function')
390            HRLogError(new Error('Fehler beim Entfernen einer Nachricht.'));
391      }
392   }
393   
394   /**
395    * Entfernt alle Warnungen am Feld.
396    * 
397    * @param field Das betreffende Formularfeld.
398    */
399   removeFieldWarningsIfExist(field)
400   {
401      try
402      {
403         if (field && field.hasAttribute('data-warning'))
404         {
405            if (field.parentElement && field.parentElement.hasAttribute('data-field-frame'))
406               removeCSSClass(field.parentElement, 'formElementWarning');
407            
408            const warningDiv = field.parentElement.getElementsByClassName("formElementWarnTextHint")[0];
409            if (warningDiv)
410            {
411               // Warnungen wieder aus der zentralen messages-Verwaltung austragen.
412               Array.from(warningDiv.querySelectorAll('.warning')).forEach((warning) => {
413                  if (warning.dataset.messageId && !this.messages.delete(parseInt(warning.dataset.messageId)) && this.messages.length > 0 && typeof(HRLogError) === 'function')
414                     HRLogError(new Error(`Fehler beim Entfernen einer Nachricht (message-id: ${parseInt(warning.dataset.messageId)}). Message-Objekt konnte nicht in messages-Verwaltung gefunden werden.`));
415                  
416                  // Die Warnung am Feld nun selbst löschen.
417                  warning.remove();
418                  
419                  // Gibt es eine Nachricht, die Nachrichten des selben Typs (z.B. required-Verletzungen) gruppiert?
420                  // Diese Nachricht muss dann auch noch entfernt werden.
421                  this.messages.forEach((obj, id) => {
422                     if (obj && typeof(HRToast) === 'function' && obj instanceof HRToast && warning.hasAttribute('data-message-type') && obj.m_MessageType === warning.dataset.messageType) // aktuell an dieser Stelle nur für Toasts implementiert, kann aber bel. erweitert werden.
423                     {
424                        // Wenn es keine weiteren Fehler mehr zu diesem Nachrichtentyp gibt, kann schließlich auch das gruppierende Toast geschlossen werden. 
425                        if (document.querySelectorAll(`.warning[data-message-type="${obj.m_MessageType}"]`).length <= 0)
426                           obj.close();
427                     }
428                  });
429               });
430               
431               // Schließlich noch das die Fehler gruppierende div entfernen.
432               warningDiv.remove();
433            }
434         }
435      }
436      catch (e)
437      {
438         if (typeof(HRLogError) === 'function')
439            HRLogError(new Error('Fehler beim Entfernen einer Nachricht.'));
440      }
441   }
442   
443   /**
444    * Anzeigen einer Nachricht in der Toast-Notification-Darstellung.
445    * 
446    * @see #showMessage(txt, messageLevel, config)
447    * @param singleton Falls true, wird die Nachricht nur ein einziges mal ausgegeben.
448    */
449   #showMessageToast(txt, messageLevel, singleton, config)
450   {
451      if (typeof(HRToastFactory) === 'function')
452      {
453         const messageHashcode = this.hashCodeMessage(txt, messageLevel, config.type, config.parent);
454         if (messageHashcode && (!this.messages || !singleton || !this.messages.get(messageHashcode)))
455         {
456            const _this = this;
457            
458            // Wenn noch nicht geschehen eine Factory für das Toasting erzeugen.
459            if (!this.#toastFactory)
460            {
461               this.#toastFactory = new HRToastFactory({
462                  duration: 3750,
463                  limit: 3,
464                  progressbar: false,
465                  position: {
466                     x: 'right',
467                     y: 'bottom'
468                  },
469                  sortMode: HRToastContainerSortMode.logLevelPrioritized,
470                  closeOnClick: config.closeOnClick,
471                  parent: config.parent,
472                  scrollOnClick: config.scrollOnClick,
473                  messageType: (config.messageType ? config.messageType.toLowerCase() : ''),
474               });
475            }
476            
477            let toastConfig = {
478               className: config.type,
479               onShow: (toast) => {
480                  toast.m_Toast.dataset.id = messageHashcode;
481                  
482                  // Den Toast in die zentrale messages-Verwaltung eintragen.
483                  _this.messages.set(messageHashcode, toast);
484               },
485               onClose: (toast) => {
486                  // Toast wieder aus der zentralen messages-Verwaltung austragen.
487                  // Anmerkung: Sollte der messagehandler entfernt oder neu angelegt werden, bevor der zu löschende 
488                  // Toast abgeräumt wurde (z.B. bei einem Ajax-Reload der Seite), kann die messages-Map leer sein.
489                  if (!_this.messages.delete(parseInt(messageHashcode)) && _this.messages.length > 0 && typeof(HRLogError) === 'function')
490                        HRLogError(new Error(`Fehler beim Entfernen einer Nachricht (message-id: ${parseInt(messageHashcode)}). Message-Objekt konnte nicht in messages-Verwaltung gefunden werden.`));
491               }
492            };
493            
494            // Toast ausgeben. Anmerkung: Das parent-Element wird hier nicht berücksichtigt. Ein Toast wird  
495            // aktuell immer bzgl. der ganzen Seite ausgegeben.
496            let toast;
497            if (messageLevel === HRMessageLevel.Error)
498            {
499               toastConfig.duration = config.duration ? config.duration : -1,
500               toastConfig.closeOnClick = false;
501               this.#toastFactory.error(txt, toastConfig);
502            }
503            else if (messageLevel === HRMessageLevel.Warning)
504            {
505               toastConfig.duration = config.duration ? config.duration : -1,
506               this.#toastFactory.warn(txt, toastConfig);
507            }
508            else
509            {
510               toastConfig.duration = config.duration ? config.duration : 3750,
511               this.#toastFactory.info(txt, toastConfig);
512            }
513         }
514      }
515   }
516   
517   /**
518    * Anzeigen einer Nachricht in der Tooltip-Notification-Darstellung.
519    *
520    * @see #showMessage(txt, messageLevel, config)
521    */
522   #showMessageTooltip(txt, messageLevel, config)
523   {
524      if (config.parent)
525      {
526         const messageHashcode = this.hashCodeMessage(txt, messageLevel, config.type, config.parent);
527         if (messageHashcode)
528         {
529            let messageContainerCssClassname;
530            if (messageLevel === HRMessageLevel.Error)
531               messageContainerCssClassname = 'formElementErrorTextHint';
532            else if (messageLevel === HRMessageLevel.Warning)
533               messageContainerCssClassname = 'formElementWarnTextHint';
534            else
535               messageContainerCssClassname = 'formElementInfoTextHint';
536            
537            // Zunächst, wenn nötig, das Container-Element für alle Nachrichten erzeugen.
538            let messageContainer = config.parent.getElementsByClassName(messageContainerCssClassname)[0];
539            if (!messageContainer)
540            {
541               messageContainer = document.createElement('span');
542               messageContainer.className = `tooltip ${messageContainerCssClassname}`;
543               config.parent.appendChild(messageContainer);
544            }
545            
546            // Die Nachricht selbst als Span in den Container legen.
547            const messageElement = document.createElement('span');
548            messageElement.textContent = txt;
549            messageElement.className = `${messageLevel} data-content ${config.className ? ' ' + config.className : ''}`;
550            if (config.id)
551               messageElement.id = config.id;
552            if (config.type)
553               messageElement.dataset.messageType = config.type.trim().toLowerCase();
554            messageElement.dataset.messageId = messageHashcode;
555            messageContainer.appendChild(messageElement);
556            
557            // Nachricht noch in die verwaltende Hashmap legen.
558            this.messages.set(messageHashcode, messageElement);
559         }
560      }
561      else
562      {
563         if (typeof(HRLogError) === 'function')
564            HRLogError(new Error('Eine Tooltip-Darstellung ohne ein parent-Element, an welches man den Text hängen kann, wird nicht unterstützt.'));
565      }
566   }
567   
568   /**
569    * Anzeigen einer informativen Nachricht.
570    * 
571    * @param txt Die anzuzeigende Textnachricht.
572    * @param config [optional] Konfiguration für erweiterte Anzeigemöglichkeiten. Siehe Kommentar showMessage.
573    */
574   info(txt, config)
575   {
576      this.#showMessage(txt, HRMessageLevel.Info, config);
577   }
578   
579   /**
580    * Anzeigen einer Warnung.
581    * 
582    * @param txt Die anzuzeigende Textnachricht.
583    * @param config [optional] Konfiguration für erweiterte Anzeigemöglichkeiten. Siehe Kommentar showMessage.
584    */
585   warn(txt, config)
586   {
587      this.#showMessage(txt, HRMessageLevel.Warning, config);
588   }
589   
590   /**
591    * Anzeigen einer Fehlermeldung.
592    * 
593    * @param txt Die anzuzeigende Textnachricht.
594    * @param config [optional] Konfiguration für erweiterte Anzeigemöglichkeiten. Siehe Kommentar showMessage.
595    */
596   error(txt, config)
597   {
598      this.#showMessage(txt, HRMessageLevel.Error, config);
599   }
600   
601   /**
602    * Anzeigen einer Fehlermeldung am übergebenen Formularfeld.
603    * 
604    * @see #showMessageAtField(name, txt, messageLevel, config)
605    */
606   errorAtField(field, txt, config)
607   {
608      this.#showMessageAtField(field, txt, HRMessageLevel.Error, config);
609   }
610   
611   /**
612    * Anzeigen einer Warnung am übergebenen Formularfeld.
613    * 
614    * @see #showMessageAtField(name, txt, messageLevel, config)
615    */
616   warnAtField(field, txt, config)
617   {
618      this.#showMessageAtField(field, txt, HRMessageLevel.Warning, config);
619   }
620   
621   /**
622    * Anzeigen einer Information am übergebenen Formularfeld.
623    * 
624    * @see #showMessageAtField(name, txt, messageLevel, config)
625    */
626   infoAtField(field, txt, config)
627   {
628      this.#showMessageAtField(field, txt, HRMessageLevel.Info, config);
629   }
630   
631   /**
632    * Anzeigen einer Nachricht am durch den übergebenen Namen festgelegten Formularfeld.
633    * 
634    * @param field Der Attributname zum Eingabefeld oder das Eingabefeld selbst.
635    * @param txt Die anzuzeigende Textnachricht.
636    * @param messageLevel Das Message-Level (info, warn, error).
637    * @param config [optional] Konfiguration für erweiterte Anzeigemöglichkeiten. Siehe Kommentar showMessage.
638    *        <ul>
639    *          <li>formName: Ein abweichender Name des Formulars, in dem sich das Feld befindet.</li>
640    *        </ul>
641    */
642   #showMessageAtField(field, txt, messageLevel, config)
643   {
644      if (field)
645      {
646         config = config || {};
647         
648         let inputEl = field;
649         if (typeof(field) === 'string')
650         {
651            let form = document.forms.submit;
652            if (config.formName)
653               form = document.forms[formName];
654            inputEl = form.elements[field];
655         }
656         if (inputEl)
657         {
658            // Falls kein Formularfeldrahmen existiert, versuchen wir einen Feldrahmen um das 
659            // Eingabefeld und ggf. assoziierte Label-Elemente zu legen.
660            if (inputEl.parentElement && !inputEl.parentElement.hasAttribute('data-field-frame'))
661            {
662               this.#createFeldrahmen(field);
663               
664               // Damit der Missstand aber auch direkt im Template behoben wird, gibt es trotzdem noch eine Warnung.
665               if (this.#debugMode)
666                  console.warn('Für Messagehandling an Formularfeldern muss ein Feldrahmen gesetzt sein. Bitte ergänzen Sie diesen noch:', field);
667            }
668            
669            if (messageLevel === HRMessageLevel.Error)
670            {
671               inputEl.dataset.error = "";
672               addCSSClass(inputEl.parentElement, 'formElementError');
673            }
674            else if (messageLevel === HRMessageLevel.Warning)
675            {
676               inputEl.dataset.warning = "";
677               addCSSClass(inputEl.parentElement, 'formElementWarning');
678            }
679            else
680               addCSSClass(inputEl.parentElement, 'formelementInfo');
681            
682            config.parent = inputEl.parentElement;
683            
684            // Wurde keine alternative Darstellungsform übergeben, verwenden wir die Default-Darstellungsform für Formularfeldausgaben.
685            if (config.displayType && config.displayType.length > 0)
686               config.displayType = config.displayType;
687            else
688               config.displayType = this.defaultFieldDisplayType;
689            
690            this.#showMessage(txt, messageLevel, config);
691         }
692      }
693      else
694      {
695         if (this.#debugMode)
696         {
697            console.error('Fehler beim Ausgeben der Nachricht: "', txt.trim(), '"');
698            console.error('Das Feld konnte nicht gefunden werden. Prüfen Sie bitte: (a) Wird das Formularfeld angezeigt? (b) Hat es ein data-Attribut data-instance-id? (c) Wurde der korrekte Feldname angegeben?');
699         }
700         
701         // Fallback: Die Nachricht als Toast ausgeben, damit wenigstens irgendeine Mitteilung an den Nutzer ausgegeben wird.
702         this.#showMessageToast(txt, messageLevel, false, config);
703      }
704   }
705   
706   /**
707    * Erzeugt einen Feldrahmen um das übergebene Feld. Mit dem Feld assoziierte Label-Elemente werden versucht aufzuspüren und mit 
708    * in den Rahmen verschoben.
709    * 
710    * @param field Das Formularfeld, um welches ein Feldrahmen erzeugt werden soll.
711    */
712   #createFeldrahmen(field)
713   {
714      if (field)
715      {
716         const feldrahmen = document.createElement('div');
717         feldrahmen.id = `${field.id}_Rahmen`;
718         feldrahmen.className = 'Feldrahmen';
719         feldrahmen.dataset.fieldFrame = '';
720         
721         // Das Feld in den Feldrahmen stecken.
722         HRWrap(field, feldrahmen);
723         
724         // Alle Label in den Feldrahmen schieben.
725         let beforeInput = true;
726         Array.from(feldrahmen.parentElement.children).forEach(child => {
727            if (child.tagName === 'LABEL' && (child.htmlFor == field.id || child.htmlFor == field.name))
728            {
729               if (beforeInput)
730                  field.before(child);
731               else
732                  field.after(child);
733            }
734            else if (child === field)
735               beforeInput = false;
736         });
737      }
738   }
739   
740   /**
741    * Erzeugt einen Hashcode, der die Nachricht möglichst eindeutig kennzeichnet.
742    * 
743    * Darf nicht privat gemacht werden, da es mind. von der toast.js aufgerufen wird.
744    * 
745    * @param txt Der Text der Nachricht.
746    * @param messageLevel Das Message-Level (info, warn, error).
747    * @param type Der Typ.
748    * @param parent Das parent-Element der Nachricht.
749    * @return Ein Hashcode der Nachricht.
750    */
751   hashCodeMessage(txt, messageLevel, type, parent)
752   {
753      if (txt && typeof(String.prototype.hashCode) === 'function')
754      {
755         let message = txt;
756         if (messageLevel)
757            message += messageLevel;
758         if (type)
759            message += type;
760         
761         // Das parent-Objekt eindeutig zu identifizieren ist noch ein wenig 
762         // holprig. In Zukunft müssen wir hier mal genauer überlegen wie man
763         // einen DOM-Knoten sinnvoll hasht.
764         if (parent)
765         {
766            if (typeof(parent) === 'string')
767               message += parent;
768            else
769            {
770               if (parent.id)
771                  message += parent.id;
772               if (parent.name)
773                  message += parent.name;
774               if (parent.type)
775                  message += parent.type;
776               if (typeof(parent.hasAttribute) === 'function' && parent.hasAttribute('data-instance-id'))
777                  message += parent.dataset.instanceId;
778            }
779         }
780         
781         return message.hashCode();
782      }
783      
784      return '';
785   }
786   
787   /**
788    * Anzeigen einer Nachricht (Info, Warnung, Error) mit den Daten aus dem serverseitig erzeugten Message-JSON.
789    * 
790    * @param jsonTxt Das serverseitig erzeugte JSON als Zeichenkette.
791    * @param messageLevel Das Message-Level (info, warn, error).
792    */
793   #showMessageFromJSON(jsonTxt, messageLevel)
794   {
795      if (jsonTxt)
796      {
797         try
798         {
799            const json = JSON.parse(jsonTxt);
800            if (json)
801            {
802               // Kurze Verifizierung, dass das übergebene Message-Level überhaupt stimmt.
803               if (!json.messageLevel || json.messageLevel === messageLevel)
804               {
805                  // Wurde eine zuordbare Quelle übergeben?
806                  let parent;
807                  if (json.source && json.sourceType && json.sourceType === SourceTypes.Anwendung)
808                     parent = '[data-instance-id="' + json.source + '"]';
809                  
810                  const config = {
811                     parent: parent,
812                     type : json.type ? json.type : ''
813                  };
814                  
815                  if (json.sourceType === SourceTypes.Attribut)
816                  {
817                     // Den Attributnamen über den Hashcode der Attribut-Instanz ermitteln.
818                     // Achtung: Damit das funktioniert, muss an den Feldern das data-Attribut
819                     // data-instance-id gesetzt sein, welches über die print-Aufrufe immer gesetzt wird.
820                     let field = document.querySelector(`[data-instance-id="${json.source}"]`);
821                     if (!field && this.#debugMode)
822                        console.error(`Fehler beim Ausgeben einer Nachricht am Attribut mit dem Hashcode "${json.source}". Bitte prüfen, ob das zugehörige HTML-Eingabefeld ein data-Attribut "data-instance-id" hat.`);
823                     this.#showMessageAtField(field, json.message, messageLevel, config);
824                  }
825                  else
826                     this.#showMessage(json.message, messageLevel, config);
827               }
828               else
829               {
830                  if (typeof(HRLogError) === 'function')
831                     HRLogError(new Error('Das im JSON transportiert Message-Level "' + json.messageLevel + '" stimmt nicht mit dem Level der aufgerufenen Funktion überein.'));
832               }
833            }
834         }
835         catch (e)
836         {
837            if (typeof(HRLogError) === 'function')
838               HRLogError(e);
839         }
840      }
841   }
842   
843   /**
844    * Anzeigen einer Information mit den Daten aus dem serverseitig erzeugten Message-JSON.
845    * 
846    * @param jsonTxt Das serverseitig erzeugte JSON als Zeichenkette.
847    */
848   infoFromJSON(jsonTxt)
849   {
850      this.#showMessageFromJSON(jsonTxt, HRMessageLevel.Info);
851   }
852   
853   /**
854    * Anzeigen einer Warnung mit den Daten aus dem serverseitig erzeugten Message-JSON.
855    * 
856    * @param jsonTxt Das serverseitig erzeugte JSON als Zeichenkette.
857    */
858   warnFromJSON(jsonTxt)
859   {
860      this.#showMessageFromJSON(jsonTxt, HRMessageLevel.Warning);
861   }
862   
863   /**
864    * Anzeigen einer Fehlermeldung mit den Daten aus dem serverseitig erzeugten Message-JSON.
865    * 
866    * @param jsonTxt Das serverseitig erzeugte JSON als Zeichenkette.
867    */
868   errorFromJSON(jsonTxt)
869   {
870      this.#showMessageFromJSON(jsonTxt, HRMessageLevel.Error);
871   }
872   
873   /**
874    * Stellt die Default-Darstellungsform für neue Nachrichten auf den übergebenen Wert um.
875    * 
876    * @param newDefaultDisplayType Die neue Default-Darstellungsform, siehe HRMessageDisplayTypes.
877    */
878   setDefaultDisplayType(newDefaultDisplayType)
879   {
880      this.defaultDisplayType = newDefaultDisplayType;
881   }
882   
883   /**
884    * Stellt die Default-Darstellungsform für neue Nachrichten an Formularfeldern auf den übergebenen Wert um.
885    * 
886    * @param newDefaultFieldDisplayType Die neue Default-Darstellungsform für Nachrichten an Formularfeldern, siehe HRMessageDisplayTypes.
887    */
888   setDefaultFieldDisplayType(newDefaultFieldDisplayType)
889   {
890      this.defaultFieldDisplayType = newDefaultFieldDisplayType;
891   }
892   
893   /**
894    * Legt fest, unter welches HTML-Element defaultmäßig alle Nachrichten gelegt werden.
895    * 
896    * @param newParent Das HTML-Element, wahlweise auch als Selektor.
897    */
898   setParentDefault(newParent)
899   {
900      this.defaultParentElement = newParent;
901   }
902   
903   /**
904    * Entfernt alle Toast-Benachrichtigungen.
905    */
906   resetToasts()
907   {
908      if (this.#toastFactory)
909      {
910         // Alle Nachrichten schließen.
911         this.#toastFactory.closeAll();
912         
913         // Die Toast-Factory inklusive der Queue der noch ausstehenden Nachrichten resetten.
914         this.#toastFactory.destroy();
915      }
916   }
917}
918
919/**
920 * Den Default-Messagehandler erzeugen.
921 */
922HRMessageHandler.createDefaultHandler();

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.