(function (ns) { 'use strict'; 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 documentEventsBound = false; function ready(callback) { if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', callback, { once: true }); return; } callback(); } function byId(id) { return typeof document.getElementById === 'function' ? document.getElementById(id) : null; } function qs(selector, root = document) { return root && typeof root.querySelector === 'function' ? root.querySelector(selector) : null; } function qsa(selector, root = document) { if (root && typeof root.querySelectorAll === 'function') return Array.from(root.querySelectorAll(selector)); if (root !== document && typeof document.querySelectorAll === 'function') return Array.from(document.querySelectorAll(selector)); return []; } function createElement(tagName, attributes = {}, text = '') { if (typeof document.createElement !== 'function') return null; 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 setAttribute(element, name, value) { if (!element || typeof element.setAttribute !== 'function') return; element.setAttribute(name, value); } function toggleClass(element, className, enabled) { if (!element || !element.classList) return; if (typeof element.classList.toggle === 'function') { element.classList.toggle(className, enabled); return; } if (enabled && typeof element.classList.add === 'function') element.classList.add(className); if (!enabled && typeof element.classList.remove === 'function') element.classList.remove(className); } function append(parent, child) { if (!parent || !child || typeof parent.appendChild !== 'function') return false; parent.appendChild(child); return true; } function prependToBody(element) { if (!document.body || !element) return false; if (typeof document.body.prepend === 'function') { document.body.prepend(element); return true; } if (typeof document.body.insertBefore === 'function') { document.body.insertBefore(element, document.body.firstChild ?? null); return true; } return append(document.body, element); } function ensureInfoControl() { let control = qs('.info-control'); let infoButton = byId('info-button') || qs('.info-button'); let dropdown = byId('info-dropdown') || qs('.info-dropdown'); if (!control && typeof document.createElement === 'function') { control = createElement('div', { className: 'info-control' }); prependToBody(control); } if (!infoButton && control) { infoButton = createElement('button', { id: 'info-button', className: 'info-button', type: 'button', 'aria-label': 'Informationsmenü öffnen', title: 'Informationen' }); const icon = createElement('span', { 'aria-hidden': 'true' }, 'i'); append(infoButton, icon); append(control, infoButton); } if (!dropdown && control) { dropdown = createElement('nav', { id: 'info-dropdown', className: 'info-dropdown', 'aria-label': 'Rechtliche und technische Informationen' }); append(control, dropdown); } if (infoButton) { setAttribute(infoButton, 'type', 'button'); setAttribute(infoButton, 'aria-haspopup', 'true'); setAttribute(infoButton, 'aria-expanded', 'false'); setAttribute(infoButton, 'aria-controls', 'info-dropdown'); if (typeof infoButton.getAttribute !== 'function' || !infoButton.getAttribute('aria-label')) { setAttribute(infoButton, 'aria-label', 'Informationsmenü öffnen'); } if (!String(infoButton.textContent ?? '').trim() && typeof document.createElement === 'function') { append(infoButton, createElement('span', { 'aria-hidden': 'true' }, 'i')); } } if (dropdown) { setAttribute(dropdown, 'aria-label', 'Rechtliche und technische Informationen'); dropdown.hidden = true; menuEntries.forEach(([target, label]) => { let button = qs(`[data-info-target="${target}"]`, dropdown) || qsa('[data-info-target]').find(item => item.dataset?.infoTarget === target); if (!button && typeof document.createElement === 'function') { button = createElement('button', { type: 'button', dataset: { infoTarget: target } }, label); append(dropdown, button); } if (!button) return; setAttribute(button, 'type', 'button'); button.dataset.infoTarget = target; if (!String(button.textContent ?? '').trim()) button.textContent = label; }); } return { control, infoButton, dropdown }; } function ensureInfoPage(content, name) { let page = qs(`[data-info-page="${name}"]`, content) || qs(`[data-info-page="${name}"]`); if (!page && content) { page = createElement('article', { className: 'info-page', dataset: { infoPage: name } }); append(page, createElement('p', {}, `${pageTitles[name]} ist noch nicht hinterlegt.`)); append(content, page); } if (page) { page.dataset.infoPage = name; if (name !== 'privacy') page.hidden = true; } return page; } function ensureInfoDialog() { let dialog = byId('info-dialog') || qs('#info-dialog'); let title = byId('info-dialog-title') || qs('#info-dialog-title', dialog); let backButton = byId('info-back') || qs('#info-back', dialog); let legacyMenu = byId('info-menu') || qs('#info-menu', dialog); let closeButton = byId('info-close') || qs('#info-close', dialog); let content = qs('.info-dialog-content', dialog) || qs('.info-dialog-content'); if (!dialog && typeof document.createElement === 'function') { dialog = createElement('dialog', { id: 'info-dialog', className: 'info-dialog', 'aria-labelledby': 'info-dialog-title' }); append(document.body, dialog); } if (dialog && (!title || !closeButton || !content) && typeof document.createElement === 'function') { let shell = qs('.info-dialog-shell', dialog); if (!shell) { shell = createElement('div', { className: 'info-dialog-shell' }); append(dialog, shell); } let header = qs('.info-dialog-header', shell); if (!header) { header = createElement('header', { className: 'info-dialog-header' }); append(shell, header); } if (!title) { title = createElement('h2', { id: 'info-dialog-title' }, pageTitles.privacy); append(header, title); } if (!closeButton) { closeButton = createElement('button', { id: 'info-close', type: 'button', 'aria-label': 'Informationsfenster schließen', title: 'Schließen' }, '×'); append(header, closeButton); } if (!content) { content = createElement('div', { className: 'info-dialog-content' }); append(shell, content); } } if (dialog) { setAttribute(dialog, 'aria-labelledby', 'info-dialog-title'); } if (backButton) { setAttribute(backButton, 'type', 'button'); setAttribute(backButton, 'aria-label', 'Zurück zum Informationsmenü'); backButton.hidden = true; } if (legacyMenu) { legacyMenu.hidden = true; } if (closeButton) { setAttribute(closeButton, 'type', 'button'); setAttribute(closeButton, 'aria-label', 'Informationsfenster schließen'); if (!String(closeButton.textContent ?? '').trim()) closeButton.textContent = '×'; } if (content) { Object.keys(pageTitles).forEach(name => ensureInfoPage(content, name)); } return { dialog, title, backButton, legacyMenu, closeButton, content }; } function ensureInfoUi() { return { ...ensureInfoControl(), ...ensureInfoDialog() }; } function isActivation(event) { if (!event) return false; if (event.type === 'pointerup') { if (event.isPrimary === false) return false; if (event.pointerType === 'mouse' && event.button !== 0) return false; return true; } if (event.type === 'keydown') return event.key === 'Enter' || event.key === ' '; return false; } function activate(element, handler) { if (!element || typeof element.addEventListener !== 'function') return; if (element.dataset) { const key = handler.name ? `wegwichtelLegalUiBound${handler.name}` : 'wegwichtelLegalUiBound'; if (element.dataset[key] === 'true') return; element.dataset[key] = 'true'; } element.addEventListener('pointerup', event => { if (!isActivation(event)) return; if (typeof event.preventDefault === 'function') event.preventDefault(); if (typeof event.stopPropagation === 'function') event.stopPropagation(); handler(event); }); element.addEventListener('keydown', event => { if (!isActivation(event)) return; if (typeof event.preventDefault === 'function') event.preventDefault(); if (typeof event.stopPropagation === 'function') event.stopPropagation(); handler(event); }); } function setDropdownOpen(open) { if (!elements?.dropdown || !elements?.infoButton) return; dropdownOpen = Boolean(open); elements.dropdown.hidden = !dropdownOpen; toggleClass(elements.dropdown, 'is-open', dropdownOpen); setAttribute(elements.infoButton, 'aria-expanded', dropdownOpen ? 'true' : 'false'); } 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(); qsa('.progress-storage-toggle').forEach(toggle => { toggle.checked = enabled; }); qsa('.cookie-toggle').forEach(toggle => { toggle.checked = false; }); } function showPage(name) { const pageName = Object.prototype.hasOwnProperty.call(pageTitles, name) ? name : 'privacy'; const page = qs(`[data-info-page="${pageName}"]`, elements?.content) || qs(`[data-info-page="${pageName}"]`); if (!page) return; if (elements?.title) elements.title.textContent = pageTitles[pageName] ?? 'Informationen'; qsa('[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 setAttribute(elements.dialog, 'open', ''); } focusSafely(elements.closeButton ?? elements.dialog); } function closeDialog() { if (elements?.dialog) { if (typeof elements.dialog.close === 'function') elements.dialog.close(); else if (typeof elements.dialog.removeAttribute === 'function') elements.dialog.removeAttribute('open'); } setDropdownOpen(false); focusSafely(elements?.infoButton); } function bindStorageToggles() { qsa('.progress-storage-toggle').forEach(toggle => { if (toggle.dataset?.wegwichtelStorageBound === 'true') return; if (toggle.dataset) toggle.dataset.wegwichtelStorageBound = 'true'; toggle.addEventListener('change', event => { const source = event.currentTarget || toggle; const enabled = setProgressStorageEnabled(Boolean(source.checked)); syncStorageToggles(); document.dispatchEvent(new CustomEvent('wegwichtel:progress-storage-changed', { detail: { enabled } })); }); }); } function bindDialog() { activate(elements?.infoButton, toggleDropdown); activate(elements?.closeButton, closeDialog); activate(elements?.backButton, () => setDropdownOpen(true)); qsa('[data-info-target]', elements?.dropdown).forEach(button => { activate(button, () => openDialog(button.dataset?.infoTarget)); }); if (!documentEventsBound) { documentEventsBound = 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; if (typeof event.preventDefault === 'function') event.preventDefault(); setDropdownOpen(false); focusSafely(elements?.infoButton); }); } if (elements?.dialog && elements.dialog.dataset?.wegwichtelLegalUiDialogBound !== 'true') { if (elements.dialog.dataset) elements.dialog.dataset.wegwichtelLegalUiDialogBound = 'true'; elements.dialog.addEventListener('pointerup', event => { if (event.target !== elements.dialog || !isActivation(event)) return; if (typeof event.preventDefault === 'function') event.preventDefault(); if (typeof event.stopPropagation === 'function') event.stopPropagation(); closeDialog(); }); elements.dialog.addEventListener('cancel', event => { if (typeof event.preventDefault === 'function') event.preventDefault(); closeDialog(); }); } } function init() { elements = ensureInfoUi(); bindDialog(); bindStorageToggles(); syncStorageToggles(); ns.LegalUi = Object.freeze({ open: openDialog, close: closeDialog, toggleMenu: toggleDropdown, closeMenu: () => setDropdownOpen(false), syncStorageToggles }); } ready(init); }(globalThis.Wegwichtel = globalThis.Wegwichtel || {}));