(function () { var root = document.documentElement; var storedMode = localStorage.getItem('neon-mode') || 'dark'; var storedAccent = localStorage.getItem('neon-accent') || 'green'; var modeButton = document.querySelector('[data-mode-toggle]'); var accentButtons = document.querySelectorAll('[data-accent]'); function labelAccent(accent) { return accent.charAt(0).toUpperCase() + accent.slice(1); } function applyMode(mode) { root.dataset.mode = mode; localStorage.setItem('neon-mode', mode); if (modeButton) { modeButton.textContent = 'Light'; modeButton.setAttribute('aria-label', 'Light mode'); modeButton.setAttribute('aria-pressed', mode === 'light' ? 'true' : 'false'); modeButton.title = mode === 'light' ? 'Disable light mode' : 'Enable light mode'; } } function applyAccent(accent) { root.dataset.accent = accent; localStorage.setItem('neon-accent', accent); accentButtons.forEach(function (button) { var selected = button.dataset.accent === accent; var name = labelAccent(button.dataset.accent || 'accent'); button.setAttribute('aria-pressed', selected ? 'true' : 'false'); button.setAttribute('aria-label', selected ? name + ' accent selected' : 'Use ' + name.toLowerCase() + ' accent'); button.title = selected ? name + ' accent selected' : 'Use ' + name.toLowerCase() + ' accent'; }); } applyMode(storedMode); applyAccent(storedAccent); if (modeButton) { modeButton.addEventListener('click', function () { applyMode(root.dataset.mode === 'light' ? 'dark' : 'light'); }); } accentButtons.forEach(function (button) { button.addEventListener('click', function () { applyAccent(button.dataset.accent || 'green'); }); }); function initLightbox() { var article = document.querySelector('.article .markdown'); if (!article) { return; } var imageExtensions = /\.(avif|gif|jpe?g|png|webp|bmp|svg)(?:[?#].*)?$/i; var videoExtensions = /\.(m4v|mov|mp4|ogv|ogg|webm)(?:[?#].*)?$/i; var entries = []; var currentIndex = 0; var lastFocus = null; var lightbox = null; var mediaFrame = null; var titleNode = null; var countNode = null; var captionNode = null; var closeButton = null; function mediaTitle(element, fallback) { return element.getAttribute('alt') || element.getAttribute('title') || element.getAttribute('aria-label') || fallback || 'Media'; } function isVideoIframe(iframe, src) { return Boolean(iframe.closest('.shortcode-embed')) || /(?:youtube\.com|youtu\.be|vimeo\.com|player\.vimeo|lone-embed\.php)/i.test(src) || videoExtensions.test(src); } function addEntry(entry) { if (!entry.src) { return; } entries.push(entry); } article.querySelectorAll('img, video, iframe, a[href]').forEach(function (element) { var tag = element.tagName.toLowerCase(); if (tag === 'img') { addEntry({ type: 'image', src: element.currentSrc || element.src || element.getAttribute('src'), title: mediaTitle(element, 'Image'), trigger: element }); return; } if (tag === 'video') { var source = element.currentSrc || element.getAttribute('src'); var sourceElement = source ? null : element.querySelector('source[src]'); if (!source && sourceElement) { source = sourceElement.getAttribute('src'); } addEntry({ type: 'video', src: source, poster: element.getAttribute('poster') || '', title: mediaTitle(element, 'Video'), trigger: element }); return; } if (tag === 'iframe') { var iframeSrc = element.getAttribute('src') || ''; if (isVideoIframe(element, iframeSrc)) { addEntry({ type: 'iframe', src: iframeSrc, title: mediaTitle(element, 'Video'), trigger: element }); } return; } if (element.querySelector('img, video, iframe')) { return; } var href = element.getAttribute('href') || ''; if (imageExtensions.test(href)) { addEntry({ type: 'image', src: href, title: mediaTitle(element, element.textContent.trim() || 'Image'), trigger: element }); } else if (videoExtensions.test(href)) { addEntry({ type: 'video', src: href, title: mediaTitle(element, element.textContent.trim() || 'Video'), trigger: element }); } }); if (!entries.length) { return; } function buildLightbox() { lightbox = document.createElement('div'); lightbox.className = 'tcms-lightbox'; lightbox.hidden = true; lightbox.setAttribute('role', 'dialog'); lightbox.setAttribute('aria-modal', 'true'); lightbox.setAttribute('aria-labelledby', 'tcms-lightbox-title'); lightbox.setAttribute('aria-describedby', 'tcms-lightbox-caption'); lightbox.innerHTML = [ '', '
', '
', '', '', '', '
', '
', '', '
', '', '
', '

', '
' ].join(''); document.body.appendChild(lightbox); mediaFrame = lightbox.querySelector('.tcms-lightbox__media'); titleNode = lightbox.querySelector('.tcms-lightbox__title'); countNode = lightbox.querySelector('.tcms-lightbox__count'); captionNode = lightbox.querySelector('.tcms-lightbox__caption'); closeButton = lightbox.querySelector('.tcms-lightbox__close'); lightbox.querySelectorAll('[data-lightbox-close]').forEach(function (button) { button.addEventListener('click', closeLightbox); }); lightbox.querySelector('[data-lightbox-previous]').addEventListener('click', function () { showLightboxItem(currentIndex - 1); }); lightbox.querySelector('[data-lightbox-next]').addEventListener('click', function () { showLightboxItem(currentIndex + 1); }); } function showLightboxItem(index) { if (!lightbox) { buildLightbox(); } if (index < 0) { index = entries.length - 1; } else if (index >= entries.length) { index = 0; } currentIndex = index; var entry = entries[currentIndex]; mediaFrame.textContent = ''; titleNode.textContent = entry.title; countNode.textContent = (currentIndex + 1) + ' of ' + entries.length; countNode.setAttribute('aria-label', 'Media ' + (currentIndex + 1) + ' of ' + entries.length); captionNode.textContent = entry.title; if (entry.type === 'image') { var image = document.createElement('img'); image.src = entry.src; image.alt = entry.title; image.addEventListener('click', function () { if (entries.length > 1) { showLightboxItem(currentIndex + 1); } }); mediaFrame.appendChild(image); return; } if (entry.type === 'video') { var video = document.createElement('video'); video.src = entry.src; video.controls = true; video.playsInline = true; video.preload = 'metadata'; video.setAttribute('aria-label', entry.title); if (entry.poster) { video.poster = entry.poster; } mediaFrame.appendChild(video); return; } var iframe = document.createElement('iframe'); iframe.src = entry.src; iframe.title = entry.title; iframe.loading = 'lazy'; iframe.allow = 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share'; iframe.allowFullscreen = true; mediaFrame.appendChild(iframe); } function openLightbox(index) { if (!lightbox) { buildLightbox(); } lastFocus = document.activeElement; lightbox.hidden = false; document.body.classList.add('tcms-lightbox-open'); showLightboxItem(index); closeButton.focus(); } function closeLightbox() { if (!lightbox || lightbox.hidden) { return; } lightbox.hidden = true; mediaFrame.textContent = ''; document.body.classList.remove('tcms-lightbox-open'); if (lastFocus && document.contains(lastFocus) && typeof lastFocus.focus === 'function') { lastFocus.focus(); } } function trapLightboxFocus(event) { var focusable; var first; var last; if (!lightbox || lightbox.hidden || event.key !== 'Tab') { return; } focusable = Array.prototype.slice.call(lightbox.querySelectorAll([ 'button:not([disabled])', 'a[href]', 'iframe', 'video[controls]', '[tabindex]:not([tabindex="-1"])' ].join(','))).filter(function (element) { return !element.hidden && element.offsetParent !== null; }); if (!focusable.length) { event.preventDefault(); closeButton.focus(); return; } first = focusable[0]; last = focusable[focusable.length - 1]; if (event.shiftKey && document.activeElement === first) { event.preventDefault(); last.focus(); } else if (!event.shiftKey && document.activeElement === last) { event.preventDefault(); first.focus(); } } function openFromEvent(event, index) { event.preventDefault(); event.stopPropagation(); openLightbox(index); } entries.forEach(function (entry, index) { var trigger = entry.trigger; var tag = trigger.tagName.toLowerCase(); var button; if (tag === 'img' || tag === 'a') { trigger.classList.add('tcms-lightboxable'); trigger.setAttribute('data-lightbox-index', String(index)); if (tag === 'img' && !trigger.hasAttribute('tabindex')) { trigger.setAttribute('tabindex', '0'); } if (tag === 'img') { trigger.setAttribute('role', 'button'); trigger.setAttribute('aria-label', 'Open image viewer for ' + entry.title); } trigger.setAttribute('aria-haspopup', 'dialog'); trigger.addEventListener('click', function (event) { openFromEvent(event, index); }); trigger.addEventListener('keydown', function (event) { if (event.key === 'Enter' || event.key === ' ') { openFromEvent(event, index); } }); return; } button = document.createElement('button'); button.className = 'tcms-lightbox-trigger'; button.type = 'button'; button.textContent = 'View'; button.setAttribute('aria-haspopup', 'dialog'); button.setAttribute('aria-label', 'Open media viewer for ' + entry.title); button.addEventListener('click', function (event) { openFromEvent(event, index); }); if (tag === 'iframe' && trigger.parentElement && trigger.parentElement.classList.contains('shortcode-embed')) { trigger.parentElement.classList.add('tcms-lightbox-host'); trigger.parentElement.appendChild(button); return; } trigger.insertAdjacentElement('afterend', button); }); document.addEventListener('keydown', function (event) { if (!lightbox || lightbox.hidden) { return; } if (event.key === 'Escape') { closeLightbox(); } else if (event.key === 'ArrowRight') { showLightboxItem(currentIndex + 1); } else if (event.key === 'ArrowLeft') { showLightboxItem(currentIndex - 1); } trapLightboxFocus(event); }); } initLightbox(); }());