PageSourceSearch

https://www.hwg-lu.de/_assets/87d6bb40e8845f2516e5a5940c2293da/JavaScript/navigation.js?1789461350

js hwg-lu.de collected 2026-10-02 16:47:24 UTC 22,644 bytes, 595 lines download raw bytes

1var brainCustomNavigation = {
2  el: {},
3  menuType: 'desktop',
4  currentNavKey: null,
5  navToggle: null,
6  init: function () {
7    let self = this;
8    const customMenu = document.getElementById('menu-main-custom');
9    if (customMenu) {
10      self.navToggle = document.getElementById('menu-toggle');
11      self.menuType = self.navToggle.offsetParent ? 'mobile' : 'desktop';
12      self.el['navbar'] = document.getElementById('page-top');
13      self.el['toggle-main'] = document.getElementById('menu-toggle');
14      self.el['main'] = document.querySelector(
15        self.el['toggle-main'].dataset.target,
16      );
17      self.initNavToggle();
18      self.initNavCommon('main');
19      self.initKeyboardNavToggle();
20      self.initDocumentNavKeyboard();
21      // customMenu.addEventListener('ba-layout-switch', function (e) {
22      //     if (e.detail.wasMobile && !e.detail.wasMobile) {
23      //
24      //     }
25      // }, false);
26    }
27  },
28  initNavToggle: function () {
29    let self = this;
30    self.navToggle.addEventListener('click', function (evt) {
31      evt.preventDefault();
32      let target = document.querySelector(self.navToggle.dataset.target);
33      if (target.classList.contains('in')) {
34        self.navToggle.classList.add('collapsed');
35        target.classList.remove('in');
36        target.classList.add('collapsing');
37        let mvbItem = document.querySelector('.menu-visible-background');
38        mvbItem.classList.add('fade-out');
39        self.slide.up(target, 350, function () {
40          // setTimeout(function(){
41          target.classList.remove('collapsing');
42          target.classList.add('collapse');
43          mvbItem.remove();
44          document.body.classList.remove('navbar-open');
45          self.navToggle.setAttribute('aria-expanded', 'false');
46          target.removeAttribute('style');
47          // }, 350);
48        });
49      } else {
50        if (document.body.classList.contains('cnav-open')) {
51          document.body.classList.remove('cnav-open');
52          const navElt = document.querySelector('.nav-content-sticky');
53          navElt.classList.remove('show');
54        }
55        document.body.classList.add('navbar-open');
56        self.navToggle.classList.remove('collapsed');
57        target.classList.remove('collapse');
58        target.classList.add('collapsing');
59        self.slide.down(target, 350, function () {
60          // setTimeout(function(){
61          target.classList.remove('collapsing');
62          target.classList.add('collapse', 'in');
63          self.navToggle.setAttribute('aria-expanded', 'true');
64          target.removeAttribute('style');
65          // }, 350);
66        });
67
68        let minHeight = jQuery('.wrap.all').outerHeight();
69        let mvbItem = document.createElement('div');
70        mvbItem.setAttribute(
71          'class',
72          'menu-visible-background fade-out visible-xs-block visible-sm-block',
73        );
74        mvbItem.style.minHeight = minHeight + 'px';
75        document.body.prepend(mvbItem);
76
77        mvbItem.classList.add('fade-in');
78        setTimeout(function () {
79          mvbItem.classList.remove('fade-out');
80          mvbItem.classList.remove('fade-in');
81        }, 400);
82        if (mvbItem) {
83          mvbItem.addEventListener('click', function (evt) {
84            self.navToggle.click();
85          });
86        }
87      }
88    });
89  },
90  isMobileLayout() {
91    return this.el['toggle-main'].dataset.layout !== 'desktop';
92  },
93  initNavCommon(navKey) {
94    let self = this;
95    const nav = self.el[navKey];
96    if (nav) {
97      nav.addEventListener(
98        'click',
99        function (evt) {
100          let link;
101          if (evt.target.matches('.js-toggle-sub-nav')) {
102            link = evt.target;
103          } else {
104            link = evt.target.closest('.js-toggle-sub-nav');
105          }
106          if (link) {
107            evt.preventDefault();
108            evt.stopPropagation();
109            if (self.isMobileLayout() || link.classList.contains('l1')) {
110              const forceState = link.classList.contains('sub-nav-close')
111                ? -1
112                : 0;
113              const linkTarget = link.getAttribute('aria-controls');
114              const parentLi =
115                document.getElementById(linkTarget).parentElement;
116              self.toggleSubMenu(nav, parentLi, forceState);
117            }
118          }
119        },
120        false,
121      );
122      if (self.navToggle.offsetParent) {
123        let activeLink = nav.querySelector('.nav-link.current');
124        if (activeLink) {
125          let ddList = [];
126          let activeParent = activeLink.parentElement;
127          while (activeParent) {
128            if (activeParent.classList.contains('dropdown')) {
129              ddList.push(activeParent);
130            }
131            if (activeParent.getAttribute('id') === 'navbar-main') {
132              break;
133            }
134            activeParent = activeParent.parentElement;
135          }
136          for (let i = ddList.length - 1; i >= 0; i--) {
137            self.toggleSubMenu(nav, ddList[i], 1);
138          }
139        }
140      }
141      /*
142            nav.addEventListener('show.bs.collapse', event => {
143                if (navToggle.offsetParent) {
144                    const widthScrollOn = document.body.offsetWidth;
145                    document.body.classList.add('nav-open');
146                    const widthScrollOff = document.body.offsetWidth;
147                    const scrollDiff = widthScrollOff - widthScrollOn;
148                    if (scrollDiff > 0) {
149                        self.el['navbar'].style.paddingRight = scrollDiff + 'px';
150                        document.getElementById('layout-inner').style.paddingRight = scrollDiff + 'px';
151                    }
152                    self.currentNavKey = navKey;
153                    navbar.classList.remove(altPrefix + '-open');
154                    navbar.classList.remove(altPrefix + '-shown');
155                    navbar.classList.add(prefix + '-open');
156                    self.onShowNavigation(navToggle, nav);
157                }
158            });
159            nav.addEventListener('shown.bs.collapse', event => {
160                navbar.classList.add(prefix + '-shown');
161            });
162            nav.addEventListener('hide.bs.collapse', event => {
163                let openSubItems = nav.querySelectorAll('.nav-item.has-sub.open');
164                for (let i = 0, n = openSubItems.length; i < n; i++) {
165                    self.toggleSubMenu(nav, openSubItems[i], -1);
166                }
167            });
168            nav.addEventListener('hidden.bs.collapse', event => {
169                if (navToggle.offsetParent) {
170                    navbar.classList.remove(prefix + '-open');
171                    navbar.classList.remove(prefix + '-shown');
172                    self.resetAriaForAllCollapseNavs();
173                    if (self.currentNavKey === navKey) {
174                        self.currentNavKey = null;
175                        document.body.classList.remove('nav-open');
176                        if (self.el['navbar'].hasAttribute('style')) {
177                            self.el['navbar'].removeAttribute('style');
178                            document.getElementById('layout-inner').removeAttribute('style');
179                        }
180                    }
181                }
182            });*/
183      self.initNavKeyboard(navKey);
184    }
185  },
186  initKeyboardNavToggle: function () {
187    let self = this;
188    const navbar = self.el['navbar'];
189    let navToggleLinks = navbar.querySelectorAll('.navbar-toggle');
190    for (let i = 0, n = navToggleLinks.length; i < n; i++) {
191      navToggleLinks[i].onkeydown = function (evt) {
192        let targetNav = document.querySelector(
193          navToggleLinks[i].dataset.target,
194        );
195        if (targetNav && targetNav.offsetParent) {
196          if (evt.key === 'ArrowUp' || evt.key === 'ArrowLeft') {
197            evt.preventDefault();
198            let tmpInstance = Collapse.getInstance(targetNav);
199            if (tmpInstance) {
200              tmpInstance.hide();
201            }
202          } else if (
203            evt.key === 'Tab' ||
204            evt.key === 'ArrowDown' ||
205            evt.key === 'ArrowRight'
206          ) {
207            evt.preventDefault();
208            targetNav.querySelector('.nav-link.l1').focus();
209          }
210        }
211      };
212    }
213  },
214  initDocumentNavKeyboard: function () {
215    let self = this;
216    document.onkeydown = function (evt) {
217      evt = evt || window.event;
218      let isEscape = evt.key === 'Escape' || evt.key === 'Esc';
219      if (isEscape) {
220        //const navbar = self.el['navbar'];
221        const targetNavMain = self.el['main'];
222        if (
223          targetNavMain.offsetParent ||
224          targetNavMain.classList.contains('show')
225        ) {
226          let tmpInstance = Collapse.getInstance(targetNavMain);
227          if (tmpInstance) {
228            tmpInstance.hide();
229          }
230        }
231      }
232    };
233  },
234  initNavKeyboard: function (navKey) {
235    let self = this;
236    const nav = self.el[navKey];
237    let navLinks = nav.querySelectorAll('.nav-link,.js-toggle-sub-nav');
238    for (let i = 0, n = navLinks.length; i < n; i++) {
239      navLinks[i].onkeydown = function (evt) {
240        evt = evt || window.event;
241        self.onSubNavToggleKeyDown(navKey, evt);
242      };
243    }
244  },
245  onSubNavToggleKeyDown: function (navKey, evt) {
246    let self = this;
247    const nav = self.el[navKey];
248    let parentLi = evt.target.parentElement;
249    let hasSubMenu = parentLi.classList.contains('dropdown');
250    let firstLevel = parentLi.classList.contains('l1');
251    if (evt.key === 'ArrowLeft') {
252      evt.preventDefault();
253      if (hasSubMenu && parentLi.classList.contains('show')) {
254        self.toggleSubMenu(nav, parentLi, -1);
255        //parentLi.querySelectorAll('.nav-link')[0].focus();
256      }
257      // if (hasSubMenu && parentLi.classList.contains('show')) {
258      //     self.toggleSubMenu(nav, parentLi, -1);
259      //     parentLi.querySelectorAll('.nav-link')[0].focus();
260      // } else if (!firstLevel) {
261      //     let tmpParent = parentLi.parentElement;
262      //     while (tmpParent) {
263      //         if (tmpParent.nodeName.toLowerCase() === 'li') {
264      //             self.toggleSubMenu(nav, tmpParent, -1);
265      //             tmpParent.querySelectorAll('.nav-link')[0].focus();
266      //             break;
267      //         }
268      //         tmpParent = tmpParent.parentElement;
269      //     }
270      // }
271    } else if (evt.key === 'Tab') {
272      if (firstLevel) {
273        if (hasSubMenu && parentLi.classList.contains('show')) {
274          parentLi.querySelectorAll('.nav-link')[0].focus();
275        } else if (hasSubMenu) {
276          self.toggleSubMenu(nav, parentLi, 1);
277        }
278      }
279    } else if (evt.key === 'ArrowRight') {
280      if (hasSubMenu) {
281        evt.preventDefault();
282        self.toggleSubMenu(nav, parentLi, 1);
283        self.focusFirstVisibleNavLink(parentLi);
284      }
285    } else if (evt.key === 'ArrowDown') {
286      evt.preventDefault();
287      if (firstLevel) {
288        if (hasSubMenu && parentLi.classList.contains('show')) {
289          parentLi.querySelectorAll('.nav-link')[0].focus();
290        } else if (hasSubMenu) {
291          self.toggleSubMenu(nav, parentLi, 1);
292        }
293      } else {
294        if (
295          parentLi.nextElementSibling &&
296          parentLi.nextElementSibling.offsetParent
297        ) {
298          parentLi.nextElementSibling.querySelector('.nav-link').focus();
299        }
300      }
301    } else if (evt.key === 'ArrowUp') {
302      evt.preventDefault();
303      if (firstLevel) {
304        if (hasSubMenu) {
305          self.toggleSubMenu(nav, parentLi, -1);
306        }
307      } else {
308        if (
309          parentLi.previousElementSibling &&
310          parentLi.previousElementSibling.offsetParent
311        ) {
312          parentLi.previousElementSibling.querySelector('.nav-link').focus();
313        }
314      }
315      // if (parentLi.previousElementSibling && parentLi.previousElementSibling.offsetParent) {
316      //     parentLi.previousElementSibling.querySelector('.nav-link').focus();
317      // } else if (firstLevel) {
318      //     // let tmpInstance = Collapse.getInstance(nav);
319      //     // if (tmpInstance) {
320      //     //     tmpInstance.hide();
321      //     //     const navToggle = self.el['toggle-' + navKey];
322      //     //     if (navToggle && navToggle.offsetParent) {
323      //     //         navToggle.focus();
324      //     //     }
325      //     // }
326      // } else {
327      //     let tmpParent = parentLi.parentElement;
328      //     while (tmpParent) {
329      //         if (tmpParent.nodeName.toLowerCase() === 'li') {
330      //             self.toggleSubMenu(nav, tmpParent, -1);
331      //             tmpParent.querySelectorAll(':scope > .nav-link')[0].focus();
332      //             break;
333      //         }
334      //         tmpParent = tmpParent.parentElement;
335      //     }
336      // }
337    }
338  },
339  focusFirstVisibleNavLink: function (parentLi) {
340    const childLinks = parentLi.querySelectorAll(
341      ':scope > .menu-sub-nav .nav-link',
342    );
343    for (let i = 0, n = childLinks.length; i < n; i++) {
344      if (childLinks[i].offsetParent) {
345        childLinks[i].focus();
346        break;
347      }
348    }
349  },
350  toggleSubMenu: function (nav, parentLi, forceState) {
351    if (
352      parentLi &&
353      (this.isMobileLayout() || parentLi.classList.contains('l1'))
354    ) {
355      let toggleList = parentLi.querySelector(':scope > .js-sub-nav');
356      if (
357        forceState < 1 &&
358        (toggleList.classList.contains('show') || forceState === -1)
359      ) {
360        this.hideSubMenu(nav, parentLi, 350);
361      } else if (forceState > -1) {
362        this.showSubMenu(nav, parentLi);
363      }
364    }
365  },
366  showSubMenu: function (nav, parentLi) {
367    let self = this;
368    let toggleList = parentLi.querySelector(':scope > .js-sub-nav');
369    let navRootElement = parentLi.parentElement;
370    if (parentLi.classList.contains('l1')) {
371      navRootElement = parentLi.parentElement.parentElement;
372    }
373    navRootElement
374      .querySelectorAll('.dropdown.show')
375      .forEach(function (openNav) {
376        self.hideSubMenu(nav, openNav, 0);
377      });
378    self.compensateBodyScrollOffset(true);
379    const parentList = toggleList.parentElement.parentElement;
380    toggleList.classList.add('show');
381    toggleList.parentElement.classList.add('open');
382    toggleList.parentElement.parentElement.classList.remove('has-open-sub');
383    parentList.scrollTop = 0;
384    parentList.classList.add('sub-nav-open');
385    let toggleContainer = parentLi.querySelector('.js-transition');
386    if (!toggleContainer) {
387      toggleContainer = toggleList;
388    }
389    toggleList.classList.toggle('open');
390    parentLi.classList.add('show');
391    parentLi.classList.add('collapsing');
392    self.slide.down(toggleContainer, 350, function () {
393      let expanded = navRootElement.querySelector('[aria-expanded="true"]');
394      if (expanded) {
395        expanded.setAttribute('aria-expanded', 'false');
396      }
397      parentLi.classList.remove('collapsing');
398      document.body.classList.remove('nav-opening');
399      //parentLi.classList.add('collapse');
400      let navGridRow = parentLi.querySelector('.nav-grid-row');
401      if (navGridRow && toggleList.offsetHeight < navGridRow.offsetHeight) {
402        toggleList.classList.add('is-scroll');
403      }
404      parentLi
405        .querySelector(':scope > .js-toggle-sub-nav')
406        .setAttribute('aria-expanded', 'true');
407      const navLink = parentLi.querySelector(':scope > .nav-link');
408      if (navLink) {
409        parentLi
410          .querySelector(':scope > .nav-link')
411          .setAttribute('aria-expanded', 'true');
412      }
413      toggleContainer.removeAttribute('style');
414    });
415  },
416  hideSubMenu: function (nav, parentLi, duration) {
417    let self = this;
418    let toggleList = parentLi.querySelector(':scope > .js-sub-nav');
419    const parentList = toggleList.parentElement.parentElement;
420    toggleList.classList.remove('show');
421    toggleList.parentElement.classList.remove('open');
422    toggleList.parentElement.parentElement.classList.remove('has-open-sub');
423    parentList.classList.remove('sub-nav-open');
424    parentLi.classList.remove('show');
425    parentLi
426      .querySelector(':scope > .js-toggle-sub-nav')
427      .setAttribute('aria-expanded', 'false');
428    const navLink = parentLi.querySelector(':scope > .nav-link');
429    if (navLink) {
430      parentLi
431        .querySelector(':scope > .nav-link')
432        .setAttribute('aria-expanded', 'false');
433    }
434    let toggleContainer = parentLi.querySelector('.js-transition');
435    if (!toggleContainer) {
436      toggleContainer = toggleList;
437    }
438    parentLi.classList.add('collapsing');
439    if (duration > 0) {
440      document.body.classList.add('nav-is-closing');
441      self.slide.up(toggleContainer, duration, function () {
442        parentLi.classList.remove('collapsing');
443        parentLi.classList.add('collapse');
444        if (toggleList.classList.contains('is-scroll')) {
445          toggleList.classList.remove('is-scroll');
446        }
447        self.compensateBodyScrollOffset(false);
448        toggleContainer.removeAttribute('style');
449      });
450    } else {
451      parentLi.classList.remove('collapsing');
452      parentLi.classList.add('collapse');
453      if (toggleList.classList.contains('is-scroll')) {
454        toggleList.classList.remove('is-scroll');
455      }
456    }
457  },
458  compensateBodyScrollOffset: function (switchOn) {
459    let self = this;
460    let navbar = self.el['navbar'];
461    const scrollOffsetEltFixed = self.el['navbar'].querySelector(
462      '.navbar-container-bottom',
463    );
464    if (scrollOffsetEltFixed && scrollOffsetEltFixed.hasAttribute('style')) {
465      scrollOffsetEltFixed.removeAttribute('style');
466    }
467    const scrollOffsetEltDefault = navbar; //self.el['navbar'].querySelector(':scope > .grid-container');
468    if (
469      scrollOffsetEltDefault &&
470      scrollOffsetEltDefault.hasAttribute('style')
471    ) {
472      scrollOffsetEltDefault.removeAttribute('style');
473    }
474    if (document.body.classList.contains('d-nav-open')) {
475      document.body.classList.remove('d-nav-open');
476    }
477    if (document.body.classList.contains('nav-is-closing')) {
478      document.body.classList.remove('nav-is-closing');
479    }
480    if (switchOn) {
481      let navOpenEvent = new CustomEvent('ba-nav-on-open');
482      document.querySelectorAll('.js-nav-on-open').forEach(function (listener) {
483        listener.dispatchEvent(navOpenEvent);
484      });
485      if (document.body.classList.contains('search-modal-open')) {
486        let btnToggleSearch = document.getElementById('search-toggle');
487        if (btnToggleSearch) {
488          btnToggleSearch.click();
489        } else {
490          document.body.classList.remove('search-modal-open');
491        }
492      }
493      const widthScrollOn = document.body.offsetWidth;
494      document.body.classList.add('d-nav-open', 'nav-opening');
495      const widthScrollOff = document.body.offsetWidth;
496      const scrollDiff = widthScrollOff - widthScrollOn;
497      if (scrollDiff > 0) {
498        if (navbar.classList.contains('header-fixed')) {
499          scrollOffsetEltFixed.style.paddingRight = scrollDiff + 'px';
500        } else {
501          scrollOffsetEltDefault.style.paddingRight = scrollDiff + 'px';
502        }
503      }
504    }
505  },
506  // onShowNavigation: function (toggleElt, targetNav) {
507  //     this.resetAriaForAllCollapseNavs();
508  //     targetNav.removeAttribute('aria-hidden');
509  //     targetNav.setAttribute('aria-modal', 'true');
510  // },
511  // resetAriaForAllCollapseNavs: function () {
512  //     let collapseNavs = document.getElementById('page-top').querySelectorAll('.navbar-collapse');
513  //     for (let i = 0, n = collapseNavs.length; i < n; i++) {
514  //         collapseNavs[i].setAttribute('aria-hidden', 'true');
515  //         collapseNavs[i].removeAttribute('aria-modal');
516  //     }
517  // },
518  slide: {
519    up: function (target, duration = 500, callback = null) {
520      target.style.transitionProperty = 'height, margin, padding';
521      target.style.transitionDuration = duration + 'ms';
522      target.style.boxSizing = 'border-box';
523      target.style.height = target.offsetHeight + 'px';
524      target.offsetHeight;
525      target.style.overflow = 'hidden';
526      target.style.height = 0;
527      target.style.paddingTop = 0;
528      target.style.paddingBottom = 0;
529      target.style.marginTop = 0;
530      target.style.marginBottom = 0;
531      window.setTimeout(() => {
532        target.style.display = 'none';
533        target.style.removeProperty('height');
534        target.style.removeProperty('padding-top');
535        target.style.removeProperty('padding-bottom');
536        target.style.removeProperty('margin-top');
537        target.style.removeProperty('margin-bottom');
538        target.style.removeProperty('overflow');
539        target.style.removeProperty('transition-duration');
540        target.style.removeProperty('transition-property');
541        if (callback) {
542          if (callback && typeof callback === 'function') {
543            callback();
544          }
545        }
546        //alert("!");
547      }, duration);
548    },
549
550    down: function (target, duration = 500, callback = null) {
551      target.style.removeProperty('display');
552      let display = window.getComputedStyle(target).display;
553
554      if (display === 'none') display = 'block';
555
556      target.style.display = display;
557      let height = target.offsetHeight;
558      target.style.overflow = 'hidden';
559      target.style.height = 0;
560      target.style.paddingTop = 0;
561      target.style.paddingBottom = 0;
562      target.style.marginTop = 0;
563      target.style.marginBottom = 0;
564      target.offsetHeight;
565      target.style.boxSizing = 'border-box';
566      target.style.transitionProperty = 'height, margin, padding';
567      target.style.transitionDuration = duration + 'ms';
568      target.style.height = height + 'px';
569      target.style.removeProperty('padding-top');
570      target.style.removeProperty('padding-bottom');
571      target.style.removeProperty('margin-top');
572      target.style.removeProperty('margin-bottom');
573      window.setTimeout(() => {
574        target.style.removeProperty('height');
575        target.style.removeProperty('overflow');
576        target.style.removeProperty('transition-duration');
577        target.style.removeProperty('transition-property');
578        if (callback) {
579          if (callback && typeof callback === 'function') {
580            callback();
581          }
582        }
583      }, duration);
584    },
585    toggle: function (target, duration = 500, callback = null) {
586      if (window.getComputedStyle(target).display === 'none') {
587        return brainCustomNavigation.slide.down(target, duration, callback);
588      } else {
589        return brainCustomNavigation.slide.up(target, duration, callback);
590      }
591    },
592  },
593};
594
595brainCustomNavigation.init();

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.