PageSourceSearch

https://www.sausewind-shop.com/templates/NIUplus/js/niu-app/niu.navWorlds.js?v=7.15.1

js sausewind-shop.com collected 2026-09-24 11:39:44 UTC 7,298 bytes, 199 lines download raw bytes

1import NiuBase from "niu-base";
2
3/**
4 * Welten-Navigation (Header-Typ 5).
5 *
6 * Zeile 1 traegt die Welten, Zeile 2 die Kategorien der jeweils gezeigten
7 * Welt. Alle Kategoriezeilen stehen bereits im DOM - dieses Modul tauscht
8 * ausschliesslich Zustandsklassen, es laedt nichts nach und baut nichts auf.
9 * Dadurch funktioniert die Navigation ohne JavaScript vollstaendig (die vom
10 * Server gewaehlte Welt ist sichtbar und verlinkt) und es entsteht kein
11 * Layout-Shift.
12 *
13 * Zwei Zustaende, die bewusst getrennt bleiben:
14 *
15 *   serverIndex  die Welt, zu der die aktuelle Seite gehoert. Kommt vom
16 *                Server und aendert sich nie.
17 *   shownIndex   die gerade angezeigte Welt. Folgt Hover und Fokus und faellt
18 *                auf serverIndex zurueck, sobald der Zeiger den Kopf verlaesst.
19 *
20 * Ohne diese Trennung wuerde ein Hover die Information "hier stehe ich
21 * gerade" ueberschreiben und nie zurueckholen.
22 */
23class NiuNavWorlds extends NiuBase {
24    static Selectors = {
25        wrapper: () => { return '[data-niu-nav-worlds]' },
26        world: '[data-niu-world]',
27        row: (index = '') => { return '[data-niu-world-row' + (index !== '' ? '="' + index + '"' : '') + ']' },
28    }
29
30    // Verzoegerung beim Wechsel per Hover. Ohne sie wechselt die Zeile schon
31    // beim blossen Ueberfahren auf dem Weg zu einem anderen Ziel.
32    static HoverOpenDelay = 150;
33    // Verzoegerung bis zum Zurueckfallen. Deckt den Weg von Zeile 1 nach
34    // Zeile 2 ab - ohne sie waere die Zeile weg, bevor sie erreicht ist.
35    static RevertDelay = 250;
36
37    constructor(wrapper) {
38        super(wrapper)
39        this.worlds = []
40        this.rows = []
41        this.serverIndex = 0
42        this.shownIndex = 0
43        this.openTimer = 0
44        this.revertTimer = 0
45        // Spiegelt js/app/helpers.js:6. Bewusst hier wiederholt statt aus dem
46        // anderen Modulbaum importiert - ein Einzeiler rechtfertigt keine
47        // Kopplung zwischen js/app und js/niu-app.
48        this.hasTouch = 'ontouchstart' in window
49        // Merkt sich auf Touch, welche Welt bereits angetippt wurde: Der erste
50        // Tap wechselt die Zeile, erst der zweite folgt dem Link.
51        this.tappedIndex = -1
52        this.scheduleRevert = this.scheduleRevert.bind(this)
53        this.cancelRevert = this.cancelRevert.bind(this)
54    }
55
56    init() {
57        this.worlds = Array.from(this.wrapper.querySelectorAll(NiuNavWorlds.Selectors.world))
58        if (this.worlds.length === 0) return
59
60        // Bewusst global gesucht: Die Kategoriezeilen liegen nicht im Wrapper,
61        // sondern eine Header-Zeile tiefer in einem eigenen Container - Zeile 1
62        // teilt sich ihre Zeile mit Logo und Icons, Zeile 2 mit der Suche.
63        this.rows = Array.from(document.querySelectorAll(NiuNavWorlds.Selectors.row()))
64
65        this.serverIndex = this.findActiveIndex()
66        this.shownIndex = this.serverIndex
67
68        this.worlds.forEach((world, index) => {
69            if (!this.hasTouch) {
70                world.addEventListener('mouseenter', () => { this.scheduleShow(index) })
71            }
72            // Fokus wechselt sofort: Wer sich mit Tab durch die Welten bewegt,
73            // soll die zugehoerigen Kategorien sehen, ohne erst etwas
74            // ausloesen zu muessen.
75            world.addEventListener('focus', () => { this.cancelRevert(); this.show(index) })
76            world.addEventListener('keydown', (e) => { this.onKeydown(e, index) })
77            world.addEventListener('click', (e) => { this.onClick(e, index) })
78        })
79
80        // Der Kopf gilt als verlassen, sobald weder die Welten-Zeile noch eine
81        // Kategoriezeile unter dem Zeiger liegt.
82        if (!this.hasTouch) {
83            this.wrapper.addEventListener('mouseenter', this.cancelRevert)
84            this.wrapper.addEventListener('mouseleave', this.scheduleRevert)
85            this.rows.forEach(row => {
86                row.addEventListener('mouseenter', this.cancelRevert)
87                row.addEventListener('mouseleave', this.scheduleRevert)
88            })
89        }
90
91        // Setzt das dzmInit-Flag aus NiuBase, damit ein erneuter Scan() den
92        // Wrapper nicht doppelt initialisiert.
93        super.init()
94        this.register()
95    }
96
97    /**
98     * Die aktive Welt steht schon im gelieferten Markup - sie wird hier
99     * ausgelesen und nicht neu bestimmt. Die Regel, welche Welt aktiv ist,
100     * gehoert in die Datenschicht und darf nicht doppelt existieren.
101     *
102     * @returns {number}
103     */
104    findActiveIndex() {
105        const active = this.worlds.findIndex(world => world.closest('li')?.classList.contains('active'))
106        return active === -1 ? 0 : active
107    }
108
109    /**
110     * @param {number} index
111     */
112    scheduleShow(index) {
113        this.cancelRevert()
114        window.clearTimeout(this.openTimer)
115        this.openTimer = window.setTimeout(() => { this.show(index) }, NiuNavWorlds.HoverOpenDelay)
116    }
117
118    scheduleRevert() {
119        window.clearTimeout(this.revertTimer)
120        this.revertTimer = window.setTimeout(() => { this.show(this.serverIndex) }, NiuNavWorlds.RevertDelay)
121    }
122
123    cancelRevert() {
124        window.clearTimeout(this.revertTimer)
125        this.revertTimer = 0
126    }
127
128    /**
129     * Tauscht die angezeigte Welt. Einziger Ort, an dem Klassen und
130     * aria-Zustaende gesetzt werden.
131     *
132     * @param {number} index
133     */
134    show(index) {
135        if (index < 0 || index >= this.worlds.length) return
136        window.clearTimeout(this.openTimer)
137        this.shownIndex = index
138
139        this.worlds.forEach((world, i) => {
140            const isShown = i === index
141            world.closest('li')?.classList.toggle('active', isShown)
142            world.setAttribute('aria-expanded', isShown ? 'true' : 'false')
143        })
144
145        this.rows.forEach(row => {
146            row.classList.toggle('is-active', Number(row.dataset.niuWorldRow) === index)
147        })
148    }
149
150    /**
151     * Tastatur: Leertaste wechselt die Welt, Enter bleibt dem Link
152     * ueberlassen. Enter umzubiegen wuerde die Welt-Seite per Tastatur
153     * unerreichbar machen - der Wechsel passiert dort ohnehin schon beim
154     * Fokussieren.
155     *
156     * @param {KeyboardEvent} e
157     * @param {number} index
158     */
159    onKeydown(e, index) {
160        if (e.key !== ' ' && e.key !== 'Spacebar') return
161        e.preventDefault()
162        this.cancelRevert()
163        this.show(index)
164    }
165
166    /**
167     * Touch: Der erste Tap auf eine Welt wechselt nur die Zeile, der zweite
168     * folgt dem Link.
169     *
170     * Das weicht bewusst vom Bestandsverhalten der Typen 1-4 ab, wo ein
171     * zweiter Tap das Panel wieder schliesst (js/app/views/header.js:224-240)
172     * und ausschliesslich ein separater Link im Panel navigiert. Ohne diese
173     * Abweichung waere die Welten-Zeile auf Tablets gar nicht bedienbar: Dort
174     * greift weder die Hover-Logik noch der mobile Slide-Modus.
175     *
176     * @param {MouseEvent} e
177     * @param {number} index
178     */
179    onClick(e, index) {
180        if (!this.hasTouch) return
181        if (this.tappedIndex === index) return
182
183        e.preventDefault()
184        this.tappedIndex = index
185        this.show(index)
186    }
187
188    register() {
189
190    }
191}
192
193NiuBase.listenTo([
194    dzm.events.controller.initialized
195
196], () => {
197
198    NiuNavWorlds.Scan()
199})

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.