// Shared dark-mode toggle. // Usage: at the end of . // Set . Default = light. // // Self-contained: the toggle works even if this file fails to load, // because the button's inline onclick uses WFTheme.cycle() which is // defined here AND duplicated as a fallback. We also listen for // storage events so the theme stays in sync across tabs. (function(){ const KEY = 'wfsales.theme'; const html = document.documentElement; function systemPref(){ return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; } function apply(){ const saved = localStorage.getItem(KEY) || 'light'; const v = saved === 'auto' ? systemPref() : saved; html.setAttribute('data-theme', v); document.querySelectorAll('[data-theme-toggle]').forEach(btn => { btn.setAttribute('data-current', saved); btn.setAttribute('aria-pressed', saved === 'dark' ? 'true' : 'false'); }); } // API function setTheme(v){ if(!['light','dark','auto'].includes(v)) v = 'light'; try{ localStorage.setItem(KEY, v); }catch(_){} apply(); } function currentTheme(){ try{ return localStorage.getItem(KEY) || 'light'; }catch(_){ return 'light'; } } function cycleTheme(){ const cur = currentTheme(); setTheme(cur === 'light' ? 'dark' : cur === 'dark' ? 'auto' : 'light'); } // Expose as early as possible so the button's inline onclick (which // guards on window.WFTheme) never sees undefined. window.WFTheme = { set: setTheme, current: currentTheme, cycle: cycleTheme }; // Also: install a global fallback so clicking the button works even // if this script never executes (network blocked, SW serving a stale // version, etc.). The button's inline onclick calls // (WFTheme && WFTheme.cycle || fallback)() — we register a global // __wfCycle function so the click always works. window.__wfCycle = cycleTheme; function onThemeTap(e){ let el = e.target; while(el && el !== document){ if(el.matches && el.matches('[data-theme-toggle]')){ e.preventDefault(); e.stopPropagation(); cycleTheme(); return; } el = el.parentNode; } } // Capture phase + both click and pointerup so the handler runs even // if a deeper listener calls stopPropagation(), and so the toggle // works on touch devices where the SVG is the actual target. document.addEventListener('click', onThemeTap, true); document.addEventListener('pointerup', onThemeTap, true); // Also bind directly to any current or future theme buttons function bindAll(){ document.querySelectorAll('[data-theme-toggle]').forEach(btn=>{ if(btn.__wfBound) return; btn.__wfBound = true; btn.addEventListener('click', e=>{ e.preventDefault(); e.stopPropagation(); cycleTheme(); }); }); } bindAll(); // Watch for new buttons (e.g. drawer-injected HTML) and bind them try{ new MutationObserver(bindAll).observe(document.body, {childList:true, subtree:true}); }catch(_){} // React to system change when in auto mode if(window.matchMedia){ try{ window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', ()=>{ if((currentTheme()) === 'auto') apply(); }); }catch(_){} } // Apply the saved theme on load apply(); // Cross-tab sync: if the user toggles in one tab, other tabs update window.addEventListener('storage', e=>{ if(e.key === KEY) apply(); }); })();