PageSourceSearch

https://waldesch-online.de/media/mod_menu/js/menu.js?60fd8b

js waldesch-online.de collected 2026-09-25 07:26:29 UTC 11,206 bytes, 263 lines download raw bytes

1function _extends() {
2  return _extends = Object.assign ? Object.assign.bind() : function (n) {
3    for (var e = 1; e < arguments.length; e++) {
4      var t = arguments[e];
5      for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]);
6    }
7    return n;
8  }, _extends.apply(null, arguments);
9}
10
11/**
12 * @copyright  (C) 2019 Open Source Matters, Inc. <https://www.joomla.org>
13 * @license    GNU General Public License version 2 or later; see LICENSE.txt
14 */
15
16(() => {
17
18  /**
19   * Navigation menu
20   *
21   * Example usage:
22   *   // Default behavior (uses menuHoverClass = 'show-menu', dir = 'ltr')
23   *   new Nav(document.querySelector('.nav'));
24   *
25   *   // Override defaults (e.g. custom open-class and RTL support)
26   *   new Nav(document.querySelector('.nav'), {
27   *     menuHoverClass: 'my-open-class',
28   *     dir:            'rtl'
29   *   });
30   *
31   * @param {HTMLElement} nav                                   The root <ul class="nav"> element
32   * @param {Object}      [settings]                            Optional overrides for defaultSettings
33   * @param {string}      [settings.menuHoverClass='show-menu'] CSS class to toggle on open submenus
34   * @param {string}      [settings.dir='ltr']                  Text direction for keyboard nav ('ltr'|'rtl')
35   */
36  class Nav {
37    constructor(nav, settings = {}) {
38      var _settings$dir, _this$nav$id, _this$nav;
39      this.nav = nav;
40
41      // read the HTML dir attribute or computed style, or fall back to defaultSettings.dir
42      const browserDir = document.documentElement.getAttribute('dir') ||
43      // <html dir="…">
44      getComputedStyle(document.documentElement).direction ||
45      // CSS direction
46
47      Nav.defaultSettings.dir;
48      this.settings = _extends({}, Nav.defaultSettings, settings);
49
50      // merge defaults, browser‐detected dir, and any explicit overrides in `settings`
51      this.settings = _extends({}, Nav.defaultSettings, {
52        dir: (_settings$dir = settings.dir) != null ? _settings$dir : browserDir
53      }, settings);
54
55      // Unique prefix for this nav instance - needed for the id of submenus and aria-controls
56      this.idPrefix = (_this$nav$id = (_this$nav = this.nav) == null ? void 0 : _this$nav.id) != null ? _this$nav$id : `nav-${Math.floor(Math.random() * 100000)}`;
57      this.topLevelNodes = this.nav.querySelectorAll(':scope > li');
58      this.topLevelNodes.forEach(topLevelEl => {
59        // get submenu ul elements within topLevelEl
60        const levelChildUls = topLevelEl.querySelectorAll('ul');
61        const ariaControls = [];
62        levelChildUls.forEach(childUl => {
63          childUl.setAttribute('aria-hidden', 'true');
64          childUl.classList.remove(this.settings.menuHoverClass);
65          childUl.id = `${this.idPrefix}-submenu${Nav.idCounter}`;
66          Nav.idCounter += 1;
67          ariaControls.push(childUl.id);
68        });
69        if (levelChildUls.length > 0) {
70          const togglebtn = topLevelEl.querySelector('[aria-expanded]');
71          togglebtn == null || togglebtn.setAttribute('aria-controls', ariaControls.join(' '));
72        }
73      });
74      nav.addEventListener('keydown', this.onMenuKeyDown.bind(this));
75      nav.addEventListener('click', this.onClick.bind(this));
76      if (this.nav.classList.contains(this.settings.preventSubmenuOpenOnload)) {
77        this.toggleAllForCurrentActive();
78      }
79    }
80    onMenuKeyDown(event) {
81      const target = event.target.closest('li');
82      if (!target) {
83        return;
84      }
85      const subLists = target.querySelectorAll('ul');
86      switch (event.key) {
87        case 'ArrowUp':
88          event.preventDefault();
89          this.tabPrev();
90          break;
91        case 'ArrowLeft':
92          event.preventDefault();
93          if (this.settings.dir === 'rtl') {
94            this.tabNext();
95          } else {
96            this.tabPrev();
97          }
98          break;
99        case 'ArrowDown':
100          event.preventDefault();
101          this.tabNext();
102          break;
103        case 'ArrowRight':
104          event.preventDefault();
105          if (this.settings.dir === 'rtl') {
106            this.tabPrev();
107          } else {
108            this.tabNext();
109          }
110          break;
111        case 'Enter':
112          if (event.target.nodeName === 'SPAN' && event.target.parentNode.nodeName !== 'A' && subLists.length > 0) {
113            var _subLists$;
114            event.preventDefault();
115            this.toggleSubMenu(target, subLists, ((_subLists$ = subLists[0]) == null ? void 0 : _subLists$.getAttribute('aria-hidden')) === 'true');
116          }
117          break;
118        case ' ':
119        case 'Spacebar':
120          if (subLists.length > 0) {
121            var _subLists$2;
122            event.preventDefault();
123            this.toggleSubMenu(target, subLists, ((_subLists$2 = subLists[0]) == null ? void 0 : _subLists$2.getAttribute('aria-hidden')) === 'true');
124          }
125          break;
126        case 'Escape':
127          {
128            event.preventDefault();
129            const currentTopLevelLi = this.getTopLevelParentLi(event.target);
130            if (!currentTopLevelLi) {
131              break;
132            }
133            const allChildListsFromTopLevelLi = currentTopLevelLi.querySelectorAll('ul');
134            if (allChildListsFromTopLevelLi.length > 0) {
135              this.toggleSubMenu(currentTopLevelLi, allChildListsFromTopLevelLi, false);
136            }
137            // set focus on the top level li child with tabindex
138            currentTopLevelLi.querySelectorAll(':scope >
138 [tabindex]:not([tabindex="-1"]), a, button').forEach(tabElement => {
139              if (tabElement.hasAttribute(['aria-expanded'])) {
140                tabElement.focus();
141              }
142            });
143            break;
144          }
145        case 'End':
146          {
147            var _target$closest;
148            event.preventDefault();
149            const currentLiList = (_target$closest = target.closest('ul')) == null ? void 0 : _target$closest.querySelectorAll(':scope > li');
150            for (let index = currentLiList.length - 1; index >= 0; index -= 1) {
151              const lastTabbable = currentLiList[index].querySelector(':scope > [tabindex]:not([tabindex="-1"]), a, button');
152              if (lastTabbable) {
153                lastTabbable.focus();
154                return;
155              }
156            }
157            break;
158          }
159        case 'Home':
160          {
161            var _target$closest2;
162            event.preventDefault();
163            const firstLi = (_target$closest2 = target.closest('ul')) == null ? void 0 : _target$closest2.querySelector(':scope > li:first-child');
164            if (firstLi) {
165              var _firstLi$querySelecto;
166              // set focus on first li child with tabindex within current list
167              (_firstLi$querySelecto = firstLi.querySelector(':scope > [tabindex]:not([tabindex="-1"]), a, button')) == null || _firstLi$querySelecto.focus();
168            }
169            break;
170          }
171      }
172    }
173    onClick(event) {
174      var _event$target, _event$target2, _event$target3, _event$target4;
175      if (!((_event$target = event.target) != null && _event$target.hasAttribute('aria-expanded')) && !((_event$target2 = event.target) != null && _event$target2.closest('[aria-expanded'))) {
176        return;
177      }
178      if (((_event$target3 = event.target) == null ? void 0 : _event$target3.nodeName) === 'A') {
179        return;
180      }
181      if (((_event$target4 = event.target) == null ? void 0 : _event$target4.nodeName) === 'SPAN' && event.target.parentNode.nodeName === 'A') {
182        return;
183      }
184      const target = event.target.closest('li');
185      const subLists = target == null ? void 0 : target.querySelectorAll('ul');
186      if (subLists && subLists.length > 0) {
187        var _subLists$3;
188        event.preventDefault();
189        this.toggleSubMenu(target, subLists, ((_subLists$3 = subLists[0]) == null ? void 0 : _subLists$3.getAttribute('aria-hidden')) === 'true');
190      }
191    }
192    toggleSubMenu(target, subLists, open = false) {
193      var _target$querySelector;
194      if (open) {
195        // close all opened submenus before opening the new one
196        const allSubMenus = this.nav.querySelectorAll('ul[aria-hidden="false"]');
197        allSubMenus.forEach(ulChild => {
198          var _this$getTopLevelPare;
199          ulChild.setAttribute('aria-hidden', 'true');
200          ulChild.classList.remove(this.settings.menuHoverClass);
201          (_this$getTopLevelPare = this.getTopLevelParentLi(ulChild)) == null || (_this$getTopLevelPare = _this$getTopLevelPare.querySelector(':scope > [aria-expanded]')) == null || _this$getTopLevelPare.setAttribute('aria-expanded', 'false');
202        });
203      }
204      subLists.forEach(ulChild => {
205        ulChild.setAttribute('aria-hidden', open ? 'false' : 'true');
206        ulChild.classList.toggle(this.settings.menuHoverClass, open);
207      });
208      (_target$querySelector = target.querySelector(':scope > [aria-expanded]')) == null || _target$querySelector.setAttribute('aria-expanded', open ? 'true' : 'false');
209    }
210    focusTabbable(direction = 1) {
211      const tabbables = Array.from(this.nav.querySelectorAll('[tabindex]:not([tabindex="-1"]), a, button')).filter(el => !el.disabled && el.tabIndex >= 0 && el.offsetParent !== null);
212      const currentIndex = tabbables.indexOf(document.activeElement);
213      if (tabbables.length === 0) return;
214      const nextIndex = (currentIndex + direction + tabbables.length) % tabbables.length;
215      tabbables[nextIndex].focus();
216    }
217    tabNext() {
218      this.focusTabbable(1);
219    }
220    tabPrev() {
221      this.focusTabbable(-1);
222    }
223    getTopLevelParentLi(element) {
224      let currentLi = element.closest('li');
225      // this.topLevelNodes is a NodeList of top-level li elements in this nav
226      while (currentLi && !Array.from(this.topLevelNodes).includes(currentLi)) {
227        const parentUl = currentLi.parentElement.closest('ul');
228        currentLi = parentUl ? parentUl.closest('li') : null;
229      }
230      return currentLi; // top-level li or null if not found, or the
231    }
232    toggleAllForCurrentActive() {
233      const active = this.nav.querySelector('.current.active');
234      if (active) {
235        this.getTopLevelParentLi(active);
236        let currentLi = active;
237        while (currentLi && !Array.from(this.topLevelNodes).includes(currentLi)) {
238          const parentUl = currentLi.parentElement.closest('ul');
239          currentLi = parentUl ? parentUl.closest('li') : null;
240          if (currentLi) {
241            var _subLists$4;
242            const subLists = currentLi.querySelectorAll('ul');
243            this.toggleSubMenu(currentLi, subLists, ((_subLists$4 = subLists[0]) == null ? void 0 : _subLists$4.getAttribute('aria-hidden')) === 'true');
244          }
245        }
246      }
247    }
248  }
249
250  // static idCounter for unique id generation of submenus
251  // Default settings for the Nav class
252  Nav.defaultSettings = {
253    menuHoverClass: 'show-menu',
254    dir: 'ltr',
255    preventSubmenuOpenOnload: 'nav-active-open'
256  };
257  Nav.idCounter = 0;
258
259  // Initialize Nav instances for all nav elements on the page
260  document.addEventListener('DOMContentLoaded', () => {
261    document.querySelectorAll('.nav').forEach(nav =>
261 new Nav(nav));
262  });
263})();

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.