Files
Wegwichtel/public/js/legal-ui.js
T
florian 3e051d798d
Sonarqube Scanner / Build and analyze (push) Canceled after 0s
Revert "Updated imprint and legal documents"
This reverts commit fd16c66142.
2026-08-29 17:32:35 +02:00

358 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
(function (ns) {
'use strict';
const dialog = document.getElementById('info-dialog');
const title = document.getElementById('info-dialog-title');
const menu = document.getElementById('info-menu');
const backButton = document.getElementById('info-back');
const closeButton = document.getElementById('info-close');
const infoButton = document.getElementById('info-button');
const pageTitles = Object.freeze({
privacy: 'Datenschutzerklärung',
imprint: 'Impressum',
technology: 'Cookies und Technik'
});
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) {
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 syncStorageToggles() {
const enabled = isProgressStorageEnabled();
document.querySelectorAll('.progress-storage-toggle').forEach(toggle => {
toggle.checked = enabled;
});
document.querySelectorAll('.cookie-toggle').forEach(toggle => {
toggle.checked = false;
});
}
function showMenu() {
title.textContent = 'Informationen';
menu.hidden = false;
backButton.hidden = true;
document.querySelectorAll('[data-info-page]').forEach(page => {
page.hidden = true;
});
}
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;
title.textContent = pageTitles[name] ?? 'Informationen';
menu.hidden = true;
backButton.hidden = false;
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() {
if (dialog.open) return;
showMenu();
syncStorageToggles();
if (typeof dialog.showModal === 'function') dialog.showModal();
else dialog.setAttribute('open', '');
closeButton.focus();
}
function closeDialog() {
if (typeof dialog.close === 'function') dialog.close();
else dialog.removeAttribute('open');
infoButton.focus();
}
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 }
}));
});
});
}
activate(infoButton, openDialog);
activate(closeButton, closeDialog);
activate(backButton, showMenu);
document.querySelectorAll('[data-info-target]').forEach(button => {
activate(button, () => showPage(button.dataset.infoTarget));
});
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: openDialog, close: closeDialog, syncStorageToggles });
}(globalThis.Wegwichtel ??= {}));