/** * Bongoto – Header JS (CLEAN FINAL) * - Drawer (ESC, click-outside, focus trap, scroll lock) * - Sticky shadow * - Desktop search dropdown * - Upload license modal (Get Pro) */ (function () { 'use strict'; /* ----------------- Helpers ----------------- */ const qs = (s, r = document) => r.querySelector(s); const qsa = (s, r = document) => Array.from(r.querySelectorAll(s)); const on = (t, h, o = document, opt = { passive: true }) => o.addEventListener(t, h, opt); const isDesktop = () => matchMedia('(min-width: 769px)').matches; const FOCUSABLE = 'a,button,input,select,textarea,[tabindex]:not([tabindex="-1"])'; /* ----------------- Drawer ------------------ */ const drawer = qs('.bt-drawer'); const drawerInner = drawer ? qs('.bt-drawer-inner', drawer) : null; const toggleBtn = qs('.bt-menu-toggle'); const closeBtn = qs('.bt-drawer-close'); let scrollY = 0, lastFocus = null; const lockScroll = () => { scrollY = window.scrollY || 0; Object.assign(document.body.style, { position: 'fixed', top: `-${scrollY}px`, left: '0', right: '0', width: '100%' }); }; const unlockScroll = () => { Object.assign(document.body.style, { position: '', top: '', left: '', right: '', width: '' }); window.scrollTo(0, scrollY); }; const openDrawer = () => { if (!drawer) return; lastFocus = document.activeElement; drawer.classList.add('is-open'); drawer.setAttribute('aria-hidden', 'false'); toggleBtn?.setAttribute('aria-expanded', 'true'); lockScroll(); drawerInner?.querySelector(FOCUSABLE)?.focus?.(); }; const closeDrawer = () => { if (!drawer) return; drawer.classList.remove('is-open'); drawer.setAttribute('aria-hidden', 'true'); toggleBtn?.setAttribute('aria-expanded', 'false'); unlockScroll(); (toggleBtn || lastFocus)?.focus?.(); }; // Init ARIA if (drawer && !drawer.hasAttribute('aria-hidden')) drawer.setAttribute('aria-hidden', 'true'); if (toggleBtn && !toggleBtn.hasAttribute('aria-controls')) toggleBtn.setAttribute('aria-controls', drawer?.id || 'bt-drawer'); // Open / Close triggers toggleBtn?.addEventListener('click', (e) => { e.preventDefault(); openDrawer(); }); closeBtn?.addEventListener('click', (e) => { e.preventDefault(); closeDrawer(); }); // Outside click closes on('click', (e) => { if (!drawer?.classList.contains('is-open')) return; const inside = drawerInner?.contains(e.target); const onToggle = toggleBtn?.contains(e.target); if (!inside && !onToggle) closeDrawer(); }); // Inside link click closes drawerInner?.addEventListener('click', (e) => { if (e.target.closest('a')) closeDrawer(); }); // Keyboard (ESC + focus trap) document.addEventListener('keydown', (e) => { if (!drawer?.classList.contains('is-open')) return; if (e.key === 'Escape') { e.preventDefault(); closeDrawer(); return; } if (e.key !== 'Tab') return; const focusables = qsa(FOCUSABLE, drawer); if (!focusables.length) return; const first = focusables[0], last = focusables.at(-1); if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); } else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); } }); // Auto-close on desktop resize on('resize', () => { if (isDesktop() && drawer?.classList.contains('is-open')) closeDrawer(); }, window); // Prevent iOS rubber-band inside drawer drawerInner?.addEventListener('touchmove', (e) => { const el = e.currentTarget; const atTop = el.scrollTop === 0; const atBottom = el.scrollHeight - el.scrollTop === el.clientHeight; if ((atTop && e.touches[0].clientY > 0) || atBottom) e.preventDefault(); }, { passive: false }); /* --------------- Sticky Shadow --------------- */ const header = qs('.bt-header'); if (header?.classList.contains('bt-header--sticky')) { const applyShadow = () => header.classList.toggle('bt-header-scrolled', window.scrollY > 10); applyShadow(); on('scroll', applyShadow, window); } /* -------- Desktop Search Dropdown ---------- */ const closeAllSearchDropdowns = () => { qsa('.bt-search-dropdown').forEach((dd) => { dd.hidden = true; dd.closest('.bt-header-search')?.querySelector('.bt-search-toggle')?.setAttribute('aria-expanded', 'false'); }); }; qsa('.bt-search-toggle').forEach((btn) => { btn.setAttribute('aria-expanded', 'false'); btn.addEventListener('click', (e) => { e.preventDefault(); if (!isDesktop()) return; const host = btn.closest('.bt-header-search'); const dropdown = host?.querySelector('.bt-search-dropdown'); if (!dropdown) return; const willOpen = dropdown.hidden; closeAllSearchDropdowns(); if (willOpen) { dropdown.hidden = false; btn.setAttribute('aria-expanded', 'true'); dropdown.querySelector('input[type="search"],input[type="text"]')?.focus?.(); } }); }); document.addEventListener('click', (e) => { if (!isDesktop()) return; const inside = e.target.closest('.bt-search-dropdown, .bt-search-toggle'); if (!inside) closeAllSearchDropdowns(); }, { passive: true }); document.addEventListener('keydown', (e) => { if (!isDesktop()) return; if (e.key === 'Escape') closeAllSearchDropdowns(); }); /* -------- Upload License Modal (Pro) -------- */ document.addEventListener('DOMContentLoaded', () => { const uploadBtn = qs('.bt-header-upload a.upload-btn'); if (!uploadBtn) return; // Only create once let modal = qs('#bongoto-pro-modal'); if (!modal) { modal = document.createElement('div'); modal.id = 'bongoto-pro-modal'; modal.style.cssText = 'display:none;position:fixed;inset:0;background:rgba(0,0,0,.6);z-index:9999;align-items:center;justify-content:center;'; modal.innerHTML = `

Bongoto Pro Required

Please activate Bongoto Pro license to use this feature.

`; document.body.appendChild(modal); } const showModal = () => { modal.style.display = 'flex'; }; const hideModal = () => { modal.style.display = 'none'; }; modal.querySelector('#bongoto-close-pro')?.addEventListener('click', hideModal); modal.querySelector('#bongoto-get-pro')?.addEventListener('click', () => { hideModal(); window.open('https://bongoto.com/product/bongoto-pro-digital-marketplace-addon/', '_blank'); }); uploadBtn.addEventListener('click', (e) => { e.preventDefault(); // Detect Pro (body class/localized flag) const isProActive = document.body.classList.contains('bongoto-pro-active') || window.BONGOTO_PRO_ACTIVE === true; if (isProActive) { window.location.href = uploadBtn.href; } else { showModal(); } }); }); })();