1(function () { 2 'use strict'; 3 4 var lightbox = null; 5 var currentGallery = []; 6 var currentIndex = 0; 7 8 function openLightbox(gallery, index) { 9 currentGallery = gallery; 10 currentIndex = index; 11 12 if (!lightbox) { 13 lightbox = document.createElement('div'); 14 lightbox.className = 'book-lightbox'; 15 lightbox.setAttribute('role', 'dialog'); 16 lightbox.setAttribute('aria-modal', 'true'); 17 lightbox.setAttribute('aria-label', 'Image gallery'); 18 lightbox.innerHTML = 19 '<button class="book-lightbox__close" aria-label="Close gallery">×</button>' + 20 '<button class="book-lightbox__prev" aria-label="Previous image">‹</button>' + 21 '<button class="book-lightbox__next" aria-label="Next image">›</button>' + 22 '<div class="book-lightbox__img-wrap">' + 23 '<img class="book-lightbox__img" src="" alt="" />' + 24 '</div>'; 25 document.body.appendChild(lightbox); 26 27 lightbox.querySelector('.book-lightbox__close').addEventListener('click', closeLightbox); 28 lightbox.querySelector('.book-lightbox__prev').addEventListener('click', showPrev); 29 lightbox.querySelector('.book-lightbox__next').addEventListener('click', showNext); 30 lightbox.addEventListener('click', function (e) { 31 if (e.target === lightbox || e.target.classList.contains('book-lightbox__img-wrap')) { 32 closeLightbox(); 33 } 34 }); 35 } 36 37 updateImage(); 38 lightbox.classList.add('is-open'); 39 document.body.style.overflow = 'hidden'; 40 lightbox.querySelector('.book-lightbox__close').focus(); 41 } 42 43 function closeLightbox() { 44 if (!lightbox) return; 45 lightbox.classList.remove('is-open'); 46 document.body.style.overflow = ''; 47 } 48 49 function showPrev() { 50 currentIndex = (currentIndex - 1 + currentGallery.length) % currentGallery.length; 51 updateImage(); 52 } 53 54 function showNext() { 55 currentIndex = (currentIndex + 1) % currentGallery.length; 56 updateImage(); 57 } 58 59 function updateImage() { 60 var img = lightbox.querySelector('.book-lightbox__img'); 61 var item = currentGallery[currentIndex]; 62 img.src = item.src; 63 img.alt = item.alt; 64 lightbox.querySelector('.book-lightbox__prev').style.visibility = currentGallery.length > 1 ? 'visible' : 'hidden'; 65 lightbox.querySelector('.book-lightbox__next').style.visibility = currentGallery.length > 1 ? 'visible' : 'hidden'; 66 } 67 68 document.addEventListener('keydown', function (e) { 69 if (!lightbox || !lightbox.classList.contains('is-open')) return; 70 if (e.key === 'Escape') closeLightbox(); 71 if (e.key === 'ArrowLeft') showPrev(); 72 if (e.key === 'ArrowRight') showNext(); 73 }); 74 75 document.addEventListener('DOMContentLoaded', function () { 76 var galleries = document.querySelectorAll('.book-gallery'); 77 galleries.forEach(function (gallery) { 78 var items = gallery.querySelectorAll('.book-gallery__item'); 79 items.forEach(function (item, i) { 80 item.addEventListener('click', function (e) { 81 e.preventDefault(); 82 var images = []; 83 items.forEach(function (el) { 84 var img = el.querySelector('img'); 85 images.push({ src: img.src, alt: img.alt }); 86 }); 87 openLightbox(images, i); 88 }); 89 }); 90 }); 91 }); 92})(); 93 94/* ---------- 95 Buy-direct modal 96 Progressive enhancement of the pure-CSS :target overlay rendered by 97 the buy-modal snippet. With JS on we take over: prevent the hash 98 change, manage focus, trap Tab, close on Escape / backdrop click, 99 and restore focus to the trigger. With JS off, the CSS :target 100 fallback keeps the shipping links fully usable. 101---------- */ 102(function () { 103 'use strict'; 104 105 var openModal = null; 106 var lastTrigger = null; 107 108 function openBuyModal(modal, trigger) { 109 openModal = modal; 110 lastTrigger = trigger; 111 modal.classList.add('is-open'); 112 document.body.style.overflow = 'hidden'; 113 var closeBtn = modal.querySelector('.buy-modal__close'); 114 if (closeBtn) closeBtn.focus(); 115 } 116 117 function closeBuyModal() { 118 if (!openModal) return; 119 openModal.classList.remove('is-open'); 120 document.body.style.overflow = ''; 121 if (lastTrigger) lastTrigger.focus(); 122 openModal = null; 123 lastTrigger = null; 124 } 125 126 document.addEventListener('DOMContentLoaded', function () { 127 var triggers = document.querySelectorAll('[data-buy-trigger]');
128 triggers.forEach(function (trigger) { 129 trigger.addEventListener('click', function (e) { 130 var id = (trigger.getAttribute('href') || '').replace('#', ''); 131 var modal = document.getElementById(id); 132 if (!modal) return; // no modal found: let the link behave normally 133 e.preventDefault(); 134 openBuyModal(modal, trigger); 135 }); 136 }); 137 138 var modals = document.querySelectorAll('.buy-modal'); 139 modals.forEach(function (modal) { 140 // Click on the backdrop (outside the inner panel) closes the modal 141 modal.addEventListener('click', function (e) { 142 if (e.target === modal) closeBuyModal(); 143 }); 144 var closeBtn = modal.querySelector('.buy-modal__close'); 145 if (closeBtn) { 146 closeBtn.addEventListener('click', function (e) { 147 e.preventDefault(); 148 closeBuyModal(); 149 }); 150 } 151 }); 152 }); 153 154 document.addEventListener('keydown', function (e) { 155 if (!openModal) return; 156 if (e.key === 'Escape') { 157 closeBuyModal(); 158 return; 159 } 160 if (e.key === 'Tab') { 161 // Simple focus trap within the open modal 162 var focusables = openModal.querySelectorAll('a[href], button'); 163 if (!focusables.length) return; 164 var first = focusables[0]; 165 var last = focusables[focusables.length - 1]; 166 if (e.shiftKey && document.activeElement === first) { 167 e.preventDefault(); 168 last.focus(); 169 } else if (!e.shiftKey && document.activeElement === last) { 170 e.preventDefault(); 171 first.focus(); 172 } 173 } 174 }); 175})();
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.