(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();
}());