(function (ns) { 'use strict'; const DROPDOWN_ANIMATION_MS = 180; const pageTitles = Object.freeze({ privacy: 'Datenschutzerklärung', imprint: 'Impressum', technology: 'Cookies und Technik' }); const menuEntries = Object.freeze([ ['privacy', 'Datenschutzerklärung'], ['imprint', 'Impressum'], ['technology', 'Cookies und verwendete JavaScripts'] ]); let elements = null; let dropdownOpen = false; let dropdownCloseTimer = null; function ready(callback) { if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', callback, { once: true }); return; } callback(); } function createElement(tagName, attributes = {}, text = '') { const element = document.createElement(tagName); Object.entries(attributes).forEach(([name, value]) => { if (value === null || value === undefined) return; if (name === 'className') { element.className = value; return; } if (name === 'dataset') { Object.entries(value).forEach(([key, dataValue]) => { element.dataset[key] = dataValue; }); return; } element.setAttribute(name, value); }); if (text) element.textContent = text; return element; } function ensureInfoControl() { let control = document.querySelector('.info-control'); let button = document.getElementById('info-button') ?? document.querySelector('[aria-controls="info-dropdown"], .info-button'); let dropdown = document.getElementById('info-dropdown') ?? document.querySelector('.info-dropdown'); if (!control) { control = createElement('div', { className: 'info-control' }); document.body.insertBefore(control, document.body.firstChild); } if (!button) { button = createElement('button', { id: 'info-button', className: 'info-button', type: 'button', 'aria-label': 'Informationsmenü öffnen', title: 'Informationen' }); const icon = createElement('span', { 'aria-hidden': 'true' }, 'i'); button.appendChild(icon); } if (button.parentElement !== control) { control.insertBefore(button, control.firstChild); } button.id = 'info-button'; button.classList.add('info-button'); button.setAttribute('type', 'button'); button.setAttribute('aria-haspopup', 'true'); button.setAttribute('aria-expanded', 'false'); button.setAttribute('aria-controls', 'info-dropdown'); if (!button.getAttribute('aria-label')) button.setAttribute('aria-label', 'Informationsmenü öffnen'); if (!button.textContent.trim()) { const icon = createElement('span', { 'aria-hidden': 'true' }, 'i'); button.appendChild(icon); } if (!dropdown) { dropdown = createElement('nav', { id: 'info-dropdown', className: 'info-dropdown', 'aria-label': 'Rechtliche und technische Informationen' }); } if (dropdown.parentElement !== control) { control.appendChild(dropdown); } dropdown.id = 'info-dropdown'; dropdown.classList.add('info-dropdown'); dropdown.setAttribute('aria-label', dropdown.getAttribute('aria-label') ?? 'Rechtliche und technische Informationen'); dropdown.hidden = true; menuEntries.forEach(([target, label]) => { let menuButton = dropdown.querySelector(`[data-info-target="${target}"]`); if (!menuButton) { menuButton = createElement('button', { type: 'button', dataset: { infoTarget: target } }, label); dropdown.appendChild(menuButton); } menuButton.setAttribute('type', 'button'); menuButton.dataset.infoTarget = target; if (!menuButton.textContent.trim()) menuButton.textContent = label; }); return { control, button, dropdown }; } function moveExistingPagesInto(content, dialog) { dialog.querySelectorAll('[data-info-page]').forEach(page => { if (page.parentElement !== content) content.appendChild(page); }); } function ensureInfoPage(content, name) { let page = content.querySelector(`[data-info-page="${name}"]`) ?? document.querySelector(`[data-info-page="${name}"]`); if (!page) { page = createElement('article', { className: 'info-page', dataset: { infoPage: name } }); page.appendChild(createElement('p', {}, `${pageTitles[name]} ist noch nicht hinterlegt.`)); content.appendChild(page); } page.classList.add('info-page'); page.dataset.infoPage = name; page.hidden = true; if (page.parentElement !== content) content.appendChild(page); return page; } function ensureInfoDialog() { let dialog = document.getElementById('info-dialog'); if (!dialog) { dialog = createElement('dialog', { id: 'info-dialog', className: 'info-dialog', 'aria-labelledby': 'info-dialog-title' }); document.body.appendChild(dialog); } dialog.id = 'info-dialog'; dialog.classList.add('info-dialog'); dialog.setAttribute('aria-labelledby', 'info-dialog-title'); let shell = dialog.querySelector('.info-dialog-shell'); if (!shell) { shell = createElement('div', { className: 'info-dialog-shell' }); while (dialog.firstChild) shell.appendChild(dialog.firstChild); dialog.appendChild(shell); } let header = shell.querySelector('.info-dialog-header'); if (!header) { header = createElement('header', { className: 'info-dialog-header' }); shell.insertBefore(header, shell.firstChild); } let title = document.getElementById('info-dialog-title') ?? header.querySelector('h1, h2, .info-dialog-title'); if (!title) { title = createElement('h2', { id: 'info-dialog-title' }, 'Informationen'); header.insertBefore(title, header.firstChild); } title.id = 'info-dialog-title'; if (!title.textContent.trim()) title.textContent = 'Informationen'; let closeButton = document.getElementById('info-close') ?? header.querySelector('[data-info-close], .info-close, .info-dialog-close, button[aria-label="Dialog schließen"], button[aria-label="Schließen"]'); if (!closeButton) { closeButton = createElement('button', { id: 'info-close', type: 'button', 'aria-label': 'Informationsfenster schließen', title: 'Schließen' }, '×'); header.appendChild(closeButton); } closeButton.id = 'info-close'; closeButton.setAttribute('type', 'button'); closeButton.setAttribute('aria-label', closeButton.getAttribute('aria-label') ?? 'Informationsfenster schließen'); closeButton.setAttribute('title', closeButton.getAttribute('title') ?? 'Schließen'); if (!closeButton.textContent.trim()) closeButton.textContent = '×'; if (closeButton.parentElement !== header) header.appendChild(closeButton); let content = shell.querySelector('.info-dialog-content'); if (!content) { content = createElement('div', { className: 'info-dialog-content' }); shell.appendChild(content); } moveExistingPagesInto(content, dialog); Object.keys(pageTitles).forEach(name => ensureInfoPage(content, name)); return { dialog, title, closeButton, content }; } function ensureInfoUi() { const control = ensureInfoControl(); const dialog = ensureInfoDialog(); return { control: control.control, infoButton: control.button, dropdown: control.dropdown, dialog: dialog.dialog, title: dialog.title, closeButton: dialog.closeButton, content: dialog.content }; } function isActivation(event) { if (event.type === 'keydown') return event.key === 'Enter' || event.key === ' '; if (event.type !== 'pointerup') return false; if (event.isPrimary === false) return false; return event.pointerType !== 'mouse' || event.button === 0; } function activate(element, handler) { if (!element || typeof element.addEventListener !== 'function') return; if (element.dataset.wegwichtelLegalUiBound === 'true') return; element.dataset.wegwichtelLegalUiBound = 'true'; element.addEventListener('pointerup', event => { if (!isActivation(event)) return; event.preventDefault(); event.stopPropagation(); handler(event); }); element.addEventListener('keydown', event => { if (!isActivation(event)) return; event.preventDefault(); event.stopPropagation(); handler(event); }); } function setClass(element, className, enabled) { if (!element || !element.classList) return; element.classList.toggle(className, enabled); } function runNextFrame(callback) { if (typeof globalThis.requestAnimationFrame === 'function') { globalThis.requestAnimationFrame(callback); return; } callback(); } function schedule(callback, timeout) { if (typeof globalThis.setTimeout === 'function') return globalThis.setTimeout(callback, timeout); callback(); return null; } function cancelSchedule(timer) { if (timer === null || typeof globalThis.clearTimeout !== 'function') return; globalThis.clearTimeout(timer); } function setDropdownOpen(open) { if (!elements?.dropdown || !elements?.infoButton) return; dropdownOpen = open; elements.infoButton.setAttribute('aria-expanded', open ? 'true' : 'false'); elements.infoButton.setAttribute('aria-label', open ? 'Informationsmenü schließen' : 'Informationsmenü öffnen'); cancelSchedule(dropdownCloseTimer); dropdownCloseTimer = null; if (open) { elements.dropdown.hidden = false; runNextFrame(() => setClass(elements.dropdown, 'is-open', true)); return; } setClass(elements.dropdown, 'is-open', false); dropdownCloseTimer = schedule(() => { if (dropdownOpen || !elements?.dropdown) return; elements.dropdown.hidden = true; }, DROPDOWN_ANIMATION_MS); } function toggleDropdown() { setDropdownOpen(!dropdownOpen); } function elementContains(element, target) { if (!element || !target) return false; if (element === target) return true; return typeof element.contains === 'function' && element.contains(target); } function isInsideInfoControl(target) { return elementContains(elements?.infoButton, target) || elementContains(elements?.dropdown, target); } function isProgressStorageEnabled() { if (!ns.PrivacyStorage || typeof ns.PrivacyStorage.isProgressStorageEnabled !== 'function') return false; return ns.PrivacyStorage.isProgressStorageEnabled(); } function setProgressStorageEnabled(enabled) { if (!ns.PrivacyStorage || typeof ns.PrivacyStorage.setProgressStorageEnabled !== 'function') return false; return ns.PrivacyStorage.setProgressStorageEnabled(enabled); } function syncStorageToggles() { const enabled = isProgressStorageEnabled(); document.querySelectorAll('.progress-storage-toggle').forEach(toggle => { toggle.checked = enabled; }); document.querySelectorAll('.cookie-toggle').forEach(toggle => { toggle.checked = false; }); } function showPage(name) { const pageName = Object.prototype.hasOwnProperty.call(pageTitles, name) ? name : 'privacy'; const page = elements?.content?.querySelector(`[data-info-page="${pageName}"]`) ?? document.querySelector(`[data-info-page="${pageName}"]`); if (!page) return; if (elements?.title) elements.title.textContent = pageTitles[pageName] ?? 'Informationen'; document.querySelectorAll('[data-info-page]').forEach(item => { item.hidden = item !== page; }); if (elements?.content) elements.content.scrollTop = 0; } function focusSafely(element) { if (!element || typeof element.focus !== 'function') return; try { element.focus({ preventScroll: true }); } catch { element.focus(); } } function openDialog(name = 'privacy') { if (!elements?.dialog) return; const pageName = Object.prototype.hasOwnProperty.call(pageTitles, name) ? name : 'privacy'; setDropdownOpen(false); showPage(pageName); syncStorageToggles(); if (!elements.dialog.open) { if (typeof elements.dialog.showModal === 'function') elements.dialog.showModal(); else elements.dialog.setAttribute('open', ''); } focusSafely(elements.closeButton ?? elements.dialog); } function closeDialog() { if (elements?.dialog) { if (typeof elements.dialog.close === 'function') elements.dialog.close(); else elements.dialog.removeAttribute('open'); } setDropdownOpen(false); focusSafely(elements?.infoButton); } function bindStorageToggles() { document.querySelectorAll('.progress-storage-toggle').forEach(toggle => { if (toggle.dataset.wegwichtelStorageBound === 'true') return; toggle.dataset.wegwichtelStorageBound = 'true'; toggle.addEventListener('change', event => { const enabled = setProgressStorageEnabled(event.currentTarget.checked); syncStorageToggles(); document.dispatchEvent(new CustomEvent('wegwichtel:progress-storage-changed', { detail: { enabled } })); }); }); } function bindDialog() { activate(elements.infoButton, toggleDropdown); activate(elements.closeButton, closeDialog); elements.dropdown.querySelectorAll('[data-info-target]').forEach(button => { activate(button, () => openDialog(button.dataset.infoTarget)); }); if (!document.documentElement.dataset.wegwichtelLegalUiDocumentBound) { document.documentElement.dataset.wegwichtelLegalUiDocumentBound = 'true'; document.addEventListener('pointerup', event => { if (!dropdownOpen || isInsideInfoControl(event.target)) return; setDropdownOpen(false); }); document.addEventListener('keydown', event => { if (!dropdownOpen || event.key !== 'Escape' || elements?.dialog?.open) return; event.preventDefault(); setDropdownOpen(false); focusSafely(elements?.infoButton); }); } if (elements.dialog.dataset.wegwichtelLegalUiDialogBound !== 'true') { elements.dialog.dataset.wegwichtelLegalUiDialogBound = 'true'; elements.dialog.addEventListener('pointerup', event => { if (event.target !== elements.dialog || !isActivation(event)) return; event.preventDefault(); event.stopPropagation(); closeDialog(); }); elements.dialog.addEventListener('cancel', event => { event.preventDefault(); closeDialog(); }); } } function init() { elements = ensureInfoUi(); bindDialog(); bindStorageToggles(); syncStorageToggles(); ns.LegalUi = Object.freeze({ open: openDialog, close: closeDialog, toggleMenu: toggleDropdown, closeMenu: () => setDropdownOpen(false), syncStorageToggles }); } ns.LegalUi = Object.freeze({ open: name => ready(() => { if (!elements) init(); openDialog(name); }), close: () => ready(() => { if (!elements) init(); closeDialog(); }), toggleMenu: () => ready(() => { if (!elements) init(); toggleDropdown(); }), closeMenu: () => ready(() => { if (!elements) init(); setDropdownOpen(false); }), syncStorageToggles: () => ready(() => { if (!elements) init(); syncStorageToggles(); }) }); ready(init); }(globalThis.Wegwichtel ??= {}));