/** * Barrierefreiheits-Manager: Panel mit 4 Schaltern (Kontrast, Focus, Schrift, Animationen). * * Liegt seit 2026-07-21 in einer eigenen Datei, weil ihn beide Oberflaechen brauchen, * das klassische Dashboard (.header-right) und die Studio-Ansicht (.studio-top-right). * Vorher steckte er in app.js und fehlte im Studio deshalb komplett. */ const A11yManager = { _key: 'osint_a11y', _isOpen: false, _settings: { contrast: false, focus: false, fontsize: false, motion: false }, init() { // Einstellungen aus localStorage laden try { const saved = JSON.parse(localStorage.getItem(this._key) || '{}'); Object.keys(this._settings).forEach(k => { if (typeof saved[k] === 'boolean') this._settings[k] = saved[k]; }); } catch (e) { /* Ungültige Daten ignorieren */ } // Button + Panel dynamisch in die Kopfzeile einfügen (vor Theme-Toggle). // Beide Oberflaechen benennen ihren rechten Kopfzeilen-Block anders. const headerRight = document.querySelector('.header-right, .studio-top-right'); const themeToggle = document.getElementById('theme-toggle'); if (!headerRight) return; if (document.getElementById('a11y-btn')) return; // schon vorhanden const container = document.createElement('div'); container.className = 'a11y-center'; container.innerHTML = `
`; if (themeToggle && themeToggle.parentNode === headerRight) { headerRight.insertBefore(container, themeToggle); } else { headerRight.prepend(container); } // Toggle-Event-Listener ['contrast', 'focus', 'fontsize', 'motion'].forEach(key => { document.getElementById('a11y-' + key).addEventListener('change', () => this.toggle(key)); }); // Button öffnet/schließt Panel document.getElementById('a11y-btn').addEventListener('click', (e) => { e.stopPropagation(); this._isOpen ? this._closePanel() : this._openPanel(); }); // Klick außerhalb schließt Panel document.addEventListener('click', (e) => { if (this._isOpen && !container.contains(e.target)) { this._closePanel(); } }); // Keyboard: Esc schließt, Pfeiltasten navigieren container.addEventListener('keydown', (e) => { if (e.key === 'Escape' && this._isOpen) { e.stopPropagation(); this._closePanel(); return; } if (!this._isOpen) return; if (e.key === 'ArrowDown' || e.key === 'ArrowUp') { e.preventDefault(); const options = Array.from(document.querySelectorAll('.a11y-option input[type="checkbox"]')); const idx = options.indexOf(document.activeElement); let next; if (e.key === 'ArrowDown') { next = idx < options.length - 1 ? idx + 1 : 0; } else { next = idx > 0 ? idx - 1 : options.length - 1; } options[next].focus(); } }); // Einstellungen anwenden + Checkboxen synchronisieren this._apply(); this._syncUI(); }, toggle(key) { this._settings[key] = !this._settings[key]; this._apply(); this._syncUI(); this._save(); }, _apply() { const root = document.documentElement; Object.keys(this._settings).forEach(k => { if (this._settings[k]) { root.setAttribute('data-a11y-' + k, 'true'); } else { root.removeAttribute('data-a11y-' + k); } }); }, _syncUI() { Object.keys(this._settings).forEach(k => { const cb = document.getElementById('a11y-' + k); if (cb) cb.checked = this._settings[k]; }); }, _save() { localStorage.setItem(this._key, JSON.stringify(this._settings)); }, _openPanel() { this._isOpen = true; document.getElementById('a11y-panel').style.display = ''; document.getElementById('a11y-btn').setAttribute('aria-expanded', 'true'); // Fokus auf erste Option setzen requestAnimationFrame(() => { const first = document.querySelector('.a11y-option input[type="checkbox"]'); if (first) first.focus(); }); }, _closePanel() { this._isOpen = false; document.getElementById('a11y-panel').style.display = 'none'; const btn = document.getElementById('a11y-btn'); btn.setAttribute('aria-expanded', 'false'); btn.focus(); } };