document.addEventListener('DOMContentLoaded', function () { // ───────────────────────────────────────────────────────────────────────────── // 1) Przycisk otwierający panel // ───────────────────────────────────────────────────────────────────────────── const accessBtn = document.createElement('div'); accessBtn.id = 'accessibility-button'; accessBtn.setAttribute('aria-label', 'Opcje dostępności'); accessBtn.innerHTML = ` `; document.body.appendChild(accessBtn); // ───────────────────────────────────────────────────────────────────────────── // 2) Panel opcji dostępności // ───────────────────────────────────────────────────────────────────────────── const accessPanel = document.createElement('div'); accessPanel.id = 'accessibility-panel'; accessPanel.innerHTML = `
Opcje dostępności
`; document.body.appendChild(accessPanel); // ───────────────────────────────────────────────────────────────────────────── // 3) Referencje // ───────────────────────────────────────────────────────────────────────────── const panel = document.getElementById('accessibility-panel'); const contrastBtn = document.getElementById('toggle-contrast'); const blueFilterBtn = document.getElementById('toggle-blue-filter'); const grayscaleBtn = document.getElementById('toggle-grayscale'); const imagesBtn = document.getElementById('toggle-images'); const cursorBtn = document.getElementById('toggle-cursor'); const fontIncBtn = document.getElementById('font-inc'); const fontDecBtn = document.getElementById('font-dec'); const fontResetBtn = document.getElementById('font-reset'); // ───────────────────────────────────────────────────────────────────────────── // 4) Panel open/close // ───────────────────────────────────────────────────────────────────────────── accessBtn.addEventListener('click', () => panel.classList.toggle('open')); document.getElementById('close-accessibility-panel').addEventListener('click', () => panel.classList.remove('open')); // ───────────────────────────────────────────────────────────────────────────── // 5) Helpery etykiet // ───────────────────────────────────────────────────────────────────────────── function updateContrastButton() { const isOn = document.body.classList.contains('high-contrast'); contrastBtn.textContent = isOn ? 'Wyłącz wysoki kontrast' : 'Włącz wysoki kontrast'; } function updateBlueFilterButton() { const isOn = !!document.getElementById('eyeAble-Bluefilter'); blueFilterBtn.textContent = isOn ? 'Wyłącz filtr niebieski' : 'Włącz filtr niebieski'; } function updateGrayscaleButton() { const isOn = document.documentElement.classList.contains('grayscale'); grayscaleBtn.textContent = isOn ? 'Wyłącz tryb szarości' : 'Włącz tryb szarości'; } function updateImagesButton() { const isOn = document.body.classList.contains('hide-images'); imagesBtn.textContent = isOn ? 'Pokaż obrazy' : 'Ukryj obrazy'; } function updateCursorButton() { const isOn = document.body.classList.contains('big-cursor'); cursorBtn.textContent = isOn ? 'Przywróć kursor' : 'Zwiększ kursor'; } // ───────────────────────────────────────────────────────────────────────────── // 6) Akcje – istniejące // ───────────────────────────────────────────────────────────────────────────── contrastBtn.addEventListener('click', () => { document.body.classList.toggle('high-contrast'); localStorage.setItem('highContrast', document.body.classList.contains('high-contrast') ? '1' : '0'); updateContrastButton(); }); blueFilterBtn.addEventListener('click', () => { const shader = document.getElementById('eyeAble-Bluefilter'); if (shader) { shader.remove(); localStorage.setItem('blueFilter', '0'); } else { const s = document.createElement('eye-able-shader'); s.id = 'eyeAble-Bluefilter'; s.setAttribute('aria-hidden', 'true'); s.style.cssText = ` background: rgba(255, 147, 41, 0.43); z-index: 2147483646; margin: 0; border-radius: 0; padding: 0; pointer-events: none; position: fixed; top: -10%; right: -10%; bottom: -10%; left: -10%; width: auto; height: auto; mix-blend-mode: multiply; display: block !important;`; document.body.appendChild(s); localStorage.setItem('blueFilter', '1'); } updateBlueFilterButton(); }); grayscaleBtn.addEventListener('click', () => { document.documentElement.classList.toggle('grayscale'); localStorage.setItem('grayscaleMode', document.documentElement.classList.contains('grayscale') ? '1' : '0'); updateGrayscaleButton(); }); imagesBtn.addEventListener('click', () => { document.body.classList.toggle('hide-images'); localStorage.setItem('hideImages', document.body.classList.contains('hide-images') ? '1' : '0'); updateImagesButton(); }); // ───────────────────────────────────────────────────────────────────────────── // 7) DUŻY KURSOR – overlay z INLINE SVG (CSP-safe), z aktywacją po pierwszym ruchu // ───────────────────────────────────────────────────────────────────────────── let overlay = null; //
let mmHandler = null; let leaveHandler = null; let enteredOnce = false; const ARROW_SVG = ` `; const HAND_SVG = ` `; const CURSORS = { arrow: { svg: ARROW_SVG, hotspotX: 6, hotspotY: 6 }, hand: { svg: HAND_SVG, hotspotX: 26, hotspotY: 12 }, }; let current = 'arrow'; function ensureOverlay() { if (overlay) return; overlay = document.createElement('div'); overlay.id = 'a11y-cursor'; overlay.setAttribute('aria-hidden', 'true'); overlay.style.cssText = [ 'position:fixed','left:0','top:0','width:48px','height:48px', 'pointer-events:none','z-index:2147483647','transform:translate3d(-9999px,-9999px,0)', 'opacity:1','display:block' ].join(';'); document.body.appendChild(overlay); } function setOverlay(name){ current = name; if (!overlay) return; overlay.innerHTML = CURSORS[name].svg; } function moveOverlay(x,y){ if(!overlay) return; const c = CURSORS[current]; overlay.style.transform = `translate3d(${x-c.hotspotX}px, ${y-c.hotspotY}px, 0)`; } function isInteractive(el){ return el && el.closest && el.closest('a[href],button,[role="button"],input[type="button"],input[type="submit"],input[type="checkbox"],input[type="radio"],select,.clickable,[onclick]'); } function isTextInput(el){ return el && el.closest && el.closest('input[type="text"],input[type="search"],input[type="email"],input[type="url"],input[type="tel"],input[type="password"],textarea,[contenteditable=""],[contenteditable="true"]'); } function enableBigCursor(){ ensureOverlay(); setOverlay('arrow'); document.body.classList.add('big-cursor'); document.body.classList.remove('big-cursor-active'); enteredOnce = false; mmHandler = (e)=>{ const t = e.target; if (!enteredOnce) { enteredOnce = true; document.body.classList.add('big-cursor-active'); } if (isTextInput(t)) { overlay.style.opacity = '0'; return; } else { overlay.style.opacity = '1'; } setOverlay(isInteractive(t) ? 'hand' : 'arrow'); moveOverlay(e.clientX, e.clientY); }; leaveHandler = ()=>{ if(overlay) overlay.style.opacity='0'; }; document.addEventListener('mousemove', mmHandler); document.addEventListener('mouseleave', leaveHandler); } function disableBigCursor(){ document.body.classList.remove('big-cursor','big-cursor-active'); if (mmHandler) document.removeEventListener('mousemove', mmHandler); if (leaveHandler) document.removeEventListener('mouseleave', leaveHandler); mmHandler = leaveHandler = null; if (overlay) { overlay.remove(); overlay = null; } } cursorBtn.addEventListener('click', () => { const willEnable = !document.body.classList.contains('big-cursor'); localStorage.setItem('bigCursor', willEnable ? '1' : '0'); if (willEnable) enableBigCursor(); else disableBigCursor(); updateCursorButton(); }); // ───────────────────────────────────────────────────────────────────────────── // 8) KONTROLKI ROZMIARU CZCIONKI – działają także dla px w CSS // ───────────────────────────────────────────────────────────────────────────── const FONT_STEP = 0.1; // 10% const FONT_MIN = 0.7; // 70% const FONT_MAX = 2.0; // 200% const TEXT_SELECTOR = 'h1,h2,h3,h4,h5,h6,p,span,li,dt,dd,blockquote,figcaption,small,strong,em,mark,code,pre,a,button,input,select,textarea,label,td,th,caption,legend'; function clamp(n, min, max){ return Math.min(Math.max(n, min), max); } function getScale(){ const v = parseFloat(localStorage.getItem('a11yFontScale') || '1'); return isNaN(v) ? 1 : clamp(v, FONT_MIN, FONT_MAX); } function updateFontButtons(){ const s = getScale(); fontDecBtn.disabled = s <= FONT_MIN; fontIncBtn.disabled = s >= FONT_MAX; fontResetBtn.textContent = s === 1 ? 'A' : `A (${Math.round(s*100)}%)`; } function applyFontScale(scale){ const nodes = document.querySelectorAll(TEXT_SELECTOR); nodes.forEach(el => { if (el.closest('#accessibility-panel, #accessibility-button')) return; // nie skaluj UI panelu if (el.hasAttribute('data-no-scale')) return; // wykluczenia opcjonalne let base = el.getAttribute('data-font-base'); if (!base) { const comp = getComputedStyle(el).fontSize; const px = parseFloat(comp); if (!isNaN(px) && px > 0) { el.setAttribute('data-font-base', String(px)); base = String(px); } } if (base) { const b = parseFloat(base); if (scale === 1) { el.style.removeProperty('font-size'); } else { const newPx = Math.round(b * scale * 100) / 100; el.style.setProperty('font-size', newPx + 'px', 'important'); } } }); } function setScale(s){ s = clamp(s, FONT_MIN, FONT_MAX); localStorage.setItem('a11yFontScale', String(s)); applyFontScale(s); updateFontButtons(); } fontIncBtn.addEventListener('click', () => setScale(getScale() + FONT_STEP)); fontDecBtn.addEventListener('click', () => setScale(getScale() - FONT_STEP)); fontResetBtn.addEventListener('click', () => setScale(1)); // ───────────────────────────────────────────────────────────────────────────── // 9) Przywracanie stanu // ───────────────────────────────────────────────────────────────────────────── if (localStorage.getItem('highContrast') === '1') document.body.classList.add('high-contrast'); if (localStorage.getItem('blueFilter') === '1') { const s = document.createElement('eye-able-shader'); s.id = 'eyeAble-Bluefilter'; s.setAttribute('aria-hidden','true'); s.style.cssText = 'background:rgba(255,147,41,0.43);z-index:2147483646;margin:0;border-radius:0;padding:0;pointer-events:none;position:fixed;top:-10%;right:-10%;bottom:-10%;left:-10%;width:auto;height:auto;mix-blend-mode:multiply;display:block!important;'; document.body.appendChild(s); } if (localStorage.getItem('grayscaleMode') === '1') document.documentElement.classList.add('grayscale'); if (localStorage.getItem('hideImages') === '1') document.body.classList.add('hide-images'); if (localStorage.getItem('bigCursor') === '1') { enableBigCursor(); } // Zastosuj i zainicjuj przyciski rozmiaru czcionki setScale(getScale()); // ───────────────────────────────────────────────────────────────────────────── // 10) Inicjalizacja etykiet // ───────────────────────────────────────────────────────────────────────────── updateContrastButton(); updateBlueFilterButton(); updateGrayscaleButton(); updateImagesButton(); updateCursorButton(); updateFontButtons(); });