1/** 2 * Lightbox for themed images 3 * Opens expanded view when clicking themed-image-container elements 4 */ 5(function () { 6 let overlay = null; 7 8 function open(container) { 9 var srcLight = container.getAttribute('data-lightbox-src-light'); 10 var srcDark = container.getAttribute('data-lightbox-src-dark'); 11 var alt = container.getAttribute('data-lightbox-alt') || ''; 12 13 // Build overlay 14 overlay = document.createElement('div'); 15 overlay.className = 'themed-image-lightbox'; 16 overlay.setAttribute('role', 'dialog'); 17 overlay.setAttribute('aria-label', alt); 18 19 var closeBtn = document.createElement('button'); 20 closeBtn.className = 'lightbox-close'; 21 closeBtn.setAttribute('aria-label', 'Close'); 22 closeBtn.innerHTML = '✕'; 23 24 var wrapper = document.createElement('div'); 25 wrapper.className = 'lightbox-image-wrapper'; 26 27 var imgLight = document.createElement('img'); 28 imgLight.src = srcLight; 29 imgLight.alt = alt; 30 imgLight.className = 'lightbox-light'; 31 32 var imgDark = document.createElement('img'); 33 imgDark.src = srcDark; 34 imgDark.alt = alt; 35 imgDark.className = 'lightbox-dark'; 36 37 wrapper.appendChild(imgDark); 38 wrapper.appendChild(imgLight); 39 overlay.appendChild(closeBtn); 40 overlay.appendChild(wrapper); 41 document.body.appendChild(overlay); 42 43 // Prevent background scroll 44 document.body.style.overflow = 'hidden'; 45 46 // Trigger enter animation on next frame 47 requestAnimationFrame(function () { 48 overlay.classList.add('lightbox-visible'); 49 }); 50 51 // Close handlers 52 closeBtn.addEventListener('click', close); 53 overlay.addEventListener('click', function (e) { 54 if (e.target === overlay) close(); 55 }); 56 document.addEventListener('keydown', onKey); 57 } 58 59 function close() { 60 if (!overlay) return; 61 62 overlay.classList.remove('lightbox-visible'); 63 document.body.style.overflow = ''; 64 65 // Wait for exit animation then remove 66 overlay.addEventListener('transitionend', function handler() { 67 overlay.removeEventListener('transitionend', handler); 68 if (overlay && overlay.parentNode) { 69 overlay.parentNode.removeChild(overlay); 70 } 71 overlay = null; 72 }); 73 74 document.removeEventListener('keydown', onKey); 75 } 76 77 function onKey(e) { 78 if (e.key === 'Escape') close(); 79 } 80 81 // Bind to all themed image containers 82 document.addEventListener('click', function (e) { 83 var container = e.target.closest('.themed-image-container[data-lightbox-src-light]'); 84 if (container) open(container); 85 }); 86 87 // Keyboard support (Enter/Space on focused container) 88 document.addEventListener('keydown', function (e) { 89 if (e.key === 'Enter' || e.key === ' ') { 90 var container = e.target.closest('.themed-image-container[data-lightbox-src-light]'); 91 if (container) { 92 e.preventDefault(); 93 open(container); 94 } 95 } 96 }); 97})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.