PageSourceSearch

https://cdn.centralbankbahamas.com/tenantcbob_prod/resources/globa…/netclues-zoombox.js?v=1790277784

js centralbankbahamas.com collected 2026-09-24 19:23:54 UTC 32,223 bytes, 798 lines download raw bytes

1document.addEventListener("DOMContentLoaded", function () {
2
3    let currentIndex = 0;
4    let groupItems = [];
5
6    let zoomEnabled = false;
7    let isFullscreen = false;
8
9    let sliderInterval = null;
10    let sliderSpeed = 2000;
11
12    // 🔥 CREATE ZoomBOX ONLY ONCE
13    if (!document.querySelector(".n-zoombox")) {
14
15        const zoomboxHTML = `
16      <div class="n-zoombox">
17      <div class="n-image-counter" style="color: #fff;"></div>
18        <div class="n-zoombox-header">
19          <button type="button" class="zoom-btn"><svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="512" height="512" x="0" y="0" viewBox="0 0 490.8 490.8" style="enable-background:new 0 0 512 512" xml:space="preserve" class="hovered-paths"><g><path d="M364.8 299.55c46.3-75.8 36.9-176.3-28.6-241.9-76.8-76.8-201.8-76.8-278.6 0s-76.8 201.8 0 278.5c65.5 65.5 166 74.9 241.9 28.6L412 477.25c18 18 47.3 18 65.3 0s18-47.3 0-65.3l-112.5-112.4zm-69.3-4c-54.4 54.4-142.8 54.4-197.1 0-54.4-54.4-54.4-142.8 0-197.1 54.4-54.4 142.8-54.4 197.1 0 54.3 54.3 54.3 142.7 0 197.1zM220 171.95h59.4v45.3H220v59.4h-45.3v-59.4h-59.3v-45.3h59.4v-59.4h45.3v59.4h-.1z" fill="#000000" opacity="1" data-original="#000000" class="hovered-path"></path></g></svg><svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="512" height="512" x="0" y="0" viewBox="0 0 490.8 490.8" style="enable-background:new 0 0 512 512" xml:space="preserve" class=""><g><path d="M364.8 299.5c46.3-75.8 36.9-176.3-28.6-241.9-76.8-76.8-201.8-76.8-278.6 0s-76.8 201.8 0 278.6c65.5 65.5 166 74.9 241.9 28.6L412 477.3c18 18 47.3 18 65.3 0s18-47.3 0-65.3L364.8 299.5zm-69.3-4c-54.4 54.4-142.8 54.4-197.1 0C44 241.1 44 152.7 98.4 98.4c54.4-54.4 142.8-54.4 197.1 0 54.3 54.3 54.3 142.7 0 197.1zM128 167.3h138.7v54.6H128v-54.6z" fill="#000000" opacity="1" data-original="#000000" class=""></path></g></svg></button>
20          <button type="button" class="slider-play-btn"><svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="512" height="512" x="0" y="0" viewBox="0 0 163.861 163.861" style="enable-background:new 0 0 512 512" xml:space="preserve" class=""><g><path d="M34.857 3.613C20.084-4.861 8.107 2.081 8.107 19.106v125.637c0 17.042 11.977 23.975 26.75 15.509L144.67 97.275c14.778-8.477 14.778-22.211 0-30.686L34.857 3.613z" fill="#000000" opacity="1" data-original="#000000" class=""></path></g></svg><svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="512" height="512" x="0" y="0" viewBox="0 0 32 32" style="enable-background:new 0 0 512 512" xml:space="preserve" class=""><g><path d="M13 5v22a3 3 0 0 1-3 3H9a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3h1a3 3 0 0 1 3 3zm10-3h-1a3 3 0 0 0-3 3v22a3 3 0 0 0 3 3h1a3 3 0 0 0 3-3V5a3 3 0 0 0-3-3z" data-name="Layer 30" fill="#000000" opacity="1" data-original="#000000"></path></g></svg></button>
21          <button type="button" class="fullscreen-btn"><svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="512" height="512" x="0" y="0" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512" xml:space="preserve" class=""><g><path d="M181.2 32a32 32 0 0 1-32 32H64v85.2a32 32 0 0 1-64 0V32A32 32 0 0 1 32 0h117.2a32 32 0 0 1 32 32zm-32 416H64v-85.2a32 32 0 0 0-64 0V480a32 32 0 0 0 32 32h117.2a32 32 0 1 0 0-64zM480 330.8a32 32 0 0 0-32 32V448h-85.2a32 32 0 1 0 0 64H480a32 32 0 0 0 32-32V362.8a32 32 0 0 0-32-32zM480 0H362.8a32 32 0 0 0 0 64H448v85.2a32 32 0 1 0 64 0V32a32 32 0 0 0-32-32z" fill="#000000" opacity="1" data-original="#000000"></path></g></svg></button>
22          <button type="button" class="close"><svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="512" height="512" x="0" y="0" viewBox="0 0 329.269 329" style="enable-background:new 0 0 512 512" xml:space="preserve" class=""><g><path d="M194.8 164.77 323.013 36.555c8.343-8.34 8.343-21.825 0-30.164-8.34-8.34-21.825-8.34-30.164 0L164.633 134.605 36.422 6.391c-8.344-8.34-21.824-8.34-30.164 0-8.344 8.34-8.344 21.824 0 30.164l128.21 128.215L6.259 292.984c-8.344 8.34-8.344 21.825 0 30.164a21.266 21.266 0 0 0 15.082 6.25c5.46 0 10.922-2.09 15.082-6.25l128.21-128.214 128.216 128.214a21.273 21.273 0 0 0 15.082 6.25c5.46 0 10.922-2.09 15.082-6.25 8.343-8.34 8.343-21.824 0-30.164zm0 0" fill="#000000" opacity="1" data-original="#000000" class=""></path></g></svg></button>
23        </div>
24        <div class="n-zoombox-content">
25          <button type="button" class="prev"><svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="512" height="512" x="0" y="0" viewBox="0 0 492.004 492.004" style="enable-background:new 0 0 512 512" xml:space="preserve" class=""><g><path d="M382.678 226.804 163.73 7.86C158.666 2.792 151.906 0 144.698 0s-13.968 2.792-19.032 7.86l
25-16.124 16.12c-10.492 10.504-10.492 27.576 0 38.064L293.398 245.9l-184.06 184.06c-5.064 5.068-7.86 11.824-7.86 19.028 0 7.212 2.796 13.968 7.86 19.04l16.124 16.116c5.068 5.068 11.824 7.86 19.032 7.86s13.968-2.792 19.032-7.86L382.678 265c5.076-5.084 7.864-11.872 7.848-19.088.016-7.244-2.772-14.028-7.848-19.108z" fill="#000000" opacity="1" data-original="#000000" class=""></path></g></svg></button>
26          <button type="button" class="next"><svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="512" height="512" x="0" y="0" viewBox="0 0 492.004 492.004" style="enable-background:new 0 0 512 512" xml:space="preserve" class=""><g><path d="M382.678 226.804 163.73 7.86C158.666 2.792 151.906 0 144.698 0s-13.968 2.792-19.032 7.86l-16.124 16.12c-10.492 10.504-10.492 27.576 0 38.064L293.398 245.9l-184.06 184.06c-5.064 5.068-7.86 11.824-7.86 19.028 0 7.212 2.796 13.968 7.86 19.04l16.124 16.116c5.068 5.068 11.824 7.86 19.032 7.86s13.968-2.792 19.032-7.86L382.678 265c5.076-5.084 7.864-11.872 7.848-19.088.016-7.244-2.772-14.028-7.848-19.108z" fill="#000000" opacity="1" data-original="#000000" class=""></path></g></svg></button>
27          <img id="n-zoombox-img">
28          <div class="n-zoombox-slider">
29            <div class="n-zoombox-track"></div>
30          </div>
31          <div class="caption"></div>
32        </div>
33        <div class="n-zoombox-thumbs">
34        </div>
35      </div>
36    `;
37
38        document.body.insertAdjacentHTML("beforeend", zoomboxHTML);
39    }
40
41    const zoombox = document.querySelector(".n-zoombox");
42    const imgBox = document.getElementById("n-zoombox-img");
43    const caption = zoombox.querySelector(".caption");
44    const thumbsContainer = zoombox.querySelector(".n-zoombox-thumbs");
45    const track = document.querySelector(".n-zoombox-track");
46    const slider = document.querySelector(".n-zoombox-slider");
47
48    // =========================
49    // 🔹 OPEN ZOOMBOX
50    // =========================
51    document.addEventListener("click", function (e) {
52        const link = e.target.closest("a[data-zoom]");
53        if (!link) return;
54        e.preventDefault();
55
56        const group = link.getAttribute("data-zoom");
57        groupItems = Array.from(document.querySelectorAll(`a[data-zoom="${group}"]`));
58        currentIndex = groupItems.indexOf(link);
59
60        // ✅ Hide next/prev if only 1 item
61        const nextBtn = document.querySelectorAll(".next, .prev");
62        const playBtn = document.querySelectorAll(".slider-play-btn");
63
64        if (groupItems.length <= 1) {
65            nextBtn.forEach(b => b.style.display = "none");
66            playBtn.forEach(b => b.style.display = "none");
67        } else {
68            nextBtn.forEach(b => b.style.display = "");
69            playBtn.forEach(b => b.style.display = "");
70        }
71
72        // Reset zoom state before opening
73        zoomEnabled = false;
74        document.querySelectorAll(".zoom-btn").forEach(b => b.classList.remove("active"));
75        zoombox.classList.remove("zoom-mode");
76        resetZoom();
77
78        buildThumbs();
79        buildSlider();
80
81        // 🔥 AUTOPLAY FIRST VIDEO ON OPEN
82        setTimeout(() => {
83            const firstSlide = track.querySelectorAll(".n-zoombox-slide")[currentIndex];
84            if (firstSlide) {
85                const firstVideo = firstSlide.querySelector("video");
86                if (firstVideo) firstVideo.play().catch(() => { });
87            }
88        }, 100);
89
90        track.style.transition = "none";
91        updateSlider();
92        track.offsetHeight; // force reflow
93        track.style.transition = "";
94
95        zoombox.classList.add("active");
96
97        setTimeout(centerActiveThumb, 10);
98    });
99
100    // =========================
101    // 🔹 BUILD THUMBS
102    // =========================
103    function buildThumbs() {
104        thumbsContainer.innerHTML = "";
105
106        let hasVideo = false;
107
108        groupItems.forEach(function (el) {
109            const src = el.getAttribute("href");
110            if (
111                /\.(mp4|webm|ogg)$/i.test(src) ||
112                src.includes("youtube") ||
113                src.includes("vimeo")
114            ) {
115                hasVideo = true;
116            }
117        });
118
119        // ❌ If video exists → hide all thumbs
120        if (hasVideo) {
121            thumbsContainer.style.display = "none";
122            return;
123        }
124
125        // ✅ Otherwise show thumbs for images
126        thumbsContainer.style.display = "";
127
128        groupItems.forEach(function (el, i) {
129            const thumbImg = el.querySelector("img");
130            const thumbSrc = thumbImg ? thumbImg.getAttribute("src") : "";
131            const btn = document.createElement("button");
132            btn.type = "button";
133            btn.className = "thumb-btn";
134            btn.innerHTML = `<img src="${thumbSrc}" data-index="${i}" draggable="false">`;
135            thumbsContainer.appendChild(btn);
136        });
137    }
138
139    // =========================
140    // 🔹 BUILD SLIDER
141    // =========================
142    let isDraggingSlider = false;
143    let didSliderDrag = false;
144    let startXSlider = 0;
145    let currentTranslate = 0;
146    let prevTranslate = 0;
147
148    function buildSlider() {
149        track.innerHTML = "";
150
151        groupItems.forEach(function (el) {
152            const src = el.getAttribute("href");
153            const isVideo = /\.(mp4|webm|ogg)$/i.test(src);
154            const isIframe = src.includes("youtube") || src.includes("vimeo");
155
156            const slide = document.createElement("div");
157            slide.className = "n-zoombox-slide";
158
159            if (isVideo) {
160                slide.innerHTML = `
161          <div class="n-zoombox-slide-img-wrap">
162            <video controls preload="metadata" playsinline autoplay>
163              <source src="${src}">
164            </video>
165          </div>`;
166            } else if (isIframe) {
167
168                const hasAutoplay = src.includes("autoplay=1");
169
170                // remove autoplay from initial load
171                let cleanSrc = src.replace(/(\?|&)autoplay=1/g, '');
172
173                slide.innerHTML = `
174                <div class="n-zoombox-slide-img-wrap">
175                    <iframe 
176                    data-src="${cleanSrc}" 
177                    data-autoplay="${hasAutoplay}" 
178                    src="" 
179                    frameborder="0" 
180                    allow="autoplay" 
181                    allowfullscreen>
182                    </iframe>
183                </div>`;
184            } else {
185                slide.innerHTML = `
186          <div class="n-zoombox-slide-img-wrap">
187            <img src="${src}" draggable="false">
188          </div>`;
189            }
190
191            track.appendChild(slide);
192            
193        });
194
195        // stop all videos
196        track.querySelectorAll("video").forEach(function (v) {
197            v.pause();
198            v.currentTime = 0;
199        });
200
201        // reset iframes (YouTube)
202        track.querySelectorAll("iframe").forEach(function (iframe) {
203            const src = iframe.getAttribute("src");
204            iframe.setAttribute("src", src);
205        });
206    }
207
208    // =========================
209    // 🔹 VIDEO CLICK TOGGLE PLAY/PAUSE
210    // =========================
211    document.addEventListener("click", function (e) {
212        const video = e.target.closest("video");
213        if (!video) return;
214
215        // prevent slider drag conflict
216        e.stopPropagation();
217
218        if (video.paused) {
219            video.play().catch(() => {});
220        } else {
221            video.pause();
222        }
223    });
224
225    // =========================
226    // 🔹 UPDATE SLIDER
227    // =========================
228    function updateSlider() {
229
230        if (zoomEnabled) {
231            zoomEnabled = false;
232            document.querySelectorAll(".zoom-btn").forEach(b => b.classList.remove("active"));
233            zoombox.classList.remove("zoom-mode");
234            resetZoom();
235        }
236
237        // ✅ Handle navigation visibility
238        const navBtns = document.querySelectorAll(".next, .prev");
239        const playBtns = document.querySelectorAll(".slider-play-btn");
240
241        if (groupItems.length <= 1) {
242            navBtns.forEach(b => b.style.display = "none");
243            playBtns.forEach(b => b.style.display = "none");
244        } else {
245            navBtns.forEach(b => b.style.display = "");
246            playBtns.forEach(b => b.style.display = "");
247        }
248
249        const offset = -currentIndex * 100;
250        track.style.transform = `translateX(${offset}%)`;
251
252        caption.textContent = groupItems[currentIndex]
253            ? (groupItems[currentIndex].getAttribute("data-caption") || "")
254            : "";
255
256        document.querySelector(".n-image-counter").textContent =
257            `${currentIndex + 1} / ${groupItems.length}`;
258
259        // Thumbs active state
260        thumbsContainer.querySelectorAll(".thumb-btn").forEach(b => b.classList.remove("active"));
261        const activeThumbBtn = thumbsContainer.querySelectorAll(".thumb-btn")[currentIndex];
262        if (activeThumbBtn) activeThumbBtn.classList.add("active");
263
264        // Slides active state
265        const slides = track.querySelectorAll(".n-zoombox-slide");
266        slides.forEach(s => s.classList.remove("active"));
267        const currentSlide = slides[currentIndex];
268
269       // 🔥 STOP all iframes
270        slides.forEach(function (slide) {
271            const iframe = slide.querySelector("iframe");
272            if (iframe) {
273                iframe.setAttribute("src", "");
274            }
275        });
276
277        // 🔥 LOAD ONLY ACTIVE SLIDE
278        if (currentSlide) {
279            const activeIframe = currentSlide.querySelector("iframe");
280
281            if (activeIframe) {
282                let src = activeIframe.getAttribute("data-src");
283                const shouldAutoplay = activeIframe.getAttribute("data-autoplay") === "true";
284
285                // ✅ Only autoplay if user added autoplay
286                if (shouldAutoplay) {
287                    if (src.includes("?")) {
288                        src += "&autoplay=1";
289                    } else {
290                        src += "?autoplay=1";
291                    }
292                }
293
294                activeIframe.setAttribute("src", src);
295            }
296        }
297
298        centerActiveThumb();
299
300        // 🔥 HIDE/SHOW ZOOM & PLAY BUTTONS FOR VIDEO/IFRAME SLIDES
301        if (currentSlide) {
302            const isVideo = currentSlide.querySelector("video") !== null;
303            const isIframe = currentSlide.querySelector("iframe") !== null;
304
305            if (isVideo || isIframe) {
306                document.querySelectorAll(".zoom-btn").forEach(b => b.style.display = "none");
307                document.querySelectorAll(".slider-play-btn").forEach(b => b.style.display = "none");
308                stopSlider();
309            } else {
310                document.querySelectorAll(".zoom-btn").forEach(b => b.style.display = "");
311                document.querySelectorAll(".slider-play-btn").forEach(b => b.style.display = "");
312            }
313        }
314
315        if (zoomEnabled && groupItems[currentIndex]) {
316            const currentSrc = groupItems[currentIndex].getAttribute("href");
317            imgBox.setAttribute("src", currentSrc);
318            resetZoom();
319        }
320
321        // 🔥 AUTOPLAY VIDEO FIX
322        track.querySelectorAll("video").forEach(function (v) {
323            v.pause();
324            v.currentTime = 0;
325        });
326
327        if (currentSlide) {
328            const activeVideo = currentSlide.querySelector("video");
329            if (activeVideo) {
330                activeVideo.play().catch(() => {
331                    
332                });
333            }
334        }
335    }
336
337    // =========================
338    // 🔹 CENTER ACTIVE THUMB
339    // =========================
340    function centerActiveThumb() {
341        const activeThumb = thumbsContainer.querySelector(".thumb-btn.active");
342        if (!activeThumb) return;
343
344        const containerWidth = thumbsContainer.offsetWidth;
345        const thumbLeft = activeThumb.offsetLeft;
346        const thumbWidth = activeThumb.offsetWidth;
347        const scrollPosition = thumbLeft - (containerWidth / 2) + (thumbWidth / 2);
348
349        thumbsContainer.scrollTo({ left: scrollPosition, behavior: "smooth" });
350    }
351
352    // =========================
353    // 🔹 PLAY / PAUSE SLIDER
354    // =========================
355    document.addEventListener("click", function (e) {
356        const btn = e.target.closest(".slider-play-btn");
357        if (!btn) return;
358
359        if (sliderInterval) {
360            clearInterval(sliderInterval);
361            sliderInterval = null;
362            btn.classList.remove("active");
363        } else {
364            btn.classList.add("active");
365
366            sliderInterval = setInterval(() => {
367                currentIndex = (currentIndex + 1) % groupItems.length;
368                track.style.transition = "transform 0.4s ease";
369                updateSlider();
370            }, sliderSpeed);
371        }
372    });
373
374    function stopSlider() {
375        if (sliderInterval) {
376            clearInterval(sliderInterval);
377            sliderInterval = null;
378            document.querySelectorAll(".slider-play-btn").forEach(b => b.classList.remove("active"));
379        }
380    }
381
382    // =========================
383    // 🔹 SLIDER DRAG (mouse + touch)
384    // =========================
385    slider.addEventListener("mousedown", function (e) {
386        if (zoomEnabled) return;
387
388        isDraggingSlider = true;
389        didSliderDrag = false;
390        slider.classList.add("dragging");
391
392        startXSlider = e.pageX;
393        prevTranslate = -currentIndex * slider.offsetWidth;
394    });
395
396    slider.addEventListener("touchstart", function (e) {
397        if (zoomEnabled) return;
398
399        isDraggingSlider = true;
400        didSliderDrag = false;
401        slider.classList.add("dragging");
402
403        startXSlider = e.touches[0].clientX;
404        prevTranslate = -currentIndex * slider.offsetWidth;
405    }, { passive: true });
406
407    document.addEventListener("mousemove", function (e) {
408        if (!isDraggingSlider) return;
409
410        const diff = e.pageX - startXSlider;
411        if (Math.abs(diff) > 5) didSliderDrag = true;
412
413        currentTranslate = prevTranslate + diff;
414        track.style.transition = "none";
415        track.style.transform = `translateX(${currentTranslate}px)`;
416    });
417
418    document.addEventListener("touchmove", function (e) {
419        if (!isDraggingSlider) return;
420
421        const diff = e.touches[0].clientX - startXSlider;
422        if (Math.abs(diff) > 5) didSliderDrag = true;
423
424        currentTranslate = prevTranslate + diff;
425        track.style.transition = "none";
426        track.style.transform = `translateX(${currentTranslate}px)`;
427    }, { passive: true });
428
429    document.addEventListener("mouseup", function () {
430        if (!isDraggingSlider) return;
431        finishSliderDrag();
432    });
433
434    document.addEventListener("touchend", function () {
435        if (!isDraggingSlider) return;
436        finishSliderDrag();
437    });
438
439    function finishSliderDrag() {
440        isDraggingSlider = false;
441        slider.classList.remove("dragging");
442
443        const movedBy = currentTranslate - prevTranslate;
444        if (movedBy < -100) currentIndex++;
445        if (movedBy > 100) currentIndex--;
446
447        currentIndex = Math.max(0, Math.min(currentIndex, groupItems.length - 1));
448        track.style.transition = "transform 0.4s ease";
449        updateSlider();
450    }
451
452    // =========================
453    // 🔹 NAVIGATION BUTTONS
454    // =========================
455    document.addEventListener("click", function (e) {
456        if (e.target.closest(".next")) {
457            if (zoomEnabled) return;
458            currentIndex = (currentIndex + 1) % groupItems.length;
459            updateSlider();
460        }
461    });
462
463    document.addEventListener("click", function (e) {
464        if (e.target.closest(".prev")) {
465            if (zoomEnabled) return;
466            currentIndex = (currentIndex - 1 + groupItems.length) % groupItems.length;
467            updateSlider();
468        }
469    });
470
471    // =========================
472    // 🔹 CLOSE ZOOMBOX
473    // =========================
474    function closeZoombox() {
475        track.style.transition = "none";
476        zoombox.classList.remove("active");
477        zoombox.classList.add("closing");
478
479        setTimeout(() => {
480            zoombox.classList.remove("closing");
481            track.innerHTML = "";
482        }, 300);
483
484        stopSlider();
485        document.querySelectorAll(".slider-play-btn").forEach(b => b.classList.remove("active"));
486
487        if (isFullscreen) {
488            if (document.exitFullscreen) document.exitFullscreen();
489            else if (document.webkitExitFullscreen) document.webkitExitFullscreen();
490        }
491    }
492
493    document.addEventListener("click", function (e) {
494        if (e.target.closest(".close")) closeZoombox();
495    });
496
497    // =========================
498    // 🔹 THUMB CLICK & DRAG
499    // =========================
500    let isDraggingThumbs = false;
501    let didThumbDrag = false;
502    let thumbsStartX;
503    let thumbsScrollLeft;
504
505    thumbsContainer.addEventListener("mousedown", function (e) {
506        isDraggingThumbs = true;
507        didThumbDrag = false;
508        thumbsContainer.classList.add("dragging");
509        thumbsStartX = e.pageX - thumbsContainer.offsetLeft;
510        thumbsScrollLeft = thumbsContainer.scrollLeft;
511    });
512
513    thumbsContainer.addEventListener("mouseleave", function () {
514        isDraggingThumbs = false;
515        thumbsContainer.classList.remove("dragging");
516    });
517
518    thumbsContainer.addEventListener("mouseup", function () {
519        isDraggingThumbs = false;
520        thumbsContainer.classList.remove("dragging");
521    });
522
523    thumbsContainer.addEventListener("mousemove", function (e) {
524        if (!isDraggingThumbs) return;
525
526        const x = e.pageX - thumbsContainer.offsetLeft;
527        const walk = x - thumbsStartX;
528
529        if (Math.abs(walk) > 5) didThumbDrag = true;
530
531        thumbsContainer.scrollLeft = thumbsScrollLeft - walk;
532    });
533
534    thumbsContainer.addEventListener("click", function (e) {
535        const thumbBtn = e.target.closest(".thumb-btn");
536        if (!thumbBtn) return;
537
538        if (didThumbDrag) {
539            didThumbDrag = false;
540            return;
541        }
542
543        const thumbIndex = Array.from(thumbsContainer.querySelectorAll(".thumb-btn")).indexOf(thumbBtn);
544
545        // Map thumb index to groupItems index (skip videos)
546        const imageItems = groupItems.filter(el => {
547            const src = el.getAttribute("href");
548            return !(/\.(mp4|webm|ogg)$/i.test(src)) &&
549                !src.includes("youtube") &&
550                !src.includes("vimeo");
551        });
552
553        currentIndex = groupItems.indexOf(imageItems[thumbIndex]);
554
555        track.style.transition = "none";
556        updateSlider();
557        track.offsetHeight; // force reflow
558        track.style.transition = "transform 0.4s ease";
559    });
560
561    // =========================
562    // 🔹 KEYBOARD
563    // =========================
564    document.addEventListener("keydown", function (e) {
565        if (!zoombox.classList.contains("active")) return;
566        if (zoomEnabled) return;
567
568        if (e.key === "Escape") closeZoombox();
569        if (e.key === "ArrowRight") document.querySelector(".next").click();
570        if (e.key === "ArrowLeft") document.querySelector(".prev").click();
571    });
572
573    // =========================
574    // 🔍 ZOOM MODE BUTTON
575    // =========================
576    document.addEventListener("click", function (e) {
577        const btn = e.target.closest(".zoom-btn");
578        if (!btn) return;
579
580        const currentSlide = track.querySelectorAll(".n-zoombox-slide")[currentIndex];
581
582        // ❌ Prevent zoom on video/iframe
583        if (currentSlide && currentSlide.querySelector("video, iframe")) return;
584
585        zoomEnabled = !zoomEnabled;
586        btn.classList.toggle("active");
587
588        if (!zoomEnabled) {
589            resetZoom();
590            zoombox.classList.remove("zoom-mode");
591        } else {
592            stopSlider();
593            zoombox.classList.add("zoom-mode");
594
595            const currentSrc = groupItems[currentIndex]
596                ? groupItems[currentIndex].getAttribute("href")
597                : "";
598            imgBox.setAttribute("src", currentSrc);
599
600            scale = 2;
601            updateTransform();
602        }
603    });
604
605
606    // =========================
607    // 🔍 ZOOM SYSTEM (with pinch-to-zoom)
608    // =========================
609    let scale = 1;
610    let isDragging = false;
611    let didZoomDrag = false;
612    let startX, startY;
613    let translateX = 0;
614    let translateY = 0;
615
616    // Pinch-to-zoom variables
617    let pinchStartDist = 0;
618    let pinchStartScale = 1;
619    let pinchCenter = { x: 0, y: 0 };
620
621    imgBox.addEventListener("click", function () {
622        if (!zoomEnabled) return;
623        if (didZoomDrag) {
624            didZoomDrag = false;
625            return;
626        }
627
628        scale = scale === 1 ? 2 : 1;
629        if (scale === 1) resetZoom();
630        else updateTransform();
631    });
632
633    imgBox.addEventListener("wheel", function (e) {
634        if (!zoomEnabled) return;
635        e.preventDefault();
636
637        scale += e.deltaY < 0 ? 0.2 : -0.2;
638        scale = Math.max(1, Math.min(scale, 4));
639        updateTransform();
640    }, { passive: false });
641
642    imgBox.addEventListener("mousedown", function (e) {
643        if (scale === 1 || !zoomEnabled) return;
644
645        isDragging = true;
646        didZoomDrag = false;
647        startX = e.clientX - translateX;
648        startY = e.clientY - translateY;
649        imgBox.style.cursor = "grabbing";
650    });
651
652    document.addEventListener("mousemove", function (e) {
653        if (!isDragging) return;
654
655        const newTranslateX = e.clientX - startX;
656        const newTranslateY = e.clientY - startY;
657
658        if (Math.abs(newTranslateX - translateX) > 5 || Math.abs(newTranslateY - translateY) > 5) {
659            didZoomDrag = true;
660        }
661
662        translateX = newTranslateX;
663        translateY = newTranslateY;
664        updateTransform();
665    });
666
667    document.addEventListener("mouseup", function () {
668        if (isDragging) {
669            isDragging = false;
670            imgBox.style.cursor = "grab";
671        }
672    });
673
674    // ===== Pinch-to-zoom for mobile =====
675    imgBox.addEventListener("touchstart", function (e) {
676        if (!zoomEnabled) return;
677        if (e.touches.length === 2) {
678            // Pinch start
679            pinchStartDist = getTouchDist(e.touches[0], e.touches[1]);
680            pinchStartScale = scale;
681            pinchCenter = getTouchCenter(e.touches[0], e.touches[1]);
682        } else if (e.touches.length === 1 && scale > 1) {
683            // Drag start
684            isDragging = true;
685            didZoomDrag = false;
686            startX = e.touches[0].clientX - translateX;
687            startY = e.touches[0].clientY - translateY;
688        }
689    }, { passive: false });
690
691    imgBox.addEventListener("touchmove", function (e) {
692        if (!zoomEnabled) return;
693        if (e.touches.length === 2) {
694            // Pinch move
695            e.preventDefault();
696            const newDist = getTouchDist(e.touches[0], e.touches[1]);
697            let newScale = pinchStartScale * (newDist / pinchStartDist);
698            newScale = Math.max(1, Math.min(newScale, 4));
699            scale = newScale;
700            updateTransform();
701        } else if (e.touches.length === 1 && isDragging && scale > 1) {
702            // Drag move
703            e.preventDefault();
704            const newTranslateX = e.touches[0].clientX - startX;
705            const newTranslateY = e.touches[0].clientY - startY;
706            if (Math.abs(newTranslateX - translateX) > 5 || Math.abs(newTranslateY - translateY) > 5) {
707                didZoomDrag = true;
708            }
709            translateX = newTranslateX;
710            translateY = newTranslateY;
711            updateTransform();
712        }
713    }, { passive: false });
714
715    imgBox.addEventListener("touchend", function (e) {
716        if (!zoomEnabled) return;
717        if (e.touches.length < 2) {
718            pinchStartDist = 0;
719        }
720        if (e.touches.length === 0) {
721            isDragging = false;
722        }
723    });
724
725    function getTouchDist(touch1, touch2) {
726        const dx = touch2.clientX - touch1.clientX;
727        const dy = touch2.clientY - touch1.clientY;
728        return Math.sqrt(dx * dx + dy * dy);
729    }
730
731    function getTouchCenter(touch1, touch2) {
732        return {
733            x: (touch1.clientX + touch2.clientX) / 2,
734            y: (touch1.clientY + touch2.clientY) / 2
735        };
736    }
737
738    function updateTransform() {
739        if (scale > 1) zoombox.classList.add("zoomed");
740        else zoombox.classList.remove("zoomed");
741
742        imgBox.style.transform = `translate(${translateX}px, ${translateY}px) scale(${scale})`;
743    }
744
745    function resetZoom() {
746        scale = 1;
747        translateX = 0;
748        translateY = 0;
749        updateTransform();
750    }
751
752    // =========================
753    // 🖥️ FULLSCREEN
754    // =========================
755    document.addEventListener("click", function (e) {
756        const btn = e.target.closest(".fullscreen-btn");
757        if (!btn) return;
758
759        if (!isFullscreen) {
760            if (zoombox.requestFullscreen) zoombox.requestFullscreen();
761            else if (zoombox.webkitRequestFullscreen) zoombox.webkitRequestFullscreen();
762
763            isFullscreen = true;
764            btn.classList.add("active");
765        } else {
766            if (document.exitFullscreen) document.exitFullscreen();
767            else if (document.webkitExitFullscreen) document.webkitExitFullscreen();
768
769            isFullscreen = false;
770            btn.classList.remove("active");
771        }
772    });
773
774    document.addEventListener("fullscreenchange", onFullscreenChange);
775    document.addEventListener("webkitfullscreenchange", onFullscreenChange);
776
777    function onFullscreenChange() {
778        if (!document.fullscreenElement && !document.webkitFullscreenElement) {
779            isFullscreen = false;
780            document.querySelectorAll(".fullscreen-btn").forEach(b => b.classList.remove("active"));
781        }
782    }
783
784    // =========================
785    // 🔹 CLOSE ON BACKDROP CLICK
786    // =========================
787    zoombox.addEventListener("click", function (e) {
788        if (didSliderDrag) {
789            didSliderDrag = false;
790            return;
791        }
792
793        if (!e.target.closest(".n-zoombox-slide-img-wrap, #n-zoombox-img, .n-zoombox-header, .n-zoombox-thumbs, .prev, .next, .caption")) {
794            closeZoombox();
795        }
796    });
797
798});

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.