(function () { // --- Utilities --- const qsa = (s, r=document) => Array.from(r.querySelectorAll(s)); const create = (tag, props={}) => Object.assign(document.createElement(tag), props); const decode = (s) => { try { return decodeURIComponent(s); } catch { return s; } }; let root = null, modal = null, head = null, title = null, closeBtn = null, content = null, lastFocus = null; let initialized = false; function ensureMounted() { if (initialized) return true; // Body might not be ready if script was placed in
without defer if (!document.body) return false; // Avoid double-mounts if this runs twice root = document.querySelector('.rt-lightbox-backdrop'); if (root) { // re-bind references if markup already exists (unlikely, but safe) modal = root.querySelector('.rt-lightbox'); head = root.querySelector('.rt-lightbox-head'); title = root.querySelector('#rt-lightbox-title') || root.querySelector('.rt-lightbox-title'); closeBtn = root.querySelector('.rt-lightbox-close'); content = root.querySelector('.rt-lightbox-content'); bindStatics(); initialized = true; return true; } // Build structure root = create('div', { className: 'rt-lightbox-backdrop', role: 'dialog', 'aria-modal': 'true', 'aria-hidden': 'true' }); modal = create('div', { className: 'rt-lightbox', role: 'document' }); head = create('div', { className: 'rt-lightbox-head' }); title = create('div', { className: 'rt-lightbox-title', id: 'rt-lightbox-title' }); closeBtn = create('button', { className: 'rt-lightbox-close', 'aria-label': 'Close lightbox', innerHTML: '×' }); content = create('div', { className: 'rt-lightbox-content' }); head.append(title, closeBtn); modal.append(head, content); root.append(modal); document.body.appendChild(root); bindStatics(); initialized = true; return true; } function bindStatics() { if (!root || !closeBtn) return; // Backdrop click root.addEventListener('click', (e) => { if (e.target === root) closeLightbox(); }); // Close button closeBtn.addEventListener('click', closeLightbox); } // Focus trap function trapFocus(e) { if (e.key !== 'Tab') return; const focusables = qsa('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])', root) .filter(el => !el.hasAttribute('disabled') && el.offsetParent !== null); if (!focusables.length) return; const first = focusables[0], last = focusables[focusables.length - 1]; if (e.shiftKey && document.activeElement === first) { last.focus(); e.preventDefault(); } else if (!e.shiftKey && document.activeElement === last) { first.focus(); e.preventDefault(); } } function onKeydown(e) { if (e.key === 'Escape') closeLightbox(); if (e.key === 'Tab') trapFocus(e); } function openLightbox({ html, url, titleText, aspect }) { // Ensure mount (lazy) if (!ensureMounted()) return; // body not ready yet; click again after DOM ready if (!content || !root) return; // additional guard // Title if (title) title.textContent = titleText || ''; // Aspect override content.classList.toggle('auto', (aspect || '').toLowerCase() === 'auto'); // Inject content content.innerHTML = html || buildIframe(url); // Show root.classList.add('is-open'); root.setAttribute('aria-hidden', 'false'); document.body.style.overflow = 'hidden'; // Focus management lastFocus = document.activeElement; (closeBtn || root).focus?.(); document.addEventListener('keydown', onKeydown); } function closeLightbox() { if (!root || !content) return; content.innerHTML = ''; // stop playback root.classList.remove('is-open'); root.setAttribute('aria-hidden', 'true'); document.body.style.overflow = ''; document.removeEventListener('keydown', onKeydown); if (lastFocus && lastFocus.focus) lastFocus.focus(); } // Delegated handler for any trigger with [data-lightbox] document.addEventListener('click', function (e) { const trigger = e.target.closest?.('[data-lightbox]'); if (!trigger) return; e.preventDefault(); const html = trigger.getAttribute('data-embed-html'); const targetSel = trigger.getAttribute('data-embed-target'); const url = trigger.getAttribute('data-embed-url') || trigger.getAttribute('data-video') || trigger.getAttribute('href') || trigger.getAttribute('data-src'); const titleText = trigger.getAttribute('data-lightbox-title') || trigger.getAttribute('aria-label') || trigger.textContent.trim(); const aspect = (trigger.getAttribute('data-lightbox-aspect') || '').toLowerCase(); if (html) { openLightbox({ html: decode(html), titleText, aspect }); return; } if (targetSel) { const srcEl = document.querySelector(targetSel); if (srcEl) { openLightbox({ html: srcEl.innerHTML, titleText, aspect }); return; } // If selector is wrong, fail back to URL if present } if (url) { openLightbox({ url, titleText, aspect }); return; } // Nothing usable found — no-op }, true); // If script loaded early, finalize mount at DOM ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', ensureMounted, { once: true }); } else { ensureMounted(); } // Build iframe embed from a URL (YouTube, Vimeo, Loom, Wistia basic) function buildIframe(rawUrl) { if (!rawUrl) return ''; let u = String(rawUrl); try { const url = new URL(rawUrl, window.location.href); u = url.toString(); } catch { /* leave u as-is */ } // YouTube if (/youtu\.be|youtube\.com/.test(u)) { let id = null; try { const url = new URL(u); if (url.hostname.includes('youtu.be')) id = url.pathname.replace('/', ''); else id = url.searchParams.get('v'); } catch { /* ignore */ } if (id) { const src = `https://www.youtube-nocookie.com/embed/${id}?autoplay=1&rel=0&modestbranding=1`; return ``; } } // Vimeo if (/vimeo\.com/.test(u)) { const id = (u.match(/vimeo\.com\/(?:video\/)?(\d+)/) || [])[1]; if (id) { const src = `https://player.vimeo.com/video/${id}?autoplay=1&title=0&byline=0&portrait=0`; return ``; } } // Loom if (/loom\.com/.test(u)) { const m = u.match(/share\/([a-z0-9]+)/i); const id = m ? m[1] : ''; if (id) { const src = `https://www.loom.com/embed/${id}?autoplay=1`; return ``; } } // Wistia if (/wistia\./.test(u)) { const m = u.match(/(?:medias|embed)\/([a-z0-9]+)/i); const id = m ? m[1] : ''; if (id) { const src = `https://fast.wistia.net/embed/iframe/${id}?autoPlay=true`; return ``; } } // Fallback: raw URL in an iframe return ``; } })();