1import {parsePositiveInteger} from '../core/measure.js'; 2import {createFacebookEmbedController} from './gallery/facebookEmbeds.js'; 3import {getGalleryIndexFromHash, replaceGalleryHash} from './gallery/galleryHash.js'; 4import {createLazyAdLoader} from './gallery/lazyAds.js'; 5import {processSocialEmbedProvider} from './gallery/socialEmbeds.js'; 6 7export function initGalleryViewer() { 8 const ANIMATION_DURATION_MS = 280; 9 const DEFAULT_ADVERT_RELOAD_NAVIGATION_INTERVAL = 5; 10 const MOBILE_BREAKPOINT_QUERY = '(max-width: 1023px)'; 11 const EMBED_REFRESH_DELAYS_MS = [300, 1000]; 12 const EMBED_SELECTOR = '.gallery-viewer__embed'; 13 const EMBED_CONTENT_SELECTOR = '.gallery-viewer__embed-content'; 14 const UPSCORE_INITIALIZED_EVENT = 'upscore:initialized'; 15 const TRACK_NO_TRANSITION_CLASS = 'gallery-viewer__track--no-transition'; 16 const TRUE_DATA_VALUE = 'true'; 17 const viewer = document.querySelector('[data-gallery-viewer]'); 18 if (!viewer) return; 19 20 const supportsOnePageMobileLayout = viewer.classList.contains('gallery-viewer--mobile-one-page'); 21 const main = viewer.querySelector('.gallery-viewer__main'); 22 const imageContainer = viewer.querySelector('.gallery-viewer__image-container'); 23 const stage = viewer.querySelector('[data-gallery-stage]'); 24 const track = viewer.querySelector('[data-gallery-track]'); 25 const originalSlides = Array.from(track?.querySelectorAll('[data-gallery-slide]') || []); 26 const infoSlides = viewer.querySelectorAll('[data-gallery-info]'); 27 const prevBtn = viewer.querySelector('[data-gallery-prev]'); 28 const nextBtn = viewer.querySelector('[data-gallery-next]'); 29 const fullscreenBtn = viewer.querySelector('[data-gallery-fullscreen]'); 30 const lightbox = viewer.querySelector('[data-gallery-lightbox]'); 31 const lightboxContent = viewer.querySelector('[data-gallery-lightbox-content]'); 32 const lightboxCloseBtn = viewer.querySelector('[data-gallery-lightbox-close]'); 33 const onePageMobileLayoutQuery = window.matchMedia(MOBILE_BREAKPOINT_QUERY); 34 const reducedMotionQuery = window.matchMedia('(prefers-reduced-motion: reduce)'); 35 36 if (!track || originalSlides.length === 0) return; 37 38 const slideCount = originalSlides.length; 39 const createClone = (slide) => { 40 const clone = slide.cloneNode(true); 41 clone.setAttribute('aria-hidden', 'true'); 42 clone.dataset.galleryClone = TRUE_DATA_VALUE; 43 clone.querySelectorAll('img').forEach((image) => { 44 image.loading = 'lazy'; 45 image.setAttribute('loading', 'lazy'); 46 image.fetchPriority = 'auto'; 47 }); 48 return clone; 49 }; 50 51 if (slideCount > 1) { 52 track.prepend(createClone(originalSlides[slideCount - 1])); 53 track.append(createClone(originalSlides[0])); 54 } 55 56 let currentIndex = 0; 57 let currentTrackIndex = slideCount > 1 ? 1 : 0; 58 let isAnimating = false; 59 let pendingTransition = null; 60 let resizeFrameId = 0; 61 let scrollSyncFrameId = 0; 62 let embedMetricFrameId = 0; 63 let facebookEmbedFrameId = 0; 64 let wasOnePageMobileLayoutActive = isOnePageMobileLayoutActive(); 65 let lightboxSlideState = null; 66 let lastTrackedUpScoreIndex = null; 67 let navigationCount = 0; 68 let advertReloadNavigationInterval = DEFAULT_ADVERT_RELOAD_NAVIGATION_INTERVAL; 69 const facebookEmbeds = createFacebookEmbedController(originalSlides); 70 const lazyAdLoader = createLazyAdLoader({ 71 owner: viewer, 72 loadAdvert(containerElement) { 73 if (typeof window.burdaAds?.addAdvert !== 'function') { 74 return; 75 } 76 77 window.burdaAds.addAdvert({containerElement}); 78 } 79 }); 80 const embedMutationObserver = typeof window.MutationObserver === 'function' 81 ? new MutationObserver(scheduleCurrentEmbedMetricSync) 82 : null; 83 const embedResizeObserver = typeof window.ResizeObserver === 'function' 84 ? new ResizeObserver(scheduleCurrentEmbedMetricSync) 85 : null; 86 87 function isOnePageMobileLayoutActive() { 88 return supportsOnePageMobileLayout && onePageMobileLayoutQuery.matches; 89 } 90 91 function resolveAdvertReloadNavigationInterval() { 92 const burdaAdsQueue = window.burdaAds?.queue; 93 if (typeof burdaAdsQueue?.push !== 'function') { 94 return; 95 } 96 97 burdaAdsQueue.push(async () => { 98 if (typeof window.burdaAds?.getGalleryConfig !== 'function') { 99 return; 100 } 101
102 try { 103 const galleryConfig = await window.burdaAds.getGalleryConfig(); 104 105 const reloadAllFrequency = galleryConfig?.reloadAllFrequency; 106 107 if (Number.isInteger(reloadAllFrequency) && reloadAllFrequency >= 0) { 108 advertReloadNavigationInterval = reloadAllFrequency; 109 } 110 } catch { 111 // Keep the fallback frequency when the BurdaAds configuration cannot be resolved. 112 } 113 }); 114 } 115 116 function reloadAdvertsAfterNavigationInterval() { 117 navigationCount++; 118 119 if ( 120 advertReloadNavigationInterval === 0 121 || navigationCount % advertReloadNavigationInterval !== 0 122 ) { 123 return; 124 } 125 126 const burdaAdsQueue = window.burdaAds?.queue; 127 if (typeof burdaAdsQueue?.push !== 'function') { 128 return; 129 } 130 131 burdaAdsQueue.push(() => { 132 if (typeof window.burdaAds?.reloadAdverts === 'function') { 133 window.burdaAds.reloadAdverts(); 134 } 135 }); 136 } 137 138 function isUpScoreReady() { 139 return window.upScoreInitialized === true && typeof window.upScore === 'function'; 140 } 141 142 function trackUpScoreGallerySlide(index) { 143 if (!Number.isInteger(index) || index < 0 || index >= slideCount || lastTrackedUpScoreIndex === index) { 144 return; 145 } 146 147 if (!isUpScoreReady()) { 148 return; 149 } 150 151 window.upScore('gallery_view', { 152 image: index + 1 153 }); 154 lastTrackedUpScoreIndex = index; 155 } 156 157 function isLightboxOpen() { 158 return Boolean(lightbox) && lightbox.hidden === false; 159 } 160 161 function supportsLightbox(index) { 162 return originalSlides[index]?.dataset.gallerySupportsLightbox === TRUE_DATA_VALUE; 163 } 164 165 function isEmbedSlide(index) { 166 return !supportsLightbox(index) && Boolean(originalSlides[index]?.querySelector(EMBED_SELECTOR)); 167 } 168 169 function requiresProviderEmbedProcessing(index) { 170 return originalSlides[index]?.dataset.galleryRequiresSocialEmbed === TRUE_DATA_VALUE; 171 } 172 173 function getEmbedProvider(index) { 174 return originalSlides[index]?.dataset.galleryEmbedProvider || ''; 175 } 176 177 function prefersReducedMotion() { 178 return reducedMotionQuery.matches; 179 } 180 181 function clearStageMetrics() { 182 if (!stage) { 183 return; 184 } 185 186 stage.style.removeProperty('--gallery-stage-width'); 187 stage.style.removeProperty('--gallery-stage-height'); 188 stage.style.removeProperty('--gallery-stage-rendered-height'); 189 } 190 191 function getSlideDimensions(index) { 192 return { 193 width: parsePositiveInteger(originalSlides[index]?.dataset.galleryWidth, 1), 194 height: parsePositiveInteger(originalSlides[index]?.dataset.galleryHeight, 1) 195 }; 196 } 197 198 function getMeasuredEmbedHeight(index) { 199 if (!isEmbedSlide(index)) { 200 return 0; 201 } 202 203 const embed = originalSlides[index]?.querySelector(EMBED_SELECTOR); 204 const embedContent = embed?.querySelector(EMBED_CONTENT_SELECTOR); 205 if (!embed) { 206 return 0; 207 } 208 209 const measuredHeight = Math.max( 210 embed.getBoundingClientRect().height, 211 embed.scrollHeight, 212 embedContent?.getBoundingClientRect().height || 0, 213 embedContent?.scrollHeight || 0 214 ); 215 216 return Number.isFinite(measuredHeight) && measuredHeight > 0 ? measuredHeight : 0; 217 } 218 219 function syncEmbedLayout(index) { 220 if (!isEmbedSlide(index)) { 221 return; 222 } 223 224 syncStageMetrics(index); 225 } 226 227 function getAvailableSlideWidth() { 228 return imageContainer?.getBoundingClientRect().width 229 || main?.getBoundingClientRect().width 230 || stage?.getBoundingClientRect().width 231 || viewer.getBoundingClientRect().width 232 || 1; 233 } 234 235 function getImageStageMetrics(index) { 236 const {width: intrinsicWidth, height: intrinsicHeight} = getSlideDimensions(index); 237 const availableWidth = getAvailableSlideWidth(); 238 239 return { 240 intrinsicWidth, 241 intrinsicHeight, 242 renderedHeight: availableWidth * intrinsicHeight / intrinsicWidth 243 }; 244 } 245 246 function getEmbedStageMetrics(index) { 247 const availableWidth = getAvailableSlideWidth(); 248 const measuredHeight = getMeasuredEmbedHeight(index); 249 250 return { 251 intrinsicWidth: availableWidth, 252 intrinsicHeight: Math.max(Math.round(measuredHeight), 1), 253 renderedHeight: Math.max(measuredHeight, 1) 254 }; 255 } 256 257 function syncStageMetrics(index) { 258 if (!stage || !originalSlides[index]) { 259 return; 260 } 261 262 if (isOnePageMobileLayoutActive()) { 263 clearStageMetrics(); 264 return; 265 } 266 267 const metrics = isEmbedSlide(index) ? getEmbedStageMetrics(index) : getImageStageMetrics(index); 268 stage.style.setProperty('--gallery-stage-width', String(metrics.intrinsicWidth)); 269 stage.style.setProperty('--gallery-stage-height', String(metrics.intrinsicHeight)); 270 stage.style.setProperty('--gallery-stage-rendered-height', `${metrics.renderedHeight}px`); 271 } 272 273 function scheduleCurrentEmbedMetricSync() { 274 window.cancelAnimationFrame(embedMetricFrameId); 275 embedMetricFrameId = window.requestAnimationFrame(() => { 276 syncEmbedLayout(currentIndex); 277 }); 278 } 279 280 function observeSocialEmbeds() { 281 if (!embedMutationObserver && !embedResizeObserver) { 282 return; 283 } 284
285 originalSlides.forEach((slide, index) => { 286 if (!isEmbedSlide(index)) { 287 return; 288 } 289 290 const embed = slide.querySelector(EMBED_SELECTOR); 291 const embedContent = slide.querySelector(EMBED_CONTENT_SELECTOR); 292 293 if (embedMutationObserver) { 294 embedMutationObserver.observe(slide, { 295 attributes: true, 296 childList: true, 297 subtree: true, 298 attributeFilter: ['class', 'height', 'style'] 299 }); 300 } 301 302 if (embedResizeObserver) { 303 if (embed) { 304 embedResizeObserver.observe(embed); 305 } 306 307 if (embedContent) { 308 embedResizeObserver.observe(embedContent); 309 } 310 } 311 }); 312 } 313 314 function setTrackTransformWithoutTransition(transformValue) { 315 track.classList.add(TRACK_NO_TRANSITION_CLASS); 316 track.style.transform = transformValue; 317 void track.offsetWidth; 318 track.classList.remove(TRACK_NO_TRANSITION_CLASS); 319 } 320 321 function setTrackPosition(trackIndex, animate) { 322 currentTrackIndex = trackIndex; 323 324 if (isOnePageMobileLayoutActive()) { 325 setTrackTransformWithoutTransition(''); 326 return; 327 } 328 329 const offset = getAvailableSlideWidth() * trackIndex; 330 const transformValue = `translate3d(${-offset}px, 0, 0)`; 331 332 if (!animate || prefersReducedMotion()) { 333 setTrackTransformWithoutTransition(transformValue); 334 return; 335 } 336 337 track.style.transform = transformValue; 338 } 339 340 function getRealTrackIndex(index) { 341 return slideCount > 1 ? index + 1 : index; 342 } 343 344 function syncFullscreenAvailability(index) { 345 if (!fullscreenBtn) { 346 return; 347 } 348 349 fullscreenBtn.hidden = isOnePageMobileLayoutActive() || !supportsLightbox(index); 350 } 351 352 function processProviderEmbed(index) { 353 if (!requiresProviderEmbedProcessing(index)) { 354 return; 355 } 356 357 processSocialEmbedProvider(getEmbedProvider(index)); 358 } 359 360 function refreshEmbed(index) { 361 if (!isEmbedSlide(index)) { 362 return; 363 } 364 365 processProviderEmbed(index); 366 syncEmbedLayout(index); 367 368 EMBED_REFRESH_DELAYS_MS.forEach((delay) => { 369 window.setTimeout(() => { 370 if (currentIndex !== index) { 371 return; 372 } 373 374 processProviderEmbed(index); 375 syncEmbedLayout(index); 376 }, delay); 377 }); 378 } 379 380 function initializeFacebookEmbeds() { 381 window.cancelAnimationFrame(facebookEmbedFrameId); 382 facebookEmbedFrameId = window.requestAnimationFrame(() => { 383 facebookEmbedFrameId = window.requestAnimationFrame(() => { 384 facebookEmbeds.initializeAll(); 385 }); 386 }); 387 } 388 389 function setActiveInfo(index) { 390 infoSlides.forEach((info, infoIndex) => { 391 info.classList.toggle('gallery-viewer__info-slide--active', infoIndex === index); 392 }); 393 } 394 395 function setDisplayedSlide(index) { 396 const isOnePageMobileLayout = isOnePageMobileLayoutActive(); 397 398 originalSlides.forEach((slide, slideIndex) => { 399 slide.setAttribute('aria-hidden', isOnePageMobileLayout || slideIndex === index ? 'false' : 'true'); 400 }); 401 } 402 403 function restoreLightboxSlide() { 404 if (!lightboxSlideState) { 405 return; 406 } 407 408 const {slide, placeholder} = lightboxSlideState; 409 slide.removeAttribute('data-gallery-lightbox-active'); 410 411 if (placeholder.parentNode) { 412 placeholder.parentNode.insertBefore(slide, placeholder); 413 placeholder.remove(); 414 } 415 416 lightboxSlideState = null; 417 } 418 419 function moveSlideToLightbox(index) { 420 const slide = originalSlides[index]; 421 const slideParent = slide?.parentNode; 422 if (!lightboxContent || !slide || !slideParent) { 423 return; 424 } 425 426 if (lightboxSlideState?.slide === slide) { 427 return; 428 } 429 430 restoreLightboxSlide(); 431 432 const {width, height} = getSlideDimensions(index); 433 lightboxContent.style.setProperty('--gallery-lightbox-width', String(width)); 434 lightboxContent.style.setProperty('--gallery-lightbox-height', String(height)); 435 436 const placeholder = document.createComment('gallery lightbox slide placeholder'); 437 slideParent.insertBefore(placeholder, slide); 438 slide.dataset.galleryLightboxActive = TRUE_DATA_VALUE; 439 slide.setAttribute('aria-hidden', 'false'); 440 lightboxContent.append(slide); 441 lightboxSlideState = {slide, placeholder}; 442 } 443 444 function syncLightboxSlide(index) { 445 if (isLightboxOpen()) { 446 moveSlideToLightbox(index); 447 } 448 } 449 450 function openLightbox() {
451 if (!lightbox || isAnimating || isOnePageMobileLayoutActive() || !supportsLightbox(currentIndex)) { 452 return; 453 } 454 455 moveSlideToLightbox(currentIndex); 456 lightbox.hidden = false; 457 document.body.classList.add('gallery-lightbox-open'); 458 lightboxCloseBtn?.focus(); 459 } 460 461 function closeLightbox() { 462 if (!lightbox) { 463 return; 464 } 465 466 lightbox.hidden = true; 467 restoreLightboxSlide(); 468 setDisplayedSlide(currentIndex); 469 setTrackPosition(getRealTrackIndex(currentIndex), false); 470 document.body.classList.remove('gallery-lightbox-open'); 471 if (fullscreenBtn && !fullscreenBtn.hidden) { 472 fullscreenBtn.focus(); 473 } 474 } 475 476 function isAdjacentTransition(fromIndex, targetIndex) { 477 if (slideCount <= 1) { 478 return false; 479 } 480 481 return targetIndex === (fromIndex + 1) % slideCount 482 || targetIndex === (fromIndex - 1 + slideCount) % slideCount; 483 } 484 485 function resolveDirection(fromIndex, targetIndex) { 486 const forwardSteps = (targetIndex - fromIndex + slideCount) % slideCount; 487 const backwardSteps = (fromIndex - targetIndex + slideCount) % slideCount; 488 489 return forwardSteps <= backwardSteps ? 'next' : 'prev'; 490 } 491 492 function applyCurrentSlideState(index, options = {}) { 493 const {updateHashValue = false} = options; 494 495 setDisplayedSlide(index); 496 setActiveInfo(index); 497 syncLightboxSlide(index); 498 syncFullscreenAvailability(index); 499 500 refreshEmbed(index); 501 502 if (updateHashValue) { 503 replaceGalleryHash(index); 504 } 505 506 trackUpScoreGallerySlide(index); 507 } 508 509 function finalizeSlideChange(index) { 510 applyCurrentSlideState(index); 511 setTrackPosition(getRealTrackIndex(index), false); 512 } 513 514 function flushPendingTransition() { 515 if (!pendingTransition) { 516 return; 517 } 518 519 const nextTransition = pendingTransition; 520 pendingTransition = null; 521 showSlide(nextTransition.index, nextTransition.options); 522 } 523 524 function getAnimatedTrackIndex(fromIndex, targetIndex, direction) { 525 if (slideCount <= 1) { 526 return 0; 527 } 528 529 if (direction === 'next' && fromIndex === slideCount - 1 && targetIndex === 0) { 530 return slideCount + 1; 531 } 532 533 if (direction === 'prev' && fromIndex === 0 && targetIndex === slideCount - 1) { 534 return 0; 535 } 536 537 return getRealTrackIndex(targetIndex); 538 } 539 540 function animateSlideChange(targetIndex, trackIndex) { 541 let isFinished = false; 542 543 const finish = () => { 544 if (isFinished) { 545 return; 546 } 547 548 isFinished = true; 549 track.removeEventListener('transitionend', handleTransitionEnd); 550 finalizeSlideChange(targetIndex); 551 isAnimating = false; 552 flushPendingTransition(); 553 }; 554 555 const handleTransitionEnd = (event) => { 556 if (event.target === track && event.propertyName === 'transform') { 557 finish(); 558 } 559 }; 560 561 track.addEventListener('transitionend', handleTransitionEnd); 562 setTrackPosition(trackIndex, true); 563 window.setTimeout(finish, ANIMATION_DURATION_MS + 80); 564 } 565 566 function scrollToOnePageSlide(index) { 567 originalSlides[index]?.scrollIntoView({ 568 behavior: 'auto', 569 block: 'start' 570 }); 571 } 572 573 function getMostVisibleOnePageSlideIndex() { 574 const viewportHeight = window.innerHeight || document.documentElement.clientHeight || 0; 575 const viewportCenter = viewportHeight / 2; 576 let bestIndex = null; 577 let bestDistance = Number.POSITIVE_INFINITY; 578 579 originalSlides.forEach((slide, index) => { 580 const rect = slide.getBoundingClientRect(); 581 if (rect.bottom <= 0 || rect.top >= viewportHeight) { 582 return; 583 } 584 585 const distanceFromViewportCenter = Math.abs((rect.top + (rect.height / 2)) - viewportCenter); 586 if (distanceFromViewportCenter < bestDistance) { 587 bestDistance = distanceFromViewportCenter; 588 bestIndex = index; 589 } 590 }); 591 592 return bestIndex; 593 } 594 595 function syncCurrentSlideFromScroll() { 596 if (!isOnePageMobileLayoutActive()) { 597 return; 598 } 599 600 const visibleIndex = getMostVisibleOnePageSlideIndex(); 601 if (visibleIndex === null || visibleIndex === currentIndex) { 602 return; 603 } 604 605 currentIndex = visibleIndex; 606 applyCurrentSlideState(visibleIndex, {updateHashValue: true}); 607 } 608 609 function scheduleOnePageScrollSync() { 610 if (!isOnePageMobileLayoutActive()) { 611 return; 612 } 613 614 window.cancelAnimationFrame(scrollSyncFrameId); 615 scrollSyncFrameId = window.requestAnimationFrame(syncCurrentSlideFromScroll); 616 } 617 618 function prepareOnePageLazyAds() { 619 if (!supportsOnePageMobileLayout) { 620 return; 621 } 622 623 lazyAdLoader.prepare(); 624 } 625 626 function applyOnePageMobileSlideChange(index, options = {}) { 627 const { 628 updateHashValue = true, 629 scrollIntoView = false 630 } = options; 631 632 if (!supportsLightbox(index) && isLightboxOpen()) { 633 closeLightbox(); 634 } 635 636 syncStageMetrics(index); 637 applyCurrentSlideState(index, {updateHashValue}); 638 639 if (scrollIntoView) { 640 scrollToOnePageSlide(index); 641 } 642 } 643 644 function showSlide(index, options = {}) { 645 if (!originalSlides[index]) { 646 return; 647 } 648 649 const { 650 direction = resolveDirection(currentIndex, index), 651 animate = true, 652 updateHashValue = true, 653 scrollIntoView = false 654 } = options; 655 656 const previousIndex = currentIndex; 657 currentIndex = index; 658 659 if (isOnePageMobileLayoutActive()) { 660 applyOnePageMobileSlideChange(index, {updateHashValue, scrollIntoView}); 661 return; 662 } 663 664 if (updateHashValue) { 665 replaceGalleryHash(index); 666 } 667 668 if (isAnimating) { 669 pendingTransition = { 670 index, 671 options: { 672 direction, 673 animate, 674 updateHashValue: false, 675 scrollIntoView: false 676 } 677 }; 678 return; 679 } 680 681 syncStageMetrics(index); 682 syncFullscreenAvailability(index); 683 syncEmbedLayout(index); 684 685 if (!supportsLightbox(index) && isLightboxOpen()) { 686 closeLightbox(); 687 } 688 689 const shouldAnimate = animate 690 && !prefersReducedMotion() 691 && index !== previousIndex 692 && isAdjacentTransition(previousIndex, index); 693 694 if (!shouldAnimate) { 695 finalizeSlideChange(index); 696 return; 697 } 698 699 isAnimating = true; 700 animateSlideChange(index, getAnimatedTrackIndex(previousIndex, index, direction)); 701 } 702 703 if (prevBtn) { 704 prevBtn.addEventListener('click', () => { 705 if (isOnePageMobileLayoutActive()) { 706 return; 707 } 708 709 const newIndex = currentIndex > 0 ? currentIndex - 1 : slideCount - 1; 710 reloadAdvertsAfterNavigationInterval(); 711 showSlide(newIndex, {direction: 'prev'}); 712 }); 713 } 714 715 if (nextBtn) { 716 nextBtn.addEventListener('click', () => { 717 if (isOnePageMobileLayoutActive()) { 718 return; 719 } 720 721 const newIndex = currentIndex < slideCount - 1 ? currentIndex + 1 : 0; 722 reloadAdvertsAfterNavigationInterval(); 723 showSlide(newIndex, {direction: 'next'}); 724 }); 725 } 726 727 fullscreenBtn?.addEventListener('click', openLightbox); 728 lightboxCloseBtn?.addEventListener('click', closeLightbox); 729 lightbox?.addEventListener('click', (e) => { 730 if (e.target === lightbox) { 731 closeLightbox(); 732 } 733 }); 734 735 // Keyboard navigation 736 document.addEventListener('keydown', (e) => { 737 if (e.key === 'Escape' && isLightboxOpen()) { 738 closeLightbox(); 739 return; 740 } 741 742 if (isLightboxOpen()) { 743 return; 744 } 745 746 if (isOnePageMobileLayoutActive()) { 747 return; 748 } 749 750 if (e.key === 'ArrowLeft' && prevBtn) { 751 prevBtn.click(); 752 } else if (e.key === 'ArrowRight' && nextBtn) { 753 nextBtn.click(); 754 } 755 }); 756 757 const initialIndex = getGalleryIndexFromHash(slideCount); 758 resolveAdvertReloadNavigationInterval(); 759 showSlide(initialIndex ?? 0, { 760 animate: false, 761 updateHashValue: false, 762 scrollIntoView: initialIndex !== null && isOnePageMobileLayoutActive() 763 }); 764 viewer.classList.add('gallery-viewer--initialized'); 765 document.documentElement.classList.remove('gallery-fragment-pending'); 766 767 if (isOnePageMobileLayoutActive()) { 768 scheduleOnePageScrollSync(); 769 } 770 771 trackUpScoreGallerySlide(currentIndex); 772 document.addEventListener(UPSCORE_INITIALIZED_EVENT, () => { 773 trackUpScoreGallerySlide(currentIndex); 774 }); 775 776 prepareOnePageLazyAds(); 777 observeSocialEmbeds(); 778 initializeFacebookEmbeds(); 779 780 window.addEventListener('hashchange', () => { 781 const hashIndex = getGalleryIndexFromHash(slideCount); 782 if (hashIndex === null) { 783 return; 784 } 785 786 if (isOnePageMobileLayoutActive()) { 787 showSlide(hashIndex, { 788 animate: false, 789 updateHashValue: false, 790 scrollIntoView: true 791 }); 792 return; 793 } 794 795 if (hashIndex !== currentIndex) { 796 showSlide(hashIndex, { 797 animate: true, 798 direction: resolveDirection(currentIndex, hashIndex), 799 updateHashValue: false 800 }); 801 } 802 }); 803 804 window.addEventListener('scroll', () => { 805 scheduleOnePageScrollSync(); 806 }, {passive: true}); 807 808 window.addEventListener('resize', () => { 809 window.cancelAnimationFrame(resizeFrameId); 810 resizeFrameId = window.requestAnimationFrame(() => { 811 if (isOnePageMobileLayoutActive()) { 812 setTrackPosition(getRealTrackIndex(currentIndex), false); 813 scheduleOnePageScrollSync(); 814 return; 815 } 816 817 syncStageMetrics(currentIndex); 818 setTrackPosition(currentTrackIndex, false); 819 }); 820 }); 821 822 const handleOnePageMobileLayoutChange = () => { 823 const isOnePageMobileLayoutNowActive = isOnePageMobileLayoutActive(); 824 if (isOnePageMobileLayoutNowActive === wasOnePageMobileLayoutActive) { 825 return; 826 } 827 828 wasOnePageMobileLayoutActive = isOnePageMobileLayoutNowActive; 829 facebookEmbeds.resetAll(); 830 831 if (isLightboxOpen()) { 832 closeLightbox(); 833 } 834 835 const targetIndex = getGalleryIndexFromHash(slideCount) ?? currentIndex; 836 showSlide(targetIndex, { 837 animate: false, 838 updateHashValue: false, 839 scrollIntoView: isOnePageMobileLayoutNowActive 840 }); 841 initializeFacebookEmbeds(); 842 }; 843 844 if (typeof onePageMobileLayoutQuery.addEventListener === 'function') { 845 onePageMobileLayoutQuery.addEventListener('change', handleOnePageMobileLayoutChange); 846 } else { 847 onePageMobileLayoutQuery.addListener(handleOnePageMobileLayoutChange); 848 } 849}
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.