https://cdn.centralbankbahamas.com/tenantcbob_prod/resources/globa…/netclues-zoombox.js?v=1790277784
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.