function hookUploadSpinners() { document.querySelectorAll('form[data-spins="true"]').forEach((form) => { form.addEventListener('submit', () => { const btn = form.querySelector('[data-submit]'); const box = form.querySelector('[data-processing]'); if (btn) { btn.classList.add('is-loading'); btn.disabled = true; } if (box) { box.classList.remove('is-hidden'); } }); }); } function hookNavbarBurger() { const burgers = Array.from(document.querySelectorAll('.navbar-burger')); burgers.forEach((b) => { b.addEventListener('click', () => { const targetId = b.dataset.target; const target = targetId ? document.getElementById(targetId) : null; b.classList.toggle('is-active'); if (target) target.classList.toggle('is-active'); }); }); // Close the menu after tapping a link on mobile document.querySelectorAll('.navbar-menu a.navbar-item').forEach((a) => { a.addEventListener('click', () => { burgers.forEach((b) => { const targetId = b.dataset.target; const target = targetId ? document.getElementById(targetId) : null; b.classList.remove('is-active'); if (target) target.classList.remove('is-active'); }); }); }); } function hookResponsiveTables() { document.querySelectorAll('table').forEach((tbl) => { // Skip tables explicitly opted out if (tbl.dataset && tbl.dataset.responsive === '0') return; const ths = Array.from(tbl.querySelectorAll('thead th')) .map((th) => (th.textContent || '').trim()) .filter((t) => t.length > 0); if (!ths.length) return; tbl.classList.add('alpr-responsive-table'); tbl.querySelectorAll('tbody tr').forEach((tr) => { const cells = Array.from(tr.children); cells.forEach((cell, idx) => { if (!cell || !cell.tagName) return; if (cell.tagName.toLowerCase() !== 'td') return; if (!cell.getAttribute('data-label')) { cell.setAttribute('data-label', ths[idx] || ''); } }); }); }); } function hookConfirmActions() { // Add data-confirm="..." on a form to show a confirm dialog before submit. document.querySelectorAll('form[data-confirm]').forEach((form) => { form.addEventListener('submit', (e) => { const msg = form.getAttribute('data-confirm'); if (msg && !window.confirm(msg)) { e.preventDefault(); e.stopPropagation(); } }); }); } function hookActiveNav() { const path = (window.location && window.location.pathname) ? window.location.pathname : ''; const items = Array.from(document.querySelectorAll('.alpr-navbar a.navbar-item')); if (!items.length) return; function score(href) { if (!href) return 0; try { // href can be relative const u = new URL(href, window.location.origin); const p = u.pathname; if (p === '/' && path === '/') return 100; if (p !== '/' && path.startsWith(p)) return 80 + Math.min(p.length, 19); return 0; } catch (e) { return 0; } } let best = null; let bestScore = 0; items.forEach((a) => { const s = score(a.getAttribute('href')); if (s > bestScore) { bestScore = s; best = a; } }); if (best) best.classList.add('is-active'); } function hookCopyButtons() { document.querySelectorAll('[data-copy]').forEach((btn) => { btn.addEventListener('click', async () => { const targetId = btn.getAttribute('data-copy'); const el = targetId ? document.getElementById(targetId) : null; if (!el) return; const text = el.textContent || ''; try { await navigator.clipboard.writeText(text); const prev = btn.textContent; btn.textContent = 'Copiado ✓'; setTimeout(() => { btn.textContent = prev; }, 900); } catch (e) { // fallback const ta = document.createElement('textarea'); ta.value = text; document.body.appendChild(ta); ta.select(); try { document.execCommand('copy'); } catch (e2) {} document.body.removeChild(ta); } }); }); } function hookTableFilters() { document.querySelectorAll('[data-table-filter]').forEach((input) => { const key = input.getAttribute('data-table-filter'); const table = key ? document.querySelector('table[data-filter-table="' + key + '"]') : null; if (!table) return; const rows = Array.from(table.querySelectorAll('tbody tr')); input.addEventListener('input', () => { const q = (input.value || '').trim().toLowerCase(); rows.forEach((tr) => { const t = (tr.textContent || '').toLowerCase(); tr.style.display = (!q || t.includes(q)) ? '' : 'none'; }); }); }); } document.addEventListener('DOMContentLoaded', () => { hookUploadSpinners(); hookNavbarBurger(); hookResponsiveTables(); hookConfirmActions(); hookActiveNav(); hookCopyButtons(); hookTableFilters(); hookThemeToggle(); hookCookieBanner(); }); function hookThemeToggle() { const btn = document.querySelector('[data-theme-toggle]'); if (!btn) return; const label = btn.querySelector('[data-theme-label]'); function currentTheme() { return document.documentElement.getAttribute('data-theme') || 'light'; } function apply(theme) { document.documentElement.setAttribute('data-theme', theme); try { localStorage.setItem('theme', theme); } catch (e) {} // store current theme on button so CSS can flip icons btn.setAttribute('data-theme-current', theme); if (label) label.textContent = theme === 'dark' ? 'Escuro' : 'Claro'; btn.setAttribute('aria-label', theme === 'dark' ? 'Tema: Escuro (mudar para Claro)' : 'Tema: Claro (mudar para Escuro)'); } // Sync UI (layout may set initial theme before DOM ready) apply(currentTheme()); btn.addEventListener('click', () => { const next = currentTheme() === 'dark' ? 'light' : 'dark'; apply(next); }); } // Prompt for password and reveal an API key (server will re-render Account page with the revealed key) function revealKey(keyId) { const pwd = window.prompt('Password para revelar esta chave:'); if (!pwd) return; const form = document.createElement('form'); form.method = 'POST'; form.action = '/ui/account/reveal_key'; const i1 = document.createElement('input'); i1.type = 'hidden'; i1.name = 'key_id'; i1.value = keyId; const i2 = document.createElement('input'); i2.type = 'hidden'; i2.name = 'password'; i2.value = pwd; form.appendChild(i1); form.appendChild(i2); document.body.appendChild(form); form.submit(); } function hookCookieBanner() { const banner = document.querySelector('[data-cookie-banner]'); if (!banner) return; try { const saved = localStorage.getItem('cookie_consent'); if (!saved) { banner.hidden = false; } const accept = banner.querySelector('[data-cookie-accept]'); const decline = banner.querySelector('[data-cookie-decline]'); if (accept) { accept.addEventListener('click', () => { localStorage.setItem('cookie_consent', 'accepted'); banner.hidden = true; // If you add analytics later, gate loading on this value. }); } if (decline) { decline.addEventListener('click', () => { localStorage.setItem('cookie_consent', 'essential'); banner.hidden = true; }); } } catch (e) { // if storage blocked, just hide banner.hidden = true; } }