PageSourceSearch

https://www.unterkirnach.de/fileadmin/templates/extensions/hw_menu/js/hw_menu__shared.js

js unterkirnach.de collected 2026-10-02 16:45:54 UTC 11,780 bytes, 452 lines download raw bytes

1// Diese Funktion parsed den Inhalt des angegebenen HTML Elements und bildet daraus ein Objekt mit den zugehörigen Daten
2export function getData(rootElement) {
3	function getElement(element) {
4		const object = {
5		};
6
7		object.title = element.dataset.title;
8		object.uid = element.dataset.uid;
9		object.href = element.dataset.href;
10		object.children = [];
11
12
13
14		if (element.dataset.text) {
15			object.text = element.dataset.text;
16		}
17
18		if (element.dataset.imageS) {
19			object.imageS = element.dataset.imageS;
20		}
21
22		if (element.dataset.imageL) {
23			object.imageL = element.dataset.imageL;
24		}
25
26		if (element.children.length > 0) {
27			Array.from(element.children).forEach(child => {
28				object.children.push(getElement(child.querySelector(':not(li)')));
29			});
30			object.expanded = false;
31		} else {
32			object.active = false;
33		}
34
35		return object;
36	}
37
38	const object = getElement(rootElement.querySelector('ul'));
39	const objectData = [object];
40
41	return objectData;
42}
43
44// Die geparsten Daten werden ergänzt
45export function buildMenuData(data, currentId, parent = null, activeElement = {
46}) {
47	let result = activeElement;
48
49	data.forEach(itemData => {
50		if (itemData.uid === currentId) {
51			itemData.active = true;
52			result = itemData;
53		} else {
54			itemData.active = false;
55		}
56
57		if (parent) {
58			itemData.parent = parent;
59			itemData.depth = parent.depth + 1;
60		} else {
61			itemData.depth = 0;
62		}
63
64		if (itemData.depth === 1 && itemData.title.split(' ').length > 1) {
65			const title = itemData.title;
66
67			itemData.title = title.substring(0, title.lastIndexOf(' '));
68			itemData.subtitle = title.substring(title.lastIndexOf(' ') + 1, title.length);
69		}
70
71		if (!itemData.parentOfActive) {
72			itemData.parentOfActive = false;
73		}
74		
75		itemData.expanded = false;
76		itemData.visible = false;
77		itemData.node;
78		
79		itemData.setVisible = function (state) {
80			this.visible = state;
81
82			if (this.node) {
83				this.node.dataset.visible = state;
84			}
85		};
86
87		itemData.setExpanded = function (state) {
88			this.expanded = state;
89
90			if (this.node && this.node.tagName === 'BUTTON') {
91				this.node.setAttribute('aria-pressed', state);
92
93				if (this.node.nextElementSibling) {
94					this.node.nextElementSibling.setAttribute('aria-expanded', state);
95
96					if (this.depth >= 1 && !this.node.nextElementSibling.classList.contains('hw_menu__page')) {
97						this.node.nextElementSibling.style.maxHeight = this.node.nextElementSibling.scrollHeight + 'px';
98
99						if (state) {
100							this.node.style.pointerEvents = 'none';
101							this.node.nextElementSibling.addEventListener('transitionend', function transListener(event) {
102								event.target.style.maxHeight = '';
103
104								if (event.target.previousElementSibling) {
105									event.target.previousElementSibling.style.pointerEvents = '';
106								}
107
108								event.target.removeEventListener('transitionend', transListener, false);
109							});
110						} else {
111							this.node.style.pointerEvents = 'none';
112							setTimeout(() => {
113								this.node.nextElementSibling.style.maxHeight = '0px';
114							}, 1);		
115							
116							this.node.nextElementSibling.addEventListener('transitionend', function transListener(event) {
117								if (event.target.previousElementSibling) {
118									event.target.previousElementSibling.style.pointerEvents = '';
119								}
120
121								event.target.removeEventListener('transitionend', transListener, false);
122							});
123						}
124					}
125				}
126			}
127		};
128
129		itemData.setChildrenVisible = function (state) {
130			getChildren(this).forEach(child => {
131				if (state && child.parent.expanded && child.node) {
132					child.setVisible(true);
133				} else if (child.node) {
134					child.setVisible(false);
135				}
136			});
137		};
138
139		if (itemData.active) {
140			getParents(itemData).forEach(parent => {
141				if (parent.parent) {
142					parent.parentOfActive = true;
143				}
144			});
145		}
146
147		if (itemData.children.length > 0) {
148			const childResult = buildMenuData(itemData.children, currentId, itemData, result);
149
150			if (childResult) {
151				result = childResult;
152			}
153		}
154	});
155
156	return result;
157}
158
159export function buildMenuList(data, maxDepth, activeElement, classPrefix = 'hw_menu') {
160	const menuList = `
161				<button type="button" class="${classPrefix}__button" data-depth="${data.depth}" aria-pressed="${data.expanded}" data-visible="${data.visible}" data-parent-of-active="${data.parentOfActive}">
162					<span class="${classPrefix}__button__inner_text">
163						<span>${data.title}</span>
164						${data.subtitle ? `<span>${data.subtitle}</span>` : ''}
165					</span>
166					<span class="${classPrefix}__expand_icon"  aria-hidden="true"></span>
167				</button>
168				<ul class="${classPrefix}__list" data-depth="${data.depth}" aria-expanded="${data.expanded}" style="max-height: 0;">
169				</ul>
170			`;
171
172
173
174	const htmlFragment = document.createRange().createContextualFragment(menuList);
175	const button = htmlFragment.querySelector(`.${classPrefix}__button`);
176
177
178	data.node = button;
179
180	button.addEventListener('click', event => {
181		event.stopImmediatePropagation();
182
183		const state = !data.expanded;
184
185		data.setExpanded(state);
186		data.setChildrenVisible(state);
187
188		const siblings = data.parent.children.filter(child => child !== data);
189
190		siblings.forEach(sibling => {
191			if (sibling.children.length > 0) {
192				sibling.setExpanded(false);
193				sibling.setChildrenVisible(false);
194
195				sibling.node.style.pointerEvents = '';
196			}
197		});
198	});
199
200	data.children.map(itemData => {
201		if (itemData.depth <= maxDepth) {
202			htmlFragment.querySelector(`.${classPrefix}__list`).append(buildMenuListElement(itemData, true, maxDepth, activeElement, classPrefix));
203		} else {
204			htmlFragment.querySelector(`.${classPrefix}__list`).append(buildMenuListElement(itemData, false, maxDepth, activeElement, classPrefix));
205		}
206	});
207
208	return htmlFragment;
209}
210
211export function buildMenuLink(data, maxDepth, activeElement, classPrefix = 'hw_menu') {
212	let menuLink;
213
214	const activeParent = getParentAtDepth(activeElement, maxDepth);
215
216	if (maxDepth !== 99 && data === activeParent) {
217		data.active = true;
218	}
219
220	if (!data.active) {
221		menuLink = `
222					<a class="${classPrefix}__link" href="${data.href}" data-depth="${data.depth}" data-active="${data.active}" data-visible="${data.visible}">
223						<span class="${classPrefix}__link__inner_text">
224							<span>${data.title}</span>
225							${data.subtitle ? `<span>${data.subtitle}</span>` : ''}
226						</span>
227					</a>
228				`;
229	} else {
230		menuLink = `
231					<span class="${classPrefix}__link" data-depth="${data.depth}" data-active="${data.active}" data-visible="${data.visible}">
232						<span>${data.title}</span>
233						${data.subtitle ? `<span>${data.subtitle}</span>` : ''}
234					</span>
235				`;
236	}
237
238	const htmlFragment = document.createRange().createContextualFragment(menuLink);
239	const node = htmlFragment.querySelector(`.${classPrefix}__link`);
240
241	data.node = node;
242
243	return htmlFragment;
244}
245
246export function buildMenuListElement(data, recursive, maxDepth, activeElement, classPrefix = 'hw_menu') {
247
248	// Den letzten Elementen wird eine Klasse gegeben, um das padding zu steuern
249	if (data.depth === 1 && data.children.length > 0) {
250		const maxColumns = 3;
251
252		for (let i = 1; i <= maxColumns; i++) {
253			let numItemsInLastRow = data.children.length % i || i;
254
255			for (let j = data.children.length - 1; j >= data.children.length - numItemsInLastRow; j--) {
256				
257				if (data.children[j].class) {
258					data.children[j].class = `${data.children[j].class} ${classPrefix}__list_element__last_row-${i}`;
259					
260				} else {
261					data.children[j].class = `${classPrefix}__list_element__last_row-${i}`;
262				}
263			}
264		}
265
266		// const numItemsInLastRowXxl = data.children.length % 3 || 3;
267
268		// for (let i = data.children.length - 1; i >= data.children.length - numItemsInLastRowXxl; i--) {
269		// 	console.log(data.children[i]);
270
271		// 	if (data.children[i]) {
272
273		// 		data.children[i].class = `${classPrefix}__list_element__last_row-xxl`;
274		// 	}
275		// }
276
277		// data.children[data.children.length - 1].class = `${classPrefix}__list_element-1`;
278		// data.children[data.children.length - 2].class = `${classPrefix}__list_element-2`;
279		// data.children[data.children.length - 3].class = `${classPrefix}__list_element-3`;
280	}
281
282	const menuListElement = `
283				<li class="${classPrefix}__list_element${data.class ? ` ${data.class}` : ''}">
284				</li>
285			`;
286
287	const htmlFragment = document.createRange().createContextualFragment(menuListElement);
288
289
290	
291	if (recursive && data.children.length > 0 && data.depth < maxDepth) {
292		htmlFragment.querySelector(`.${classPrefix}__list_element`).append(buildMenuList(data, maxDepth, activeElement, classPrefix));
293	} else {
294		htmlFragment.querySelector(`.${classPrefix}__list_element`).append(buildMenuLink(data, maxDepth, activeElement, classPrefix));
295	}
296
297	return htmlFragment;
298}
299
300export function getElementsAtDepth(data, depth, elementsAtDepth = []) {
301	data.forEach(itemData => {
302		if (itemData.depth === depth) {
303			elementsAtDepth.push(itemData);
304		} else {
305			getElementsAtDepth(itemData.children, depth, elementsAtDepth);
306		}
307	});
308
309	return elementsAtDepth;
310}
311
312function getChildren(data, children = []) {
313	if (!data.children) {
314		return children;
315	} else {
316		data.children.forEach(child => {
317			children.push(child);
318
319			getChildren(child, children);
320		});
321	}
322
323	return children;
324}
325
326export function checkForExpanded(data) {
327	if (data.children) {
328		if (data.expanded && !data.parentOfActive) {
329			data.expanded = false;
330		}
331
332		data.children.forEach(child => {
333			if (child.expanded && !child.parentOfActive) {
334				child.expanded = false;
335			}
336
337			checkForExpanded(child);
338		});
339	}
340}
341
342export function getParentAtDepth(element, depth) {
343	let parentAtDepth = element.parent;
344
345	for (let i = element.depth - 1; i > depth; i--) {
346		parentAtDepth = parentAtDepth.parent;
347	}
348
349	return parentAtDepth;
350}
351
352export function getElementWithUrl(data, url, objects = []) {
353
354	data.forEach(itemData => {
355		console.log(itemData);
356
357		if (itemData.href.indexOf(url) === 0) {
358			objects.push(itemData);
359		} else {
360			getElementWithUrl(itemData.children, url, objects);
361		}
362	});
363
364	return objects[0];
365}
366
367export function getParents(data, parents = []) {
368	if (!data.parent) {
369		return parents;
370	} else {
371		parents.push(data.parent);
372
373		getParents(data.parent, parents);
374	}
375
376	return parents;
377}
378
379export function initializeMenu(data, menuType, depth = 0) {
380	const activeElement = data[0].activeElement;
381	const parents = getParents(activeElement);
382
383	parents.forEach(parent => {
384		switch (menuType) {
385		case 'mainMenu':
386			if (parent.parent && parent.depth > depth) {
387				parent.setExpanded(true);
388			}
389
390			break;
391
392		case 'subMenu':
393			if (parent.parent) {
394				parent.setExpanded(true);
395				parent.setChildrenVisible(true);
396			}
397
398			break;
399
400		case 'mobileMenu':
401			if (parent.parent) {
402				parent.setExpanded(true);
403			}
404
405			break;
406		}
407	});
408
409	switch (menuType) {
410	case 'mainMenu':
411		getElementsAtDepth(data, depth).forEach(element => {
412
413			if (element.parentOfActive) {
414				element.setExpanded(true);
415			}
416
417			// element.setVisible(true);
418		});
419
420		break;
421
422	case 'subMenu':
423		getElementsAtDepth(data, depth + 1).forEach(element => {
424			element.setVisible(true);
425		});
426
427		break;
428	}
429}
430
431export function closeOpenMenu(data) {
432	data.parent.children.filter(child => child.expanded === true).forEach(child => {
433		child.setExpanded(false);
434		child.setChildrenVisible(false);
435	});
436}
437
438export function closeMenu(data) {
439	data.setExpanded(false);
440	data.setChildrenVisible(false);
441
442	if (document.querySelector('.hw_menu__mobile_button__search')) {
443		const mobileSearchButton = document.querySelector('.hw_menu__mobile_button__search');
444
445		if (mobileSearchButton.getAttribute('aria-pressed') === 'true') {
446			mobileSearchButton.setAttribute('aria-pressed', false);
447			mobileSearchButton.nextElementSibling.setAttribute('aria-expanded', false);
448		}
449	}
450
451	document.body.classList.remove('hw_menu__open');
452}

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.