PageSourceSearch

https://www.bergbahn-heidelberg.de/en/static/js/HRSysLib/ibox/16/HRIBox.js?nc=1788947158158

js bergbahn-heidelberg.de collected 2026-09-24 18:57:32 UTC 76,779 bytes, 2,207 lines download raw bytes

1/* ************************************************************************** */
2// Firma:        Hehner Reus Systems GmbH
3// Projekt:      com.cerno.basis
4// Datei:        HRIBox.js
5// Erstellung:   16.12.2025
6// Verwendung:   Erzeugt eine i-Box und bietet bereits verschiedene vorgefertigte 
7//               Signaturen, um diversen Content über die i-Box anzeigen zu lassen.
8/* ************************************************************************** */
9
10/**
11 * Enumeration für die Themes der i-Box
12 */
13var HRIBoxThemes = {
14   Default: 'default',
15   Popup: 'popup'
16}
17
18/**
19 * Enumeration für die möglichen Richtungen des Popups.
20 */
21var HRPopupDirection = {
22   /*
23    * Keine Richtung.
24    */
25   none: 'none',
26   
27   /*
28    * Popup fährt von unten nach oben.
29    */
30   Up: 'up',
31   
32   /*
33    * Popup fährt von oben nach unten.
34    */
35   Down: 'down',
36   
37   /*
38    * Popup fährt von rechts nach links.
39    */
40   Left: 'left',
41   
42   /*
43    * Popup fährt von rechts nach links.
44    */
45   Right: 'right'
46}
47
48/**
49 * Konstruktor für eine neue i-Box.
50 * 
51 * @param config [optional] Kapselt optionale Konfigurationsparameter
52 * <ul>
53 *    <li>draggable: true, falls Drag and Drop der i-Box ermöglicht werden soll. Default: true.</li>
54 *    <li>htmlElement: Darzustellendes HTML-Element oder ID des darzustellenden HTML-Elementes. Default: null.</li>
55 *    <li>url: Der Content der Seite wird in der i-Box dargestellt. Default: null.</li>
56 *    <li>closeButton: true, falls ein Schließen-Button ergänzt werden soll. Default: false.</li>
57 *    <li>closeButtonTagName: Gibt an, was für ein Tag-Namen der Schließen-Button haben soll. Default: div.</li>
58 *    <li>closeButtonAsFirstElement: Sorgt dafür, dass der Schließen-Button das erste Element der i-Box ist. Default: false.</li>
59 *    <li>imagesNextPrevAsButtons: Sorgt dafür, dass der die Vor- und Zurück-Buttons der i-Box mit dem button-Tag erzeugt werden. Default: false.</li>
60 *    <li>imagesTabNavigationAsButtons: Sorgt dafür, dass der die Tabnavigation der i-Box mit button-Tags erzeugt werden. Default: false.</li>
61 *    <li>cssSuffix: CSS-Klasse, die als Suffix an alle i-Box-HTML-Elemente ergänzt wird. Default: ''.</li>
62 *    <li>additionalIBoxClass: Zusätzliche CSS-Klasse, die  an das äußerste i-Box-Div gehängt wird. Default: ''.</li>
63 *    <li>additionalIBoxContentClass: Zusätzliche CSS-Klasse, die an das den Content darstellende Div gehängt wird. Default: ''.</li>
64 *    <li>bodyClass: CSS-Klasse, die an den HTML-Body gehängt wird. Default: 'overlay-open'.</li>
65 *    <li>onOpen: Callback-Funktion, die nach dem Öffnen der i-Box aufgerufen wird. Default: null.</li>
66 *    <li>onClose: Callback-Funktion, die nach dem Schließen der i-Box aufgerufen wird. Default: null.</li>
67 *    <li>onEscape: Callback-Funktion, die bei Escape aufgerufen wird. Default: null.</li>
68 *    <li>onContentLoaded: Callback-Funktion, die aufgerufen wird, wenn der Content der i-Box geladen wurde. Es wird das äußerste Div der i-Box als Parameter angeboten. Default: null.</li>
69 *    <li>alignX: Horizontale Ausrichtung der i-Box. Default: center.</li>
70 *    <li>alignY: Vertikale Ausrichtung der i-Box. Default: center.</li>
71 *    <li>parentDiv: In dieses Element wird die i-box hineingesetzt. Default: null.</li>
72 *    <li>width: Breite der i-Box. Dient dem Übersteuern der durch CSS gesetzten Breite. Default: null.</li>
73 *    <li>height: Höhe der i-Box. Dient dem Übersteuern der durch CSS gesetzten Höhe. Default: null.</li>
74 *    <li>widthUnit: Einheit der gesetzten Breite der i-Box. Default: 'px'.</li>
75 *    <li>heightUnit: Einheit der gesetzten Breite der i-Box. Default: 'px'.</li>
76 *    <li>redirectUrlOnClose: Url, auf die beim Schließen der URL verwiesen wird.</li>
77 *    <li>blackOverlay: Erzeugt einen schwarz-transparenten Hintergrund zum Abdunkeln. Default: false.</li>
78 *    <li>removeByBlackOverlayClick: Entfernt die i-Box, wenn auf das Black-Overlay geklickt wird. Default: true.</li>
79 *    <li>singleton: Wenn aktiv, ist diese i-Box die Einzige ("Highlander-Modus"). Alle weiteren i-Boxes werden unterdrückt und alle bestehenden i-Boxen entfernt. Default: false.</li>
80 *    <li>replaceAll: Etwas schwächer als 'singleton'. Entfernt lediglich vor der eigenen Darstellung alle bestehenden Instanzen der i-Box. Default: false.</li>
81 *    <li>theme: Möglichkeit die i-Box in einem bestimmten Theme anzuzeigen. Beispielsweise als Popup. Default: 'default'. Siehe Enum 'HRIBoxThemes'.</li>
82 *    <li>modal: Implementierung als modaler Dialog. Hier wird insbesondere das Focus-Trapping ermöglicht. Default: false.</li>
83 *    <li>popupConfig: Konfigurationsobjekt für alle Einstellungen rund um das Popup-Theme.
84 *       <ul>
85 *          <li>showAfter: startet die Anzeigeanimation des Popups verzögert nach der angegebenen Zeit [ms]. Default: 250 ms.</li>
86 *          <li>hideAfter: lässt das Popup verzögert nach der angegebenen Zeit [ms] verschwinden. Default: keine verzögertes Verstecken.</li>
87 *          <li>direction: legt die "Sliderichtung" fest. Default: Klassischer Popup, d.h. 'up'. Siehe Enum 'HRPopupDirection'.</li>
88 *       </ul>
89 *    </li>
90 * </ul>
91 */
92HRIBox = function(config)
93{
94   /*
95    * Eine UUID, um die Komponenten der i-Box eindeutig bestimmbar zu machen.
96    */ 
97   this.m_UUID = HRCrypt.getUUID();
98   
99   // Konfigurationen
100   this.m_Draggable = true;
101   this.m_Url = null;
102   this.m_CloseButton = false;
103   this.m_CloseButtonTagName = 'div';
104   this.m_CloseButtonAsFirstElement = false;
105   this.m_ImagesNextPrevAsButtons = false;
106   this.m_ImagesTabNavigationAsButtons = false;
107   this.m_OnEscape = null;
108   this.m_CSSSuffix = '';
109   this.m_AdditionalIBoxClass = '';
110   this.m_AdditionalIBoxContentClass = '';
111   this.m_BodyClass = 'overlay-open';
112   this.m_OnOpen = null;
113   this.m_OnClose = null;
114   this.m_OnContentLoaded = null;
115   this.m_ParentDiv = null;
116   this.m_AlignX = 'center';
117   this.m_AlignY = 'center';
118   this.m_Width = null;
119   this.m_WidthUnit = 'px';
120   this.m_Height = null;
121   this.m_HeightUnit = 'px';
122   this.m_RedirectUrlOnClose = null;
123   this.m_BlackOverlay = false;
124   this.m_RemoveByBlackOverlayClick = true;
125   this.m_Singleton = false;
126   this.m_ReplaceAll = false;
127   this.m_Theme = HRIBoxThemes.Default;
128   this.m_Modal = false;
129   this.m_PopupConfig = {};
130   
131   // HTML-Struktur der i-Box
132   this.m_IBox;
133   
134   /*
135    * Member für die Darstellung von HTML-Content.
136    */
137   this.m_HtmlElement = null;
138   
139   /*
140    * Ein temporärer Wrapper um das HTML-Element. Wird gebraucht, um das Element 
141    * beim Schließen der i-Box wieder an die ursprüngliche Stelle im DOM zu bekommen. 
142    */
143   this.m_HtmlElementTmpWrapper = null;
144   
145   /*
146    * Merkt sich das zuletzt fokussierte Element. Dieses wird erneut fokussiert, wenn die i-Box geschlossen wird.
147    */
148   this.m_LastFocusedElement = document.activeElement;
149   
150   /*
151    * Member für die Darstellung von Bildern.
152    */
153   this.m_ImageSources = null;
154   this.m_ImageAlttexts = null;
155   this.m_ImageCaptions = null;
156   this.m_ImageTitles = null;
157   this.m_ImageLinks = null;
158   this.m_ImageLinkTargets = null;
159   this.m_ImageMaxWidth = Math.round(HRGetViewPortWidth() * 0.8);
160   this.m_ImageMaxHeight = Math.round(HRGetViewPortHeight() * 0.8);
161   this.m_ImageIdx = 0;
162   this.m_ImageAddTabNavigation = true;
163   this.m_ImageSrcAsBackgroundImage = true;
164   
165   /*
166    * Member für die Darstellung eines Prompts.
167    */
168   this.m_Prompt = false;
169   this.m_PromptText = '';
170   this.m_PromptDefaultText = '';
171   this.m_PromptCallBack = function(returnValue){return true;};
172   this.m_PromptOkayText = 'Ok';
173   this.m_PromptCancelText = 'Abbrechen';
174   this.m_PromptSaveResultInCookie = false;
175   this.m_PromptSaveResultInLocalStorage = false;
176   this.m_PromptConfigureInputHTML = null;
177   
178   /*
179    * Member für die Darstellung eines Hinweistextes.
180    */
181   this.m_Note = false;
182   this.m_NoteState = 'none';
183   
184   /*
185    * Aktuelle Breite des window-Objektes.
186    * 
187    * Brauchen wir für folgende Problematik: Unter iOS wird ein resize-Event ausgelöst, wenn
188    * - Virtuelle Tastatur eingeblendet wird,
189    * - Scroll- oder Aufklappmenüs,
190    * - Zooms, Systemgesten,
191    * - Scrollen (!)
192    * Den Resize wollen wir nur dann ausführen, wenn es effektiv auch eine Änderung in der 
193    * Breite des window-Objektes gab.
194    */
195   this.m_CurrentWindowWidth = window.innerWidth;
196   
197   //
198   // (Optionale) Konfigurationen auslesen.
199   //
200   if (config)
201   {
202      if (!config.draggable)
203         this.m_Draggable = false;
204      
205      // Wurde HTML-Element oder ID des darzustellenden Contents angegeben?
206      this.m_HtmlElement = config.htmlElement;
207      if (config.htmlElement && typeof config.htmlElement === 'string')
208         this.m_HtmlElement = document.getElementById(config.htmlElement);
209      
210      if (config.url)
211         this.m_Url = url;
212      
213      if (config.closeButton && config.closeButton === true)
214         this.m_CloseButton = true;
215      
216      if (config.closeButtonTagName && typeof config.closeButtonTagName === 'string')
217         this.m_CloseButtonTagName = config.closeButtonTagName;
218      
219      if (config.closeButtonAsFirstElement && config.closeButtonAsFirstElement === true)
220         this.m_CloseButtonAsFirstElement = true;
221      
222      if (config.imagesNextPrevAsButtons && config.imagesNextPrevAsButtons === true)
223         this.m_ImagesNextPrevAsButtons = true;
224      
225      if (config.imagesTabNavigationAsButtons && config.imagesTabNavigationAsButtons === true)
226         this.m_ImagesTabNavigationAsButtons = true;
227      
228      if (config.onEscape && typeof(config.onEscape) === 'function')
229         this.m_OnEscape = config.onEscape;
230      
231      if (config.cssSuffix)
232         this.m_CSSSuffix = config.cssSuffix;
233      
234      if (config.additionalIBoxClass)
235         this.m_AdditionalIBoxClass = config.additionalIBoxClass;
236      
237      if (config.additionalIBoxContentClass)
238         this.m_AdditionalIBoxContentClass = config.additionalIBoxContentClass;
239      
240      if (config.bodyClass)
241         this.m_BodyClass = config.bodyClass;
242      
243      if (config.onOpen && typeof(config.onOpen) === 'function')
244         this.m_OnOpen = config.onOpen;
245      
246      if (config.onClose && typeof(config.onClose) === 'function')
247         this.m_OnClose = config.onClose;
248      
249      if (config.onContentLoaded && typeof(config.onContentLoaded) === 'function')
250         this.m_OnContentLoaded = config.onContentLoaded;
251      
252      this.m_ParentDiv = config.parentDiv;
253      if (config.parentDiv && typeof config.parentDiv === 'string')
254         this.m_ParentDiv = document.getElementById(config.parentDiv);
255      
256      if (config.alignX) 
257         this.m_AlignX = config.alignX;
258      
259      if (config.alignY)
260         this.m_AlignY = config.alignY;
261      
262      if (config.width)
263      {
264         this.m_Width = config.width;
265         
266         if (typeof(config.width) === 'string')
267         {
268            if (config.widthUnit && typeof(config.width) === 'string')
269               this.m_WidthUnit = config.widthUnit;
270            else
271            {
272               // Hat sonst die Breitenangabe bereits eine Einheit?
273               const unit = HRCSS.getUnit(config.width);
274               if (unit)
275                  this.m_WidthUnit = unit;
276            }
277            this.m_Width = parseInt(config.width);
278         }
279      }
280      
281      if (config.height)
282      {
283         this.m_Height = config.height;
284         if (typeof(config.height) === 'string')
285         {
286            if (config.heightUnit && typeof(config.height) === 'string')
287               this.m_HeightUnit = config.heightUnit;
288            else
289            {
290               // Hat sonst die Höhenangabe bereits eine Einheit?
291               const unit = HRCSS.getUnit(config.height);
292               if (unit)
293                  this.m_HeightUnit = unit;
294            }
295            this.m_Height = parseInt(config.height);
296         }
297      }
298      
299      if (config.redirectUrlOnClose)
300         this.m_RedirectUrlOnClose = config.redirectUrlOnClose;
301      
302      if (config.blackOverlay && config.blackOverlay === true)
303         this.m_BlackOverlay = true;
304      else if (!config.bodyClass)
305         this.m_BodyClass = "";
306      
307      if (config.removeByBlackOverlayClick === false)
308         this.m_RemoveByBlackOverlayClick = false;
309      
310      if (config.singleton && config.singleton === true)
311         this.m_Singleton = true;
312      
313      if (config.replaceAll && config.replaceAll === true)
314         this.m_ReplaceAll = true;
315      
316      if (config.theme)
317         this.m_Theme = config.theme;
318      
319      if (config.modal && config.modal === true)
320         this.m_Modal = config.modal;
321      
322      if (config.popupConfig)
323         this.m_PopupConfig = config.popupConfig;
324   }
325}
326
327/**
328 * Erzeugt und öffnet die iBox.
329 */
330HRIBox.prototype.open = function()
331{
332   const _this = this;
333   
334   // Bestehende Instanzen schließen.
335   if (this.m_Singleton || this.m_ReplaceAll)
336      HRCloseAllIBoxes();
337   
338   let existingSingleton = false;
339   let isParentIBox = false;
340   if (window.HRIBoxRegistry)
341   {
342      window.HRIBoxRegistry.forEach(ibox => {
343         if (ibox.m_Singleton && ibox != this)
344         {
345            existingSingleton = true;
346            
347            // Noch prüfen, ob der Singleton ein parent-Element der i-Box ist.
348            // Dazu schauen wir uns an, ob das HTML-ELement dieser i-Box unterhalb der Singleton-i-Box liegt.
349            // Funktioniert aktuell also nicht für i-Boxen, die ihren Content über ein i-Frame rendern.
350            isParentIBox = this.m_HtmlElement && ibox.m_HtmlElement && ibox.m_HtmlElement.contains(this.m_HtmlElement);
351         }
352      });
353   }
354   
355   /*
356    * Die i-Box nur dann öffnen, wenn es keine andere Box mit der Singleton-Eigenschaft gibt oder aber die neue 
357    * i-Box aus der Singleton-Box heraus geöffnet wird. Ein Use-Case von letzterem ist bspw. der Consent-Manager. Unter 
358    * diesem können die Erklärungstexte zu den einzelnen Cookie-Einstellungen wahlweise auch über eine i-Box geöffnet werden.
359    * Der Consent-Manager ist ein Singleton und die i-Boxen legen sich dann über diesen. Dieser Fall sollte aber die Ausnahme
360    * bleiben, weil geschachtelte i-Boxen generell keine gute UX darstellen.
361    */
362   if (!existingSingleton || isParentIBox)
363   {
364      this.addEventListener();
365      
366      // I-Box-Skelett erzeugen.
367      this.createMinimalSkeleton();
368      
369      // Listener für das Laden des DOM-Contents.
370      if (typeof(HREventManager.addListener) === 'function')
371         HREventManager.addListener(this.m_IBox, 'HRIBoxContentLoaded', this.domContentLoadedHandler.bind(this), {useStandard: false, notifiedByChildren: false});
372      
373      // Body erhält zusätzliche Klasse.
374      if (this.m_BodyClass)
375         HRAddBodyCSSClass(this.m_BodyClass);
376      
377      if (!this.m_CloseButtonAsFirstElement && this.m_CloseButton)
378         this.createIBoxCloseButton();
379      
380      // Content setzen.
381      if (this.m_HtmlElement)
382         this.setIBoxContentHtmlElement();
383      else if (this.m_Url)
384         this.setIBoxContentUrl();
385      else if (this.m_ImageSources)
386         this.showImage(this.m_ImageIdx);
387      
388      // Zusätzlich zu einem Prompt machen?
389      if (this.m_Prompt)
390         this.createPrompt();
391      
392      // Hintergrund schwarz-transparent abdunkeln.
393      if (this.m_BlackOverlay)
394         this.createBlackOverlay();
395      
396      if (this.m_AlignX)
397         this.alignIBoxHorizontalTo(this.m_AlignX);
398      
399      if (this.m_AlignY)
400         this.alignIBoxVerticalTo(this.m_AlignY);
401      
402      // Initialisiert das gewählte Theme
403      this.initTheme()
404      
405      // Der Fokus wird initial auf den Schließen-Button gelegt, sofern vorhanden.
406      if (this.m_CloseButton)
407      {
408         const _this = this;
409         setTimeout(function(){
410            if (document.getElementById(_this.getUID('idIBoxDivClose')))
411               document.getElementById(_this.getUID('idIBoxDivClose')).focus();
412         }, 500);
413      }
414      
415      // Die i-Box im window-Objekt merken.
416      if (!window.HRIBoxRegistry)
417         window.HRIBoxRegistry = new Array();
418      
419      window.HRIBoxRegistry.push(this);
420      
421      if (this.m_OnOpen && typeof(this.m_OnOpen) === 'function')
422         this.m_OnOpen();
423   }
424}
425
426/**
427 * Reagiert auf das Laden des DOM-Contents.
428 */
429HRIBox.prototype.domContentLoadedHandler = function(event)
430{
431   // ggf. nochmal Optimierung der Positionierung der i-Box
432   // anhand des nun geladenen Contents.
433   if (this.m_AlignX)
434      this.alignIBoxHorizontalTo(this.m_AlignX);
435   
436   if (this.m_AlignY)
437      this.alignIBoxVerticalTo(this.m_AlignY);
438   
439   // Der Fokus wird initial auf das erste fokussierbare Element gelegt.
440   if (!this.m_CloseButton && event.detail)
441   {
442      const focusableEl = HRDOM.getFirstFocusableDescendant(event.detail);
443      if (focusableEl)
444         focusableEl.focus();
445   }
446   
447   if (this.m_OnContentLoaded && typeof(this.m_OnContentLoaded) === 'function')
448      this.m_OnContentLoaded(this.m_IBox);
449}
450
451/**
452 * Reagiert auf einen Resize der i-Box.
453 */
454HRIBox.prototype.resizeHandler = function()
455{
456   // Sicherstellen, dass unter iOS die window-Breite tatsächlich geändert wurde und 
457   // der Resize nicht einfach nur durch ein Scroll-Event ausgelöst wird. 
458   if (window.innerWidth != this.m_CurrentWindowWidth)
459   {
460      this.m_CurrentWindowWidth = window.innerWidth;
461      
462      if (this.m_AlignX)
463         this.alignIBoxHorizontalTo(this.m_AlignX);
464      
465      if (this.m_AlignY)
466         this.alignIBoxVerticalTo(this.m_AlignY);
467  }
468}
469
470HRIBox.prototype.escapeKeydownHandler = function(event)
471{
472   event.preventDefault(); // sonst werden z.B. window.location.href unterbrochen.
473   
474   if (typeof(this.m_OnEscape) === 'function')
475   {
476      if (window.HRIBoxRegistry)
477      {
478         let ibox = window.HRIBoxRegistry[window.HRIBoxRegistry.length - 1];
479         if (this == ibox)
480            this.m_OnEscape();
481      }
482   }
483   
484   if (this.m_CloseButton || this.m_Prompt)
485      this.close();
486   
487   return false;
488}
489
490/**
491 * Das Nutzen der Escape-Taste zum Schließen der i-Box.
492 */
493HRIBox.prototype.keydownHandler = function()
494{
495   if (event.key === 'Escape')
496   {
497      this.escapeKeydownHandler(event);
498   }
499   else if (event.key === 'ArrowLeft')
500   {
501      // Durch Bilder navigieren
502      if (this.m_ImageSources && this.m_ImageSources.length > 1)
503      {
504         const previousButton = document.querySelector('[data-image-control-previous]');
505         if (previousButton)
506            previousButton.click();
507      }
508   }
509   else if (event.key === 'ArrowRight')
510   {
511      // Durch Bilder navigieren.
512      if (this.m_ImageSources && this.m_ImageSources.length > 1)
513      {
514         const nextButton = document.querySelector('[data-image-control-next]');
515         if (nextButton)
516            nextButton.click();
517      }
518   }
519}
520
521/**
522 * Behandelt die Events der i-Box.
523 * 
524 * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#the_value_of_this_within_the_handler
525 */
526HRIBox.prototype.handleEvent = function(event)
527{
528   switch (event.type)
529   {
530      case 'resize':
531         this.resizeHandler();
532        break;
533      case 'keydown':
534         this.keydownHandler();
535         break;
536   }
537}
538
539/**
540 * Ergänzt die globalen Event-Listener.
541 */
542HRIBox.prototype.addEventListener = function()
543{
544   HRAddEventListener(window, 'resize', this);
545   HRAddEventListener(window, 'keydown', this);
546}
547
548/**
549 * Entfernt die globalen Event-Listener.
550 */
551HRIBox.prototype.removeEventListener = function()
552{
553   HRRemoveEventListener(window, 'resize', this);
554   HRRemoveEventListener(window, 'keydown', this);
555}
556
557/**
558 * Öffnet den im übergebenen HTML-Element enthaltenen Content in einer i-Box.
559 * 
560 * @param element Das HTML-Element mit dem darzustellenden Content.
561 */
562HRIBox.prototype.openHtmlElement = function(element)
563{
564   this.m_HtmlElement = element;
565   if (element && typeof element === 'string')
566      this.m_HtmlElement = document.getElementById(element);
567   
568   this.open();
569}
570
571/**
572 * Öffnet die durch die übergebene Url angesprochene Seite in einer i-Box.
573 * 
574 * @param url Die Url der Seite, die angezeigt werden soll.
575 * 
576 * Anmerkung: Über den Darstellungs-Parameter können Gips-Seiten auch ohne Rahmen
577 * angezeigt werden.
578 */
579HRIBox.prototype.openUrl = function(url)
580{
581   this.m_Url = url;
582   this.open();
583}
584
585/**
586 * Öffnet eine Prompt-Box analog zur Javascript-prompt()-Methode.
587 * 
588 * @param text Der Text des Prompts.
589 * @param defaultText (optional)
590 * @param callBack (optional) Callback-Funktion, die nach dem Akzeptieren des Prompts ausgelöst wird und den eingegebenen Wert des Nutzers übergibt. Die Funktion erwartet eine Boolean-Rückgabe.
591 *        Wird false zurückgegeben, wird das Prompt nicht akzeptiert und bleibt stehen. Default: keine Verarbeitung der Eingabe. Das Popup wird akzeptiert.
592 * @param okayText (optional) Der Text der in dem 'Okay'-Button steht. Default: 'Ok'.
593 * @param cancelText (optional) Der Text der in dem 'Cancel'-Button steht. Default: 'Abbrechen'.
594 * @param saveResultInCookie (optional) Wenn true, dann wird ein Cookie mit dem Inhalt des Eingabefelds gesetzt. Default: false.
595 * @param saveResultInLocalStorage (optional) Wenn true, dann wird der Inhalt des Inputfelds im LocalStorage gespeichert. Default: false.
596 * @param configureInputHTML Callback-Funktion, um den erzeugten Promptinhalt in Spez
596ialfällen noch verändern zu können. 
597 */
598HRIBox.prototype.openPrompt = function(text, defaultText, callBack, okayText, cancelText, saveResultInCookie, saveResultInLocalStorage, configureInputHTML)
599{
600   if (text)
601   {
602      this.m_Prompt = true;
603      
604      this.m_PromptText = text;
605      this.m_PromptDefaultText = defaultText;
606      if (callBack)
607         this.m_PromptCallBack = callBack;
608      if (okayText)
609         this.m_PromptOkayText = okayText;
610      if (cancelText)
611         this.m_PromptCancelText = cancelText;
612      this.m_PromptSaveResultInCookie = saveResultInCookie;
613      this.m_PromptSaveResultInLocalStorage = saveResultInLocalStorage;
614      this.m_PromptConfigureInputHTML = configureInputHTML;
615      
616      /*
617       * Spezialität für Prompts: Schließen des Fensters nicht manuell ermöglichen.
618       */
619      this.m_CloseButton = false;
620      this.m_RemoveByBlackOverlayClick = false;
621      
622      this.open();
623   }
624}
625
626/**
627 * Stellt den übergebenen Text als Hinweis mit einem OK-Button dar.
628 * 
629 * @param text Der darzustellende Hinweistext.
630 * @param state [optional] Der Status des Hinweises (none, info, warning, error). Default: none.
631 * @param buttonText [optional] Ein abweichender Text für den OK-Button. Default: OK.
632 */
633HRIBox.prototype.openNote = function(text, state, buttonText)
634{
635   if (text)
636   {
637      this.m_Note = true;
638      this.m_NoteState = state || 'none';
639      
640      // Es handelt sich hier um ein Prompt, dessen Cancel-Button fehlt.
641      this.openPrompt(text, null, null, buttonText);
642   }
643}
644
645/**
646 * Öffnet ein Bild in der i-Box.
647 * 
648 * @param source Bildquelle: Absolute oder relative URL
649 * @param caption Unterschrift des Bildes (hier ist auch HTML erlaubt, die Unterschrift wird per innerHTML gesetzt)
650 * @param title Titel des Bildes
651 * @param link Verlinkung des Bildes
652 * @param linkTarget Das Target (_blank, _self, _parent, _top)
653 */
654HRIBox.prototype.openImage = function(source, caption, title, link, linkTarget)
655{
656   this.openImages(new Array(source), new Array(caption), new Array(title), new Array(link), new Array(linkTarget), null, null, 0);
657}
658
659/**
660 * Öffnet die übergebenen Bilder in der i-Box.
661 * 
662 * @param sources [Array] Bildquellen: Absolute oder relative URLs
663 * @param captions [Array] Unterschriften der Bilder (hier ist auch HTML erlaubt, die Unterschrift wird jeweils per innerHTML gesetzt)
664 * @param titles [Array] Titel der Bilder
665 * @param links [Array] Verlinkung der Bilder
666 * @param linkTargets [Array] Die Targets der Links (_blank, _self, _parent, _top)
667 * @param maxWidth maximale von den Bildern eingenommene Breite
668 * @param maxHeight maximale von den Bildern eingenommene Höhe
669 * @param first Index oder Quelle desjenigen Bildes, welches initial angezeigt werden soll
670 * @param addTabNavigation Ergänzt eine Tab-Navigation unter den Bildern.
671 * @param setAsBackground Setzt das Bild als Hintergrundbild eines umgebenden Divs ein.
672 */
673HRIBox.prototype.openImages = function(sources, captions, titles, links, linkTargets, maxWidth, maxHeight, first, addTabNavigation, setAsBackground, alttexts)
674{
675   this.m_ImageSources = sources;
676   this.m_ImageAlttexts = alttexts;
677   this.m_ImageCaptions = captions;
678   this.m_ImageTitles = titles;
679   this.m_ImageLinks = links;
680   this.m_ImageLinkTargets = linkTargets;
681   if (!addTabNavigation)
682      this.m_ImageAddTabNavigation = false;
683   if (maxWidth)
684      this.m_ImageMaxWidth = maxWidth;
685   if (maxHeight)
686      this.m_ImageMaxHeight = maxHeight;
687   if (setAsBackground && setAsBackground == false)
688      this.m_ImageSrcAsBackgroundImage = false;
689   
690   if (this.m_ImageSources)
691   {
692      /*
693       * Bestimme Index des initial anzuzeigenden Bildes.
694       */
695      if (this.isUnsignedInteger(first))
696      {
697         this.m_ImageIdx = first;
698      }
699      else
700      {
701         if (typeof first === 'string')
702         {
703            var i;
704            var l = sources.length;
705            for (var i = 0; i < l; i++)
706            {
707               if (sources[i] && sources[i] == first) 
708               {
709                  this.m_ImageIdx = i;
710                  break;
711               }
712            }
713         }
714      }
715      
716      this.open();
717   }
718}
719
720/**
721 * Erzeugt die minimale Struktur der i-Box.
722 */
723HRIBox.prototype.createMinimalSkeleton = function()
724{
725   /*
726    * Ist schon eine I-Box geöffnet? Dann schmeißen wir diese erst weg.
727    */
728   if (this.m_IBox)
729   {
730      let contentNode;
731      
732      // i-Box-Content darf mit dem Wegwerfen der i-Box nicht wegfallen.
733      if (document.getElementById(this.getUID('idIBoxContent')))
734         contentNode = document.getElementById(this.getUID('idIBoxContent')).firstChild;
735      
736      this.removeDOMElement(this.m_IBox);
737      
738      // Hänge den Content wieder an den Body.
739      if (contentNode)
740      {
741         contentNode.style.display = 'none';
742         document.getElementsByTagName('body')[0].appendChild(contentNode);
743      }
744   }
745   
746   /*
747    * Erzeuge i-Box
748    */
749   this.m_IBox = document.createElement('div');
750   this.m_IBox.tabIndex = 0;
751   var basisCSSKlasse = 'HrSys-Gips-DivIBox';
752   if (this.m_Theme === HRIBoxThemes.Popup)
753      basisCSSKlasse = 'hr-popup';
754   else if (this.m_Prompt)
755   {
756      basisCSSKlasse = 'hr-prompt' + (this.m_Note ? '-note' : '');
757      if (this.m_Note)
758      {
759         // Den Status noch ergänzen: none, info, warning, error.
760         if (this.m_NoteState && this.m_NoteState.trim() !== 'none')
761            basisCSSKlasse += ' ' + this.m_NoteState;
762      }
763   }
764   
765   if (this.m_Modal)
766   {
767      this.m_IBox.ariaModal = true;
768      this.m_IBox.role = 'dialog';
769   }
770
771   var alignmentXCSSKlasse = 'v-' + this.m_AlignX;
772   var alignmentYCSSKlasse = 'h-' + this.m_AlignY;
773   
774   this.m_IBox.setAttribute('class', basisCSSKlasse + ' ' + alignmentXCSSKlasse + ' ' + alignmentYCSSKlasse + ' ' + this.m_CSSSuffix + ' ' + this.m_AdditionalIBoxClass);
775   
776   if (this.m_BodyClass){
777      this.m_IBox.setAttribute('data-hribox-bodyclass', this.m_BodyClass);
778      if(this.m_BodyClass == "consent-bg"){
779         this.m_IBox.setAttribute('aria-labelledby', 'consent-dialog-title');
780         this.m_IBox.setAttribute('aria-describedby', 'consent-dialog-desc');
781      }
782   }
783   this.m_IBox.setAttribute('data-hribox-bodyclass', this.m_BodyClass);
784      
785   this.m_IBox.setAttribute('id', this.getUID('idHrSysGipsDivIBox'));
786   
787   if (!this.m_HtmlElement && !this.m_Url)
788   {
789      if (this.m_Width && this.m_Width > 0)
790      {
791         // Für Pixelangaben begrenzen wir die Breite auf maximal 80% des Viewports. Für 
792         // andere Einheiten übernehmen wir einfach mal die Breite.
793         if (this.m_WidthUnit.trim() === 'px')
794            this.m_IBox.style.width = Math.min(this.m_Width, Math.round(HRGetViewPortWidth() * 0.85)) + this.m_WidthUnit;
795         else
796            this.m_IBox.style.width = this.m_Width + this.m_WidthUnit;
797      }
798      else
799         this.m_IBox.style.width = HRGetViewPortWidth() * 0.85  + 'px';
800      
801      if (this.m_Height && this.m_Height > 0)
802         this.m_IBox.style.height = this.m_Height + this.m_HeightUnit;
803   }
804   
805   if (this.m_ParentDiv)
806   {
807      this.m_ParentDiv.appendChild(this.m_IBox);
808      this.m_ParentDiv.style.position = 'relative';
809   }
810   else
811   {
812      //
813      // Positionierung: Wir benötigen für das korrekte Alignment eine fixierte Positionierung.
814      //
815      if (!getStyleValue(this.m_IBox, 'position') || getStyleValue(this.m_IBox, 'position').length <= 0)
816         this.m_IBox.style.position = 'fixed';
817      
818      // Als erstes einfügen, damit der Fokus sofort im Fenster ist.
819      HRDOM.insertAsFirstChild(this.m_IBox, document.body);
820   }
821   
822   if (this.m_CloseButtonAsFirstElement && this.m_CloseButton)
823      this.createIBoxCloseButton();
824   
825   /*
826    * Erzeuge i-Box-Content
827    */
828   var content = document.createElement('div');
829   content.setAttribute('id', this.getUID('idIBoxContent'));
830   content.setAttribute('class', 'HrSys-Gips-iBoxDivContent' + ' ' + this.m_CSSSuffix + ' ' + this.m_AdditionalIBoxContentClass);
831   this.m_IBox.appendChild(content);
832   try
833   {
834      if (this.m_Draggable)
835         new Draggable(content.id);
836   }
837   catch(e)
838   {
839   }
840}
841
842/**
843 * Erzeugt einen Schließen-Button für die i-Box.
844 */
845HRIBox.prototype.createIBoxCloseButton = function()
846{
847   if (this.m_IBox)
848   {
849      var _this = this;
850      closeDiv = document.createElement(this.m_CloseButtonTagName);
851      closeDiv.setAttribute('id', this.getUID('idIBoxDivClose'));
852      closeDiv.setAttribute('class', 'HrSys-Gips-iBoxDivClose' + (this.m_ImageSources ? 'Image' : ''));
853      
854      // Wenn der Schließen-Button den Tag-Namen: Button hat, dann werden 
855      // die Events direkt auf dem Button ausgeführt.
856      if (this.m_CloseButtonTagName === 'button')
857      {
858         closeDiv.setAttribute('aria-label', 'i-Box schließen');
859         closeDiv.addEventListener('mouseup', function(e) {
860            _this.close();
861            return false;
862         });
863         HRAddEventListener(closeDiv, 'keydown', (e) => {
864            if (event.key === 'Enter')
865            {
866               _this.close();
867               event.preventDefault();
868               return false;
869            }
870         });
871      }
872      else 
873      {
874         let closeElement = document.createElement('a');
875         closeElement.setAttribute('href', '#');
876         closeElement.setAttribute('aria-label', 'i-Box schließen');
877         closeElement.addEventListener('mouseup', function(e) {
878            _this.close();
879            return false;
880         });
881         HRAddEventListener(closeElement, 'keydown', (e) => {
882            if (event.key === 'Enter')
883            {
884               _this.close();
885               return false;
886            }
887         });
888         closeDiv.appendChild(closeElement);
889      }
890   }
891   this.m_IBox.appendChild(closeDiv);
892}
893
894/**
895 * Erzeugt einen schwarz-transparenten Hintergrund zum Abdunkeln
896 */
897HRIBox.prototype.createBlackOverlay = function()
898{
899   var _this = this;
900   
901   var blackOverlay = document.getElementById('idHrSysGipsDivBlackTransparentOverlay');
902   if (blackOverlay) 
903   {
904      this.removeDOMElement(blackOverlay);
905   }
906   
907   var overlayWidth = (this.m_ParentDiv ? getElementWidthValue(this.m_ParentDiv) : this.getBodyWidth());
908   var overlayHeight = (this.m_ParentDiv ? getElementHeightValue(this.m_ParentDiv) : this.getBodyHeight());
909   
910   //
911   // Erstelle das Overlay. Beachte: Das Overlay soll nicht eindeutig pro Instanz sein, s.d. die ID des repräsentierenden Divs nicht über eine UUID eindeutig gemacht wird.
912   //
913   blackOverlay = this.createFloatingElement('div', overlayWidth, overlayHeight, 0, 0, 'idHrSysGipsDivBlackTransparentOverlay');
914   blackOverlay.className = 'HrSys-Gips-iBoxIFrameOverlay';
915   blackOverlay.style.backgroundColor = '#000000';
916   blackOverlay.style.visibility = 'visible';
917   blackOverlay.style.opacity = '0.6';
918   
919   if (this.m_RemoveByBlackOverlayClick)
920   {
921      blackOverlay.onmouseup = function()
922      {
923         _this.close();
924      }
925   }
926   else if (this.m_Modal)
927   {
928      HRAddEventListener(blackOverlay, 'click', () => {
929         _this.m_IBox.focus();
930      });
931   }
932   
933   // Das Overlay soll den gesamten Body überdecken.
934   if (this.m_ParentDiv)
935   {
936      blackOverlay.style.width  = getElementWidthValue(this.m_ParentDiv) + 'px'; 
937      blackOverlay.style.height = getElementHeightValue(this.m_ParentDiv) + 'px';
938   }
939   else
940   {
941      blackOverlay.style.width  = _this.getBodyWidth() + 'px'; 
942      blackOverlay.style.height = _this.getBodyHeight() + 'px';
943   }
944   
945   if (this.m_ParentDiv)
946   {
947      this.m_ParentDiv.appendChild(blackOverlay);
948   }
949   else
950   {
951      document.getElementsByTagName('body')[0].appendChild(blackOverlay);
952   }
953   
954   this.m_IBox.setAttribute('data-hribox-overlay', 'J');
955   
956   // Beim Resize des Fensters werden die Dimensionen des Overlays angepasst.
957   window.onresize = function()
958   {
959      if (blackOverlay)
960      {
961         blackOverlay.style.width  = _this.getBodyWidth() + 'px'; 
962         blackOverlay.style.height = _this.getBodyHeight() + 'px';
963      }
964   }
965}
966
967/**
968 * Setzt den darzustellenden Inhalt der i-Box: HTML-Content.
969 */
970HRIBox.prototype.setIBoxContentHtmlElement = function()
971{
972   if (this.m_HtmlElement)
973   {
974      // Wir legen zunächst um das ursprüngliche Element einen Wrapper, um beim Schließen
975      //  der i-Box das Element wieder an seine ursprüngliche Stelle verschieben zu können.
976      this.m_HtmlElementTmpWrapper = document.createElement('div');
977      this.m_HtmlElementTmpWrapper.style.display = 'none';
978      HRWrap(this.m_HtmlElement, this.m_HtmlElementTmpWrapper);
979      
980      this.m_HtmlElement.style.visibility = 'visible';
981      this.m_HtmlElement.dataset.hriboxContent = 'J'; // Aktuell für das Schließen aller i-Boxen.
982      
983      //
984      // Da häufig auch mit display: none; zum Verstecken von
985      // Divs gearbeitet wird, setzen wir ggfs auch diesen Schalter um.
986      //
987      if (this.m_HtmlElement.style.display == 'none')
988         this.m_HtmlElement.style.display = this.m_Prompt ? 'flex' : 'block'; // flex im Falle des Prompts
989      
990      var contentDiv = document.getElementById(this.getUID('idIBoxContent'));
991      if (contentDiv)
992      {
993         contentDiv.appendChild(this.m_HtmlElement);
994         
995         if (this.m_Width && this.m_Width > 0)
996         {
997            var contentWidth = getElementWidthValue(this.m_HtmlElement);
998            if (contentWidth && parseInt(contentWidth) > this.m_Width)
999            {
1000               contentDiv.style.overflowX = 'auto';
1001               contentDiv.style.overflowY = 'hidden';
1002               contentDiv.style.height = '100%';
1003            }
1004            
1005            // Für Pixelangaben begrenzen wir die Breite auf maximal 80% de
1005s Viewports. Für 
1006            // andere Einheiten übernehmen wir einfach mal die Breite.
1007            if (this.m_WidthUnit.trim() === 'px')
1008               this.m_IBox.style.width = Math.min(this.m_Width, Math.round(HRGetViewPortWidth() * 0.85)) + this.m_WidthUnit;
1009            else
1010               this.m_IBox.style.width = this.m_Width + this.m_WidthUnit;
1011         }
1012         if (this.m_Height && this.m_Height > 0)
1013         {
1014            var contentHeight = getElementHeightValue(this.m_HtmlElement);
1015            if (contentHeight && parseInt(contentHeight) > this.m_Height)
1016            {
1017               contentDiv.style.overflowY = 'auto';
1018               contentDiv.style.overflowX = 'hidden';
1019               contentDiv.style.height = '100%';
1020            }
1021            this.m_IBox.style.height = this.m_Height + this.m_HeightUnit;
1022         }
1023         
1024         // Die i-Box als modalen Dialog setzen.
1025         if (this.m_Modal)
1026            HRDOM.trapFocus(this.m_IBox);
1027         
1028         if (typeof HREventManager === 'object')
1029            HREventManager.dispatchEvent(this.m_IBox, 'HRIBoxContentLoaded', {useStandard: false, notifyParents: false, detail: this.m_IBox});
1030      }
1031   }
1032}
1033
1034/**
1035 * Setzt den Seiteninhalt der übergebenen Url als Inhalt der i-Box.
1036 * 
1037 * Wir verwenden dazu ein i-Frame.
1038 */
1039HRIBox.prototype.setIBoxContentUrl = function()
1040{
1041   if (this.m_Url)
1042   {
1043      try
1044      {
1045         var iframe = document.createElement('iframe');
1046         iframe.src = this.m_Url;
1047         iframe.frameborder = 0;
1048         
1049         // Die Breite des i-Frames wird gesetzt. Wird keine Breite übergeben,
1050         // wird das Fenster auf 85 % des Viewports gesetzt. Die Höhe dazu wird dann
1051         // später anhand des geladenen Inhalts bestimmt.
1052         if (this.m_Width && this.m_Width > 0)
1053         {
1054            // Für Pixelangaben begrenzen wir die Breite auf maximal 80% des Viewports. Für 
1055            // andere Einheiten übernehmen wir einfach mal die Breite.
1056            if (this.m_WidthUnit.trim() === 'px')
1057               iframe.width = Math.min(this.m_Width, Math.round(HRGetViewPortWidth() * 0.85)) + this.m_WidthUnit;
1058            else
1059               iframe.width = this.m_Width + this.m_WidthUnit;
1060         }
1061         else
1062            iframe.width = Math.round(HRGetViewPortWidth() * 0.85) + 'px';
1063         
1064         document.getElementById(this.getUID('idIBoxContent')).appendChild(iframe);
1065         
1066         // Für den Ladeprozess des externen Inhalts einen Spinner anzeigen.
1067         var spinner = showSpinner(this.m_IBox);
1068         
1069         const _this = this;
1070         
1071         iframe.onload = function () {
1072            if (spinner)
1073            {
1074               spinner.stop();
1075               spinner = null;
1076            }
1077            
1078            // Den keydown-Handler müssen wir auch an das content-Window hängen, um bspw. 
1079            // das Schließen per Escape-Taste zu ermöglichen.
1080            HRAddEventListener(iframe.contentWindow, 'keydown', _this.keydownHandler.bind(_this));
1081            
1082            // Fokussierbarkeit und Barrierefreiheit.
1083            const iframeBody = iframe.contentWindow.document.body;
1084            if (iframeBody)
1085            {
1086               // Der Inhalt wurde geladen, jetzt können wir die Höhe anhand des Inhalts
1087               // und der fest gegebenen Breite ermitteln. Es wird aber maximal 85 % der
1088               // Höhe des Viewports angenommen.
1089               if (_this.m_Height && _this.m_Height > 0)
1090                  iframe.height = _this.m_Height;
1091               else
1092                  iframe.height = Math.round(HRGetViewPortHeight() * 0.85);
1093               
1094               // Die i-Box als modalen Dialog setzen.
1095               if (_this.m_Modal)
1096                  HRDOM.trapFocus(iframeBody);
1097            }
1098            
1099            if (typeof HREventManager === 'object')
1100               HREventManager.dispatchEvent(_this.m_IBox, 'HRIBoxContentLoaded', {useStandard: false, notifyParents: false, detail: iframeBody});
1101         };
1102      }
1103      catch (e)
1104      {
1105         if (spinner)
1106         {
1107            spinner.stop();
1108            spinner = null;
1109         }
1110      }
1111   }
1112}
1113
1114/**
1115 * Bildergallerie: Erzeugt die Navigationspfeile für den Bildwechsel.
1116 */
1117HRIBox.prototype.createImageNavigation = function()
1118{
1119   var _this = this;
1120   
1121   var caption = document.getElementById(this.getUID('idIBoxDivCaption'));
1122   if (caption)
1123   {
1124      // Zurück-Button
1125      var previous = document.getElementById(this.getUID('idIBoxDivPrevious'));
1126      if (!previous)
1127      {
1128         if (this.m_ImagesNextPrevAsButtons) {
1129            previous = document.createElement("button");
1130         } else {
1131            previous = document.createElement("div");
1132         }
1133         previous.id = this.getUID('idIBoxDivPrevious');
1134         previous.dataset.imageControlPrevious = true;
1135         previous.setAttribute('aria-label', 'Vorheriger Slide');
1136         previous.setAttribute('aria-controls', _this.getUID('idIBoxDiv4Image'));
1137         
1138         caption.appendChild(previous);
1139      }
1140      previous.onclick = function() {
1141         if (_this.m_ImageIdx != 0)
1142         {
1143            _this.showImage(--_this.m_ImageIdx);
1144         }
1145      };
1146      
1147      // Next-Button
1148      var next = document.getElementById(this.getUID('idIBoxDivNext'));
1149      if (!next)
1150      {
1151         if (this.m_ImagesNextPrevAsButtons) {
1152            next = document.createElement("button");
1153         } else {
1154            next = document.createElement("div");
1155         }
1156         next.id = this.getUID('idIBoxDivNext');
1157         next.dataset.imageControlNext = true;
1158         next.setAttribute('aria-label', 'Nächster Slide');
1159         next.setAttribute('aria-controls', _this.getUID('idIBoxDiv4Image'));
1160         caption.appendChild(next);
1161      }
1162      
1163      next.onclick = function() {
1164         if (_this.m_ImageIdx != _this.m_ImageSources.length - 1)
1165         {
1166            _this.showImage(++_this.m_ImageIdx);
1167         }
1168      };
1169   }
1170}
1171
1172/**
1173 * Bildergallerie: Erzeugt die Tabnavigation für den Bildwechsel.
1174 * 
1175 * @param selectedIdx Der Index des selektierten Tabs.
1176 */
1177HRIBox.prototype.createImageTabs = function(selectedIdx)
1178{
1179   var _this = this;
1180   
1181   var tabnavigation = document.getElementById(this.getUID('tabnavigation'));
1182   if (!tabnavigation)
1183   {
1184      if (this.m_ImagesTabNavigationAsButtons) {
1185         var tabnavigation = document.createElement('div');
1186      } else {
1187         var tabnavigation = document.createElement('ul');
1188      }
1189      tabnavigation.className = 'tab-navigation';
1190      tabnavigation.id = this.getUID('tabnavigation');
1191      tabnavigation.setAttribute('role', 'tablist');
1192      tabnavigation.setAttribute('aria-label', 'Bildergalerie Navigation');
1193      
1194      // Pro Bild wird ein Tab erstellt.
1195      for (var i = 0; i < this.m_ImageSources.length; i++)
1196      {
1197         if (this.m_ImagesTabNavigationAsButtons) {
1198            var tab = document.createElement('button');
1199         } else {
1200            var tab = document.createElement('li');
1201         }
1202         tab.className = 'tab';
1203         if (i === parseInt(selectedIdx)){
1204            addCSSClass(tab, 'selected');
1205            tab.setAttribute('aria-selected', 'true');
1206            tab.setAttribute('tabindex', '0');
1207         } else {
1208            removeCSSClass(tab, 'selected');
1209            tab.setAttribute('aria-selected', 'false');
1210            tab.setAttribute('tabindex', '-1');
1211         }
1212         tab.dataset.idx = i;
1213         tab.onclick = function() {
1214            _this.m_ImageIdx = this.dataset.idx;
1215            toggleCSSClass(document.getElementById(_this.getUID('idIBoxContent')), 'transparent');
1216            _this.showImage(this.dataset.idx);
1217         };
1218         tabnavigation.appendChild(tab);
1219      }
1220      
1221      this.m_IBox.appendChild(tabnavigation);
1222   }
1223   
1224   // Noch das selektierte Tab ermitteln.
1225   for (var i = 0; i < this.m_ImageSources.length; i++)
1226   {
1227      if (i === parseInt(selectedIdx)){
1228         addCSSClass(tabnavigation.querySelector('[data-idx="' + i + '"]'), 'selected');
1229         tabnavigation.querySelector('[data-idx="' + i + '"]').setAttribute('aria-selected', 'true');
1230         tabnavigation.querySelector('[data-idx="' + i + '"]').setAttribute('tabindex', '0');
1231      } else {
1232         removeCSSClass(tabnavigation.querySelector('[data-idx="' + i + '"]'), 'selected');
1233         tabnavigation.querySelector('[data-idx="' + i + '"]').setAttribute('aria-selected', 'false');
1234         tabnavigation.querySelector('[data-idx="' + i + '"]').setAttribute('tabindex', '-1');
1235      }
1236   }
1237}
1238
1239/**
1240 * Erzeugt den inneren Caption-Bereich des Bildes: In diesem werden Titel, Beschreibung und ggf. Bildnummer dargestellt.
1241 */
1242HRIBox.prototype.createImageInnerCaption = function()
1243{
1244   var _this = this;
1245   
1246   var caption = document.getElementById(this.getUID('idIBoxDivCaption'));
1247   if (caption)
1248   {
1249      var innerCaption = document.getElementById(this.getUID('idIBoxDivInnerCaption'));
1250      if (!innerCaption)
1251      {
1252         innerCaption = document.createElement("div");
1253         innerCaption.setAttribute('id', this.getUID('idIBoxDivInnerCaption'));
1254         innerCaption.setAttribute('class', 'HrSys-Gips-iBoxDivInnerCaption');
1255         caption.appendChild(innerCaption);
1256      }
1257      
1258      //
1259      // Setzen der Bildunterschrift.
1260      //
1261      innerCaption.innerHTML = '<div id="' + this.getUID('idIBoxDivTitle') + '" class="HrSys-Gips-iBoxDivTitle">'
1262      + '   <div id="'  + this.getUID('idIBoxDivNumber') + '" class="HrSys-Gips-iBoxDivNumber" aria-live="polite" aria-atomic="true"></div>' 
1263      + '</div>' 
1264      + '<div id="'  + this.getUID('idIBoxDivDescription') + '" class="HrSys-Gips-iBoxDivDescription fade-in"></div>'
1265      + '<div id="'  + this.getUID('idIBoxDivNumber') + '" class="HrSys-Gips-iBoxDivNumber"></div>';
1266   }
1267}
1268
1269/**
1270 * Setter für die Image-Caption. Hier werden insbesondere im Falle mehrerer Bilder Vor- 
1271 * und Zurückbutton für den Bildwechsel erzeugt.
1272 */
1273HRIBox.prototype.createImageCaption = function()
1274{
1275   var caption = document.getElementById(this.getUID('idIBoxDivCaption'));
1276   if (!caption)
1277   {
1278      caption = document.createElement('div');
1279      caption.setAttribute('id', this.getUID('idIBoxDivCaption'));
1280      caption.setAttribute('class', 'HrSys-Gips-iBoxDivCaption' + this.m_CSSSuffix);
1281      this.m_IBox.appendChild(caption);
1282   }
1283}
1284
1285/**
1286 * Bildergallerie: Wechselt zum Bild des angegebenen Indexes.
1287 * 
1288 * @param idx der Index des anzuzeigenden Bildes.
1289 */
1290HRIBox.prototype.showImage = function(idx)
1291{
1292   if ((this.m_ImageTitles && this.m_ImageTitles[idx]) || (this.m_ImageCaptions && this.m_ImageCaptions[idx]) || this.m_ImageSources.length > 1)
1293      this.createImageCaption(idx);
1294   
1295   // Bildnavigation (Vor- und Zurückpfeile) erzeugen.
1296   if (this.m_ImageSources.length > 1)
1297   {
1298      if (this.m_ImageAddTabNavigation)
1299         this.createImageTabs(this.m_ImageIdx);
1300      this.createImageNavigation();
1301   }
1302   
1303   this.createImageInnerCaption();
1304   
1305   // Bildbeschreibung setzen.
1306   if (this.m_ImageCaptions && this.m_ImageCaptions[idx])
1307      this.setImageDescription(this.m_ImageCaptions[idx]);
1308   
1309   // Bildtitel setzen.
1310   if (this.m_ImageTitles && this.m_ImageTitles[idx])
1311      this.setImageTitle(this.m_ImageTitles[idx]);
1312   
1313   // Bildnummer setzen.
1314   if (this.m_ImageSources.length > 1)
1315      this.setImageNumber(idx + 1); // Anzeigenummer nicht null-basiert.
1316   
1317   // CSS-Klasse für den Zurück-Button setzen.
1318   var prevButton = document.getElementById(this.getUID('idIBoxDivPrevious'));
1319   if (prevButton)
1320   {
1321      prevButton.setAttribute('class', 'HrSys-Gips-' + (this.m_ImageIdx != 0 ? 'iBoxDivPrevious' : 'iBoxDivPreviousInactive'));
1322      if (this.m_ImageIdx == 0)
1323      {
1324         prevButton.setAttribute('disabled', '');
1325      }
1326   }
1327   
1328   // CSS-Klasse für den Vor-Button setzen.
1329   var nextButton = document.getElementById(this.getUID('idIBoxDivNext'));
1330   if (nextButton)
1331   {
1332      nextButton.setAttribute('class', 'HrSys-Gips-' + (this.m_ImageIdx != this.m_ImageSources.length - 1 ? 'iBoxDivNext' : 'iBoxDivNextInactive'));
1333      if (this.m_ImageIdx == this.m_ImageSources.length - 1)
1334      {
1335         nextButton.setAttribute('disabled', '');
1336      }
1337         
1338   }
1339   
1340   //
1341   // Den Content des Bildes setzen.
1342   //
1343   this.setImageContent(idx);
1344}
1345
1346/**
1347 * Setzt die Beschreibung des Bildes in der iBox.
1348 * 
1349 * @param description Die Bildbeschreibung. Diese wird per innerHTML gesetzt, d.h. hier kann auch HTML wie Spans verwendet werden.
1350 */
1351HRIBox.prototype.setImageDescription = function(description)
1352{
1353   var descriptionElement = document.getElementById(this.getUID('idIBoxDivDescription'));
1354   
1355   if (description && description.length > 0)
1356   {
1357      if (description.length > 60)
1358        this.m_minImageWidth = 450;
1359      else 
1360        this.m_minImageWidth = 300;
1361        
1362      descriptionElement.innerHTML = description;
1363      descriptionElement.style.display = "block";
1364   }
1365   else
1366   {
1367      descriptionElement.innerHTML = "";
1368      descriptionElement.style.display = "none";
1369   }
1370}
1371
1372/**
1373 * Setter für den Bildtitel.
1374 * 
1375 * @param title Der neue Titel.
1376 */
1377HRIBox.prototype.setImageTitle = function(title)
1378{
1379   var titleElement = document.getElementById(this.getUID('idIBoxDivTitle'));
1380   if (titleElement && title)
1381   {
1382      if (title.length > 0)
1383      {
1384         titleElement.innerHTML = title;
1385         titleElement.style.display = 'block';
1386      }
1387      else
1388      {
1389         titleElement.innerHTML = '';
1390         titleElement.style.display = 'none';
1391      }
1392   }
1393}
1394
1395/**
1396 * Setzt die aktuelle Zahl bzw. den Index des anzuzeigenden Bildes.
1397 * 
1398 * @param number Die zu setzende Bildnummer.
1399 */
1400HRIBox.prototype.setImageNumber = function(number)
1401{
1402   var numberDiv = document.getElementById(this.getUID('idIBoxDivNumber'));
1403   if (numberDiv)
1404   {
1405      numberDiv.innerHTML = number + ' von ' + this.m_ImageSources.length;
1406   }
1407}
1408
1409/**
1410 * Setzt den darzustellenden Inhalt der i-Box: Bilder.
1411 * 
1412 * @param idx Der Index des anzuzeigenden Bildes.
1413 */
1414HRIBox.prototype.setImageContent = function(idx)
1415{
1416   _this = this;
1417   
1418   if (this.m_ImageSources && this.m_ImageSources[idx])
1419   {
1420      /*
1421       * Bevor nicht alle Einstellungen des Bildes gesetzt wurden, zeigen wir auch nicht      */
1422      this.m_IBox.style.visibility = 'hidden';
1423      
1424      var imageDiv = document.getElementById(this.getUID('idIBoxDiv4Image'));
1425      if (!imageDiv)
1426      {
1427         imageDiv = document.createElement('DIV');
1428         imageDiv.setAttribute('id', this.getUID('idIBoxDiv4Image'));
1429         imageDiv.setAttribute('class', 'HrSys-Gips-iBoxDiv4Image');
1430      }
1431      else
1432      {
1433         // Das alte Bild wird gelöscht, ...
1434         this.removeDOMElement(imageDiv.querySelector('[data-old]'));
1435         
1436         // ... das aktuelle als alt markiert.
1437         var selectedImage = imageDiv.querySelector('[data-selected]');
1438         if (selectedImage)
1439         {
1440            selectedImage.removeAttribute('data-selected');
1441            selectedImage.setAttribute('data-old', 'J');
1442         }
1443      }
1444      
1445      var content = document.getElementById(this.getUID('idIBoxContent'));
1446      if (content)
1447         content.appendChild(imageDiv);
1448      
1449      var img = document.createElement('img');
1450      img.setAttribute('id', this.getUID('idIBoxContentImage'));
1451      img.setAttribute('class', 'HrSys-Gips-iBoxImage' + (imageDiv.querySelector('[data-old]') ? ' hide' : ''));
1452      img.setAttribute('data-selected', 'J');
1453      if (this.m_ImageAlttexts && this.m_ImageAlttexts[idx])
1454         img.setAttribute('alt', this.m_ImageAlttexts[idx]);
1455      img.tabIndex = "0";
1456      
1457      // ggf. das Bild als Hintergrundbild eines Divs setzen.
1458      var selectedImageDiv = null;
1459      if (this.m_ImageSrcAsBackgroundImage)
1460      {
1461         selectedImageDiv = document.getElementById(this.getUID('selectedimagecontainer'));
1462         if (!selectedImageDiv)
1463         {
1464            selectedImageDiv = document.createElement('div');
1465            selectedImageDiv.id = this.getUID('selectedimagecontainer');
1466            selectedImageDiv.style.backgroundImage  = 'url(\'' + this.m_ImageSources[idx] + '\')';
1467            selectedImageDiv.className = 'selected-image-container';
1468            selectedImageDiv.appendChild(img);
1469            imageDiv.appendChild(selectedImageDiv);
1470         }
1471      }
1472      else
1473         imageDiv.appendChild(img);
1474      
1475      var linkContainer = img;
1476      if (this.m_ImageSrcAsBackgroundImage)
1477         linkContainer = selectedImageDiv;
1478      
1479      if (this.m_ImageLinks && this.m_ImageLinks[idx])
1480      {
1481         const openImageLink = () => {
1482            if (_this.m_ImageLinks[idx].toLowerCase().indexOf('http') == 0 || (_this.m_ImageLinkTargets && _this.m_ImageLinkTargets[idx] == '_blank'))
1483            {
1484               /*
1485                * In neuem Browserfenster/ Tab öffnen.
1486                */
1487               window.open(_this.m_ImageLinks[idx], '_blank');
1488               HRCloseAllIBoxes();
1489            }
1490            else
1491            {
1492               /*
1493                * In selbem Browserfenster/ Tab öffnen.
1494                */
1495               window.location.href = _this.m_ImageLinks[idx];
1496            }
1497         };
1498         
1499         linkContainer.onclick = openImageLink;
1500         linkContainer.onkeydown = (event) => {
1501            
1502            if (event.key === 'Enter')
1503               openImageLink();
1504         };
1505         linkContainer.style.cursor = 'pointer';
1506      }
1507      else
1508      {
1509         if (this.m_ImageIdx != this.m_ImageSources.length - 1)
1510         {
1511            linkContainer.onclick = function()
1512            {
1513               // Anzeige des nächsten Bildes, wenn die maximale Bildanzahl nicht überschritten wurde.
1514               if (_this.m_ImageIdx < _this.m_ImageSources.length - 1)
1515                  _this.showImage(++_this.m_ImageIdx);
1516            }
1517         }
1518      }
1519      
1520      img.src = this.m_ImageSources[idx];
1521      
1522      /*
1523       * Bildgröße ermitteln 
1524       */
1525      var dummyImg = new Image();
1526      dummyImg.onload = function()
1527      {
1528         var currentContentWidth  = dummyImg.width;
1529         var currentContentHeight = dummyImg.height;
1530         
1531         if (_this.m_Height && _this.m_Height > 0)
1532         {
1533            currentContentHeight = _this.m_Height;
1534            
1535            //
1536            // Wenn keine Breite gesetzt wurde, skalieren wir die Breite im richtigen Verhältnis mit.
1537            //
1538            if (!_this.m_Width)
1539               currentContentWidth = img.width / (img.height / _this.m_Height);
1540         }
1541         else
1542         {
1543            // Bild höher als erlaubt?
1544            if (_this.m_ImageMaxHeight && currentContentHeight > _this.m_ImageMaxHeight)
1545            {
1546               // Verkleinern und Seitenverhältnis erhalten
1547               currentContentHeight = _this.m_ImageMaxHeight;
1548               currentContentWidth  = img.width / (img.height / _this.m_ImageMaxHeight);
1549            }
1550         }
1551         
1552         if (_this.m_Width && _this.m_Width > 0)
1553         {
1554            currentContentWidth = _this.m_Width;
1555            
1556            //
1557            // Wenn keine Höhe gesetzt wurde, skalieren wir die Höhe im richtigen Verhältnis mit.
1558            //
1559            if (!_this.m_Height)
1560            {
1561               currentContentHeight = img.height / (img.width / _this.m_Width);
1562            }
1563         }
1564         else
1565         {
1566            // Bild breiter als erlaubt
1567            if (_this.m_ImageMaxWidth && currentContentWidth > _this.m_ImageMaxWidth)
1568            {
1569               // verkleinern
1570               currentContentHeight = img.height / (img.width / _this.m_ImageMaxWidth);
1571               currentContentWidth  = _this.m_ImageMaxWidth;
1572            }
1573            
1574            // Breite zu klein?
1575            var divWidth = currentContentWidth;
1576            if (!this.m_Width && currentContentWidth < _this.m_ImageMinWidth)
1577            {
1578               divWidth = _this.m_ImageMinWidth;
1579            }
1580            // die Breite der Divs für das Bild und Titel/Beschreibung unter dem Bild
1581            if (content)
1582               content.style.width = divWidth + 'px';
1583            
1584            var caption = document.getElementById(_this.getUID('idIBoxDivCaption'));
1585            if (caption)
1586               caption.style.width = divWidth + 'px';
1587         }
1588         
1589         // Bild skalieren
1590         img.height = currentContentHeight;
1591         img.width  = currentContentWidth;
1592         
1593         // Bild dran hängen
1594         if (_this.m_ImageSrcAsBackgroundImage)
1595            selectedImageDiv.appendChild(img);
1596         else
1597            document.getElementById(_this.getUID('idIBoxDiv4Image')).appendChild(img);
1598         
1599         // Fade-in-Transition.
1600         var oldImage = imageDiv.querySelector('[data-old]'); 
1601         if (_this.m_ImageSrcAsBackgroundImage && selectedImageDiv && selectedImageDiv.querySelector('[data-old]'))
1602            oldImage = selectedImageDiv.querySelector('[data-old]');
1603         
1604         if (oldImage)
1605         {
1606            oldImage.style.display = 'none';
1607            addCSSClass(oldImage, 'hide');
1608            removeCSSClass(oldImage, 'fade-in');
1609         }
1610         
1611         if (img)
1612         {
1613            img.style.display = '';
1614            removeCSSClass(img, 'hide');
1615            addCSSClass(img, 'fade-in');
1616            
1617            //
1618            // ggf. noch das Hintergrundbild austauschen.
1619            //
1620            if (_this.m_ImageSrcAsBackgroundImage && selectedImageDiv)
1621               selectedImageDiv.style.backgroundImage = 'url(\'' + _this.m_ImageSources[idx] + '\')';
1622         }
1623         
1624         _this.alignIBoxVerticalTo('center');
1625         _this.alignIBoxHorizontalTo('center');
1626         _this.m_IBox.style.visibility = 'visible';
1627         
1628         // Die i-Box als modalen Dialog setzen.
1629         if (_this.m_Modal)
1630            HRDOM.trapFocus(_this.m_IBox);
1631         
1632         if (typeof HREventManager === 'object')
1633            HREventManager.dispatchEvent(_this.m_IBox, 'HRIBoxContentLoaded', {useStandard: false, notifyParents: false, detail: _this.m_IBox});
1634      };
1635      
1636      /*
1637       * Achtung: Für den IE ist es notwendig, dass das Setzen der 
1638       * Source vor dem Zuweisen des onload stattfindet.
1639       */
1640      dummyImg.src = this.m_ImageSources[idx];
1641   }
1642}
1643
1644/**
1645 * Löscht und entfernt die i-Box.
1646 */
1647HRIBox.prototype.close = function()
1648{
1649   if (this.m_IBox)
1650   {
1651      // Das HTML-Element wieder an die ursprüngliche Stelle im DOM zurückschieben.
1652      if (this.m_HtmlElement)
1653      {
1654         this.m_HtmlElement.style.display = "none";
1655         HRInsertAfter(this.m_HtmlElement, this.m_HtmlElementTmpWrapper);
1656         this.m_HtmlElementTmpWrapper.remove();
1657      }
1658      
1659      this.removeDOMElement(this.m_IBox);
1660      
1661      // Entferne das Overlay, falls keine andere Instanz dieses mehr benötigt (prüfe entsprechendes data-Attribut).
1662      if (document.querySelectorAll('[data-hribox-overlay=J]').length <= 0)
1663      {
1664         this.removeDOMElement(document.getElementById('idHrSysGipsDivBlackTransparentOverlay'));
1665         window.onresize = '';
1666      }
1667      
1668      // Alle globalen Events wieder entfernen.
1669      this.removeEventListener();
1670      
1671      // Leitet auf gesetzte Url weiter, falls m_RedirectUrlOnClose einen nicht-leeren String enthaelt
1672      if (this.m_RedirectUrlOnClose && (typeof this.m_RedirectUrlOnClose === "string" || (typeof this.m_RedirectUrlOnClose === "object" && this.m_RedirectUrlOnClose.constructor === String)) && this.m_RedirectUrlOnClose != "")
1673         top.location.href = this.m_RedirectUrlOnClose;
1674      else if (this.m_LastFocusedElement)
1675         this.m_LastFocusedElement.focus();
1676      
1677      // Aufruf Callback-Funktion.
1678      if (this.m_OnClose && typeof(this.m_OnClose) === 'function')
1679         this.m_OnClose();
1680      
1681      // Entferne die Body-Klasse, falls keine andere Instanz die Klasse benötigt (prüfe entsprechendes data-Attribut).
1682      if (this.m_BodyClass && document.querySelectorAll('[data-hribox-bodyclass=' + this.m_BodyClass + ']').length <= 0)
1683         HRRemoveBodyCSSClass(this.m_BodyClass);
1684      
1685      // Das Element wieder aus der window-Registry herausnehmen.
1686      const idx = window.HRIBoxRegistry.indexOf(this);
1687      if (idx > -1)
1688         window.HRIBoxRegistry.splice(idx, 1);
1689   }
1690}
1691
1692/**
1693 * Richtet die iBox vertikal aus.
1694 * 
1695 * @param alignment Die Ausrichtungsart ('top', 'bottom', 'center').
1696 */
1697HRIBox.prototype.alignIBoxVerticalTo = function(alignment)
1698{
1699   
1700   if (alignment.toLowerCase() == 'top')
1701   {
1702      this.m_IBox.style.bottom = '';
1703      this.m_IBox.style.top = (this.m_CloseButton ? 20 : 0) + 'px';
1704   }
1705   else if (alignment.toLowerCase() == 'center')
1706   {
1707      // Bei absoluter Positionierung wird auch die Scrollposition ergänzt.
1708      // Für fixierte Positionierung entfällt dieses natürlich.
1709      let scrollYPosition = 0;
1710      if (getStyleValue(this.m_IBox, 'position') === 'absolute')
1711         scrollYPosition = window.scrollY || window.pageYOffset;
1712      
1713      this.m_IBox.style.bottom = '';
1714      if (this.m_ParentDiv)
1715         this.m_IBox.style.top = Math.max(((getElementHeightValue(this.m_ParentDiv) - this.m_IBox.offsetHeight) / 2), (this.m_CloseButton ? 20 : 0)) + scrollYPosition + 'px';
1716      else
1717         this.m_IBox.style.top = Math.max(((HRGetViewPortHeight() - this.m_IBox.offsetHeight) / 2), (this.m_CloseButton ? 20 : 0)) + scrollYPosition + 'px';
1718   }
1719   else if (alignment.toLowerCase() == 'bottom')
1720   {
1721      this.m_IBox.style.top = '';
1722      this.m_IBox.style.bottom = '0%';
1723   }
1724}
1725
1726/**
1727 * Richtet die i-Box horizontal aus.
1728 * 
1729 * @param alignment Die Ausrichtungsart ('left', 'right', 'center').
1730 */
1731HRIBox.prototype.alignIBoxHorizontalTo = function(alignment)
1732{
1733   
1734   if (alignment.toLowerCase() == 'left')
1735   {
1736      this.m_IBox.style.right = '';
1737      this.m_IBox.style.left =  '0px';
1738   }
1739   else if (alignment.toLowerCase() == 'center')
1740   {
1741      // Bei absoluter Positionierung wird auch die Scrollposition ergänzt.
1742      // Für fixierte Positionierung entfällt dieses natürlich.
1743      let scrollXPosition = 0;
1744      if (getStyleValue(this.m_IBox, 'position') === 'absolute')
1745         scrollXPosition = window.scrollX || window.pageXOffset;
1746      
1747      this.m_IBox.style.right = '';
1748      if (this.m_ParentDiv)
1749         this.m_IBox.style.left  = Math.max(((getElementWidthValue(this.m_ParentDiv) - this.m_IBox.offsetWidth) / 2), 0) + scrollXPosition + 'px';
1750      else
1751         this.m_IBox.style.left  = Math.max(((HRGetViewPortWidth() - this.m_IBox.offsetWidth) / 2), 0) + scrollXPosition + 'px';
1752   }
1753   else if (alignment.toLowerCase() == 'right')
1754   {
1755      this.m_IBox.style.left = '';
1756      this.m_IBox.style.right  = '0px';
1757   }
1758}
1759
1760/**
1761 * Initialisiert das Theme der i-Box, nachdem dessen Grundgerüst bereits erzeugt wurde.
1762 */
1763HRIBox.prototype.initTheme = function()
1764{
1765   if (this.m_IBox)
1766   {
1767      switch(this.m_Theme)
1768      {
1769         case HRIBoxThemes.Default:
1770            // Default: Hier gibt es nichts zu tun.
1771            break;
1772         case HRIBoxThemes.Popup:
1773            this.initPopupTheme();
1774            break;
1775      }
1776   }
1777}
1778
1779/**
1780 * Initialisiert das Popup-Theme.
1781 */
1782HRIBox.prototype.initPopupTheme = function()
1783{
1784   if (this.m_PopupConfig)
1785   {
1786      var direction = HRPopupDirection.Up;
1787      if (this.m_PopupConfig.direction)
1788         direction = this.m_PopupConfig.direction;
1789      
1790      // Transition initialisieren.
1791      this.initPopupTransition(direction);
1792      
1793      var showAfter = this.m_PopupConfig.showAfter;
1794      if (!showAfter)
1795      {
1796         showAfter = 250;
1797      }
1798      
1799      //
1800      // Verzögertes Einblenden.
1801      //
1802      window.setTimeout(function(ibox, popupConfig) {
1803         if (direction === HRPopupDirection.Left)
1804         {
1805            ibox.style.transform = 'translateX(0)';
1806         }
1807         else if (direction === HRPopupDirection.Up)
1808         {
1809            ibox.style.transform = 'translateY(0)';
1810         }
1811         else if (direction === HRPopupDirection.Right)
1812         {
1813            ibox.style.transform = 'translateX(0)';
1814         }
1815         else if (direction === HRPopupDirection.Down)
1816         {
1817            ibox.style.transform = 'translateY(0)';
1818         }
1819         ibox.style.pointerEvents = 'all';
1820         ibox.style.opacity = 1;
1821      }, showAfter, this.m_IBox, this.m_PopupConfig);
1822      
1823      //
1824      // Verzögertes Ausblenden.
1825      //
1826      if (this.m_PopupConfig.hideAfter)
1827      {
1828         window.setTimeout(function(iboxObj) {
1829            iboxObj.initPopupTransition(direction);
1830            window.setTimeout(function(iboxObj) {
1831               iboxObj.close();
1832            }, 1000, iboxObj);
1833         }, this.m_PopupConfig.hideAfter, this);
1834      }
1835   }
1836}
1837
1838/**
1839 * Initialisiert die Popup-Transition abhängig von der gewählten Slide-Richtung.
1840 * 
1841 * Default: Up (von unten nach oben fahren).
1842 * 
1843 * @param direction Die gewählte Slide-Richtung (@see HRPopupDirection)
1844 */
1845HRIBox.prototype.initPopupTransition = function(direction)
1846{
1847   if (direction === HRPopupDirection.Left)
1848   {
1849      this.m_IBox.style.transform = 'translateX(calc(100% + ' + this.m_IBox.getBoundingClientRect().left + 'px))';
1850   }
1851   else if (direction === HRPopupDirection.Up)
1852   {
1853      this.m_IBox.style.transform = 'translateY(calc(100% + ' + this.m_IBox.getBoundingClientRect().top + 'px))';
1854   }
1855   else if (direction === HRPopupDirection.Right)
1856   {
1857      this.m_IBox.style.transform = 'translateX(calc(-100% - ' + this.m_IBox.getBoundingClientRect().right + 'px))';
1858   }
1859   else if (direction === HRPopupDirection.Down)
1860   {
1861      this.m_IBox.style.transform = 'translateY(calc(-100% - ' + this.m_IBox.getBoundingClientRect().bottom + 'px))';
1862   }
1863   this.m_IBox.style.pointerEvents = 'none';
1864   this.m_IBox.style.opacity = 0;
1865}
1866
1867/**
1868 * Erzeugt die HTML-Struktur des Hinweistextes.
1869 */
1870HRIBox.prototype.createPrompt = function()
1871{
1872   const _this = this;
1873   
1874   const contentDiv = document.getElementById(this.getUID('idIBoxContent'));
1875   if (contentDiv)
1876   {
1877      if (this.m_PromptText)
1878      {
1879         const text = document.createElement('p');
1880         text.innerHTML = this.m_PromptText;
1881         text.className = 'prompt-text';
1882         contentDiv.appendChild(text);
1883      }
1884      
1885      const inputDiv = document.createElement('div');
1886      inputDiv.className = 'prompt-input';
1887      
1888      // Feldrahmen erzeugen.
1889      const promptFieldframe = document.createElement('div');
1890      promptFieldframe.className = 'Feldrahmen';
1891      promptFieldframe.dataset.fieldFrame = '';
1892      inputDiv.appendChild(promptFieldframe);
1893      
1894      const promptInput = document.createElement('input');
1895      promptInput.type = 'text';
1896      promptInput.name = 'dummy';
1897      promptInput.id = this.getUID('prompt_input');
1898      promptInput.dataset.promptInput = '';
1899      if (this.m_PromptDefaultText)
1900         promptInput.value = this.m_PromptDefaultText;
1901      promptFieldframe.appendChild(promptInput);
1902      
1903      // Callback-Funktion, um den bestehenden Inhalt ggf. noch abändern zu können.
1904      if (this.m_PromptConfigureInputHTML && typeof(this.m_PromptConfigureInputHTML) === 'function')
1905         this.m_PromptConfigureInputHTML(inputDiv);
1906      
1907      const buttonDiv = document.createElement('div');
1908      buttonDiv.className = 'prompt-buttons';
1909      const buttonOk = document.createElement('input');
1910      buttonOk.type = 'button';
1911      buttonOk.className = 'button-ok';
1912      buttonOk.value = this.m_PromptOkayText;
1913      buttonOk.onclick = function ok() {
1914         const returnValue = promptInput.value;
1915         if (_this.m_PromptSaveResultInCookie)
1916            HRSetCookie('PromptReturn', returnValue, 'expires: -1');
1917         
1918         if (_this.m_PromptSaveResultInLocalStorage)
1919            localStorage.setItem('PromptReturn', returnValue);
1920         
1921         if (_this.m_PromptCallBack)
1922            if (_this.m_PromptCallBack(returnValue))
1923               _this.close();
1924      };
1925      buttonDiv.appendChild(buttonOk);
1926      
1927      if (!this.m_Note)
1928      {
1929         const buttonCancel = document.createElement('input');
1930         buttonOk.className = 'button-cancel';
1931         buttonCancel.type = 'button';
1932         buttonCancel.onclick = function cancel() {
1933            _this.close()
1934         };
1935         buttonCancel.value = this.m_PromptCancelText;
1936         buttonDiv.appendChild(buttonCancel);
1937      }
1938      
1939      contentDiv.appendChild(inputDiv);
1940      contentDiv.appendChild(buttonDiv);
1941      
1942      // Die i-Box als modalen Dialog setzen.
1943      if (this.m_Modal)
1944         HRDOM.trapFocus(contentDiv);
1945      
1946      if (typeof HREventManager === 'object')
1947         HREventManager.dispatchEvent(this.m_IBox, 'HRIBoxContentLoaded', {useStandard: false, notifyParents: false, detail: this.m_IBox});
1948   }
1949}
1950
1951/**
1952 * Entfernt das übergebene HTML-Element und löscht es aus dem DOM.
1953 * 
1954 * @param element Das zu löschende Element.
1955 */
1956HRIBox.prototype.removeDOMElement = function(element)
1957{
1958   if (element && element.parentNode)
1959   {
1960      element.parentNode.removeChild(element);
1961   }
1962}
1963
1964/**
1965 * Erstellt ein HTML-Element vom übergebenen Typ mit absoluter Positionierung.
1966 * 
1967 * @param tagName Der Name des Tags.
1968 * @param width Die Breite des Elements.
1969 * @param height Die Höhe des Elements.
1970 * @param xpos Die x-Position des Elements.
1971 * @param ypos Die y-Position des Elements.
1972 * @param id die ID des Elements.
1973 */
1974HRIBox.prototype.createFloatingElement = function(tagName, width, height, xpos, ypos, id)
1975{
1976   var element = document.createElement(tagName);
1977   
1978   element.id = id;
1979   element.style.position = 'absolute';
1980   element.style.left = xpos + 'px';
1981   element.style.top = ypos + 'px';
1982   if (width)
1983   {
1984      element.style.width  = width + 'px';
1985   }
1986   if (height)
1987   {
1988      element.style.height = height + 'px';
1989   }
1990   
1991   return element;
1992}
1993
1994/**
1995 * Liefert die Breite des Body-Tags. Wenn die Fensterbreite größer als 
1996 * der gerenderte Body ist, wird die Fensterbreite zurückgegeben.
1997 * 
1998 * @returns Die Breite des Body-Tags in Pixeln.
1999 */
2000HRIBox.prototype.getBodyWidth = function()
2001{
2002   if (document.documentElement.scrollWidth) 
2003   {
2004      return document.documentElement.scrollWidth;
2005   }
2006   else if (document.documentElement.offsetWidth)
2007   {
2008      return document.documentElement.offsetWidth;
2009   }
2010   
2011   var widthValue = getElementWidthValue(document.getElementsByTagName("body")[0]);
2012   return Math.ceil(widthValue);
2013}
2014
2015/**
2016 * Liefert die Höhe des Body-Tags. Wenn die Fensterhöhe größer als 
2017 * der gerenderte Body ist, wird die Fenstergröße zurückgegeben.
2018 * 
2019 * @returns Die Höhe des Body-Tags in Pixeln.
2020 */
2021HRIBox.prototype.getBodyHeight = function()
2022{
2023   if (document.documentElement.scrollHeight) 
2024   {
2025      return document.documentElement.scrollHeight;
2026   }
2027   else if (document.documentElement.offsetHeight)
2028   {
2029      return document.documentElement.offsetHeight;
2030   }
2031   
2032   var heightValue = getElementHeightValue(document.getElementsByTagName("body")[0]);
2033   return Math.ceil(heightValue);
2034}
2035
2036/**
2037 * Erzeugt zu der gewünschten ID eine pro i-Boxinstanz eindeutige ID.
2038 * 
2039 * @param id Die ID.
2040 * @return eine pro Instanz durch einen UUID-Suffix eindeutige ID. 
2041 */
2042HRIBox.prototype.getUID = function(id) 
2043{
2044   return id + '_' + this.m_UUID;
2045}
2046
2047/**
2048 * @param number Die zu prüfende Zahl
2049 * @return true, falls die übergeben Zahl eine positive ganze Zahl oder 0 ist.
2050 */
2051HRIBox.prototype.isUnsignedInteger = function(number) 
2052{
2053  return (number.toString().search(/^[0-9]+$/) == 0);
2054}
2055
2056/**
2057 * Öffnet den im übergebenen HTML-Element enthaltenen Content in einer i-Box.
2058 * 
2059 * @param element Das HTML-Element mit dem darzustellenden Content oder ein eindeutiger Selektor zu einem Element.
2060 * @param configuration Die (optionalen) Konfigurationen.
2061 * @return Die Instanz der neu geöffneten i-Box.
2062 */
2063function HROpenHtmlElementInIbox(element, configuration)
2064{
2065   if (element)
2066   {
2067      if (typeof(element) === 'string')
2068         element = document.querySelector(element);
2069      
2070      if (element)
2071      {
2072         let config = configuration || {};
2073         config.htmlElement = element;
2074         const ibox = new HRIBox(config);
2075         ibox.open();
2076         return ibox;
2077      }
2078   }
2079   
2080   return null;
2081}
2082
2083/**
2084 * Öffnet die Url in einer i-Box.
2085 * 
2086 * @param url Die in der i-Box darzustellende Url.
2087 * @param configuration Die (optionalen) Konfigurationen.
2088 * @return Die Instanz der neu geöffneten i-Box.
2089 */
2090function HROpenUrlInIbox(url, configuration)
2091{
2092   const ibox = new HRIBox(configuration || {});
2093   ibox.openUrl(url);
2094   return ibox;
2095}
2096
2097/**
2098 * Öffnet ein Bild in der i-Box.
2099 * 
2100 * @param source Bildquelle: Absolute oder relative URL
2101 * @param caption Unterschrift des Bildes (hier ist auch HTML erlaubt, die Unterschrift wird per innerHTML gesetzt)
2102 * @param title Titel des Bildes
2103 * @param link Verlinkung des Bildes
2104 * @param linkTarget Das Target (_blank, _self, _parent, _top)
2105 * @param configuration Die (optionalen) Konfigurationen.
2106 * @return Die Instanz der neu geöffneten i-Box.
2107 */
2108function HROpenImageInIbox(source, caption, title, link, linkTarget, configuration)
2109{
2110   const ibox = new HRIBox(configuration || {});
2111   ibox.openImage(source, caption, title, link, linkTarget);
2112   return ibox;
2113}
2114
2115/**
2116 * Öffnet die übergebenen Bilder in der i-Box.
2117 * 
2118 * @param sources [Array] Bildquellen: Absolute oder relative URLs
2119 * @param captions [Array] Unterschriften der Bilder (hier ist auch HTML erlaubt, die Unterschrift wird jeweils per innerHTML gesetzt)
2120 * @param imagesConfig Weitere Konfigurationen für die Bilder.
2121 * <ul>
2122 *    <li>titles: Array bestehend aus Titeln der Bilder.</li>
2123 *    <li>links: Array bestehend aus Verlinkungen der Bilder.</li>
2124 *    <li>linkTargets: Array bestehend aus den Targets der Links (_blank, _self, _parent, _top).</li>
2125 *    <li>maxWidth: maximale von den Bildern eingenommene Breite.</li>
2126 *    <li>maxHeight: maximale von den Bildern eingenommene Höhe.</li>
2127 *    <li>first: Index oder Quelle desjenigen Bildes, welches initial angezeigt werden soll.</li>
2128 *    <li>addTabNavigation: Ergänzt eine Tab-Navigation unter den Bildern.</li>
2129 *    <li>
2129setAsBackground: Setzt das Bild als Hintergrundbild eines umgebenden Divs ein.</li>
2130 *    <li>alttexts [Arrray]: Array bestehend aus den Alternativtexten der Bilder.</li>
2131 * </ul>
2132 * @param config Die (optionalen) Konfigurationen.
2133 * @return Die Instanz der neu geöffneten i-Box.
2134 */
2135function HROpenImagesInIbox(sources, captions, imagesConfig, config)
2136{
2137   const ibox = new HRIBox(config || {});
2138   ibox.openImages(sources, captions, imagesConfig.titles, imagesConfig.links, imagesConfig.linkTargets, imagesConfig.maxWidth, imagesConfig.maxHeight, imagesConfig.first, imagesConfig.addTabNavigation, imagesConfig.setAsBackground, imagesConfig.alttexts);
2139   return ibox;
2140}
2141
2142/**
2143 * Stellt den übergebenen Text als Hinweis mit einem OK-Button dar.
2144 * 
2145 * @param text Der darzustellende Hinweistext.
2146 * @param state [optional] Der Status des Hinweises (none, info, warning, error). Default: none.
2147 * @param buttonText [optional] Ein abweichender Text für den OK-Button. Default: OK.
2148 * @param configuration Die (optionalen) Konfigurationen.
2149 * @return Die Instanz der neu geöffneten i-Box.
2150 */
2151function HROpenNoteInIBox(text, state, buttonText, configuration)
2152{
2153   let config = configuration || {};
2154   config.blackOverlay = true;
2155   config.removeByBlackOverlayClick = true;
2156   
2157   const ibox = new HRIBox(config);
2158   ibox.openNote(text, state, buttonText);
2159   return ibox;
2160}
2161
2162/**
2163 * Öffnet eine Prompt-Box analog zur Javascript-prompt()-Methode.
2164 * 
2165 * @param text Der Text des Prompts.
2166 * @param defaultText (optional)
2167 * @param callBack (optional) Callback-Funktion, die nach dem Akzeptieren des Prompts ausgelöst wird und den eingegebenen Wert des Nutzers übergibt. Die Funktion erwartet eine Boolean-Rückgabe.
2168 *        Wird false zurückgegeben, wird das Prompt nicht akzeptiert und bleibt stehen. Default: keine Verarbeitung der Eingabe. Das Popup wird akzeptiert.
2169 * @param okayText (optional) Der Text der in dem 'Okay'-Button steht. Default: 'Ok'.
2170 * @param cancelText (optional) Der Text der in dem 'Cancel'-Button steht. Default: 'Abbrechen'.
2171 * @param saveResultInCookie (optional) Wenn true, dann wird ein Cookie mit dem Inhalt des Eingabefelds gesetzt. Default: false.
2172 * @param saveResultInLocalStorage (optional) Wenn true, dann wird der Inhalt des Inputfelds im LocalStorage gespeichert. Default: false.
2173 * @param configureInputHTML Callback-Funktion, um den erzeugten Promptinhalt in Spezialfällen noch verändern zu können.
2174 * @param configuration Die (optionalen) Konfigurationen.
2175 * @return Die Instanz der neu geöffneten i-Box. 
2176 */
2177HROpenPrompt = (text, defaultText, callBack, okayText, cancelText, saveResultInCookie, saveResultInLocalStorage, configureInputHTML, configuration) => {
2178   let config = configuration || {};
2179   const ibox = new HRIBox(config);
2180   ibox.openPrompt(text, defaultText, callBack, okayText, cancelText, saveResultInCookie, saveResultInLocalStorage, configureInputHTML);
2181   return ibox;
2182}
2183
2184/**
2185 * Convenience-Funktion, um alle i-Boxen auf einen Schlag zu entfernen.
2186 */
2187function HRCloseAllIBoxes()
2188{
2189   if (window.HRIBoxRegistry)
2190   {
2191      for (let i = window.HRIBoxRegistry.length - 1; i >= 0; i--)
2192      {
2193         window.HRIBoxRegistry[i].close();
2194      }
2195      
2196      window.HRIBoxRegistry = undefined;
2197   }
2198   try
2199   {
2200      // Auch alte iBoxen schließen. Wenn alle Auftritte eine Versionsnr. > 12 haben, kann der Code entfallen.
2201      if (typeof(removeAllIBoxElements) === 'function')
2202         removeAllIBoxElements();
2203   }
2204   catch(e)
2205   {
2206   }
2207}

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.