1var YEAR2026 = (function () { 2 'use strict'; 3 4 function throttle(func, waitMs) { 5 var lastTime = 0; 6 var timeoutId = null; 7 var lastArgs = null; 8 var lastThis = null; 9 10 return function () { 11 var now = Date.now(); 12 var remaining = waitMs - (now - lastTime); 13 lastArgs = arguments; 14 lastThis = this; 15 16 if (remaining <= 0) { 17 if (timeoutId) { 18 clearTimeout(timeoutId); 19 timeoutId = null; 20 } 21 lastTime = now; 22 func.apply(lastThis, lastArgs); 23 lastArgs = null; 24 lastThis = null; 25 return; 26 } 27 28 if (timeoutId) { 29 return; 30 } 31 32 timeoutId = setTimeout(function () { 33 timeoutId = null; 34 lastTime = Date.now(); 35 func.apply(lastThis, lastArgs); 36 lastArgs = null; 37 lastThis = null; 38 }, remaining); 39 }; 40 } 41 42 function YEAR2026() { 43 // enforces new 44 if (!(this instanceof YEAR2026)) { 45 var a = new YEAR2026(); 46 return a; 47 } 48 } 49 50 YEAR2026.prototype.clearUrlHash = function () { 51 if (!window.location.hash) { 52 return; 53 } 54 // Preserve path + query, remove hash so refresh won't jump. 55 window.history.replaceState({}, '', window.location.href.split('#')[0]); 56 } 57 58 YEAR2026.prototype.clearUrlHashIfPageAnchor = function () { 59 if (!window.location.hash) { 60 return; 61 } 62 63 // Don't interfere with modal/history hashes. 64 if (window.location.hash === '#modal-open' || window.location.hash === '#') { 65 return; 66 } 67 68 var hashId = window.location.hash.replace('#', ''); 69 if (!hashId) { 70 return; 71 } 72 73 // Only clear hashes that point to an in-page element. 74 if (document.getElementById(hashId)) { 75 this.clearUrlHash(); 76 } 77 } 78 79 YEAR2026.prototype.clearUrlHashIfAnchorNotNearViewportTop = function () { 80 if (!window.location.hash) { 81 return; 82 } 83 84 // Don't interfere with modal/history hashes. 85 if (window.location.hash === '#modal-open' || window.location.hash === '#') { 86 return; 87 } 88 89 var hashId = window.location.hash.replace('#', ''); 90 if (!hashId) { 91 return; 92 } 93 94 var el = document.getElementById(hashId); 95 if (!el) { 96 return; 97 } 98 99 // If the anchored element is no longer near the viewport top, clear the hash. 100 // This covers "subsection" anchor navigation: once the user scrolls to another 101 // subsection, the old hash should not persist across refresh. 102 var rect = el.getBoundingClientRect(); 103 if (rect.top < -300 || rect.top > 300) { 104 this.clearUrlHash(); 105 } 106 } 107 108 YEAR2026.prototype.Init = function () { 109 var self = this; 110 111 // anti bot protection 112 if (lazySizesConfig.disabled) { 113 return; 114 } 115 116 this.globalTimelines = {}; 117 118 119 hasWebP().then(function () { 120 $('body').addClass('webp-support'); 121 }, function () { 122 $('body').addClass('no-webp-support'); 123 }); 124 125 this.initLazyload(); 126 this.fadeInBubble(); 127 128 setTimeout(function () { 129 EventTracker.trackScroll(); 130 }, 1000); 131 132 133 134 this.initIntro(); 135 this.initMutedAutoplayLoopVidoes(); 136 this.init4DAutoplayBehindLogos(); 137 this.initVideosModal('.year-in-review-2026-video__modal'); 138 139 this.initLoopAutoPlay(); 140 this.initVideos(); 141 this.initVideoAutoplay(); 142 this.stickyVideoAutoplay(); 143 this.initMutedAutoplayVidoes(); 144 this.initImageCompareTrigger(); 145 146 this.initLandmarkSlideShow(); 147 148 if (!this.isIosVersionAtMost17() && !globalGetParams.screencap) { 149 this.initSliderImages(); 150 151 this.initMainMenu() 152 this.initScrollPosition(); 153 this.initIntroScroll(); 154 155 this.initButtonFadein(); 156 if(!this.isMobile()) { 157 this.initFloats(); 158 this.initGrowingLines(); 159 } 160 161 this.initNav(); 162 this.initControls(); 163 164 165 166 this.initTechnologyForLife(); 167 this.initLRHHall(); 168 this.initLRHHallTeaser(); 169 170 this.initIASStats(); 171 172 this.initFEBCSlamTitles(); 173 174 175 this.initHouseWipe(); 176 this.initParallax(); 177 178 179 this.initBdGameWinners(); 180 181 this.initRotateImages(); 182 183 this.initEndCurious(); 184 185 this.initNetworkSection(); 186 187 // this.initIASCCHRtransition(); 188
189 // this.initBackgroundVideo(); 190 this.initMosaicRows(); 191 192 this.sectionReveal(); 193 194 this.initAwardsScroller(); 195 this.initUnstoppablePower(); 196 197 this.initScnNWBackground(); 198 Digital.Init('.digital-matrix'); 199 200 // year-in-review-2026-globe-inner 201 if(this.isChrome119OrLower() || this.isMobile()) { 202 $('.year-in-review-2026-globe-static').removeClass('d-none') 203 $('.year-in-review-2026-globe-wrapper').addClass('year-in-review-2026-globe-wrapper__static'); 204 205 gsap.timeline({ 206 scrollTrigger: { 207 trigger: '.year-in-review-2026-globe-inner', 208 start: '25% center', 209 endTrigger: '.year-in-review-2026--febc-end-trigger', 210 end: 'bottom bottom', 211 toggleActions: "play none resume reverse", 212 pin: true, 213 pinSpacing: false, 214 invalidateOnRefresh: true, 215 anticipatePin: 1, 216 } 217 }); 218 } else { 219 this.initLazyloadTHREEJS(); 220 } 221 222 loadjs([ 223 "/js-static/year-in-review-2026/stars.js?_=" + cacheId, 224 ]); 225 226 } else { 227 228 $('.year-in-review-2026-title--gradient').addClass("year-in-review-2026-title--gradient__disabled"); 229 gsap.set('.year-in-review-2026-first-images', { 230 autoAlpha: 1, 231 y: 0, 232 }); 233 234 $('.year-in-review-2026-lrh-hall-teaser, .year-in-review-2026-winners, .year-in-review-2026--lrh-hall-wrapper').css('height', 'auto'); 235 236 $('.year-in-review-2026--video-autoplay--lrh-hall, .year-in-review-2026--video-autoplay--lrh-hall, .year-in-review-2026-lrh-line-4-wrapper').css('position', 'relative'); 237 238 239 $('.year-in-review-2026-slam-wrapper').addClass('year-in-review-2026-slam-wrapper__disabled'); 240 $('.year-in-review-2026-slam-inner').css('height', 'auto'); 241 242 $('.year-in-review-2026-bd-game-winner').addClass('d-none'); 243 244 $('.year-in-review-2026-lrh-house').css({ 245 'margin-bottom': '40px', 246 }); 247 248 $('.year-in-review-2026--autoplay-video-replay-animation').remove(); 249 250 $('.year-in-review-2026--video-autoplay--lrh-hall, .year-in-review-2025-ias-stat, .year-2026-our-message-wrapper, .year-in-review-2026--our-tech-intro').css('position', 'relative'); 251 $('.digital-matrix, .year-2026-our-message-video').css('margin-top', '0'); 252 253 $('.year-2025-awards-wall-wrapper').css('height', 'auto'); 254 $('.year-2025--awards-row').css('display', 'block'); 255 256 $('.year-in-review-2026-4d-mosaic--image__featured').each(function (index, el) { 257 gsap.set(el, { 258 scale: 2.5, 259 zIndex: 1000, 260 }); 261 }); 262 263 $('.year-in-review-2026--video-autoplay-element').each(function (_, el) { 264 $(el).css('margin-top', '0px'); 265 }); 266 267 $('.year-in-review-2026-slam-in-sequence').each(function (_, el) { 268 gsap.set(el, { 269 autoAlpha: 1, 270 position: 'relative', 271 height: 'auto', 272 }); 273 }); 274 275 $('.year-in-review-2026-slam-wrapper').css('margin-top', '0px'); 276 277 $('.year-in-review-2026--intro-titles > div').each(function (_, el) { 278 279 gsap.set(el.querySelector('span'), { 280 autoAlpha: 1, 281 }); 282 }); 283 284 this.stickyVideoLastFrame(); 285 286 } 287 288 if(globalGetParams.stoppoints) { 289 var style = document.createElement('style'); 290 style.textContent = [ 291 ".navigation-step {", 292 " width: 100%;", 293 " height: 1px;", 294 " background: red;", 295 " z-index: 9999;", 296 "}" 297 ].join('\n'); 298 document.head.appendChild(style); 299 } 300 301 302 if (globalGetParams.dev) { 303 // load js "/plugins/gsapv3/GSDevTools.min.js", 304 loadjs([ 305 "/plugins/gsapv3/GSDevTools.min.js", 306 ], 'gsap-dev', { 307 success: function () { 308 GSDevTools.create(); 309 } 310 }); 311 } 312 313 if ($('.show-codes-button').length) { 314 $('.show-codes-button:not(.copyversion)').click(function (event) { 315 $('.image-code').toggleClass('hidden'); 316 }); 317 $('.mag-image-lazyload__abs').each(function (index, el) { 318 // console.log( $(el).find('img').attr('data-src') ); 319 if ($(el).find('source').length > 0) { 320 var src = $(el).find('source').attr('data-srcset'); 321 if (!src) { 322 return; 323 } 324 // most are _XX9999.jpg 325 var parts = src.match(/[-_](([0-9]+[a-zA-Z]+|[a-zA-Z]+[0-9]+)[0-9a-zA-Z]*)(-(inet|1))?(-r)?(_en)?.jpg/); 326 if (parts === null) { 327 // Some are DJI_0242 328 parts = src.match(/[-_]([A-Z]+_[0-9]+).jpg/); 329 } 330 if (parts && parts.length > 0) { 331 var code = parts[1]; 332 $(el).append('<div class="image-code hidden">' + code + '</div>') 333 } 334 } 335 }); 336 loadjs(['/plugins/bootstrap/js/bootstrap.bundle.min.js?_=' + cacheId], 'bootstrapjs', { async: false }); 337 loadjs.ready('bootstrapjs', function () { 338 $('.image-code').tooltip({ 339 html: true, 340 title: '<span class="icon-zoom-in"></span> VIEW FULL SCREEN', 341 placement: 'bottom', 342 }); 343 }); 344 345 // load up photoswipe 346 loadjs('/plugins/photoswipe/photoswipe.min.js?_=' + new Date().getTime()); 347 loadjs('/plugins/photoswipe/photoswipe-ui-default.min.js?_=' + new Date().getTime()); 348 349 loadjs(['css!/plugins/photoswipe/photoswipe.css?_=' + cacheId, 'css!/plugins/photoswipe/default-skin/default-skin.css?_=' + cacheId]); 350 351 $('.pswp__container').html('<div class="pswp__item"></div><div class="pswp__item"></div><div class="pswp__item"></div>'); 352 353 $(document).on('click', '.image-code', function (e) { 354 e.stopPropagation(); 355 e.stopImmediatePropagation(); 356 e.preventDefault(); 357 358 var $imageItem = $(this).closest('.mag-image-lazyload__abs'); 359 var src = $imageItem.find('img')[0].currentSrc.replace(/@w=[0-9]*/, ''); 360 // if(src[0] != "/") {
361 // src = "/" + src; 362 // } 363 // photoswipe zoom in 364 // console.log('src',src); 365 if (src) { 366 // var url = new URL(window.location.href); 367 // url.pathname = src; 368 var pswpElement = document.querySelectorAll('.pswp')[0]; 369 // build items array 370 var items = [ 371 { 372 // src: unescape(url.toString()), 373 src: src, 374 w: 0, 375 h: 0, 376 }, 377 ]; 378 379 // define options (if needed) 380 var options = { 381 index: 0, 382 shareEl: false, 383 history: false 384 }; 385 var gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, options); 386 387 // dynamicly set W & H 388 gallery.listen('gettingData', function (index, item) { 389 if (item.w < 1 || item.h < 1) { 390 var img = new Image(); 391 img.onload = function () { 392 item.w = this.width; 393 item.h = this.height; 394 gallery.updateSize(true); 395 }; 396 img.src = item.src; 397 } 398 }); 399 400 gallery.init(); 401 } 402 }); 403 404 } 405 406 var isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); 407 if (isSafari) { 408 $('body').addClass('safari'); 409 } 410 411 if (globalGetParams.id) { 412 window.scrollTo(0, $("#" + globalGetParams.id).offset().top - 150); 413 } 414 415 // on mouse wheel move 416 window.addEventListener('wheel', function (event) { 417 if (self.lastScrollAnimation) { 418 self.lastScrollAnimation.pause(); 419 } 420 }, { passive: false }); 421 } 422 423 YEAR2026.prototype.init4DAutoplayBehindLogos = function () { 424 var update = function () { 425 var containers = document.querySelectorAll('.bs-extender__4d-background > .relative'); 426 if (!containers || !containers.length) { 427 return; 428 } 429 430 var isMobile = window.matchMedia && window.matchMedia('(max-width: 575.98px)').matches; 431 var scale = isMobile ? 1.2 : 1.5; 432 var padding = isMobile ? 30 : 60; 433 var minSize = isMobile ? 280 : 420; 434 var maxSize = isMobile ? 440 : 760; 435 436 Array.prototype.forEach.call(containers, function (container) { 437 var autoplayEl = container.querySelector('.year-in-review-2026-autoplay--4d'); 438 if (!autoplayEl) { 439 return; 440 } 441 442 var logos = container.querySelectorAll('.year-in-review-2026-image-grid-logo'); 443 if (!logos || !logos.length) { 444 return; 445 } 446 447 var containerRect = container.getBoundingClientRect(); 448 if (!containerRect.width || !containerRect.height) { 449 return; 450 } 451 452 var autoplayRect = autoplayEl.getBoundingClientRect(); 453 var autoplayCx = autoplayRect.left + autoplayRect.width / 2; 454 var autoplayCy = autoplayRect.top + autoplayRect.height / 2; 455 456 var closestLogo = logos[0]; 457 var closestDist = Infinity; 458 Array.prototype.forEach.call(logos, function (logoEl) { 459 var r = logoEl.getBoundingClientRect(); 460 var cx = r.left + r.width / 2; 461 var cy = r.top + r.height / 2; 462 var dx = cx - autoplayCx; 463 var dy = cy - autoplayCy; 464 var dist = dx * dx + dy * dy; 465 if (dist < closestDist) { 466 closestDist = dist; 467 closestLogo = logoEl; 468 } 469 }); 470 471 var logoRect = closestLogo.getBoundingClientRect(); 472 var x = (logoRect.left + logoRect.width / 2) - containerRect.left; 473 var y = (logoRect.top + logoRect.height / 2) - containerRect.top; 474 var size = Math.max(logoRect.width, logoRect.height) * scale + padding; 475 size = Math.max(minSize, Math.min(maxSize, size)); 476 477 container.style.setProperty('--year-in-review-2026-4d-x', x + 'px'); 478 container.style.setProperty('--year-in-review-2026-4d-y', y + 'px'); 479 container.style.setProperty('--year-in-review-2026-4d-size', size + 'px'); 480 }); 481 }; 482 483 var updateThrottled = throttle(update, 120); 484 updateThrottled(); 485 486 window.addEventListener('resize', updateThrottled, { passive: true }); 487 window.addEventListener('load', updateThrottled, { passive: true }); 488 } 489 490 491 YEAR2026.prototype.initLazyloadTHREEJS = function () { 492 let hasExecuted = false;
493 494 // Function to load scripts and initialize ThreeJSGlobe 495 const initializeThreeJS = () => { 496 if (hasExecuted) return; // Prevent further execution 497 hasExecuted = true; 498 499 loadjs.ready('threejs', function () { 500 new ThreeJSGlobe(); 501 }); 502 503 if (!loadjs.isDefined('threejs')) { 504 loadjs([ 505 'module!/plugins/threejs/threejs-wrapper.js?_=' + cacheId, 506 // '/plugins/threejs/three-nebula.min.js?_=' + cacheId, 507 '/plugins/threejs/globe-2.34.2.gl.min.js?_=' + cacheId, 508 '/js-static/year-in-review-2026/threejs.js?_=' + cacheId, 509 ], 'threejs', { 510 async: false, 511 }); 512 } 513 514 }; 515 516 const observerOptions = { 517 root: null, // Use the viewport as the root 518 rootMargin: '0px 0px 500px 0px', // Adjusts the viewport margin 519 threshold: 0, 520 }; 521 522 // Create IntersectionObserver 523 const observer = new IntersectionObserver((entries, observer) => { 524 entries.forEach(entry => { 525 if (entry.isIntersecting) { 526 initializeThreeJS(); // Trigger initialization 527 observer.disconnect(); // Stop observing after the first trigger 528 } 529 }); 530 }, observerOptions); 531 532 // Elements to observe 533 const elementsToObserve = ['.year-in-review-2026-globe-wrapper']; 534 535 elementsToObserve.forEach(selector => { 536 const element = document.querySelector(selector); 537 if (element) { 538 observer.observe(element); 539 } 540 }); 541 } 542 543 YEAR2026.prototype.initImageCompareTrigger = function () { 544 // Guard if the slider initializer is missing 545 if (typeof initComparison !== 'function') { 546 return; 547 } 548 549 550 initComparison(); 551 552 var containers = document.querySelectorAll('.cd-image-container'); 553 if (!containers.length) { 554 return; 555 } 556 557 containers.forEach(function (container) { 558 ScrollTrigger.create({ 559 trigger: container, 560 start: 'top 50%', // run when this container is roughly half visible 561 once: true, 562 onEnter: function () { 563 container.click(); 564 } 565 }); 566 }); 567 } 568 569 YEAR2026.prototype.resizeAnimations = function () { 570 var resizeEvent = new CustomEvent('resize'); 571 // Dispatch the event 572 window.dispatchEvent(resizeEvent); 573 } 574 575 YEAR2026.prototype.isChrome119OrLower = function () { 576 var userAgent = navigator.userAgent; 577 var chromeAgent = userAgent.indexOf("Chrome") > -1; 578 579 if (chromeAgent) { 580 var chromeVersion = userAgent.match(/Chrome\/(\d+)/); 581 if (chromeVersion && chromeVersion.length > 1) { 582 var versionNumber = parseInt(chromeVersion[1], 10); 583 return versionNumber <= 119; 584 } 585 } 586 return false; 587 } 588 589 YEAR2026.prototype.initFloats = function () { 590 var self = this; 591 592 $('.year-in-review-2026--float-bottom').each(function () { 593 var $el = $(this); 594 var yOffset = 50; 595 var xOffset = 0; 596 var offset = 100; 597 var duration = 0.5; 598 var delay = 0; 599 var triggerElement = this; 600 601 if (typeof $el.data('trigger') != "undefined") { 602 triggerElement = $el.data('trigger'); 603 } 604 if (typeof $el.data('yoffset') != "undefined") { 605 yOffset = $el.data('yoffset'); 606 } 607 if (typeof $el.data('xoffset') != "undefined") { 608 xOffset = $el.data('xoffset'); 609 } 610 if (typeof $el.data('offset') != "undefined") { 611 offset = $el.data('offset'); 612 } 613 if (typeof $el.data('duration') != "undefined") { 614 duration = $el.data('duration'); 615 } 616 if (typeof $el.data('delay') != "undefined") {
617 delay = $el.data('delay'); 618 } 619 620 gsap.from(this, { 621 duration: duration, 622 delay: delay, 623 y: yOffset, 624 x: xOffset, 625 autoAlpha: 0, 626 ease: Power4.easeOut, 627 scrollTrigger: { 628 trigger: this, 629 toggleActions: "play pause resume reverse", 630 start: "+=" + offset + " bottom", 631 } 632 }); 633 // new ScrollMagic.Scene({ triggerElement: triggerElement, triggerHook: 1, reverse: true, offset: offset, duration: 0 }) 634 // .setTween(tween) 635 // .setClassToggle(this, 'visible') 636 // // .addIndicators() // add indicators (requires plugin) 637 // .addTo(self.controller); 638 }); 639 640 $('.year-2026--text-scale').each(function () { 641 var $el = $(this); 642 var duration = 0.5; 643 var delay = 0; 644 var offset = 100; 645 var scale = 3; 646 var triggerElement = this; 647 648 if (typeof $el.data('trigger') != "undefined") { 649 triggerElement = $el.data('trigger'); 650 } 651 if (typeof $el.data('duration') != "undefined") { 652 duration = $el.data('duration'); 653 } 654 if (typeof $el.data('offset') != "undefined") { 655 offset = $el.data('offset'); 656 } 657 if (typeof $el.data('delay') != "undefined") { 658 delay = $el.data('delay'); 659 } 660 if (typeof $el.data('scale') != "undefined") { 661 scale = $el.data('scale'); 662 } 663 664 gsap.from(this.querySelector('.year-2026--text-scale--inner'), { 665 duration: duration, 666 delay: delay, 667 scale: scale, 668 autoAlpha: 0, 669 ease: Power4.easeOut, 670 scrollTrigger: { 671 trigger: this, 672 toggleActions: "play pause resume reverse", 673 start: "+=" + offset + " bottom", 674 } 675 }); 676 // new ScrollMagic.Scene({ triggerElement: triggerElement, triggerHook: 1, reverse: true, offset: offset, duration: 0 }) 677 // .setTween(tween) 678 // .setClassToggle(this, 'visible') 679 // // .addIndicators() // add indicators (requires plugin) 680 // .addTo(self.controller); 681 }); 682 683 $('.year-in-review-2026--scale-in').each(function () { 684 var options = { 685 scale: 2, 686 duration: 2, 687 ease: "Power4.easeOut", 688 scrollTrigger: { 689 trigger: this, 690 toggleActions: "play pause resume reverse", 691 } 692 }; 693 if($(this).hasClass('year-in-review-2026--scale-in__center')) { 694 options.transformOrigin = "center"; 695 } 696 gsap.from(this.querySelector('img'), options); 697 gsap.from(this, 698 { 699 scale: 0.8, 700 duration: 2, 701 autoAlpha: 0, 702 y: 20, 703 ease: "Power4.easeOut", 704 scrollTrigger: { 705 trigger: this, 706 toggleActions: "play pause resume reverse", 707 } 708 }); 709 }); 710 711 $('.year-in-review-2026--scale-from').each(function () { 712 var options = { 713 scale: 3, 714 duration: 1, 715 autoAlpha: 0, 716 ease: "Power4.easeOut", 717 scrollTrigger: { 718 trigger: this, 719 toggleActions: "play pause resume reverse", 720 } 721 }; 722 gsap.from(this.querySelector('img'), options); 723 }); 724 } 725 726 YEAR2026.prototype.initHouseWipe = function () { 727 if (typeof gsap === 'undefined' || typeof ScrollTrigger === 'undefined') { 728 return; 729 } 730 731 var section = document.querySelector('.year-in-review-2025--lrh-landmarks'); 732 if (!section) { 733 return; 734 } 735 736 var wrapper = section.querySelector('.year-in-review-2025--lrh-landmark-images-wrapper'); 737 var images = wrapper ? wrapper.querySelectorAll('.year-in-review-2025--lrh-landmark-image') : []; 738 var columns = section.querySelectorAll('.year-in-review-2025--lrh-landmark-column'); 739 740 if (!wrapper || images.length < 3 || columns.length < 3) { 741 return; 742 } 743 744 ScrollTrigger.create({ 745 trigger: '.year-in-review-2025--lrh-landmark-image__first', 746 start: 'center center', 747 endTrigger: columns[2], 748 end: 'center center', 749 pin: wrapper, 750 anticipatePin: 1, 751 invalidateOnRefresh: true, 752 }); 753
754 images.forEach(function (img, index) { 755 gsap.set(img, { zIndex: index + 1 }); 756 }); 757 758 var wipes = [ 759 { column: columns[1], image: images[1] }, 760 { column: columns[2], image: images[2] }, 761 ]; 762 763 wipes.forEach(function (item) { 764 if (!item.column || !item.image) { 765 return; 766 } 767 768 gsap.fromTo(item.image, 769 { 770 clipPath: 'polygon(0 100%, 100% 100%, 100% 100%, 0 100%)', 771 }, 772 { 773 clipPath: 'polygon(0 0%, 100% 0%, 100% 100%, 0 100%)', 774 ease: 'none', 775 scrollTrigger: { 776 trigger: item.column, 777 start: 'top center+=30%', 778 end: 'center center+=10%', 779 scrub: true, 780 } 781 }); 782 }); 783 } 784 785 YEAR2026.prototype.initLandmarkSlideShow = function () { 786 gsap.timeline({ 787 scrollTrigger: { 788 trigger: ".year-in-review-2026-lrh-house-wrapper", 789 start: "top center+=20%", 790 toggleActions: "play none resume reverse", 791 } 792 }) 793 .from('.year-in-review-2026-lrh-house--inner', { 794 duration: 1, 795 autoAlpha: 0, 796 stagger: { 797 amount: 2, 798 from: 0 799 } 800 }) 801 802 var wrapper = document.querySelector('.year-in-review-2026-lrh-houses'); 803 if (!wrapper) { 804 return; 805 } 806 807 var slides = wrapper.querySelectorAll('.year-in-review-2026-lrh-house'); 808 if (!slides || !slides.length) { 809 return; 810 } 811 812 var nav = document.querySelector('.year-2026-lrh-houses-nav'); 813 if (!nav) { 814 return; 815 } 816 817 var prevBtn = nav.querySelector('.year-2026-lrh-houses--arrow__prev'); 818 var nextBtn = nav.querySelector('.year-2026-lrh-houses--arrow__next'); 819 if (!prevBtn || !nextBtn) { 820 return; 821 } 822 823 var isLikelyVisible = function (el) { 824 if (!el) { 825 return false; 826 } 827 // quick style checks 828 try { 829 var cs = window.getComputedStyle(el); 830 if (!cs || cs.display === 'none' || cs.visibility === 'hidden' || parseFloat(cs.opacity || '1') <= 0) { 831 return false; 832 } 833 } catch (e) { } 834 835 // viewport intersection check 836 var rect = el.getBoundingClientRect(); 837 if (!rect || (!rect.width && !rect.height)) { 838 return false; 839 } 840 var vw = window.innerWidth || document.documentElement.clientWidth || 0; 841 var vh = window.innerHeight || document.documentElement.clientHeight || 0; 842 // require at least a small portion visible 843 var threshold = 8; 844 var inX = rect.right > threshold && rect.left < (vw - threshold); 845 var inY = rect.bottom > threshold && rect.top < (vh - threshold); 846 return inX && inY; 847 }; 848 849 var shouldHandleKeys = function () { 850 // Only handle keys when the nav is on-screen. 851 if (!isLikelyVisible(nav)) { 852 return false; 853 } 854 // Don't steal arrow keys when typing/using form elements. 855 var ae = document.activeElement; 856 if (ae) { 857 var tag = (ae.tagName || '').toLowerCase(); 858 if (tag === 'input' || tag === 'textarea' || tag === 'select' || ae.isContentEditable) { 859 return false; 860 } 861 } 862 return true; 863 }; 864 865 // Prevent duplicate listeners if Init runs more than once. 866 if (wrapper._lrhCarousel && typeof wrapper._lrhCarousel.destroy === 'function') { 867 wrapper._lrhCarousel.destroy(); 868 } 869 870 var rtl = document.documentElement && document.documentElement.getAttribute('dir') === 'rtl'; 871 var viewport = wrapper.parentElement || document.body; 872 873 var state = { 874 index: 0, 875 step: 0, 876 maxIndex: 0, 877 bounds: { min: 0, max: 0 }, 878 }; 879 880 var getCurrentX = function () { 881 // All slides share the same x transform; read from the first. 882 try { 883 var v = gsap.getProperty(slides[0], 'x'); 884 return (typeof v === 'number' && !isNaN(v)) ? v : 0; 885 } catch (e) { 886 return 0; 887 } 888 }; 889 890 var setXImmediate = function (x) { 891 gsap.set(slides, { x: x, willChange: 'transform' }); 892 }; 893 894 var updateNavState = function () { 895 var atStart = state.index <= 0; 896 var atEnd = state.index >= state.maxIndex - 1; 897 898 if (prevBtn) { 899 prevBtn.classList.toggle('disabled', atStart); 900 prevBtn.setAttribute('aria-disabled', atStart ? 'true' : 'false'); 901 } 902 if (nextBtn) { 903 nextBtn.classList.toggle('disabled', atEnd); 904 nextBtn.setAttribute('aria-disabled', atEnd ? 'true' : 'false'); 905 } 906 }; 907 908 var computeMetrics = function () { 909 // Step = slide width + horizontal margins. 910 var first = slides[0]; 911 if (!first) { 912 return; 913 } 914 915 var style = window.getComputedStyle(first); 916 var marginLeft = parseFloat(style.marginLeft) || 0; 917 var marginRight = parseFloat(style.marginRight) || 0; 918
919 state.step = (first.offsetWidth || 0) + marginLeft + marginRight; 920 921 // Bounds based on scrollable overflow. 922 var viewportWidth = viewport.clientWidth || window.innerWidth || 0; 923 // var maxOffset = Math.max(0, (wrapper.scrollWidth || 0) - viewportWidth); 924 var maxOffset = Math.max(0, state.step * (slides.length - 1)); 925 926 state.bounds.min = -maxOffset; 927 state.bounds.max = 0; 928 929 if (!state.step || state.step <= 0) { 930 state.maxIndex = 0; 931 } else { 932 state.maxIndex = Math.max(0, Math.ceil(maxOffset / state.step)); 933 } 934 935 // Keep current index valid. 936 state.index = Math.max(0, Math.min(state.maxIndex, state.index)); 937 938 // Clamp current visual position (do NOT snap to state.index; that causes jumps when 939 // rapid clicks interrupt an in-flight tween). 940 var currentX = getCurrentX(); 941 var clampedX = gsap.utils.clamp(state.bounds.min, state.bounds.max, currentX); 942 if (clampedX !== currentX) { 943 setXImmediate(clampedX); 944 } 945 946 updateNavState(); 947 }; 948 949 // Ensure a deterministic starting position. 950 setXImmediate(0); 951 computeMetrics(); 952 953 var gotoIndex = function (targetIndex) { 954 computeMetrics(); 955 956 targetIndex = Math.max(0, Math.min(state.maxIndex, targetIndex)); 957 state.index = targetIndex; 958 959 updateNavState(); 960 961 var targetX = -1 * state.index * state.step; 962 targetX = gsap.utils.clamp(state.bounds.min, state.bounds.max, targetX); 963 964 var startX = getCurrentX(); 965 var dist = Math.abs(targetX - startX); 966 var isInterrupt = !!(wrapper._lrhCarouselTween && typeof wrapper._lrhCarouselTween.isActive === 'function' && wrapper._lrhCarouselTween.isActive()); 967 var perStep = isInterrupt ? 0.35 : 0.55; 968 var duration = state.step > 0 ? (dist / state.step) * perStep : 0.45; 969 duration = gsap.utils.clamp(0.2, 0.85, duration); 970 971 if (wrapper._lrhCarouselTween) { 972 try { wrapper._lrhCarouselTween.kill(); } catch (e) { } 973 wrapper._lrhCarouselTween = null; 974 } 975 976 wrapper._lrhCarouselTween = gsap.to(slides, { 977 x: targetX, 978 duration: duration, 979 ease: 'power3.inOut', 980 overwrite: true, 981 }); 982 }; 983 984 var onPrev = function (e) { 985 e && e.preventDefault && e.preventDefault(); 986 if (prevBtn && prevBtn.classList && prevBtn.classList.contains('disabled')) { 987 return; 988 } 989 gotoIndex(state.index - 1); 990 }; 991 992 var onNext = function (e) { 993 e && e.preventDefault && e.preventDefault(); 994 if (nextBtn && nextBtn.classList && nextBtn.classList.contains('disabled')) { 995 return; 996 } 997 gotoIndex(state.index + 1); 998 }; 999 1000 var onKeyDown = function (e) { 1001 if (!e) { 1002 return; 1003 } 1004 if (e.defaultPrevented) { 1005 return; 1006 } 1007 if (e.altKey || e.ctrlKey || e.metaKey) { 1008 return; 1009 } 1010 if (!shouldHandleKeys()) { 1011 return; 1012 } 1013 1014 // Support modern + older key values. 1015 var key = e.key || e.code || e.keyCode; 1016 if (key === 'ArrowLeft' || key === 'Left' || key === 37) { 1017 e.preventDefault(); 1018 // In RTL, ArrowLeft should advance visually to the "next" item. 1019 if (rtl) { 1020 onNext(e); 1021 } else { 1022 onPrev(e); 1023 } 1024 } 1025 if (key === 'ArrowRight' || key === 'Right' || key === 39) { 1026 e.preventDefault(); 1027 // In RTL, ArrowRight should go to the "previous" item. 1028 if (rtl) { 1029 onPrev(e); 1030 } else { 1031 onNext(e); 1032 } 1033 } 1034 }; 1035 1036 prevBtn.addEventListener('click', onPrev); 1037 nextBtn.addEventListener('click', onNext); 1038 window.addEventListener('keydown', onKeyDown, { passive: false }); 1039 1040 var onResize = throttle(function () { 1041 computeMetrics(); 1042 }, 150); 1043 1044 window.addEventListener('resize', onResize, { passive: true }); 1045 window.addEventListener('load', onResize, { passive: true }); 1046 1047 wrapper._lrhCarousel = { 1048 destroy: function () { 1049 try { prevBtn.removeEventListener('click', onPrev); } catch (e) { } 1050 try { nextBtn.removeEventListener('click', onNext); } catch (e) { } 1051 try { window.removeEventListener('keydown', onKeyDown); } catch (e) { } 1052 try { window.removeEventListener('resize', onResize); } catch (e) { } 1053 try { window.removeEventListener('load', onResize); } catch (e) { } 1054 if (wrapper._lrhCarouselTween) { 1055 try { wrapper._lrhCarouselTween.kill(); } catch (e) { } 1056 wrapper._lrhCarouselTween = null; 1057 } 1058 } 1059 }; 1060 } 1061 1062 YEAR2026.prototype.initLandmarkTimeline = function () { 1063 var section = document.querySelector('.year-in-review-2026-lrh-houses'); 1064 if (!section || typeof gsap === 'undefined' || typeof ScrollTrigger === 'undefined') { 1065 return; 1066 } 1067 1068 1069 return; 1070 1071 var houses = section.querySelectorAll('.year-in-review-2026-lrh-house'); 1072 if (!houses.length) { 1073 return; 1074 } 1075 1076 // Calculate offsets so each house centers in the viewport. 1077 var getMetrics = function () { 1078 var viewportWidth = window.innerWidth; 1079 var first = houses[0]; 1080 var last = houses[houses.length - 1]; 1081 var firstCenter = first.offsetLeft + (first.offsetWidth / 2); 1082 var lastCenter = last.offsetLeft + (last.offsetWidth / 2); 1083 1084 var startX = (viewportWidth / 2) - firstCenter; 1085 var endX = (viewportWidth / 2) - lastCenter; 1086 1087 var denominator = (endX - startX) || 1;
1087 // avoid divide-by-zero 1088 1089 var snapPoints = Array.from(houses).map(function (house) { 1090 var center = house.offsetLeft + (house.offsetWidth / 2); 1091 var targetX = (viewportWidth / 2) - center; 1092 return (targetX - startX) / denominator; 1093 }); 1094 1095 return { 1096 startX: startX, 1097 endX: endX, 1098 snapPoints: snapPoints 1099 }; 1100 }; 1101 1102 var timelines = section.querySelectorAll('.year-in-review-2026-lrh-house-timeline'); 1103 1104 gsap.timeline({ 1105 scrollTrigger: { 1106 trigger: ".year-in-review-2026-lrh-house-wrapper", 1107 start: "top center+=20%", 1108 toggleActions: "play none resume reverse", 1109 } 1110 }) 1111 .from('.year-in-review-2026-lrh-house-wrapper', { 1112 x: 500, 1113 duration: 2, 1114 ease: 'power4.out', 1115 autoAlpha: 0, 1116 }) 1117 1118 var timeline = gsap.timeline({ 1119 defaults: { 1120 ease: 'none' 1121 }, 1122 scrollTrigger: { 1123 trigger: ".year-in-review-2026-lrh-house-trigger", 1124 start: "top top", 1125 // duration: "100%", 1126 end: "bottom bottom", 1127 pin: '.year-in-review-2026-lrh-houses-pin', 1128 scrub: true, 1129 anticipatePin: 1, 1130 invalidateOnRefresh: true, 1131 pinSpacing: false, 1132 snap: { 1133 snapTo: function (value) { 1134 var metrics = getMetrics(); 1135 var snapper = gsap.utils.snap(metrics.snapPoints); 1136 return snapper(value); 1137 }, 1138 duration: 1, 1139 ease: "power1.inOut" 1140 } 1141 } 1142 }); 1143 1144 1145 timeline.fromTo( 1146 section, 1147 { 1148 x: function () { 1149 var metrics = getMetrics(); 1150 return metrics.startX; 1151 }, 1152 }, 1153 { 1154 x: function () { 1155 var metrics = getMetrics(); 1156 return metrics.endX; 1157 }, 1158 }, 1159 0 1160 ); 1161 1162 // Move timeline graphics at half the distance for a parallax-style offset. 1163 if (timelines.length) { 1164 timeline.fromTo( 1165 timelines, 1166 { 1167 x: function () { 1168 var metrics = getMetrics(); 1169 return -metrics.startX; 1170 }, 1171 }, 1172 { 1173 x: function () { 1174 var metrics = getMetrics(); 1175 return -0.5 * (metrics.endX - metrics.startX); 1176 }, 1177 }, 1178 0 1179 ); 1180 } 1181 1182 // Keep the first house centered on refresh/resizes before scrubbing begins. 1183 ScrollTrigger.addEventListener('refreshInit', function () { 1184 var metrics = getMetrics(); 1185 gsap.set(section, { x: metrics.startX }); 1186 }); 1187 } 1188 1189 YEAR2026.prototype.initBdGameWinners = function () { 1190 if (typeof gsap === 'undefined' || typeof ScrollTrigger === 'undefined') { 1191 return; 1192 } 1193 1194 var section = document.querySelector('.year-in-review-2026-winners'); 1195 if (!section) { 1196 return; 1197 } 1198 1199 var pinEl = section.querySelector('.year-in-review-2026-winners-inner') || section; 1200 1201 var imagesWrapper = section.querySelector('.year-in-review-2026-images'); 1202 var images = imagesWrapper ? Array.prototype.slice.call(imagesWrapper.querySelectorAll('.year-in-review-2026-bd-game-winner')) : []; 1203 1204 var featuredWrapper = section.querySelector('.year-in-review-2026-featured-image'); 1205 var title = section.querySelector('.year-in-review-2026-title'); 1206 var subtitle = section.querySelector('.year-in-review-2026-subtitle'); 1207 1208 if (!featuredWrapper || !images.length) { 1209 return; 1210 } 1211 1212 // Ensure only one instance exists. 1213 if (section._bdWinnersTl) { 1214 try { section._bdWinnersTl.scrollTrigger && section._bdWinnersTl.scrollTrigger.kill(true); } catch (e) { } 1215 try { section._bdWinnersTl.kill(); } catch (e) { } 1216 section._bdWinnersTl = null; 1217 } 1218 1219 // Kill any existing per-element ScrollTriggers created earlier (initFloats / scale-in triggers) 1220 // that would otherwise fight with this scrubbed timeline. 1221 var killTriggersForEl = function (el) { 1222 if (!el) { 1223 return; 1224 } 1225 try { 1226 gsap.killTweensOf(el); 1227 } catch (e) { } 1228 try {
1229 ScrollTrigger.getAll().forEach(function (st) { 1230 if (st && st.trigger === el) { 1231 st.kill(true); 1232 } 1233 }); 1234 } catch (e) { } 1235 }; 1236 1237 if (title) { 1238 killTriggersForEl(title); 1239 title.classList.remove('year-in-review-2026--float-bottom'); 1240 } 1241 if (subtitle) { 1242 killTriggersForEl(subtitle); 1243 subtitle.classList.remove('year-in-review-2026--float-bottom'); 1244 } 1245 1246 // Featured image often has `.year-in-review-2026--scale-in` on a nested wrapper. 1247 // Remove it within this section to avoid the generic scale-in ScrollTrigger. 1248 var featuredScaleIn = featuredWrapper.querySelector('.year-in-review-2026--scale-in'); 1249 if (featuredScaleIn) { 1250 killTriggersForEl(featuredScaleIn); 1251 featuredScaleIn.classList.remove('year-in-review-2026--scale-in'); 1252 } 1253 1254 // Deterministic pseudo-random dispersal: emit from center, fling in varied directions. 1255 var rand01 = function (index, salt) { 1256 var x = Math.sin((index + 1) * 999 + salt * 1013) * 10000; 1257 return x - Math.floor(x); 1258 }; 1259 1260 var getDisperse = function (globalIndex, localIndex, waveSize, waveNumber, multiplier) { 1261 var w = window.innerWidth || 1200; 1262 var h = window.innerHeight || 800; 1263 1264 // Keep images mostly within the viewport regardless of device size. 1265 // These clamps are conservative to avoid cropping (cards are ~300px wide). 1266 var clampX = gsap.utils.clamp(-w * 0.28, w * 0.28); 1267 var clampY = gsap.utils.clamp(-h * 0.20, h * 0.20); 1268 1269 var base = Math.min(w, h) * 0.34 * multiplier; 1270 1271 // Evenly spaced angles within each wave to avoid clustering/overlap. 1272 // Add a small deterministic jitter so it doesn't look too geometric. 1273 var twoPi = Math.PI * 2; 1274 var jitter = (rand01(globalIndex, 7) - 0.5) * (Math.PI / 10); 1275 var waveRot = (waveNumber * 0.9) + (rand01(waveNumber, 9) * 0.6); 1276 var angle = ((localIndex / Math.max(waveSize, 1)) * twoPi) + waveRot + jitter; 1277 1278 // Radius varies by localIndex so items in a wave don't sit on top of each other. 1279 var localSpread = 0.55 + (localIndex * 0.22); 1280 var radius = base * localSpread; 1281 1282 // Slight vertical bias so it feels more "thrown". 1283 var yBias = 0.85 + (rand01(globalIndex, 3) * 0.25); 1284 1285 var x = Math.cos(angle) * radius; 1286 var y = Math.sin(angle) * radius * yBias; 1287 1288 return { 1289 x: clampX(x), 1290 y: clampY(y), 1291 }; 1292 }; 1293 1294 // Helper: treat the stream as groups of 4 so only 4 positions are active at a time. 1295 // The group number rotates the scatter so it doesn't feel repetitive. 1296 var getGroupMetrics = function (i) { 1297 var groupIndex = Math.floor(i / 4); 1298 var groupStart = groupIndex * 4; 1299 var waveSize = Math.min(4, Math.max(1, images.length - groupStart)); 1300 var localIndex = i - groupStart; 1301 return { 1302 groupIndex: groupIndex, 1303 groupStart: groupStart, 1304 waveSize: waveSize, 1305 localIndex: localIndex, 1306 waveNumber: groupIndex + 1, 1307 }; 1308 }; 1309 1310 // Per-item offset so the stream doesn't look like repeating "waves of 4". 1311 // We still keep the 4-slot safety (via waveSize/localIndex), but add deterministic 1312 // jitter to angle/rotation so each new item lands slightly differently. 1313 var getStreamDisperse = function (i, multiplier) { 1314 var m = getGroupMetrics(i); 1315 var slotJitter = (rand01(i, 21) - 0.5) * 0.65; // breaks obvious 0-1-2-3 repetition 1316 var rotJitter = (rand01(i, 22) - 0.5) * 0.55; 1317 return getDisperse(i, m.localIndex + slotJitter, m.waveSize, m.waveNumber + rotJitter, multiplier); 1318 }; 1319 1320 // Set starting states. 1321 // Place winner cards already dispersed (near their final positions) s
1321o they 1322 // don't crash into each other while scaling in. 1323 var disperseMult = 1.45; 1324 // Start closer together so the cards travel more during the stream-in, 1325 // while still avoiding a single stacked center point. 1326 var disperseStartScale = 0.55; 1327 1328 var setInitialStates = function () { 1329 gsap.set(pinEl, { willChange: 'transform' }); 1330 gsap.set(featuredWrapper, { transformOrigin: '50% 50%', scale: 0.4, autoAlpha: 0 }); 1331 1332 gsap.set(images, { 1333 transformOrigin: '50% 50%', 1334 scale: 0.4, 1335 x: function (i) { 1336 var d = getStreamDisperse(i, disperseMult); 1337 return d.x * disperseStartScale; 1338 }, 1339 y: function (i) { 1340 var d = getStreamDisperse(i, disperseMult); 1341 return d.y * disperseStartScale; 1342 }, 1343 autoAlpha: 0, 1344 }); 1345 if (title) { 1346 gsap.set(title, { autoAlpha: 0, y: 20 }); 1347 } 1348 if (subtitle) { 1349 gsap.set(subtitle, { autoAlpha: 0, y: 20 }); 1350 } 1351 }; 1352 1353 setInitialStates(); 1354 1355 gsap.timeline({ 1356 defaults: { ease: 'none' }, 1357 scrollTrigger: { 1358 trigger: '.year-in-review-2026-winners', 1359 start: 'top top', 1360 end: 'bottom bottom', 1361 scrub: true, 1362 pin: '.year-2025-lrh-bd-winners-bg', 1363 anticipatePin: 1, 1364 invalidateOnRefresh: true, 1365 } 1366 }); 1367 1368 var tl = gsap.timeline({ 1369 defaults: { ease: 'none' }, 1370 scrollTrigger: { 1371 trigger: section, 1372 start: 'top top', 1373 end: 'bottom bottom', 1374 scrub: true, 1375 pin: pinEl, 1376 anticipatePin: 1, 1377 invalidateOnRefresh: true, 1378 onRefreshInit: function () { 1379 setInitialStates(); 1380 }, 1381 } 1382 }); 1383 1384 // Continuous stream: reveal one-by-one, keeping at most 4 visible. 1385 // Timings are in "timeline seconds" which map to scroll distance via scrub. 1386 var itemInDur = 0.34; 1387 // Faster fades for a slicker handoff. 1388 var fadeInDur = 0.08; 1389 var fadeOutDur = 0.10; 1390 var scaleEnd = 1.12; 1391 1392 // Spacing between the start of each card entering. 1393 // Clamp to guarantee no more than 4 visible at once. 1394 var maxVisible = 4; 1395 // Keep this close to the theoretical minimum so multiple cards overlap (1â4 visible). 1396 // Minimum is roughly itemInDur/maxVisible; keep a tiny cushion for float math. 1397 var streamStep = (itemInDur / maxVisible) + 0.006; 1398 1399 var startTimes = images.map(function (_, i) { 1400 return i * streamStep; 1401 }); 1402 1403 // Collect all inner images once so we can fade them with their cards. 1404 var cardInnerImages = images.map(function (card) { 1405 var imgs = card ? card.querySelectorAll('img') : null; 1406 return (imgs && imgs.length) ? Array.prototype.slice.call(imgs) : []; 1407 }); 1408 1409 images.forEach(function (card, i) { 1410 if (!card) { 1411 return; 1412 } 1413 1414 var startTime = startTimes[i] || 0; 1415 var m = getGroupMetrics(i); 1416 1417 // Fade in fast while scale continues. 1418 tl.to(card, { 1419 autoAlpha: 1, 1420 duration: fadeInDur, 1421 ease: 'linear', 1422 }, startTime); 1423 1424 // Scale + move into its dispersed position. 1425 tl.to(card, { 1426 scale: scaleEnd, 1427 duration: itemInDur, 1428 ease: 'linear', 1429 x: function () { 1430 var d = getStreamDisperse(i, disperseMult); 1431 return d.x; 1432 }, 1433 y: function () { 1434 var d = getStreamDisperse(i, disperseMult); 1435 return d.y; 1436 }, 1437 }, startTime); 1438 1439 // Fade out quickly so the card is gone by the time it finishes scaling. 1440 var fadeOutAt = startTime + itemInDur - fadeOutDur; 1441 tl.to(card, { 1442 autoAlpha: 0, 1443 duration: fadeOutDur, 1444 ease: 'linear', 1445 }, fadeOutAt); 1446 1447 var imgs = cardInnerImages[i]; 1448 if (imgs && imgs.length) { 1449 tl.to(imgs, { 1450 autoAlpha: 0, 1451 duration: fadeOutDur, 1452 ease: 'linear', 1453 }, fadeOutAt); 1454 } 1455 }); 1456 1457 // Featured image comes in after the stream clears. 1458 var lastStart = startTimes.length ? startTimes[startTimes.length - 1] : 0; 1459 var streamEnd = lastStart + itemInDur; 1460 var featuredIn = streamEnd - 0.2; 1461 tl.to(featuredWrapper, { 1462 autoAlpha: 1, 1463 duration: 0.1, 1464 ease: 'none', 1465 }, featuredIn); 1466 tl.to(featuredWrapper, { 1467 scale: 1, 1468 duration: 0.5, 1469 ease: 'power4.out', 1470 }, featuredIn); 1471 1472 // 3) Finally title + subtitle fade in after the animation is over. 1473 if (title) { 1474 tl.to(title, { 1475 autoAlpha: 1, 1476 y: 0, 1477 duration: 0.08, 1478 ease: 'power2.out', 1479 }, featuredIn + 0.1); 1480 } 1481 if (subtitle) { 1482 tl.to(subtitle, { 1483 autoAlpha: 1, 1484 y: 0, 1485 duration: 0.08, 1486 ease: 'power2.out', 1487 }, featuredIn + 0.15); 1488 } 1489 1490 section._bdWinnersTl = tl; 1491 } 1492 1493 YEAR2026.prototype.initRotateImages = function () { 1494 var wrappers = document.querySelectorAll('.year-in-review-2026-rotate-wrapper'); 1495 if (!wrappers.length) { 1496 return; 1497 } 1498
1499 wrappers.forEach(function (wrapper) { 1500 1501 if($(wrapper).closest('.year-in-review-2026-ao-row').length > 0) { 1502 wrapper = $(wrapper).closest('.year-in-review-2026-ao-row')[0]; 1503 } 1504 1505 var rotateContainers = wrapper.querySelectorAll('.year-in-review-2026-rotate-in-outer'); 1506 if (!rotateContainers.length) { 1507 return; 1508 } 1509 1510 var innerItems = []; 1511 rotateContainers.forEach(function (container, containerIndex) { 1512 gsap.set(container, { zIndex: containerIndex + 1 }); 1513 var inner = container.querySelector('.year-in-review-2026-rotate-in-inner'); 1514 if (inner) { 1515 innerItems.push(inner); 1516 } 1517 }); 1518 1519 var replayButtons = wrapper.querySelectorAll('.year-in-review-2026--autoplay-video-replay-animation'); 1520 gsap.set(replayButtons, { autoAlpha: 0 }); 1521 1522 var tl = gsap.timeline({ 1523 defaults: { ease: 'power4.out' }, 1524 scrollTrigger: { 1525 trigger: wrapper, 1526 start: 'top center+=20%', 1527 toggleActions: "play none resume reverse", 1528 }, 1529 onComplete: function () { 1530 gsap.to(replayButtons, { autoAlpha: 1, duration: 0.3 }); 1531 } 1532 }); 1533 1534 // Important: previous runs may have left some containers hidden. 1535 // Ensure a clean starting state so replay + reverse work reliably. 1536 tl.set(rotateContainers, { autoAlpha: 1 }, 0); 1537 1538 tl.from(rotateContainers, { 1539 rotation: 20, 1540 scale: 0, 1541 transformOrigin: '50% 50%', 1542 duration: 1, 1543 stagger: 1.1, 1544 }, 0); 1545 1546 if (innerItems.length) { 1547 tl.from(innerItems, { 1548 scale: 1.7, 1549 transformOrigin: '50% 50%', 1550 duration: 1.1, 1551 stagger: 1.1, 1552 }, 0); 1553 } 1554 1555 // After each next image finishes animating on top, hide the previous one. 1556 // Use the longer of the outer/inner durations so the handoff happens after the full "pop". 1557 if (rotateContainers.length > 1) { 1558 var rotateStagger = 1.1; 1559 var rotateEndDur = innerItems.length ? 1.1 : 1; 1560 1561 rotateContainers.forEach(function (_, i) { 1562 if (i === 0) { 1563 return; 1564 } 1565 var hideAt = (i * rotateStagger) + rotateEndDur; 1566 tl.to(rotateContainers[i - 1], { 1567 autoAlpha: 0, 1568 duration: 0.01, 1569 ease: 'none', 1570 }, hideAt); 1571 }); 1572 } 1573 1574 tl.from(wrapper.querySelectorAll('.year-in-review-2026-ao-row--image__left'), { 1575 x: -100, 1576 autoAlpha: 0, 1577 duration: 0.8, 1578 }, "-=0.5"); 1579 1580 tl.from(wrapper.querySelectorAll('.year-in-review-2026-ao-row--image__right'), { 1581 x: 100, 1582 autoAlpha: 0, 1583 duration: 0.8, 1584 }, "-=0.5"); 1585 1586 1587 replayButtons.forEach(function (btn) { 1588 btn.addEventListener('click', function (e) { 1589 e.preventDefault && e.preventDefault(); 1590 gsap.set(replayButtons, { autoAlpha: 0 }); 1591 gsap.set(rotateContainers, { autoAlpha: 1 }); 1592 tl && tl.restart(true, false); 1593 }); 1594 }); 1595 }); 1596 } 1597 1598 YEAR2026.prototype.initIASStats = function () { 1599 1600 var stats = document.querySelectorAll('.year-in-review-2025-ias-stats .year-in-review-2025-ias-stat--inner'); 1601 1602 gsap.timeline({ 1603 scrollTrigger: { 1604 trigger: '.year-in-review-2025-ias-accomplishments', 1605 start: 'top top', 1606 toggleActions: "play none resume reverse", 1607 pin: '.year-in-review-ias-bg-wing', 1608 end: 'bottom bottom', 1609 pinSpacing: false, 1610 }, 1611 }) 1612 .from(stats[0].parentElement, { 1613 autoAlpha: 0, 1614 scale: 8, 1615 duration: 0.25, 1616 }); 1617 1618 gsap.timeline({ 1619 scrollTrigger: { 1620 trigger: '.year-in-review-2025-our-help-stats-trigger-1', 1621 start: 'top top', 1622 toggleActions: "play none resume reverse", 1623 pinSpacing: false, 1624 }, 1625 }) 1626 .to(stats[0], { 1627 autoAlpha: 0, 1628 duration: 0.1, 1629 }) 1630 .from(stats[1], { 1631 autoAlpha: 0, 1632 scale: 8, 1633 duration: 0.25, 1634 }, "<") 1635 1636 // pin .year-in-review-2025-our-help-lead-in 1637 gsap.timeline({ 1638 scrollTrigger: {
1639 trigger: '.year-in-review-2025-ias-accomplishments', 1640 start: 'top top', 1641 toggleActions: "play none resume reverse", 1642 pin: '.year-in-review-2025-our-help-stats', 1643 end: 'bottom bottom', 1644 pinSpacing: false, 1645 }, 1646 }); 1647 } 1648 1649 YEAR2026.prototype.initEndCurious = function () { 1650 1651 gsap.timeline({ 1652 scrollTrigger: { 1653 trigger: '.year-in-review-2026-bottom-image--bg', 1654 start: 'bottom bottom', 1655 endTrigger: '.year-in-review-2026-bottom-wrapper', 1656 end: 'bottom bottom', 1657 scrub: true, 1658 toggleActions: "play none resume reverse", 1659 duration: "100%", 1660 pin: true, 1661 pinSpacing: false, 1662 anticipatePin: 1, 1663 invalidateOnRefresh: true, 1664 }, 1665 }); 1666 1667 gsap.timeline({ 1668 scrollTrigger: { 1669 trigger: '.year-in-review-2026-bottom-stars-wrapper', 1670 start: 'bottom bottom', 1671 endTrigger: '.year-in-review-2026-bottom-wrapper', 1672 end: 'bottom bottom', 1673 scrub: true, 1674 toggleActions: "play none resume reverse", 1675 duration: "100%", 1676 pin: true, 1677 pinSpacing: false, 1678 anticipatePin: 1, 1679 invalidateOnRefresh: true, 1680 }, 1681 }); 1682 1683 var tl = gsap.timeline({ 1684 scrollTrigger: { 1685 trigger: '.year-in-review-2026-bottom-image--man', 1686 start: 'bottom bottom', 1687 endTrigger: '.year-in-review-2026-bottom-wrapper', 1688 end: 'bottom bottom', 1689 scrub: true, 1690 toggleActions: "play none resume reverse", 1691 duration: "100%", 1692 pin: true, 1693 pinSpacing: false, 1694 anticipatePin: 1, 1695 invalidateOnRefresh: true, 1696 }, 1697 }); 1698 1699 tl.fromTo('.year-in-review-2026-bottom-image--man', { 1700 yPercent: 5, 1701 }, { 1702 yPercent: -10, 1703 }); 1704 1705 var bottomTexts = document.querySelectorAll('.year-in-review-2026-bottom-text'); 1706
1707 bottomTexts.forEach(function (title) { 1708 var split = title._splitTextInstance; 1709 if (!split) { 1710 split = new SplitText(title, { 1711 type: 'words,chars', 1712 wordsClass: 'word', 1713 charsClass: 'char' 1714 }); 1715 title._splitTextInstance = split; 1716 } 1717 1718 var words = (split && split.words) ? split.words : []; 1719 if (!words.length) { 1720 return; 1721 } 1722 1723 var wordTimeline = gsap.timeline({ 1724 defaults: { 1725 ease: 'power1.inOut' 1726 }, 1727 scrollTrigger: { 1728 trigger: title, 1729 start: 'top center', 1730 end: 'bottom center', 1731 toggleActions: 'play reverse play reverse', 1732 // markers: true, 1733 }, 1734 }); 1735 1736 var allChars = []; 1737 words.forEach(function (word) { 1738 var chars = word.querySelectorAll('.char'); 1739 chars.forEach(function (char) { 1740 if (char.parentNode) { 1741 gsap.set(char.parentNode, { perspective: 2000 }); 1742 } 1743 allChars.push(char); 1744 }); 1745 }); 1746 1747 if (allChars.length) { 1748 wordTimeline.fromTo(allChars, { 1749 'will-change': 'opacity, transform', 1750 opacity: 0, 1751 rotationX: -90, 1752 yPercent: 50 1753 }, { 1754 duration: 0.25, 1755 opacity: 1, 1756 rotationX: 0, 1757 yPercent: 0, 1758 stagger: { 1759 amount: 0.25, 1760 from: 0 1761 } 1762 }, 0); 1763 } 1764 }); 1765 } 1766 1767 YEAR2026.prototype.initParallax = function () { 1768 1769 _.each($('.year-review-2026--top-bg-parallax'), function (element, index) { 1770 var offset = element.getAttribute('data-parallax-offset'); 1771 1772 gsap.to(element, { 1773 scrollTrigger: { 1774 trigger: '.year-in-review-2026--hero-section', 1775 scrub: true, 1776 start: "top top", 1777 end: "bottom top", 1778 }, 1779 y: offset, 1780 ease: 'none' 1781 }); 1782 }); 1783 1784 _.each($('.year-review-2025--parallax'), function (element, index) { 1785 var offset = element.getAttribute('data-parallax-offset'); 1786 1787 var triggerOptions = { 1788 trigger: element, 1789 scrub: true, 1790 start: "top bottom", 1791 end: "bottom top", 1792 }; 1793 1794 if(element.getAttribute('data-trigger')) { 1795 triggerOptions.trigger = element.getAttribute('data-trigger'); 1796 } 1797 1798 if(element.getAttribute('data-end-trigger')) { 1799 triggerOptions.endTrigger = element.getAttribute('data-end-trigger'); 1800 } 1801 1802 if(element.getAttribute('data-end')) { 1803 triggerOptions.end = element.getAttribute('data-end'); 1804 } 1805 1806 gsap.from(element.querySelector('img'), { 1807 scrollTrigger: triggerOptions, 1808 y: offset, 1809 ease: 'none' 1810 }); 1811 }); 1812 } 1813 1814 1815 YEAR2026.prototype.initAwardsScroller = function () { 1816 let sections = gsap.utils.toArray(".year-2025--awards-item"); 1817 1818 var rtl = $('html').attr('dir') == 'rtl'; 1819 1820 gsap.timeline({ 1821 scrollTrigger: { 1822 trigger: ".year-2025-awards-wall-pin-trigger", 1823 start: "top top", 1824 endTrigger: ".year-2025-awards-wall-wrapper", 1825 end: "bottom bottom", 1826 pin: '.year-2026-awards-wall-container', 1827 anticipatePin: true, 1828 pinSpacing: false, 1829 scrub: true, 1830 } 1831 }).to('.year-2025-awards-wall', { 1832 x: "-30%", 1833 }); 1834 1835 1836 gsap.to(sections, { 1837 xPercent: rtl ? 100 * (sections.length - 1) : -100 * (sections.length - 1), 1838 ease: "none", 1839 scrollTrigger: { 1840 trigger: ".year-2025--awards-row", 1841 pin: true, 1842 pinSpacing: false, 1843 scrub: 1, 1844 snap: { 1845 snapTo: function(value) { 1846 var interval = 1 / (sections.length - 1); 1847 return Math.round(value / interval) * interval; 1848 }, 1849 duration: { min: 0.2, max: 0.5 },
1850 ease: "power2.inOut" 1851 }, 1852 end: "100%", 1853 } 1854 }); 1855 1856 } 1857 1858 YEAR2026.prototype.isIosVersionAtMost17 = function () { 1859 if(globalGetParams.iphone == "1") { 1860 return true; 1861 } 1862 var iosMatch = navigator.userAgent.match(/(?:iPhone|iPad|iPod).*OS\s+(\d+)_/i); 1863 if (!iosMatch) { 1864 return false; 1865 } 1866 var majorVersion = parseInt(iosMatch[1], 10); 1867 return !isNaN(majorVersion) && majorVersion <= 17; 1868 }; 1869 1870 YEAR2026.prototype.initMosaicRows = function () { 1871 var mosaics = document.querySelectorAll('.year-in-review-2026-4d-mosaic--row'); 1872 1873 if (!mosaics.length || typeof gsap === 'undefined' || typeof ScrollTrigger === 'undefined') { 1874 return; 1875 } 1876 1877 // Responsive columns: 1878 // - < 768px: 2 columns 1879 // - >= 992px: 3 columns 1880 // (768â785 keeps the 2-col behavior to match authored breakpoint intent) 1881 var getColumnCount = function () { 1882 return (window.innerWidth >= 992) ? 3 : 2; 1883 }; 1884 1885 var killTimeline = function (tl) { 1886 if (!tl) { 1887 return; 1888 } 1889 try { 1890 if (tl.scrollTrigger) { 1891 tl.scrollTrigger.kill(true); 1892 } 1893 } catch (e) { } 1894 try { 1895 tl.kill(); 1896 } catch (e) { } 1897 }; 1898 1899 var buildParallax = function (mosaic, columnCount) { 1900 if (!mosaic) { 1901 return; 1902 } 1903 1904 // Ensure only one parallax timeline exists per mosaic. 1905 if (mosaic._mosaicParallaxTl) { 1906 killTimeline(mosaic._mosaicParallaxTl); 1907 mosaic._mosaicParallaxTl = null; 1908 } 1909 1910 var colsSelector1 = (columnCount === 2) ? '2n+1' : '3n+1'; 1911 var colsSelector2 = (columnCount === 2) ? '2n+2' : '3n+2'; 1912 var colsSelector3 = (columnCount === 2) ? null : '3n+3'; 1913 1914 var col1 = mosaic.querySelectorAll('.year-in-review-2026-4d-mosaic--columns > :nth-child(' + colsSelector1 + ')'); 1915 var col2 = mosaic.querySelectorAll('.year-in-review-2026-4d-mosaic--columns > :nth-child(' + colsSelector2 + ')'); 1916 var col3 = colsSelector3 ? mosaic.querySelectorAll('.year-in-review-2026-4d-mosaic--columns > :nth-child(' + colsSelector3 + ')') : []; 1917 1918 var endTriggerEl = mosaic.closest('.bs-extender__4d-background') || mosaic; 1919 1920 var parallaxTl = gsap.timeline({ 1921 scrollTrigger: { 1922 trigger: mosaic, 1923 start: 'top bottom', 1924 end: 'bottom+=25% bottom', 1925 endTrigger: endTriggerEl, 1926 scrub: true, 1927 invalidateOnRefresh: true, 1928 } 1929 }); 1930 1931 if (col1.length) parallaxTl.to(col1, { y: -50, ease: 'none', duration: 1, }, 0); 1932 if (col2.length) parallaxTl.to(col2, { y: -100, ease: 'none', duration: 1, }, 0); 1933 if (col3.length) parallaxTl.to(col3, { y: -80, ease: 'none', duration: 1, }, 0); 1934 1935 parallaxTl.to(mosaic, { 1936 autoAlpha: 0, 1937 duration: 0.2, 1938 }, 0.8); 1939 1940 mosaic._mosaicParallaxTl = parallaxTl; 1941 }; 1942 1943 mosaics.forEach(function (mosaic) { 1944 var tl = gsap.timeline({ 1945 scrollTrigger: { 1946 trigger: mosaic.querySelectorAll('.year-in-review-2026-4d-mosaic--image')[0], 1947 start: 'bottom bottom', 1948 toggleActions: "play none resume reverse", 1949 } 1950 }); 1951 1952 tl.fromTo(mosaic.querySelector('.year-in-review-2026-4d-mosaic--desc-inner'), { 1953 y: 50, 1954 scale: 5, 1955 autoAlpha: 0, 1956 }, { 1957 y: 0, 1958 scale: 1, 1959 autoAlpha: 1, 1960 duration: 0.4, 1961 ease: 'power2.out', 1962 }); 1963 1964 // Pin the desc, and during the pin: pop the featured image + dim the rest. 1965 var allImages = mosaic.querySelectorAll('.year-in-review-2026-4d-mosaic--image'); 1966 1967 var pinTl = gsap.timeline({ 1968 scrollTrigger: { 1969 trigger: mosaic.querySelector('.year-in-review-2026-rotate-wrapper__4d'), 1970 start: 'center center', 1971 toggleActions: "play none resume reverse", 1972 pin: true, 1973 pinSpacing: false, 1974 invalidateOnRefresh: true, 1975 anticipatePin: 1, 1976 endTrigger: mosaic, 1977 end: 'bottom bottom', 1978 } 1979 }); 1980 1981 gsap.timeline({ 1982 scrollTrigger: { 1983 trigger: mosaic.querySelector('.year-in-review-2026-4d-mosaic--desc-wrapper'), 1984 start: 'center center', 1985 toggleActions: "play none resume reverse", 1986 pin: true, 1987 pinSpacing: false, 1988 invalidateOnRefresh: true, 1989 endTrigger: mosaic, 1990 end: 'bottom-=50% center', 1991 } 1992 }); 1993 1994 buildParallax(mosaic, getColumnCount()); 1995 }); 1996 1997 // Rebuild parallax timelines on breakpoint changes so nth-child
1997selectors 1998 // always match the active column layout (prevents odd gaps on mobile/tablet). 1999 var self = this; 2000 if (self._mosaicRowsResizeHandler) { 2001 try { window.removeEventListener('resize', self._mosaicRowsResizeHandler); } catch (e) { } 2002 self._mosaicRowsResizeHandler = null; 2003 } 2004 2005 var lastColumnCount = getColumnCount(); 2006 self._mosaicRowsResizeHandler = function () { 2007 // Cheap debounce: coalesce multiple resize events. 2008 if (self._mosaicRowsResizeRaf) { 2009 return; 2010 } 2011 self._mosaicRowsResizeRaf = window.requestAnimationFrame(function () { 2012 self._mosaicRowsResizeRaf = null; 2013 var nextCount = getColumnCount(); 2014 if (nextCount === lastColumnCount) { 2015 return; 2016 } 2017 lastColumnCount = nextCount; 2018 mosaics.forEach(function (mosaic) { 2019 buildParallax(mosaic, nextCount); 2020 }); 2021 try { 2022 ScrollTrigger.refresh(true); 2023 } catch (e) { } 2024 }); 2025 }; 2026 window.addEventListener('resize', self._mosaicRowsResizeHandler); 2027 2028 var wrappers = document.querySelectorAll('.bs-extender__4d-background'); 2029 wrappers.forEach(function (wrapper) { 2030 gsap.timeline({ 2031 scrollTrigger: { 2032 trigger: wrapper, 2033 start: 'top top', 2034 end: 'bottom bottom', 2035 scrub: true, 2036 pin: wrapper.querySelector('.year-in-review-2026-vignette'), 2037 pinSpacing: false, 2038 invalidateOnRefresh: true, 2039 } 2040 }); 2041 }); 2042 } 2043 2044 YEAR2026.prototype.initIASCCHRtransition = function () { 2045 if (typeof gsap === 'undefined' || typeof ScrollTrigger === 'undefined') { 2046 return; 2047 } 2048 2049 var logo = document.querySelector('.year-in-review-2026-4d-logo-row--item--cchr-image'); 2050 var background = document.querySelector('.bs-extender__cchr-background'); 2051 2052 if (!logo) { 2053 return; 2054 } 2055 2056 // Helpers to center the logo and scale it to cover the viewport. 2057 var coverScale = 50; 2058 var centerOffset = { x: 0, y: 0 }; 2059 var isRefreshing = false; 2060 2061 var tl; // timeline defined later; hoisted reference so recalc can invalidate safely 2062 2063 var recalcCover = function () {
2064 // cache current transform so measuring does not snap the animation 2065 var prevTransform = { 2066 x: gsap.getProperty(logo, 'x'), 2067 y: gsap.getProperty(logo, 'y'), 2068 scale: gsap.getProperty(logo, 'scale'), 2069 autoAlpha: gsap.getProperty(logo, 'autoAlpha'), 2070 }; 2071 2072 // reset transforms so measurements are accurate 2073 gsap.set(logo, { x: 0, y: 0, scale: 1 }); 2074 2075 var rect = logo.getBoundingClientRect(); 2076 var vw = window.innerWidth || 1; 2077 var vh = window.innerHeight || 1; 2078 2079 if (rect.width && rect.height) { 2080 // scale large enough to cover viewport; keeps animation consistent after resize/refresh 2081 // coverScale = Math.max(vw / rect.width, vh / rect.height) * 1.2; 2082 } 2083 2084 var cx = rect.left + rect.width / 2; 2085 var cy = rect.top + rect.height / 2; 2086 centerOffset = { 2087 x: vw / 2 - cx, 2088 y: vh / 2 - cy, 2089 }; 2090 2091 // restore previous transform so scrubbed timelines do not jump 2092 gsap.set(logo, prevTransform); 2093 2094 // keep any attached timeline in sync with the new measurements 2095 if (tl && tl.scrollTrigger) { 2096 var st = tl.scrollTrigger; 2097 var currentProgress = st.progress; 2098 var currentScrollY = window.pageYOffset || document.documentElement.scrollTop || 0; 2099 tl.invalidate(); 2100 2101 // refresh start/end/pin bounds once per recalc to keep reverse scrub aligned 2102 if (!isRefreshing) { 2103 isRefreshing = true; 2104 st.refresh(); 2105 isRefreshing = false; 2106 } 2107 2108 // restoring scroll position prevents refresh-induced jumps to the top 2109 window.scrollTo(0, currentScrollY); 2110 tl.progress(currentProgress); 2111 } 2112 }; 2113 2114 recalcCover(); 2115 ScrollTrigger.addEventListener('refreshInit', recalcCover); 2116 ScrollTrigger.addEventListener('refresh', recalcCover); 2117 2118 gsap.set(logo, { 2119 transformOrigin: '50% 50%', 2120 zIndex: 50, 2121 willChange: 'transform, opacity', 2122 }); 2123 2124 tl = gsap.timeline({ 2125 defaults: { ease: 'power1.inOut' }, 2126 scrollTrigger: { 2127 trigger: logo, 2128 start: 'center center', 2129 endTrigger: background || logo, 2130 end: 'top+=15% top', 2131 scrub: true, 2132 pin: true, 2133 pinSpacing: true, 2134 anticipatePin: 1, 2135 invalidateOnRefresh: true, 2136 onEnter: recalcCover, 2137 onEnterBack: recalcCover, 2138 onRefresh: recalcCover, 2139 } 2140 }); 2141 2142 2143 tl.fromTo(logo, { 2144 x: 0, 2145 y: 0, 2146 scale: 1, 2147 autoAlpha: 1, 2148 }, { 2149 x: function () { return centerOffset.x; }, 2150 y: function () { return centerOffset.y; }, 2151 scale: function () { return coverScale; }, 2152 autoAlpha: 1, 2153 duration: 0.7, 2154 }, 0); 2155 2156 2157 tl.to(logo, { 2158 autoAlpha: 0, 2159 duration: 0.1, 2160 }, 0.7); 2161 2162 tl.fromTo('.year-in-review-ias-cchr-bg', { 2163 autoAlpha: 0, 2164 }, { 2165 autoAlpha: 1, 2166 duration: 0.2, 2167 }, 0.3); 2168 2169 if (background) { 2170 tl.fromTo(background, { 2171 autoAlpha: 0, 2172 }, { 2173 autoAlpha: 1, 2174 duration: 0.2, 2175 }, 0.3); 2176 } 2177 } 2178 2179 YEAR2026.prototype.sectionReveal = function () { 2180 if (typeof gsap === 'undefined' || typeof ScrollTrigger === 'undefined') { 2181 return; 2182 } 2183 2184 var topSections = document.querySelectorAll('.video-section-reveal-animation'); 2185 2186 var zIndex = 1000; 2187 2188 topSections.forEach(function (topSection) { 2189 var bottomSection = topSection.nextElementSibling; 2190 2191 if (bottomSection && bottomSection.classList.contains('video-section-reveal-animation')) { 2192 gsap.set(topSection, { zIndex: zIndex, position: 'relative' }); 2193 gsap.set(bottomSection, { zIndex: zIndex - 1, position: 'relative' }); 2194 zIndex -= 2; 2195 2196 gsap.fromTo(bottomSection, 2197 { 2198 y: "-100vh", 2199 }, 2200 { 2201 y: 0, 2202 ease: 'none', 2203 scrollTrigger: { 2204 trigger: topSection, 2205 start: 'bottom bottom', 2206 end: 'bottom top+=10%', 2207 scrub: true 2208 } 2209 } 2210 ); 2211 } 2212 }); 2213 } 2214 2215 YEAR2026.prototype.initSliderImages = function () { 2216 var self = this; 2217 2218 $('.year-end-2026--carousel-images').each(function () { 2219 var $row = $(this); 2220 2221 // Note: When the row is narrower than the viewport, translating it will *create* visible gaps. 2222 // We keep it centered (no scroll movement) in that case, and clamp movement when wider. 2223 var getAttrNumber = function (name, fallback) { 2224 var raw = $row.attr('data-' + name); 2225 if (raw === undefined || raw === null || raw === '') { 2226 return fallback; 2227 } 2228 var n = parseFloat(raw); 2229 return isNaN(n) ? fallback : n; 2230 }; 2231 2232 var clamp = function (value, min, max) { 2233 if (value < min) { 2234 return min; 2235 } 2236 if (value > max) { 2237 return max; 2238 } 2239 return value; 2240 }; 2241 2242 var calcRowWidth = function () { 2243 var totalWidth = 0; 2244 $row.children().each(function (_, child) { 2245 totalWidth += $(child).outerWidth(true) || 0; 2246 }); 2247 return totalWidth; 2248 }; 2249 2250 var calcPositions = function () { 2251 var windowWidth = $(window).width() || 0; 2252 var rowWidth = calcRowWidth(); 2253 2254 // Compute bounds based on where the row naturally sits in the viewport.
2255 // This makes the slider work for both LTR and RTL layouts (right-aligned rows). 2256 var rowEl = $row[0]; 2257 var currentX = 0; 2258 try { 2259 currentX = parseFloat(gsap.getProperty(rowEl, 'x')) || 0; 2260 } catch (e) { 2261 currentX = 0; 2262 } 2263 2264 var rowRect = rowEl ? rowEl.getBoundingClientRect() : { left: 0 }; 2265 // baseLeft = the row's viewport-left position when x === 0 2266 var baseLeft = (rowRect.left || 0) - currentX; 2267 2268 // Bounds that prevent showing empty space at either edge. 2269 // Solve for x such that: (baseLeft + x) <= 0 AND (baseLeft + x + rowWidth) >= windowWidth 2270 // => x in [windowWidth - rowWidth - baseLeft, -baseLeft] 2271 var minX = (windowWidth - rowWidth) - baseLeft; 2272 var maxX = -baseLeft; 2273 2274 // Centered position (x that makes left == (windowWidth-rowWidth)/2) 2275 var centerX = ((windowWidth - rowWidth) / 2) - baseLeft; 2276 2277 var startX = getAttrNumber('start-x', 0); 2278 var endX = getAttrNumber('end-x', 0); 2279 2280 // Determine intended direction from authored values (or previous computed values). 2281 // Used only when the row is narrower than the viewport. 2282 var authoredStart = startX; 2283 var authoredEnd = endX; 2284 var directionIsIncreasing = authoredStart <= authoredEnd; 2285 2286 // If the content doesn't span the viewport, keep it centered and static. 2287 if (rowWidth <= windowWidth) { 2288 // We *cannot* translate freely without revealing empty space. 2289 // Instead, do a subtle parallax within a small range around center. 2290 var availableGap = (windowWidth - rowWidth) / 2; 2291 // Range is a fraction of the available gap, capped to a reasonable max. 2292 // (If there is barely any gap, this becomes ~0 and effectively locks.) 2293 var parallaxRange = Math.min(availableGap * 0.35, 60); 2294 if (parallaxRange < 1) { 2295 parallaxRange = 0; 2296 } 2297 2298 var narrowStart = centerX - parallaxRange; 2299 var narrowEnd = centerX + parallaxRange; 2300 2301 startX = directionIsIncreasing ? narrowStart : narrowEnd; 2302 endX = directionIsIncreasing ? narrowEnd : narrowStart; 2303 } else { 2304 // Clamp to ensure we never translate beyond the edges. 2305 startX = clamp(startX, minX, maxX); 2306 endX = clamp(endX, minX, maxX); 2307 } 2308 2309 // Special-case: large images row centers the 2nd card at scroll midpoint, 2310 // then continues moving as user scrolls out. 2311 if ($row.hasClass('year-end-2026--carousel-images__large-images')) { 2312 var $parent = $row.parent(); 2313 var parentWidth = $parent.width() || windowWidth; 2314 var parentRect = $parent.length ? $parent[0].getBoundingClientRect() : { left: 0 }; 2315 var targetChild = $row.children().eq(1); 2316 if (targetChild.length > 0) { 2317 var targetOffset = targetChild.position().left + (targetChild.outerWidth(true) / 2); 2318 // This offset centers the 2nd image in its parent viewport. 2319 // targetOffset is relative to the row's left edge; baseLeft/parentRect.left anchor it in viewport coords. 2320 var desiredCenter = (parentRect.left || 0) + (parentWidth / 2); 2321 var targetCenterAtX0 = baseLeft + targetOffset; 2322 var centeringOffset = desiredCenter - targetCenterAtX0; 2323 2324 if (rowWidth <= windowWidth) { 2325 // Narrow: subtle parallax around center. 2326 var availableGapNarrow = (windowWidth - rowWidth) / 2; 2327 var parallaxRangeNarrow = Math.min(availableGapNarrow * 0.35, 60); 2328 if (parallaxRangeNarrow < 1) { 2329 parallaxRangeNarrow = 0; 2330 } 2331 2332 // Preserve authored direction even in the subtle parallax case. 2333 var narrowA = centerX - parallaxRangeNarrow; 2334 var narrowB = centerX + parallaxRangeNarrow; 2335 startX = directionIsIncreasing ? narrowA : narrowB; 2336 endX = directionIsIncreasing ? narrowB : narrowA; 2337 } else {
2338 // Wide: start before centered, pass through centered at midpoint, end past centered. 2339 // Increase travel so it doesn't feel like it "sticks" at center. 2340 var centered = clamp(centeringOffset, minX, maxX); 2341 2342 // Desired travel before/after center. Bigger = faster feel. 2343 // Cap to avoid overly long slides on ultra-wide screens. 2344 var desiredTravel = Math.min(rowWidth * 0.28, 420); 2345 2346 // Never exceed bounds: centered +/- travel must stay within [minX, maxX]. 2347 var maxTravel = Math.max(0, Math.min(maxX - centered, centered - minX)); 2348 var travel = Math.min(desiredTravel, maxTravel); 2349 2350 var wideA = centered - travel; 2351 var wideB = centered + travel; 2352 startX = directionIsIncreasing ? wideA : wideB; 2353 endX = directionIsIncreasing ? wideB : wideA; 2354 } 2355 } 2356 2357 // Respect bounds after applying the centering offset. 2358 if (rowWidth <= windowWidth) { 2359 // Keep the already-computed narrow parallax values. 2360 } else { 2361 startX = clamp(startX, minX, maxX); 2362 endX = clamp(endX, minX, maxX); 2363 } 2364 2365 if ($row.data('reversed') === true || $row.data('reversed') === "true") { 2366 var tmp = startX; 2367 startX = endX; 2368 endX = tmp; 2369 } 2370 } 2371 2372 // Keep attributes updated for debugging/inspection (avoid jQuery .data cache issues). 2373 $row.attr('data-start-x', startX); 2374 $row.attr('data-end-x', endX); 2375 2376 return { startX: startX, endX: endX }; 2377 }; 2378 2379 var tl = gsap.timeline({ 2380 scrollTrigger: { 2381 trigger: this, 2382 start: 'top bottom', 2383 end: function () { 2384 return self.getWindowHeight() + "px"; 2385 }, 2386 scrub: true, 2387 invalidateOnRefresh: true, // invalidate on resize 2388 }, 2389 }); 2390 2391 tl.fromTo(this, { 2392 x: function () { 2393 return calcPositions().startX; 2394 }, 2395 }, { 2396 x: function () { 2397 return calcPositions().endX; 2398 }, 2399 }); 2400 }); 2401 } 2402 2403 YEAR2026.prototype.initVideosModal = function (selector) { 2404 var self = this; 2405 2406 $('.year-review-2022--back').click(function () { 2407 $('.modal-full-viewport .close').click(); 2408 }); 2409 2410 $(selector).click(function () { 2411 var $modal = $('.modal-full-viewport__video').first(); 2412 $modal.addClass('visible'); 2413 2414 for (var player in videojs.players) { 2415 if (videojs.players.hasOwnProperty(player) && videojs.players[player]) { 2416 // try { 2417 // videojs.players[player].pause(); 2418 // } catch (error) { 2419 // } 2420 if ($(videojs.players[player].el_).closest('.modal-full-viewport').length > 0) { 2421 videojs.players[player].dispose(); 2422 } 2423 } 2424 } 2425 2426 var videoId = $(this).data('videoid'); 2427 self.currentVideoId = videoId; 2428 2429 TweenMax.from($modal, 0.75, { scale: 0.9, autoAlpha: 0, ease: Power4.easeOut, clearProps: 'all' }); 2430 2431 // self.initModalCarousel(); 2432 2433 if ($(this).attr('id')) { 2434 window.history.replaceState({}, '', '#' + $(this).attr('id')); 2435 trackEvent("Video", "Play", $(this).attr('id')); 2436 } 2437 2438 var $el = $(this); 2439 2440 var showyoutube = false;
2441 var scntvlink = $el.data('scntv'); 2442 2443 if ($el.data('youtube')) { 2444 showyoutube = true; 2445 } 2446 2447 $('.modal-youtube-embed--inited').remove(); 2448 2449 if (scntvlink) { 2450 $modal.find('.modal-scntv-video').clone().removeClass("d-none").prependTo($modal.find('.modal-video-wrapper')).addClass('modal-scntv-video--inited'); 2451 var src = $('.modal-scntv-video--inited iframe').attr('data-src').replace(/YOUTUBE_ID/g, $el.data('youtube')); 2452 $('.modal-scntv-video--inited').removeClass('d-none'); 2453 var src = $('.modal-scntv-video--inited iframe').data('src').replace("URL", scntvlink); 2454 $('.modal-scntv-video--inited iframe').attr('src', src); 2455 return; 2456 } else if (showyoutube) { 2457 $modal.find('.modal-youtube-embed').clone().prependTo($modal.find('.modal-video-wrapper')).addClass('modal-youtube-embed--inited'); 2458 var src = $('.modal-youtube-embed--inited iframe').attr('data-src').replace(/YOUTUBE_ID/g, $el.data('youtube')); 2459 $('.modal-youtube-embed--inited').removeClass('d-none'); 2460 $('.modal-youtube-embed--inited iframe').attr('src', src); 2461 return; 2462 } 2463 // https://www.youtube.com/watch?v=FiW5kejelns 2464 2465 $('.year-review-2022--primary-menu-bg, .banner-wrapper, .external-links').hide(); 2466 // $('.year-review-2021-nav').removeClass('year-review-2022-nav__visible'); 2467 2468 2469 var options = jQuery.extend({}, videoOptions); 2470 options.controls = true; 2471 options.poster = $(this).data('videoposter'); 2472 options.sources = $(this).data('videosrc'); 2473 2474 self.initModalVideo($modal, options, videoId); 2475 2476 // var url = new URL(window.location.href); 2477 // url.search = "?shareanchor=" + $(this).attr('id'); 2478 // url.hash = ""; 2479 // var link = url.href; 2480 // var title = $('[href="#'+$(this).attr('id')+'"]').first().text().trim(); 2481 // self.updateSharingButtons(title, link) 2482 }); 2483 2484 2485 window.onpopstate = function (event) { 2486 if ($('.modal-full-viewport').hasClass('visible')) { 2487 history.replaceState({}, ''); 2488 event.preventDefault(); 2489 event.stopPropagation(); 2490 $('.modal-full-viewport .close').click(); 2491 } 2492 } 2493 2494 $('.modal-full-viewport .close, .modal-full-viewport .year-review-2021--back__underlined').click(function () { 2495 $('.show-codes-button').removeClass('d-none'); 2496 TweenMax.to($('.modal-full-viewport'), 0.75, { 2497 scale: 0.9, autoAlpha: 0, clearProps: 'all', ease: Power4.easeOut, onComplete: function () { 2498 $('.modal-full-viewport').removeClass('visible'); 2499 } 2500 }); 2501 $('.modal-video-wrapper-up-next--button__cancel').click(); 2502 window.history.replaceState({}, '', ''); 2503 if (window.videojs) { 2504 for (var player in videojs.players) { 2505 if (videojs.players[player] && $(videojs.players[player].el_).closest('.modal-full-viewport').length > 0) { 2506 if (videojs.players.hasOwnProperty(player) && videojs.players[player]) { 2507 try { 2508 videojs.players[player].pause(); 2509 } catch (error) { 2510 } 2511 videojs.players[player].dispose(); 2512 } 2513 } 2514 } 2515 } 2516 _.each(self.videoPlayers, function (player) { 2517 if (player && videojs.players[player] && $(videojs.players[player].el_).closest('.modal-full-viewport').length > 0) { 2518 player.dispose(); 2519 } 2520 }); 2521 self.videoPlayers = []; 2522 $('.modal-youtube-embed--inited').remove(); 2523 $('.modal-scntv-video--inited').remove(); 2524 VideoPlayerHotkeys.player = null; 2525 $('.year-review-2022--primary-menu-bg, .banner-wrapper, .external-links').show(); 2526 // $('.year-review-2021-nav').addClass('year-review-2022-nav__visible'); 2527 2528 _.each($('.year-end-2022--playbutton-area'), function (el, index) { 2529 var $el = $(el); 2530 if (isFullyVisible($el) && $el.find('video').muted) { 2531 $el.find('video')[0].play(); 2532 } 2533 }); 2534 2535 history.replaceState(null, null, "#"); 2536 }); 2537 } 2538 2539 YEAR2026.prototype.initModalVideo = function ($modal, options, videoId) {
2540 $modal.find('.modal-player__model').clone().prependTo($modal.find('.modal-video-wrapper')).removeClass('d-none').removeClass('modal-player__model'); 2541 var videoEl = $('[data-videosrc][data-videoid="' + videoId + '"]'); 2542 var self = this; 2543 videojs($modal.find('.video-js')[0], options).ready(function () { 2544 if (VideoPlayerHotkeys) { 2545 VideoPlayerHotkeys.player = this; 2546 } 2547 EventTracker.trackVideo(this); 2548 this.on('play', function () { 2549 $('.modal-video-wrapper-up-next-wrapper').addClass('d-none'); 2550 }); 2551 this.play(); 2552 self.videoPlayers = self.videoPlayers || []; 2553 self.videoPlayers.push(this); 2554 2555 var player = this; 2556 2557 2558 this.on("ended", function () { 2559 $('.modal-full-viewport .close').click(); 2560 // player.poster(''); 2561 // var currentVideo = $('.modal-full-viewport .swiper-slide__modal-video[data-id="' + self.currentVideoId + '"]'); 2562 // var $next = currentVideo.next(); 2563 // if ($next.length == 0) { 2564 // $next = $('.swiper-slide__modal-video').first(); 2565 // } 2566 // $('.modal-video-wrapper-up-next-wrapper').removeClass('d-none'); 2567 // $('.modal-video-wrapper-up-next-wrapper .modal-video-wrapper-up-next--title').html($next.find('.swiper-slide__modal-video--title').html()); 2568 // $('.modal-video-wrapper-up-next-wrapper img').attr('srcset', $next.find('source').attr('srcset')); 2569 // $('.modal-video-wrapper-up-next-wrapper img').attr('data-srcset', $next.find('source').attr('data-srcset')); 2570 // $('.modal-video-wrapper-up-next-wrapper img').addClass('lazyload'); 2571 // self.setUpNext($next); 2572 }); 2573 }); 2574 } 2575 2576 YEAR2026.prototype.initIntro = function () { 2577 var tl = gsap.timeline({ 2578 // id: "initIntro", 2579 onComplete: () => { 2580 }, 2581 }); 2582 if (!globalGetParams.screencap) { 2583 this.initIntroTitles(); 2584 } 2585 2586 tl.from('.year-in-review-2026--background-static', { 2587 autoAlpha: 0, 2588 duration: 1, 2589 ease: Power4.easeOut, 2590 }); 2591 2592 tl.from('.year-in-review-2026--hero-text__text-wrapper, .year-in-review-2026--hero-text__span', { 2593 autoAlpha: 0, 2594 duration: 0.75, 2595 scale: 3, 2596 ease: Power4.easeOut, 2597 stagger: 0.1, 2598 }); 2599 2600 tl.from('.year-in-review-2026--hero-subtext span', { 2601 autoAlpha: 0, 2602 duration: 0.75, 2603 y: 20, 2604 ease: Power4.easeOut, 2605 stagger: 0.1, 2606 }, "-=0.5"); 2607 2608 tl.from('.year-2025--bounce', { 2609 autoAlpha: 0, 2610 duration: 0.75, 2611 y: 50, 2612 ease: Power4.easeOut, 2613 }, "-=0.5"); 2614 2615 2616 gsap.timeline({ 2617 scrollTrigger: { 2618 trigger: '.year-in-review-2026--hero-header-top-spacer', 2619 start: 'top top', 2620 pin: '.year-in-review-2026--hero-text', 2621 end: "+=100%", 2622 pinSpacing: false, 2623 scrub: true, 2624 invalidateOnRefresh: true, 2625 } 2626 }).to('.year-in-review-2026--hero-text', { 2627 y: function () { 2628 return -window.innerHeight / 2; 2629 }, 2630 ease: "none" 2631 }); 2632 } 2633 2634 YEAR2026.prototype.initIntroTitles = function () { 2635 $('.year-in-review-2026--intro-titles > div').each(function (index, el) { 2636 2637 gsap.from(el.querySelector('span'), { 2638 scrollTrigger: { 2639 trigger: el, 2640 start: 'top bottom-=100', 2641 toggleActions: "play none resume reverse", 2642 }, 2643 2644 autoAlpha: 0, 2645 duration: 0.75, 2646 scale: 5, 2647 y: 20, 2648 ease: Power4.easeOut, 2649 }); 2650 }); 2651 } 2652 2653 YEAR2026.prototype.initMainMenu = function () { 2654 var self = this; 2655 var anchors = []; 2656 var lastHashId = ""; 2657 2658 2659 $('.ny-2024--menuitem, .ny-2024--primary-menu-logo').each(function (index) { 2660 var $link = $(this).find('a'); 2661 if ($(this)[0].tagName == "A") { 2662 $link = $(this); 2663 } 2664 var id = $link.attr('href').replace('#', ''); 2665 var $el = $('#' + id); 2666 if ($el.length == 0) { 2667 return; 2668 } 2669 var triggerElement = $el[0]; 2670 anchors.push(id); 2671 2672 self.activeShareImageIndex = 0; 2673 2674 ScrollTrigger.create({ 2675 trigger: triggerElement, 2676 start: "top center", 2677 end: "bottom center", 2678 onEnter: function () { 2679 var id = $el.attr('id'); 2680 var $parent = $('.ny-2024--menuitem [href="#' + id + '"]').parent(); 2681 if ($parent.hasClass('ny-2024--menuitem')) { 2682 $('.ny-2024--menuitem__active').removeClass('ny-2024--menuitem__active'); 2683 $parent.addClass('ny-2024--menuitem__active'); 2684 $('.year-review-2021--mobile-menuitem-link[href="#' + id + '"]').addClass('ny-2024--menuitem__active'); 2685 } 2686 // Clear the hash in the URL when the active section changes. 2687 self.clearUrlHashIfPageAnchor(); 2688 }, 2689 onLeaveBack: function () { 2690 var id = $el.attr('id'); 2691 var $parent = $('.ny-2024--menuitem [href="#' + id + '"]').parent() 2692 var $prev = $parent.prev(); 2693 $('.ny-2024--menuitem__active').removeClass('ny-2024--menuitem__active'); 2694 if ($prev.length > 0 && $prev.hasClass('ny-2024--menuitem')) { 2695 $prev.addClass('ny-2024--menuitem__active'); 2696 $('.year-review-2021--mobile-menuitem-link[href="#' + id + '"]').parent().prev().find('a').addClass('ny-2024--menuitem__active'); 2697 } 2698 // Clear the hash in the URL when the active section changes. 2699 self.clearUrlHashIfPageAnchor(); 2700 } 2701 }); 2702 }); 2703 2704 // When a user clicks a menu/submenu anchor, let the page scroll/jump, 2705 // then immediately remove the hash so refresh won't re-jump. 2706 $(document).on('click', '.ny-2024--menuitem a[href^="#"], .year-review-2021--mobile-menuitem-link[href^="#"]', function () { 2707 setTimeout(function () { 2708 self.clearUrlHashIfPageAnchor(); 2709 }, 0); 2710 }); 2711 // if(self.videoSelected()) { 2712 // self.updateUrlHash(window.location.hash.replace("#", "")); 2713 // } else if(lastHashId) { 2714 // self.updateUrlHash(lastHashId); 2715 // } 2716 2717 $('.year-review-2021--mobile-menuitem').click(function () { 2718 $('.year-review-2021-mobile-menu').removeClass('year-review-2021-mobile-menu__visible'); 2719 }); 2720 2721 $('.ny-2024--menuitem-link__mobile-overlay').click(function (event) { 2722 if ($(window).width() > 992) { 2723 return; 2724 } 2725 2726 event.preventDefault(); 2727 event.stopPropagation(); 2728 2729 trackEvent("Menu", "Opened"); 2730 2731 $('.year-review-2021-mobile-menu').toggleClass('year-review-2021-mobile-menu__visible'); 2732 }); 2733 2734 $('.year-review-2021-mobile-menu--close').click(function () { 2735 $('.year-review-2021-mobile-menu').removeClass('year-review-2021-mobile-menu__visible'); 2736 }); 2737 } 2738 2739 2740 YEAR2026.prototype.initScrollPosition = function () { 2741 var self = this; 2742 self.calculateSectionSize(); 2743 var halfScreen = $(window).height() * 0.5; 2744 $(window).resize(function () { 2745 self.calculateSectionSize(); 2746 halfScreen = $(window).height() * 0.5; 2747 }); 2748 2749 $(window).scroll(function () { 2750 var scrollTop = window.scrollY + halfScreen; 2751 if(window.scrollY == 0) { 2752 scrollTop = 0; 2753 } 2754 var activeSection = self.sections[self.sections.length - 1]; 2755 var foundSection = false;
2756 var acttiveSectionIndex = 0; 2757 for (var index = 0; index < self.sections.length; index++) { 2758 var section = self.sections[index]; 2759 if (section.start <= scrollTop && scrollTop < section.end) { 2760 foundSection = true; 2761 activeSection = self.sections[index]; 2762 acttiveSectionIndex = index; 2763 break; 2764 } 2765 } 2766 2767 if (!foundSection) { 2768 TweenMax.set('.ny-2024--primary-menu__progress', { scaleX: 0 }); 2769 return; 2770 } 2771 2772 // if window.pixelRatio is fractional, we need to scale the progress bar 2773 var windowScaleFactor = 1; 2774 if (window.devicePixelRatio != Math.round(window.devicePixelRatio)) { 2775 windowScaleFactor = window.devicePixelRatio / Math.floor(window.devicePixelRatio); 2776 } 2777 2778 var activeSectionProgress = (scrollTop - activeSection.start) / (activeSection.end - activeSection.start); 2779 2780 var totalProgress; 2781 var totalProgressW = 0; 2782 if (self.isMobile()) { 2783 totalProgress = activeSectionProgress; 2784 } else { 2785 for (var index = 0; index < self.sections.length; index++) { 2786 var section = self.sections[index]; 2787 if (section.start < activeSection.start) { 2788 totalProgressW += section.width; 2789 } 2790 } 2791 2792 totalProgressW += activeSectionProgress * activeSection.width * windowScaleFactor; 2793 totalProgress = totalProgressW / ($('.ny-2024--primary-menu').width() * windowScaleFactor); 2794 } 2795 2796 if(totalProgress > 1) { 2797 totalProgress = 1; 2798 } 2799 2800 TweenMax.set('.ny-2024--primary-menu__progress', { scaleX: totalProgress }); 2801 2802 // Clear any lingering in-page anchor hash when the user scrolls away from it. 2803 self.clearUrlHashIfAnchorNotNearViewportTop(); 2804 }); 2805 } 2806 2807 YEAR2026.prototype.calculateSectionSize = function () { 2808 var sections = []; 2809 var start = $('.page-menu-start').offset().top; 2810 var isMobile = this.isMobile(); 2811 // if window.pixelRatio is fractional, we need to scale the progress bar 2812 var windowScaleFactor = 1; 2813 if (window.devicePixelRatio != Math.round(window.devicePixelRatio)) { 2814 windowScaleFactor = window.devicePixelRatio / Math.floor(window.devicePixelRatio); 2815 } 2816 2817 $('.ny-2024--menuitem').each(function (index) { 2818 var $el = $(this); 2819 // var margin = parseInt($el.css("paddingRight").replace('px', '')) * 2; 2820 // if (isNaN(margin)) { 2821 // margin = 0; 2822 // } 2823 // calcualte outerWidth 2824 var width = this.offsetWidth * windowScaleFactor; 2825 // length = $($el.find('a').attr('href')).offset().top; 2826 var end = 0; 2827 if ($el.next().length > 0 && $el.next().find('a').length > 0) { 2828 end = $($el.next().find('a').attr('href')).offset().top; 2829 } else { 2830 end = $('.end-of-article').offset().top; 2831 } 2832 if (isMobile) { 2833 width = $(window).width(); 2834 } 2835 sections.push({ 2836 start: start, 2837 end: end, 2838 width: width, 2839 }); 2840 start = end; 2841 }); 2842 this.sections = sections; 2843 } 2844 2845 2846 2847 2848 YEAR2026.prototype.fadeInBubble = function () { 2849 setTimeout(function () { 2850 $('.enlightenment-launcher-container').addClass('enlightenment-launcher-container__opened'); 2851 TweenLite.from($('.enlightenment-launcher-container'), 0.75, { width: 24, ease: Power4.easeInOut }); 2852 TweenLite.to($('.enlightenment-launcher-container'), 0.75, { opacity: 1, clearProps: 'width', ease: Power4.easeInOut }); 2853 2854 setTimeout(function () { 2855 $('.enlightenment-launcher-container').removeClass('enlightenment-launcher-container__opened'); 2856 }, 10000); 2857 }, 10000); 2858 2859 var self = this; 2860 2861 $('.oca-form-button, .oca-start--button, .who-we-are-cta-button__oca').click(function (event) { 2862 event.preventDefault(); 2863 event.stopPropagation(); 2864 2865 var $modal = $('.modal-full-viewport__who-we-are-oca').first(); 2866 $modal.addClass('visible'); 2867 history.pushState({}, '', '#modal-open'); 2868 self.disableBodyScroll(); 2869 setTimeout(function () { 2870 self.checkModalScroll(); 2871 }, 500); 2872
2873 TweenMax.from($modal, 0.25, { autoAlpha: 0, ease: Power4.easeOut, clearProps: 'all' }); 2874 TweenMax.from($modal.find('.who-we-are-oca__content-wrapper'), 0.75, { y: "100%", ease: Power4.easeOut }); 2875 2876 }); 2877 } 2878 2879 YEAR2026.prototype.initLazyload = function () { 2880 //add simple support for background images: 2881 document.addEventListener('lazybeforeunveil', function (e) { 2882 var bg = e.target.getAttribute('data-bg'); 2883 if (bg) { 2884 e.target.style.backgroundImage = 'url(' + bg + ')'; 2885 } 2886 }); 2887 setTimeout(() => { 2888 $('[data-bg]').each(function () { 2889 if ($(this).hasClass('lazyloaded')) { 2890 var bg = this.getAttribute('data-bg'); 2891 if (bg) { 2892 this.style.backgroundImage = 'url(' + bg + ')'; 2893 } 2894 } 2895 }); 2896 }, 500); 2897 2898 $('slide [data-src]').each(function () { 2899 var $el = $(this); 2900 if (!$el.hasClass('lazyload')) { 2901 $el.attr('data-srcset', $el.attr('data-src') + " 1x, " + $el.attr('data-src2x') + " 2x"); 2902 $el.addClass('lazyload'); 2903 } 2904 }); 2905 2906 2907 var self = this; 2908 $(window).resize(function () { 2909 self.updateCache(); 2910 }); 2911 2912 $(document).ready(function () { 2913 self.updateCache(); 2914 }); 2915 } 2916 2917 YEAR2026.prototype.updateCache = function () { 2918 window.YEAR2026.cachedPageHeight = $(document).height(); 2919 window.YEAR2026.cachedWindowHeight = $(window).height(); 2920 window.YEAR2026.cachedOuterWindowWidth = $(window).outerWidth(); 2921 window.YEAR2026.cachedOuterWindowHeight = $(window).outerHeight(); 2922 } 2923 2924 YEAR2026.prototype.initNav = function () { 2925 var self = this; 2926 2927 // new ScrollMagic.Scene({ triggerElement: ".year-review-2022--top-wrapper", triggerHook: "onLeave", reverse: true }) 2928 // .setClassToggle('.year-review-2021-nav', 'year-review-2022-nav__visible') 2929 // .addTo(self.controller); 2930 2931 $('.year-review-2021-nav__up').click(function () { 2932 if (self.scrollToPrevInset()) { 2933 return; 2934 } 2935 2936 if (typeof self.activeStepIndex == "undefined") { 2937 var index = 0; 2938 } else { 2939 var index = self.activeStepIndex - 1; 2940 } 2941 if (self.scrollToIndex > -1) { 2942 index = self.scrollToIndex - 1; 2943 } 2944 2945 var duration = $(this).data('duration'); 2946 2947 var $lastInset = self.getLastInsetFor(index); 2948 if ($lastInset) { 2949 self.scrollToElement($lastInset, duration, { forceEase: Power4.easeOut }); 2950 } else { 2951 self.lastScrollAnimation = gsap.to(window, { 2952 duration: 2, 2953 scrollTo: { 2954 y: 0, 2955 }, 2956 ease: Power4.easeOut, 2957 }); 2958 } 2959 2960 }); 2961 2962 $('.year-review-2021-nav__down, .year-2025--bounce').click(function () { 2963 if (self.scrollToNextInset()) { 2964 return; 2965 } 2966 if (typeof self.activeStepIndex == "undefined") { 2967 var index = 0; 2968 } else { 2969 var index = self.activeStepIndex + 1; 2970 } 2971 if (self.scrollToIndex > -1) { 2972 index = self.scrollToIndex + 1; 2973 } 2974 }); 2975 } 2976 2977 YEAR2026.prototype.initControls = function () { 2978 var self = this; 2979 2980 $('.navigation-step').each(function (index) { 2981 var $el = $(this); 2982 $el.attr('data-index', index); 2983 }); 2984 2985 $(document).keydown(function (e) { 2986 var arrow = { left: 37, up: 38, right: 39, down: 40, pageUp: 33, pageDown: 34 }; 2987 2988 switch (e.which) { 2989 // case arrow.left: 2990 // for (var index = 0; index < self.swipers.length; index++) { 2991 // var swiper = self.swipers[index]; 2992 // if(isInViewportVisible(swiper.$el)) { 2993 // var index = swiper.$el.attr('data-swiper-index'); 2994 // self.swipers[parseInt(index)].slidePrev(); 2995 // } 2996 // } 2997 // break; 2998 case arrow.up: 2999 case arrow.pageUp: 3000 if (self.scrollToPrevInset()) { 3001 e.preventDefault(); 3002 e.stopPropagation(); 3003 $('.year-review-2021-nav__up').addClass('year-review-2021-nav__up-pressed'); 3004 setTimeout(function () { 3005 $('.year-review-2021-nav__up').removeClass('year-review-2021-nav__up-pressed'); 3006 }, 1100); 3007 return; 3008 } 3009 break; 3010 // case arrow.right: 3011 // for (var index = 0; index < self.swipers.length; index++) { 3012 // var swiper = self.swipers[index]; 3013 // if(isInViewportVisible(swiper.$el)) { 3014 // var index = swiper.$el.attr('data-swiper-index'); 3015 // self.swipers[parseInt(index)].slideNext(); 3016 // } 3017 // } 3018 // break; 3019 case arrow.down: 3020 case arrow.pageDown: 3021 if (self.scrollToNextInset()) { 3022 e.preventDefault(); 3023 e.stopPropagation(); 3024 $('.year-review-2021-nav__down').addClass('year-review-2021-nav__down__pressed'); 3025 setTimeout(function () { 3026 $('.year-review-2021-nav__down').removeClass('year-review-2021-nav__down__pressed'); 3027 }, 1100); 3028 return; 3029 } 3030 break; 3031 // case 32: 3032 // for (var index in videojs.players) { 3033 // if (Object.hasOwnProperty.call(videojs.players, index)) { 3034 // var video = videojs.players[index]; 3035 // var $el = $(video.el()); 3036 // if(isInViewportVisible($el) && self.qualifiedAutoplay($el)) { 3037 // var $wrapper = $el.closest('.show-on-the-road--playbutton-area'); 3038 // var videoPlaying = $wrapper.hasClass('show-on-the-road-videoautoplay__inline');
3039 // if(!videoPlaying) { 3040 // $wrapper.click(); 3041 // } else if(video.paused()) { 3042 // video.play(); 3043 // } else { 3044 // video.pause(); 3045 // } 3046 3047 // e.preventDefault(); 3048 // e.stopPropagation(); 3049 // } 3050 // } 3051 // } 3052 // break; 3053 } 3054 }); 3055 } 3056 3057 3058 3059 3060 YEAR2026.prototype.getLastInsetFor = function (index) { 3061 var $container = $('.navigation-step[data-id=' + index + ']'); 3062 if ($container.length == 0) { 3063 return false; 3064 } 3065 return $container.last(); 3066 } 3067 3068 YEAR2026.prototype.getPrevInset = function () { 3069 var self = this; 3070 var $insets = $('.navigation-step'); 3071 if ($insets.length == 0) { 3072 self._prevInsetActiveIndex = null; 3073 return false; 3074 } 3075 3076 var activeInsetIndex = -1; 3077 var scrollTop = $(window).scrollTop(); 3078 var windowHeight = $(window).height(); 3079 var $activeInset; 3080 3081 $insets.each(function (index, inset) { 3082 var $inset = $(inset); 3083 var alignBottom = $inset.data('align-bottom'); 3084 var offset = 200; 3085 if ($inset.data("offset")) { 3086 offset = parseInt($inset.data("offset")); 3087 } 3088 3089 if($inset.closest('.who-we-are-iaas-grid-active').length > 0) { 3090 return; 3091 } 3092 3093 if (self.getScrollPosition($inset) + 100 < scrollTop) { 3094 $activeInset = $inset; 3095 activeInsetIndex = index; 3096 } 3097 }); 3098 3099 var $target; 3100 3101 // did not scroll past first inset, go to it 3102 if (activeInsetIndex == -1) { 3103 // $target = $($insets[0]); 3104 self._prevInsetActiveIndex = null; 3105 return false; 3106 } else if (activeInsetIndex == 0) { 3107 $target = $('.navigation-step').first(); 3108 } else if (activeInsetIndex - 1 < 0) { 3109 self._prevInsetActiveIndex = null; 3110 return false; 3111 } else { 3112 $target = $($insets[activeInsetIndex]); 3113 } 3114 3115 // Expose the computed active index so scrollToPrevInset can derive the 3116 // "from" (current) inset and use its duration. 3117 self._prevInsetActiveIndex = activeInsetIndex; 3118 3119 return $target; 3120 } 3121 3122 YEAR2026.prototype.scrollToPrevInset = function () { 3123 var $target = this.getPrevInset(); 3124 3125 if (!$target || !$target.length) { 3126 return false; 3127 } 3128 3129 // For reverse navigation we want the same timing as the forward move 3130 // that brought us here. Durations are authored per *destination* inset, 3131 // so when going up we read duration from the inset we're leaving. 3132 var duration; 3133 var activeIndex = (typeof this._prevInsetActiveIndex === 'number') ? this._prevInsetActiveIndex : null; 3134 var $from; 3135 if (activeIndex !== null) { 3136 var $insets = $('.navigation-step'); 3137 if (activeIndex + 1 < $insets.length) { 3138 $from = $($insets[activeIndex + 1]); 3139 } 3140 } 3141 3142 duration = ($from && $from.length) ? $from.data('duration') : $target.data('duration'); 3143 3144 if (typeof duration == "undefined") { 3145 duration = 1000; 3146 } 3147 3148 this._lastInsetScrollTime = 0; 3149 3150 // Always use ease-out when navigating backwards (up arrow / back button). 3151 this.scrollToElement($target, duration, { forceEase: Power4.easeOut }); 3152 3153 this.clearUrlHashIfPageAnchor(); 3154 3155 return true; 3156 } 3157 3158 YEAR2026.prototype.getScrollPosition = function (el) { 3159 var self = this; 3160 if (self.lastScrollAnimation) { 3161 self.lastScrollAnimation.pause(); 3162 } 3163 3164 var id = el.attr('id'); 3165 var alignBottom = el.data('align-bottom'); 3166 var ease = Power4.easeInOut; 3167 3168 var topOffset = el.offset().top; 3169 3170 if (el.data('align-center')) { 3171 topOffset = topOffset - $(window).height() / 2 + el.height() / 2; 3172 } 3173 3174 var scrollingDown = topOffset - window.scrollY > 0; 3175 3176 var topSpace = 66; 3177 var bottomAligner = 20; 3178 if (typeof el.data("top") != "undefined") { 3179 topSpace = parseInt(el.data("top")); 3180 bottomAligner = parseInt(el.data("top")); 3181 } 3182 3183 if (typeof el.data("ease") != "undefined") { 3184 ease = Power4.easeOut; 3185 } 3186 3187 if (typeof el.data("easein") != "undefined") { 3188 ease = Power4.easeInOut; 3189 } 3190 3191 3192 if (alignBottom) { 3193 return topOffset + bottomAligner - $(window).height(); 3194 } 3195 return topOffset - topSpace; 3196 } 3197 3198 YEAR2026.prototype.scrollToNextInset = function () { 3199 3200 var $target = this.getNextInset(); 3201 3202 this._lastInsetScrollTime = new Date().getTime(); 3203 3204 var duration = $target.data('duration'); 3205 this.scrollToElement($target, duration); 3206 3207 this.clearUrlHashIfPageAnchor(); 3208 3209 return true; 3210 } 3211 3212 YEAR2026.prototype.getNextInset = function () { 3213 var self = this; 3214 var $insets = $('.navigation-step'); 3215 if ($insets.length == 0) { 3216 return false;
3217 } 3218 var $activeInset; 3219 var activeInsetIndex = 0; 3220 var scrollTop = $(window).scrollTop(); 3221 3222 var windowHeight = $(window).height(); 3223 3224 3225 $insets.each(function (index, inset) { 3226 if ($activeInset) { 3227 return; 3228 } 3229 var $inset = $(inset); 3230 var offset = 100; 3231 3232 if($inset.closest('.who-we-are-iaas-grid-active').length > 0) { 3233 return; 3234 } 3235 3236 var alignBottom = $inset.data('align-bottom'); 3237 if (typeof $inset.data('offset') != "undefined") { 3238 offset = parseInt($inset.data('offset')); 3239 } 3240 3241 if (self.getScrollPosition($inset) - offset > scrollTop) { 3242 $activeInset = $inset; 3243 activeInsetIndex = index; 3244 } 3245 }); 3246 3247 var $target; 3248 3249 // did not scroll past first inset, go to it 3250 if (!$activeInset) { 3251 if (window.scrollY > 2000) { 3252 return; 3253 } 3254 $target = $($insets[0]); 3255 } else { 3256 $target = $activeInset; 3257 } 3258 3259 return $target; 3260 } 3261 3262 3263 YEAR2026.prototype.getScrollTopOffset = function (el) { 3264 var alignBottom = el.data('align-bottom'); 3265 3266 var topSpace = 75; 3267 var bottomAligner = 20; 3268 if (typeof el.data("top") != "undefined") { 3269 topSpace = parseInt(el.data("top")); 3270 bottomAligner = 0; 3271 } else if (alignBottom) { 3272 topSpace = 20; 3273 bottomAligner = 0; 3274 3275 if (typeof el.data("top") != "undefined") { 3276 topSpace = parseInt(el.data("top")); 3277 } 3278 } 3279 3280 3281 var topOffset = el.offset().top; 3282 if (el.data('align-center')) { 3283 topOffset = topOffset - $(window).height() / 2 + el.height() / 2; 3284 } else if (alignBottom) { 3285 topOffset = topOffset + topSpace + bottomAligner - $(window).height(); 3286 } else { 3287 topOffset = topOffset - topSpace; 3288 } 3289 3290 return topOffset; 3291 } 3292 3293 YEAR2026.prototype.scrollToElement = function (el, duration, options) { 3294 var self = this; 3295 if (self.lastScrollAnimation) { 3296 self.lastScrollAnimation.pause(); 3297 } 3298 3299 var topOffset = self.getScrollTopOffset(el); 3300 3301 if(globalGetParams.stoppoints) { 3302 console.log("Scrolling to element: ", el); 3303 } 3304 3305 var dur = 2; 3306 var ease = Power4.easeInOut; 3307 3308 // Optional override for callers (e.g. back button) that must force an ease. 3309 if (options && options.forceEase) { 3310 ease = options.forceEase; 3311 } else { 3312 if (typeof el.data("ease") != "undefined") { 3313 ease = el.data("ease"); 3314 } 3315 3316 if (typeof el.data("ease-curve") != "undefined") { 3317 ease = el.data("ease-curve"); 3318 } 3319 3320 if (typeof el.data("ease-curve-custom") != "undefined") { 3321 ease = CustomEase.create("custom", el.data("ease-curve-custom")); 3322 } 3323 } 3324 3325 if (duration) { 3326 dur = duration / 1000; 3327 } 3328 3329 3330 var scrollForward = topOffset - window.scrollY > 0; 3331 3332 if (scrollForward) { 3333 var $prevInset; 3334 var currentIndex = parseInt(el.attr('data-index')); 3335 if (currentIndex > 0) { 3336 $prevInset = $('.navigation-step[data-index="' + (currentIndex - 1) + '"]'); 3337 } 3338 3339 3340 if ($prevInset) { 3341 var currentPosition = self.getScrollTopOffset($prevInset); 3342 var totalScrollDistance = Math.abs(currentPosition - topOffset); 3343 var remainingScrollDistance = Math.abs(topOffset - window.scrollY); 3344 3345 if(typeof el.data('dynamic-duration') == 'undefined') { 3346 dur = dur * remainingScrollDistance / totalScrollDistance; 3347 } 3348 } 3349 } else { 3350 var $nextInset; 3351 var currentIndex = parseInt(el.attr('data-index')); 3352 if (currentIndex < $('.navigation-step').length - 1) { 3353 $nextInset = $('.navigation-step[data-index="' + (currentIndex + 1) + '"]'); 3354 } 3355 3356 3357 if ($nextInset) { 3358 var currentPosition = self.getScrollTopOffset($nextInset); 3359 var totalScrollDistance = Math.abs(currentPosition - topOffset); 3360 var remainingScrollDistance = Math.abs(topOffset - window.scrollY); 3361 3362 dur = dur * remainingScrollDistance / totalScrollDistance; 3363 } 3364 } 3365 3366 if (dur < 0.5) { 3367 dur = 0.5; 3368 } 3369 3370 self.lastScrollAnimation = gsap.to(window, { 3371 duration: dur, 3372 scrollTo: { 3373 y: topOffset, 3374 }, 3375 ease: ease, 3376 }); 3377 3378 } 3379 3380 3381 YEAR2026.prototype.getPageHeight = function () { 3382 // called from ScrollMagic 3383 var self = window.YEAR2026 || this;
3384 if (!self.cachedPageHeight) { 3385 self.cachedPageHeight = $(document).height(); 3386 } 3387 return self.cachedPageHeight; 3388 } 3389 3390 YEAR2026.prototype.getWindowHeight = function () { 3391 // called from ScrollMagic 3392 var self = window.YEAR2026 || this; 3393 if (!self.cachedWindowHeight) { 3394 self.cachedWindowHeight = $(window).height(); 3395 } 3396 return self.cachedWindowHeight; 3397 } 3398 3399 YEAR2026.prototype.getWindowOuterWidth = function () { 3400 // called from ScrollMagic 3401 var self = window.YEAR2026 || this; 3402 if (!self.cachedOuterWindowWidth) { 3403 self.cachedOuterWindowWidth = $(window).outerWidth(); 3404 } 3405 return self.cachedOuterWindowWidth; 3406 } 3407 3408 YEAR2026.prototype.isMobile = function () { 3409 return $(window).width() < 767; 3410 } 3411 3412 YEAR2026.prototype.isMobileAgent = function () { 3413 return isMobile.any; 3414 } 3415 3416 3417 YEAR2026.prototype.isTablet = function () { 3418 return $(window).width() >= 768 && $(window).width() < 992; 3419 } 3420 3421 YEAR2026.prototype.isAndroid = function () { 3422 var ua = navigator.userAgent.toLowerCase(); 3423 var isAndroid = ua.indexOf("android") > -1; 3424 return isAndroid; 3425 } 3426 3427 YEAR2026.prototype.isIpod = function () { 3428 var ua = navigator.userAgent.toLowerCase(); 3429 var isIpod = ua.indexOf("iPod") > -1; 3430 return isIpod; 3431 } 3432 3433 YEAR2026.prototype.initButtonFadein = function () { 3434 var self = this; 3435 $('.year-in-review-2026-button-wrapper').each(function () { 3436 var $button = $(this); 3437 var paused = $button.data('paused'); 3438 var options = {}; 3439 3440 if (!paused) { 3441 options.scrollTrigger = { 3442 trigger: this, 3443 start: 'bottom bottom+=5%', 3444 end: "100%", 3445 toggleActions: "play none resume reverse", 3446 }; 3447 } 3448 3449 var timeline = gsap.timeline(options); 3450 if (paused) { 3451 timeline.pause(); 3452 } 3453 if ($(this).attr('id')) { 3454 self.globalTimelines[$(this).attr('id')] = timeline; 3455 } 3456 timeline.fromTo(this.querySelector('.year-in-review-2026-button-inner'), { 3457 scale: 0, 3458 },{ 3459 duration: 0.25, 3460 scale: 1, 3461 autoAlpha: 1, 3462 }); 3463 timeline.fromTo(this.querySelector('.year-in-review-2026-button-outer-circle'), { 3464 scale: 0, 3465 },{ 3466 duration: 0.5, 3467 scale: 1.5, 3468 autoAlpha: 1, 3469 }, "<"); 3470 3471 timeline.fromTo(this.querySelector('.year-in-review-2026-button-outer-circle'), { 3472 scale: 1.5, 3473 },{ 3474 duration: 0.5, 3475 scale: 0, 3476 }); 3477 timeline.from(this.querySelector('.year-in-review-2026-button-inner'), { 3478 duration: 0.75, 3479 width: 50, 3480 height: 50, 3481 clearProps: 'width, height', 3482 }, "-=0.3"); 3483 timeline.from(this.querySelectorAll('.year-in-review-2026-button-inner-text'), { 3484 duration: 0.25, 3485 autoAlpha: 0, 3486 }); 3487 }); 3488 3489 // Fade in LRH houses nav (same overall "pop" feel as buttons: scale + alpha). 3490 $('.year-2026-lrh-houses-nav').each(function () { 3491 var navEl = this; 3492 3493 // Prevent duplicate ScrollTriggers/timelines if Init runs more than once. 3494 if (navEl._fadeinTl) { 3495 try { navEl._fadeinTl.scrollTrigger && navEl._fadeinTl.scrollTrigger.kill(true); } catch (e) { } 3496 try { navEl._fadeinTl.kill(); } catch (e) { } 3497 navEl._fadeinTl = null; 3498 } 3499 var tl = gsap.timeline({ 3500 scrollTrigger: { 3501 trigger: navEl, 3502 start: 'bottom bottom+=5%', 3503 end: '100%', 3504 toggleActions: 'play none resume reverse', 3505 }, 3506 delay: 0.5 3507 }); 3508 3509 tl.fromTo(navEl, { 3510 autoAlpha: 0, 3511 scale: 0, 3512 transformOrigin: '50% 50%' 3513 }, { 3514 duration: 0.25, 3515 autoAlpha: 1, 3516 scale: 1, 3517 clearProps: 'transformOrigin' 3518 }); 3519 3520 var arrows = navEl.querySelectorAll('.year-2026-lrh-houses--arrow'); 3521 if (arrows && arrows.length) { 3522 tl.fromTo(arrows, { 3523 autoAlpha: 0, 3524 scale: 0.85, 3525 transformOrigin: '50% 50%' 3526 }, { 3527 duration: 0.25, 3528 autoAlpha: 1, 3529 scale: 1, 3530 stagger: 0.08, 3531 clearProps: 'transformOrigin' 3532 }, '<'); 3533 } 3534 3535 navEl._fadeinTl = tl; 3536 }); 3537 } 3538 3539 3540 3541 YEAR2026.prototype.initIntroScroll = function () { 3542 gsap.fromTo('.year-in-review-2026-first-images', { 3543 autoAlpha: 0, 3544 y: 50, 3545 }, { 3546 autoAlpha: 1, 3547 y: 0, 3548 scrollTrigger: { 3549 // trigger: '.year-2025-top-inner', 3550 // start: "bottom center+=10%", 3551 trigger: '.year-in-review-2026-first-images', 3552 start: "center bottom", 3553 } 3554 }) 3555 } 3556 3557 YEAR2026.prototype.initGrowingLines = function () { 3558 var self = this; 3559 3560 $('.year-in-review-2026-vertical-line').each(function () { 3561 gsap.from(this.querySelector('.year-in-review-2026-glow-line'), { 3562 height: 0, 3563 scrollTrigger: { 3564 start: 'top bottom', 3565 end: 'top center', 3566 duration: 1, 3567 // scrub: true, 3568 trigger: this, 3569 toggleClass: 'year-in-review-2026-glow-line__visible', 3570 toggleActions: "play none resume reverse", 3571 } 3572 }); 3573 3574 }); 3575 } 3576 3577
3578 YEAR2026.prototype.playBackgroundVideo = function () { 3579 var self = this; 3580 // Select the video elements 3581 var startVideoElement = document.querySelector('.who-we-are--bg-video-start'); 3582 var loopVideoElement = document.querySelector('.who-we-are--bg-video'); 3583 3584 // Initialize the start video player 3585 var startPlayer = videojs(startVideoElement, { 3586 controls: false, 3587 muted: true, 3588 loop: false, 3589 autoplay: true 3590 }); 3591 3592 // Initialize the loop video player 3593 var loopPlayer = videojs(loopVideoElement, { 3594 controls: false, 3595 muted: true, 3596 loop: true, 3597 autoplay: false, 3598 }); 3599 3600 // Initially hide the loop video 3601 $(loopPlayer.el()).hide(); 3602 3603 // Set source for start video 3604 startPlayer.ready(function () { 3605 var src = $(this.el_).data('src'); 3606 gsap.set(this.el_, { autoAlpha: 1 }); 3607 if (src) { 3608 this.src(src); 3609 this.load(); 3610 this.play(); 3611 var inited = false; 3612 this.one('play', function () { 3613 setTimeout(function(){ 3614 if(!inited) { 3615 inited = true; 3616 // self.initTopHeader(); 3617 } 3618 }, 2000); 3619 }); 3620 this.on('error', function () { 3621 if(!inited) { 3622 inited = true; 3623 // self.initTopHeader(); 3624 } 3625 }); 3626 } 3627 }); 3628 3629 // Set source for loop video 3630 loopPlayer.ready(function () { 3631 var src = $(this.el_).data('src'); 3632 gsap.set(this.el_, { autoAlpha: 1 }); 3633 if (src) { 3634 this.src(src); 3635 this.load(); 3636 } 3637 }); 3638 3639 // When the start video ends, switch to the loop video 3640 startPlayer.on('ended', function () { 3641 gsap.set(this.el_, { autoAlpha: 0 }); 3642 loopPlayer.play(); 3643 }); 3644 3645 // Optional: Ensure the loop video is ready before hiding 3646 loopPlayer.on('loadedmetadata', function() { 3647 // Optionally seek to the first frame if needed 3648 gsap.set(this.el_, { autoAlpha: 1 }); 3649 $(loopPlayer.el()).show(); 3650 this.currentTime(0); 3651 }); 3652 }; 3653 3654 YEAR2026.prototype.initFireworksBackgroundVideo = function () { 3655 var self = this; 3656 var elements = document.querySelectorAll('.year-in-review-2026-fireworks-video-autoplay'); 3657 elements.forEach(function (element) { 3658 var videoInitied = false; 3659 var video = element.querySelector('.year-in-review-2026-fireworks-video video'); 3660 gsap.timeline({ 3661 scrollTrigger: { 3662 trigger: element, 3663 start: 'top top', 3664 pin: element.querySelector('.year-in-review-2026-fireworks-wrapper'), 3665 end: "+=100%", 3666 pinSpacing: false, 3667 onEnter: function (progress, direction, isActive) { 3668 if (!video.muted) { 3669 return; 3670 } 3671 3672 video.currentTime = 0; 3673 3674 gsap.fromTo( element.querySelectorAll('.year-in-review-2026-fireworks-video, .year-in-review-2026-fireworks-video .video-js, .year-in-review-2026-fireworks-video-overlay'), { 3675 autoAlpha: 0, 3676 }, { 3677 autoAlpha: 1, 3678 }); 3679 3680 if(!videoInitied) { 3681 videoInitied = true; 3682 self.playBackgroundFireworksVideo(video); 3683 return; 3684 } 3685 3686 console.log('video', video); 3687 3688 video.controls = false; 3689 video.play().catch((error) => { 3690 if (error.name === 'AbortError') { 3691 console.log('Playback was interrupted:', error); 3692 } else { 3693 console.error('An unexpected error occurred:', error); 3694 window.sentry && Sentry.captureException(error); 3695 } 3696 }); 3697 }, 3698 onEnterBack: function (progress, direction, isActive) { 3699 if (!video.muted) { 3700 return; 3701 } 3702 3703 video.currentTime = 0; 3704 3705 gsap.fromTo(element.querySelectorAll('.year-in-review-2026-fireworks-video, .year-in-review-2026-fireworks-video-overlay'), { 3706 autoAlpha: 0, 3707 }, { 3708 autoAlpha: 1, 3709 }); 3710
3711 if(!videoInitied) { 3712 videoInitied = true; 3713 self.playBackgroundFireworksVideo(video); 3714 return; 3715 } 3716 3717 video.controls = false; 3718 video.play().catch((error) => { 3719 if (error.name === 'AbortError') { 3720 console.log('Playback was interrupted:', error); 3721 } else { 3722 console.error('An unexpected error occurred:', error); 3723 window.sentry && Sentry.captureException(error); 3724 } 3725 }); 3726 }, 3727 onLeave: function (progress, direction, isActive) { 3728 // video started playing by user interaction 3729 if (!video.muted) { 3730 return; 3731 } 3732 video.pause(); 3733 gsap.to(element.querySelectorAll('.year-in-review-2026-fireworks-video, .year-in-review-2026-fireworks-video-overlay'), { 3734 autoAlpha: 0, 3735 }); 3736 }, 3737 onLeaveBack: function (progress, direction, isActive) { 3738 // video started playing by user interaction 3739 if (!video.muted) { 3740 return; 3741 } 3742 if (direction === "reverse") { 3743 video.pause(); 3744 } 3745 gsap.to(element.querySelectorAll('.year-in-review-2026-fireworks-video, .year-in-review-2026-fireworks-video-overlay'), { 3746 autoAlpha: 0, 3747 }); 3748 } 3749 }, 3750 }); 3751 }); 3752 } 3753 3754 YEAR2026.prototype.playBackgroundFireworksVideo = function (startVideoElement) { 3755 var self = this; 3756 // Select the video elements 3757 3758 // Initialize the start video player 3759 var startPlayer = videojs(startVideoElement, { 3760 controls: false, 3761 muted: true, 3762 loop: false, 3763 autoplay: true 3764 }); 3765 3766 // Set source for start video 3767 startPlayer.ready(function () { 3768 var src = $(this.el_).data('src'); 3769 gsap.set(this.el_, { autoAlpha: 1 }); 3770 if (src) { 3771 this.src(src); 3772 this.load(); 3773 this.play(); 3774 } 3775 }); 3776 3777 }; 3778 3779 YEAR2026.prototype.initLoopAutoPlay = function () { 3780 var self = this; 3781 3782 if (typeof videojs === 'undefined') { 3783 return; 3784 } 3785 3786 var players = []; 3787 3788 var parseVideoSrc = function (videoEl) { 3789 var src = $(videoEl).data('videosrc'); 3790 if (!src) { 3791 src = videoEl.getAttribute('data-videosrc'); 3792 } 3793 if (!src) { 3794 return null; 3795 } 3796 if (typeof src === 'string') { 3797 try { 3798 return JSON.parse(src); 3799 } catch (e) { 3800 return null; 3801 } 3802 } 3803 return src; 3804 }; 3805 3806 var fadeInEl = function (el) { 3807 if (typeof gsap === 'undefined' || !el) { 3808 return; 3809 } 3810 gsap.to(el, { autoAlpha: 1, duration: 0.25, overwrite: true }); 3811 }; 3812 3813 var fadeOutEl = function (el) { 3814 if (typeof gsap === 'undefined' || !el) { 3815 return; 3816 } 3817 gsap.to(el, { autoAlpha: 0, duration: 0.25, overwrite: true }); 3818 }; 3819 3820 var attemptPlay = function (player, videoEl) { 3821 try { 3822 player.muted(true); 3823 player.loop(true); 3824 player.controls(false); 3825 var p = player.play(); 3826 if (p && typeof p.then === 'function') { 3827 p.then(function () { 3828 fadeInEl(videoEl); 3829 }); 3830 } 3831 if (p && typeof p.catch === 'function') { 3832 p.catch(function (error) {
3833 if (error && error.name === 'AbortError') { 3834 return; 3835 } 3836 console.error('Autoplay error:', error); 3837 window.sentry && Sentry.captureException(error); 3838 }); 3839 } 3840 } catch (e) { 3841 console.error('Autoplay exception:', e); 3842 window.sentry && Sentry.captureException(e); 3843 } 3844 }; 3845 3846 $('.year-2026-our-voice--video').each(function () { 3847 var videoEl = this.querySelector('video'); 3848 var videoElWrapper = this; 3849 var videoSrc = parseVideoSrc(videoEl); 3850 if (!videoSrc) { 3851 return; 3852 } 3853 3854 // Start hidden; fade in on successful autoplay. 3855 fadeOutEl(videoElWrapper); 3856 3857 videoEl.muted = true; 3858 videoEl.loop = true; 3859 videoEl.controls = false; 3860 videoEl.setAttribute('muted', ''); 3861 videoEl.setAttribute('playsinline', ''); 3862 videoEl.setAttribute('webkit-playsinline', ''); 3863 videoEl.setAttribute('loop', ''); 3864 3865 videojs(videoEl, { controls: false, muted: true, loop: true, preload: 'none', autoplay: false }).ready(function () { 3866 var player = this; 3867 players.push(player); 3868 3869 player.src(videoSrc); 3870 player.pause(); 3871 3872 player.on('play', function () { 3873 fadeInEl(videoElWrapper); 3874 }); 3875 player.on('pause', function () { 3876 fadeOutEl(videoElWrapper); 3877 }); 3878 3879 if (typeof ScrollTrigger !== 'undefined') { 3880 ScrollTrigger.create({ 3881 trigger: '.year-2026-our-message-video', 3882 start: 'top bottom', 3883 end: 'bottom top', 3884 toggleActions: 'play none resume reverse', 3885 onEnter: function () { 3886 attemptPlay(player, videoElWrapper); 3887 }, 3888 onEnterBack: function () { 3889 attemptPlay(player, videoElWrapper); 3890 }, 3891 onLeave: function () { 3892 player.pause(); 3893 }, 3894 onLeaveBack: function () { 3895 player.pause(); 3896 }, 3897 }); 3898 } else { 3899 attemptPlay(player, videoElWrapper); 3900 } 3901 }); 3902 }); 3903 3904 // Fallback: some browsers require a user gesture even for muted autoplay. 3905 if (!self._loopAutoplayUnlockBound && players.length > 0) { 3906 self._loopAutoplayUnlockBound = true; 3907 var unlock = function () { 3908 for (var i = 0; i < players.length; i++) { 3909 attemptPlay(players[i], players[i] && players[i].el() ? players[i].el() : null); 3910 } 3911 document.removeEventListener('click', unlock); 3912 document.removeEventListener('touchstart', unlock); 3913 document.removeEventListener('keydown', unlock); 3914 }; 3915 document.addEventListener('click', unlock, { passive: true, once: true }); 3916 document.addEventListener('touchstart', unlock, { passive: true, once: true }); 3917 document.addEventListener('keydown', unlock, { passive: true, once: true }); 3918 } 3919 } 3920 3921 YEAR2026.prototype.initNetworkSection = function () { 3922 3923 // pin 150 awards section 3924 gsap.timeline({ 3925 scrollTrigger: { 3926 trigger: '.year-in-review-2024-150-awards-wrapper', 3927 start: 'top top', 3928 endTrigger: '.year-in-review-2025-awards-image-wrapper', 3929 end: 'bottom bottom', 3930 pin: '.year-in-review-2024-150-awards', 3931 pinSpacing: false, 3932 anticipatePin: 1, 3933 // markers: true, 3934 } 3935 }); 3936 3937 // pin awawrds section 3938 gsap.timeline({ 3939 scrollTrigger: { 3940 trigger: '.year-review-2025--tv-awards__wrapper', 3941 start: 'top top', 3942 endTrigger: '.year-in-review-2024-150-awards-wrapper', 3943 end: 'bottom bottom', 3944 pin: true, 3945 pinSpacing: false, 3946 anticipatePin: 1, 3947 // markers: true, 3948 } 3949 }); 3950 3951 gsap.timeline({ 3952 scrollTrigger: { 3953 trigger: '.year-in-review-2024-150-awards__trigger', 3954 start: 'top 30%', 3955 toggleActions: "play none resume reverse", 3956 } 3957 }) 3958 .from('.year-in-review-2024-150-awards .year-2025--text-scale--inner', { 3959 autoAlpha: 0, 3960 scale: 10, 3961 duration: 0.25, 3962 }); 3963 3964 gsap.timeline({ 3965 scrollTrigger: { 3966 trigger: '.year-in-review-2025-awards-image-wrapper', 3967 start: 'center bottom', 3968 toggleActions: "play none resume reverse", 3969 } 3970 }) 3971 .fromTo('.year-in-review-2025-awards-over-trophies-bg', { 3972 autoAlpha: 0, 3973 duration: 0.5, 3974 ease: Power4.easeOut, 3975 }, { 3976 autoAlpha: 0.6, 3977 duration: 0.5, 3978 ease: Power4.easeOut, 3979 }); 3980 3981 // gsap.timeline({ 3982 // scrollTrigger: { 3983 // trigger: '.year-in-review-2025-awards-image-wrapper', 3984 // start: 'bottom bottom', 3985 // end: 'bottom bottom', 3986 // endTrigger: '.year-in-review-2024-150-awards-wrapper', 3987 // pin: '.year-in-review-2025-awards-image-wrapper', 3988 // toggleActions: "play none resume reverse", 3989 // pinSpacing: false, 3990 // } 3991 // }); 3992 3993 } 3994 3995 3996
3997 YEAR2026.prototype.initScnNWBackground = function () { 3998 var target = '.year-review-2025-network-section-bg'; 3999 ScrollTrigger.create({ 4000 trigger: '.year-2026-tv-section-wrapper', 4001 start: 'top top', 4002 end: 'bottom bottom', 4003 pin: target, 4004 pinSpacing: false, 4005 toggleClass: { 4006 targets: target, 4007 className: 'year-review-2025-network-section-bg__visible' 4008 }, 4009 toggleActions: 'play reverse play reverse' 4010 }); 4011 4012 // ScrollTrigger.create({ 4013 // trigger: '.year-review-2022-network', 4014 // start: 'bottom top', 4015 // toggleClass: { 4016 // targets: target, 4017 // className: 'year-review-2025-network-section-bg__hidden' 4018 // }, 4019 // toggleActions: 'play reverse play reverse' 4020 // }); 4021 } 4022 4023 YEAR2026.prototype.initFEBCSlamTitles = function () { 4024 if (typeof gsap === 'undefined' || typeof ScrollTrigger === 'undefined') { 4025 return; 4026 } 4027 4028 var wrappers = document.querySelectorAll('.year-in-review-2026-slam-wrapper'); 4029 if (!wrappers || wrappers.length === 0) { 4030 return; 4031 } 4032 4033 wrappers.forEach(function (wrapper) { 4034 var titles = wrapper.querySelectorAll('.year-in-review-2026-slam-in-sequence'); 4035 if (!titles || titles.length === 0) { 4036 return; 4037 } 4038 4039 var saluteBg = wrapper.querySelector('.year-in-review-2026-febc-salute') || document.querySelector('.year-in-review-2026-febc-salute'); 4040 if (saluteBg) { 4041 gsap.set(saluteBg, { 4042 autoAlpha: 0, 4043 scale: 0, 4044 rotate: 0, 4045 transformOrigin: '50% 50%', 4046 willChange: 'transform, opacity', 4047 }); 4048 } 4049 4050 gsap.set(titles, { 4051 autoAlpha: 0, 4052 scale: 10, 4053 transformOrigin: '50% 50%', 4054 willChange: 'transform, opacity', 4055 }); 4056 4057 var activeIndex = -1; 4058 4059 var slamIn = function (el) { 4060 gsap.killTweensOf(el); 4061 return gsap.fromTo(el, { 4062 autoAlpha: 0, 4063 scale: 10, 4064 }, { 4065 autoAlpha: 1, 4066 scale: 1, 4067 duration: 0.35, 4068 ease: Power4.easeOut, 4069 overwrite: true, 4070 }); 4071 }; 4072 4073 var fadeOut = function (el) { 4074 if (!el) { 4075 return; 4076 } 4077 gsap.killTweensOf(el); 4078 gsap.to(el, { 4079 autoAlpha: 0, 4080 duration: 0.2, 4081 overwrite: true, 4082 }); 4083 }; 4084 4085 var showIndex = function (nextIndex) { 4086 if (nextIndex < 0 || nextIndex >= titles.length) { 4087 return; 4088 } 4089 if (nextIndex === activeIndex) { 4090 return; 4091 } 4092 4093 var prevIndex = activeIndex; 4094 activeIndex = nextIndex; 4095 4096 for (var i = 0; i < titles.length; i++) { 4097 if (i !== nextIndex && i !== prevIndex) { 4098 gsap.set(titles[i], { autoAlpha: 0 }); 4099 } 4100 } 4101 4102 if (prevIndex > -1 && titles[prevIndex]) { 4103 fadeOut(titles[prevIndex]); 4104 } 4105 4106 // Add a subtle shake to the salute background on each slam. 4107 if (saluteBg) { 4108 var rand = function (min, max) { 4109 return min + Math.random() * (max - min); 4110 }; 4111 gsap.killTweensOf(saluteBg, 'x,y'); 4112 gsap.fromTo(saluteBg, { 4113 x: 0, 4114 y: 0, 4115 }, { 4116 x: rand(-10, 10), 4117 y: rand(-8, 8), 4118 duration: 0.06, 4119 repeat: 6, 4120 yoyo: true, 4121 ease: 'none', 4122 overwrite: 'auto', 4123 onComplete: function () { 4124 gsap.set(saluteBg, { x: 0, y: 0 }); 4125 } 4126 }); 4127 } 4128 4129 slamIn(titles[nextIndex]); 4130 }; 4131 4132 // Scroll controls *when* each slam runs; the slam animation itself is time-based (no scrub). 4133 var pinEl = wrapper.querySelector('.year-in-review-2026-slam-inner') || wrapper; 4134
4135 // Background salute: intro bounce (time-based) + rotation (scrubbed). 4136 if (saluteBg) { 4137 ScrollTrigger.create({ 4138 trigger: wrapper, 4139 start: 'top top', 4140 end: 'bottom bottom', 4141 invalidateOnRefresh: true, 4142 onEnter: function () { 4143 // Don't kill the scroll-driven rotation tween. 4144 gsap.killTweensOf(saluteBg, 'autoAlpha,scale'); 4145 gsap.fromTo(saluteBg, { 4146 autoAlpha: 0, 4147 scale: 0, 4148 }, { 4149 autoAlpha: 1, 4150 scale: 1.6, 4151 duration: 0.8, 4152 ease: 'back.out(1.6)', 4153 overwrite: 'auto', 4154 }); 4155 }, 4156 onEnterBack: function () { 4157 gsap.set(saluteBg, { autoAlpha: 1, scale: 1.6 }); 4158 }, 4159 onLeave: function () { 4160 gsap.to(saluteBg, { autoAlpha: 0, duration: 0.35, overwrite: 'auto' }); 4161 }, 4162 onLeaveBack: function () { 4163 gsap.to(saluteBg, { autoAlpha: 0, duration: 0.25, overwrite: 'auto' }); 4164 }, 4165 }); 4166 4167 gsap.to(saluteBg, { 4168 rotate: 18, 4169 ease: 'none', 4170 scrollTrigger: { 4171 trigger: wrapper, 4172 start: 'top top', 4173 end: 'bottom bottom', 4174 scrub: true, 4175 invalidateOnRefresh: true, 4176 } 4177 }); 4178 } 4179 4180 ScrollTrigger.create({ 4181 trigger: wrapper, 4182 start: 'top top', 4183 end: 'bottom bottom', 4184 pin: pinEl, 4185 pinSpacing: false, 4186 anticipatePin: 1, 4187 invalidateOnRefresh: true, 4188 onEnter: function () { 4189 showIndex(0); 4190 }, 4191 onEnterBack: function () { 4192 if (activeIndex === -1) { 4193 showIndex(0); 4194 } 4195 }, 4196 onUpdate: function (self) { 4197 // Map scroll progress to a discrete title index. 4198 var nextIndex = Math.min(titles.length - 1, Math.floor(self.progress * titles.length)); 4199 showIndex(nextIndex); 4200 }, 4201 onLeave: function () { 4202 // Keep the final title visible after the sequence completes. 4203 var lastIndex = titles.length - 1; 4204 for (var i = 0; i < titles.length; i++) { 4205 if (i === lastIndex) { 4206 gsap.set(titles[i], { autoAlpha: 1, scale: 1 }); 4207 } else { 4208 gsap.set(titles[i], { autoAlpha: 0 }); 4209 } 4210 } 4211 activeIndex = lastIndex; 4212 }, 4213 }); 4214 }); 4215 } 4216 4217 YEAR2026.prototype.initUnstoppablePower = function () { 4218 var self = this; 4219 4220 gsap.timeline({ 4221 scrollTrigger: { 4222 trigger: '.year-in-review-2026-fireworks--stratosphere', 4223 start: 'top top', 4224 pin: '.year-in-review-2026-fireworks--stratosphere .year-in-review-2026-fireworks-wrapper', 4225 end: 'bottom center', 4226 endTrigger: '.year-in-review-2026-stratosphere-line4', 4227 }, 4228 }); 4229 4230 var videoInitied = false; 4231 4232 var video = document.querySelector('.year-in-review-2026-fireworks--stratosphere .year-in-review-2026-fireworks-video video'); 4233 4234 // Fade out exactly 1s before the video ends 4235 var bindFadeOutBeforeEnd = function (vid) { 4236 if (!vid || vid._fadeOutBeforeEndBound) { 4237 return; 4238 } 4239 vid._fadeOutBeforeEndBound = true; 4240 4241 var fadeTargets = '.year-in-review-2026-fireworks--stratosphere, .year-in-review-2026-fireworks--stratosphere .year-in-review-2026-fireworks-video-overlay'; 4242 var hasFaded = false;
4243 4244 var resetFadeState = function () { 4245 hasFaded = false; 4246 gsap.to(fadeTargets, { autoAlpha: 1, duration: 0.15, overwrite: true }); 4247 }; 4248 4249 var onTimeUpdate = function () { 4250 if (hasFaded) return; 4251 4252 var dur = vid.duration; 4253 if (!dur || !isFinite(dur)) return; 4254 4255 if ((dur - vid.currentTime) <= 1) { 4256 hasFaded = true; 4257 gsap.to(fadeTargets, { autoAlpha: 0, duration: 1, ease: 'power1.out', overwrite: true }); 4258 } 4259 }; 4260 4261 vid.addEventListener('play', resetFadeState); 4262 vid.addEventListener('timeupdate', onTimeUpdate); 4263 4264 // Safety: ensure it's hidden at the end too. 4265 vid.addEventListener('ended', function () { 4266 gsap.to(fadeTargets, { autoAlpha: 0, duration: 0.1, overwrite: true }); 4267 }); 4268 }; 4269 4270 if (video) { 4271 bindFadeOutBeforeEnd(video); 4272 } 4273 4274 gsap.timeline({ 4275 scrollTrigger: { 4276 trigger: '.year-in-review-2026-fireworks--stratosphere', 4277 start: 'center center', 4278 toggleActions: "play none resume reverse", 4279 }, 4280 onComplete: function () { 4281 gsap.to('.year-in-review-2026-fireworks--stratosphere, .year-in-review-2026-fireworks--stratosphere .year-in-review-2026-fireworks-video-overlay', { 4282 autoAlpha: 1, 4283 }); 4284 4285 if (!videoInitied) { 4286 videoInitied = true; 4287 self.playBackgroundFireworksVideo(video); 4288 4289 // Re-bind after videojs init (safe no-op if already bound) 4290 bindFadeOutBeforeEnd(video); 4291 return; 4292 } 4293 4294 // Ensure itâs visible when replaying and let the timeupdate handler fade it out near the end. 4295 gsap.to('.year-in-review-2026-fireworks--stratosphere, .year-in-review-2026-fireworks--stratosphere .year-in-review-2026-fireworks-video-overlay', { 4296 autoAlpha: 1, 4297 duration: 0.15, 4298 overwrite: true, 4299 }); 4300 4301 video.play(); 4302 } 4303 }) 4304 .from('.year-in-review-2026-stratosphere-line1', { 4305 duration: 0.25, 4306 autoAlpha: 0, 4307 y: 50, 4308 }) 4309 .from('.year-in-review-2026-stratosphere-line2 .year-2026--text-scale--inner', { 4310 duration: 0.25, 4311 autoAlpha: 0, 4312 scale: 10 4313 }, "-=0.15") 4314 .from('.year-in-review-2026-stratosphere-line3', { 4315 duration: 0.25, 4316 autoAlpha: 0, 4317 y: 50, 4318 }, "-=0.15") 4319 .from('.year-in-review-2026-stratosphere-line4 .year-2026--text-scale--inner', { 4320 duration: 0.25, 4321 autoAlpha: 0, 4322 scale: 10 4323 }, "-=0.15") 4324 } 4325 4326 YEAR2026.prototype.initMutedAutoplayVidoes = function () { 4327 var self = this; 4328 4329 $('.year-in-review-2026--autoplay-video-block').each(function () { 4330 var $el = $(this); 4331 var videoSrc = $el.find('video').data('videosrc'); 4332 videojs($el.find('video')[0], { controls: false, muted: true, loop: false }).ready(function () { 4333 this.src(videoSrc); 4334 this.pause(); 4335 var player = this; 4336 4337 ScrollTrigger.create({ 4338 trigger: $el[0], 4339 start: "center bottom", 4340 end: "bottom top", 4341 toggleActions: "play none resume reverse", 4342 onEnter: function (progress, direction, isActive) { 4343 var vid = player.el().querySelector('video'); 4344 vid.muted = true; 4345 vid.controls = false; 4346 player.play(); 4347 }, 4348 onEnterBack: function (progress, direction, isActive) { 4349 player.play(); 4350 }, 4351 onLeave: function (progress, direction, isActive) { 4352 player.pause(); 4353 }, 4354 onLeaveBack: function (progress, direction, isActive) { 4355 player.pause(); 4356 } 4357 }); 4358 4359 $el.find('.year-in-review-2026--autoplay-video-replay').click(function () { 4360 player.currentTime(0); 4361 player.play(); 4362 }); 4363 }); 4364 }); 4365 } 4366 4367 YEAR2026.prototype.initMutedAutoplayLoopVidoes = function () { 4368 var self = this; 4369 $('.year-in-review-2026--autoplay-video-loop').each(function () { 4370 var $el = $(this); 4371 var videoSrc = $el.find('video').data('videosrc'); 4372 videojs($el.find('video')[0], { controls: false, muted: true, loop: true }).ready(function () { 4373 this.src(videoSrc); 4374 this.pause(); 4375 var player = this; 4376 4377 $el.removeClass('d-none'); 4378 4379 var options = { 4380 trigger: $el[0], 4381 start: "center bottom", 4382 end: "bottom top", 4383 toggleActions: "play none resume reverse", 4384 } 4385 4386 if($el.hasClass('year-in-review-2026--lrh-hall-stars__video')) { 4387 options.trigger = ".year-in-review-2026--lrh-hall-stars"; 4388 options.start = "top-=50px top"; 4389 } 4390 4391 ScrollTrigger.create({ 4392 trigger: options.trigger, 4393 start: options.start, 4394 end: options.end, 4395 toggleActions: "play none resume reverse", 4396 onEnter: function (progress, direction, isActive) {
4397 var vid = player.el().querySelector('video'); 4398 vid.muted = true; 4399 vid.controls = false; 4400 player.play(); 4401 }, 4402 onEnterBack: function (progress, direction, isActive) { 4403 player.play(); 4404 }, 4405 onLeave: function (progress, direction, isActive) { 4406 player.pause(); 4407 }, 4408 onLeaveBack: function (progress, direction, isActive) { 4409 player.pause(); 4410 } 4411 }); 4412 }); 4413 }); 4414 } 4415 4416 4417 YEAR2026.prototype.initBackgroundVideo = function () { 4418 var self = this; 4419 var videoInitied = false; 4420 var video = document.querySelector('.who-we-are--bg-video'); 4421 4422 gsap.timeline({ 4423 scrollTrigger: { 4424 trigger: '.year-in-review-2026--triptych', 4425 start: 'top top', 4426 end: "bottom top", 4427 pin: '.who-we-are--bg-video-inner', 4428 pinSpacing: false, 4429 onEnter: function (progress, direction, isActive) { 4430 if (!video.muted) { 4431 return; 4432 } 4433 4434 if(!videoInitied) { 4435 videoInitied = true; 4436 self.playBackgroundVideo(); 4437 return; 4438 } 4439 4440 video.controls = false; 4441 video.play().catch((error) => { 4442 if (error.name === 'AbortError') { 4443 console.log('Playback was interrupted:', error); 4444 } else { 4445 console.error('An unexpected error occurred:', error); 4446 window.sentry && Sentry.captureException(error); 4447 } 4448 }); 4449 gsap.fromTo('.who-we-are--bg-video-inner', { 4450 autoAlpha: 0, 4451 }, { 4452 autoAlpha: 1, 4453 }); 4454 }, 4455 onEnterBack: function (progress, direction, isActive) { 4456 if (!video.muted) { 4457 return; 4458 } 4459 4460 if(!videoInitied) { 4461 videoInitied = true; 4462 self.playBackgroundVideo(); 4463 return; 4464 } 4465 4466 video.controls = false; 4467 video.play().catch((error) => { 4468 if (error.name === 'AbortError') { 4469 console.log('Playback was interrupted:', error); 4470 } else { 4471 console.error('An unexpected error occurred:', error); 4472 window.sentry && Sentry.captureException(error); 4473 } 4474 }); 4475 gsap.fromTo('.who-we-are--bg-video-inner', { 4476 autoAlpha: 0, 4477 }, { 4478 autoAlpha: 1, 4479 }); 4480 }, 4481 onLeave: function (progress, direction, isActive) { 4482 // video started playing by user interaction 4483 if (!video.muted) { 4484 return; 4485 } 4486 video.pause(); 4487 gsap.to('.who-we-are--bg-video-inner', { 4488 autoAlpha: 0, 4489 }); 4490 }, 4491 onLeaveBack: function (progress, direction, isActive) { 4492 // video started playing by user interaction 4493 if (!video.muted) { 4494 return; 4495 } 4496 if (direction === "reverse") { 4497 video.pause(); 4498 } 4499 gsap.to('.who-we-are--bg-video-inner', { 4500 autoAlpha: 0, 4501 }); 4502 } 4503 }, 4504 }); 4505 } 4506 4507 4508 4509 YEAR2026.prototype.initVideos = function () { 4510 var self = this; 4511 $('.year-end-2022--playbutton-area').click(function () { 4512 var $el = $(this); 4513 var videoElId = $el.find('.video-js').attr('id'); 4514 var video = videojs.players[videoElId]; 4515 4516 var scntvlink = $el.data('scntv'); 4517 if (scntvlink) { 4518 } 4519 4520 if (!video) { 4521 return; 4522 } 4523 4524 var videoId = $(this).data('videoid'); 4525 if (!video.controls()) { 4526 for (var player in videojs.players) { 4527 if (videojs.players.hasOwnProperty(player) && videojs.players[player] && $(videojs.players[player].el_).closest('.year-end-2022--playbutton-area').length > 0) { 4528 videojs.players[player].pause(); 4529 } 4530 } 4531 4532 video.controls(true); 4533 video.muted(false); 4534 video.currentTime(0); 4535 video.loop(false); 4536 video.poster($el.data('videoposter')); 4537 $el.addClass('year-end-2022-videoautoplay__inline'); 4538 EventTracker.trackVideo(video); 4539 video.one('timeupdate', function () { 4540 self.addCaptionsIfNeeded(video, videoId); 4541 }); 4542 video.play(); 4543 trackEvent("Video", "Play", videoId); 4544 } else if (video.paused()) { 4545 trackEvent("Video", "Play", videoId); 4546 } 4547 }); 4548 } 4549 YEAR2026.prototype.stickyVideoAutoplay = function () { 4550 var isMobile = this.isMobile(); 4551 var self = this; 4552 $('.year-in-review-2026--video-autoplay').each(function (index, wrapper) { 4553 var skipPin = wrapper.getAttribute('data-skip-pin') === 'true'; 4554 if (!isMobile && !skipPin) { 4555 ScrollTrigger.create({ 4556 trigger: wrapper.querySelector('.year-in-review-2026--video-autoplay-element'), 4557 start: 'top top', 4558 end: 'bottom top', 4559 scrub: true, 4560 pin: wrapper.querySelector('.year-in-review-2026--video-autoplay-element'), 4561 pinSpacing: false, 4562 }); 4563 } 4564 4565 gsap.to(wrapper.querySelector('.year-in-review-2026--intro-video-dim'), { 4566 scrollTrigger: { 4567 trigger: wrapper.querySelector('.year-in-review-2026--video-autoplay-element'), 4568 start: 'top top', 4569 toggleActions: "play none resume reverse", 4570 }, 4571 autoAlpha: 0.7, 4572 duration: 0.5, 4573 ease: Power4.easeOut, 4574 }); 4575 4576 var videoEl = wrapper.querySelector('.video-js'); 4577 if (!videoEl || typeof videojs === 'undefined') { 4578 return; 4579 } 4580 4581 var parseVideoSrcAttr = function (attrName) { 4582 var raw = videoEl.getAttribute(attrName); 4583 if (!raw) { 4584 return null; 4585 } 4586 if (typeof raw !== 'string') { 4587 return raw; 4588 } 4589 try { 4590 return JSON.parse(raw); 4591 } catch (e) { 4592 console.error('Invalid JSON for ' + attrName, e); 4593 window.sentry && Sentry.captureException(e); 4594 return null; 4595 } 4596 }; 4597 4598 var useDesktopSrc = window.innerWidth > 1200;
4599 var videoSource = (useDesktopSrc && parseVideoSrcAttr('data-desktop-videosrc')) || parseVideoSrcAttr('data-videosrc'); 4600 if (!videoSource) { 4601 return; 4602 } 4603 4604 videojs(videoEl, { controls: false, muted: true, loop: false }).ready(function () { 4605 var player = this; 4606 this.src(videoSource); 4607 player.muted(true); 4608 4609 player.on('ended', function () { 4610 var now = new Date().getTime(); 4611 if(self._lastInsetScrollTime && (now - self._lastInsetScrollTime) < 15000 && wrapper.getAttribute('data-auto-scroll') === 'true') { 4612 self.scrollToNextInset(); 4613 } 4614 }); 4615 4616 4617 gsap.timeline({ 4618 scrollTrigger: { 4619 trigger: wrapper.querySelector('.year-in-review-2026--video-autoplay--fade-trigger'), 4620 start: 'center center', 4621 toggleActions: "play none resume reverse", 4622 onToggle: (self) => { 4623 var shouldPlay = self.direction == 1 || player.currentTime() < 0.5; 4624 if (self.isActive && shouldPlay) { 4625 player.play(); 4626 } 4627 }, 4628 }, 4629 }).fromTo(wrapper.querySelector('.year-in-review-2026--videoautoplay'), { 4630 autoAlpha: 0, 4631 }, { 4632 autoAlpha: 1, 4633 duration: 0.25, 4634 }); 4635 }); 4636 4637 }); 4638 } 4639 4640 YEAR2026.prototype.initTechnologyForLife = function () { 4641 if (typeof gsap === 'undefined' || typeof ScrollTrigger === 'undefined') { 4642 return; 4643 } 4644 4645 var section = document.querySelector('.year-2026-our-message-video'); 4646 if (!section) { 4647 return; 4648 } 4649 4650 var intro = section.querySelector('.year-in-review-2026--our-tech-intro'); 4651 var message = section.querySelector('.year-2026-our-message-wrapper'); 4652 var videoLayer = section.querySelector('.year-2026-our-voice--video'); 4653 var videoEl = videoLayer ? videoLayer.querySelector('video') : null; 4654 4655 if (!intro || !message || !videoLayer) { 4656 return; 4657 } 4658 4659 // Ensure predictable initial state (prevents flashes). 4660 gsap.set(videoLayer, { autoAlpha: 0, visibility: 'hidden' }); 4661 gsap.set(intro, { autoAlpha: 0, y: 60, willChange: 'transform,opacity', pointerEvents: 'auto' }); 4662 gsap.set(message, { autoAlpha: 0, y: 60, willChange: 'transform,opacity', pointerEvents: 'none' }); 4663 4664 // Optional: autoplay the background video while this section is active. 4665 var player = null; 4666 var ensurePlayer = function () { 4667 if (!videoEl || typeof videojs === 'undefined') { 4668 return null; 4669 } 4670 if (player) { 4671 return player; 4672 } 4673 4674 var parseVideoSrcAttr = function (attrName) { 4675 var raw = videoEl.getAttribute(attrName); 4676 if (!raw) { 4677 return null; 4678 } 4679 if (typeof raw !== 'string') { 4680 return raw; 4681 } 4682 try { 4683 return JSON.parse(raw); 4684 } catch (e) { 4685 console.error('Invalid JSON for ' + attrName, e); 4686 window.sentry && Sentry.captureException(e); 4687 return null; 4688 } 4689 }; 4690 4691 var videoSource = parseVideoSrcAttr('data-videosrc'); 4692 if (!videoSource) { 4693 return null; 4694 } 4695 4696 player = videojs(videoEl, { controls: false, muted: true, loop: true, preload: 'none' }); 4697 try { 4698 player.src(videoSource); 4699 player.muted(true); 4700 } catch (e) { 4701 console.error('Video init failed', e); 4702 window.sentry && Sentry.captureException(e); 4703 } 4704 return player; 4705 }; 4706 4707 var playVideo = function () { 4708 var p = ensurePlayer(); 4709 if (!p) { 4710 return; 4711 } 4712 try { 4713 var vid = p.el() && p.el().querySelector('video'); 4714 if (vid) { 4715 vid.muted = true; 4716 vid.controls = false;
4717 } 4718 // Keep it subtle: restart only when entering from outside. 4719 p.currentTime(0); 4720 p.play(); 4721 } catch (e) { 4722 // Autoplay may be blocked in some contexts. 4723 } 4724 }; 4725 4726 var pauseVideo = function () { 4727 if (!player) { 4728 return; 4729 } 4730 try { 4731 player.pause(); 4732 } catch (e) { 4733 // ignore 4734 } 4735 }; 4736 4737 // 1) Sticky/pinned video for the entire section scroll. 4738 // Use a safer approach on mobile (pinning can be unstable on iOS). 4739 var isMobile = this.isMobile && this.isMobile(); 4740 4741 ScrollTrigger.create({ 4742 trigger: section, 4743 start: 'top top', 4744 end: 'bottom top', 4745 pin: videoLayer, 4746 pinSpacing: false, 4747 anticipatePin: 1, 4748 invalidateOnRefresh: true, 4749 onEnter: function () { 4750 gsap.to(videoLayer, { autoAlpha: 1, visibility: 'visible', duration: 0.25, ease: 'power2.out', overwrite: true }); 4751 playVideo(); 4752 }, 4753 onEnterBack: function () { 4754 gsap.to(videoLayer, { autoAlpha: 1, visibility: 'visible', duration: 0.25, ease: 'power2.out', overwrite: true }); 4755 playVideo(); 4756 }, 4757 onLeave: function () { 4758 pauseVideo(); 4759 gsap.to(videoLayer, { autoAlpha: 0, duration: 0.2, ease: 'power2.out', overwrite: true }); 4760 }, 4761 onLeaveBack: function () { 4762 pauseVideo(); 4763 gsap.to(videoLayer, { autoAlpha: 0, duration: 0.2, ease: 'power2.out', overwrite: true }); 4764 }, 4765 }); 4766 4767 ScrollTrigger.create({ 4768 trigger: '.year-2026-our-message-video', 4769 start: 'top top', 4770 end: "bottom bottom", 4771 pin: '.year-2026-our-message-inner', 4772 pinSpacing: false, 4773 toggleActions: "play pause resume reverse", 4774 anticipatePin: 1, 4775 invalidateOnRefresh: true, 4776 }); 4777 4778 // 2) 50/50 crossfade between the two text panels, each fading up from the bottom. 4779 // Timeline duration is 2 "units" so the switch point is exactly at 50% (t=1). 4780 gsap.timeline({ 4781 // defaults: { ease: 'none' }, 4782 scrollTrigger: { 4783 // trigger: section, 4784 trigger: '.year-2026-our-message-trigger', 4785 start: 'top top', 4786 endTrigger: section, 4787 end: 'bottom bottom', 4788 scrub: true, 4789 toggleActions: "play pause resume reverse", 4790 invalidateOnRefresh: true, 4791 }, 4792 }) 4793 // Intro fades in early 4794 .to(intro, { autoAlpha: 1, y: 0, duration: 0.2, ease: 'power3.out' }, 0) 4795 // Crossfade around the midpoint (t=1) 4796 .to(intro.querySelector('.year-2026-inner-content'), { autoAlpha: 0, y: -40, duration: 0.2, ease: 'power2.inOut' }) 4797 .fromTo(message, { 4798 autoAlpha: 0, 4799 y: 50, 4800 duration: 0.4, 4801 ease: 'power3.out', 4802 }, { 4803 autoAlpha: 1, 4804 y: 0, 4805 }, "<"); 4806 4807 // Keep message stable through the second half 4808 // .to(message, { autoAlpha: 1, y: 0, duration: 0.85 }, 1.15); 4809 } 4810 4811 YEAR2026.prototype.initLRHHallTeaser = function () { 4812 4813 var teaser = document.querySelector('.year-in-review-2026-lrh-hall-teaser'); 4814 if (!teaser) { 4815 return; 4816 } 4817 4818 var titles = Array.prototype.slice.call(teaser.querySelectorAll('.year-in-review-2026-lrh-hall-teaser-title')); 4819 if (!titles || titles.length === 0) { 4820 return; 4821 } 4822 4823 // Overlay titles so we can sequence them (similar to FEBC slam, but different motion). 4824 gsap.set(teaser, { 4825 position: 'relative', 4826 overflow: 'hidden', 4827 }); 4828 4829 gsap.set(titles, { 4830 position: 'absolute', 4831 top: 0, 4832 left: 0, 4833 right: 0, 4834 autoAlpha: 0, 4835 }); 4836 4837 var hasSplitText = (typeof SplitText !== 'undefined'); 4838 var splitByIndex = {}; 4839 4840 var getTextEl = function (titleEl) { 4841 return titleEl.querySelector('.year-in-review-2026-subheading__medium') || titleEl; 4842 }; 4843 4844 var getTextEls = function (titleEl) { 4845 return titleEl.querySelectorAll('.year-in-review-2026-subheading__medium') || titleEl; 4846 }; 4847 4848 var ensureSplit = function (idx) { 4849 if (!hasSplitText) { 4850 return null; 4851 } 4852 if (splitByIndex[idx]) { 4853 return splitByIndex[idx]; 4854 } 4855 4856 var el = getTextEl(titles[idx]); 4857 if (!el) { 4858 return null; 4859 } 4860 4861 // Split once; keep reference for subsequent plays. 4862 try { 4863 splitByIndex[idx] = new SplitText(el, { 4864 type: 'lines,words,chars', 4865 linesClass: 'line', 4866 wordsClass: 'word', 4867 charsClass: 'char' 4868 }); 4869 } catch (e) { 4870 console.error('SplitText init failed:', e); 4871 window.sentry && Sentry.captureException(e); 4872 splitByIndex[idx] = null; 4873 } 4874 return splitByIndex[idx]; 4875 }; 4876 4877 var animateTitleIn = function (idx) { 4878 var titleEl = titles[idx]; 4879 if (!titleEl) { 4880 return; 4881 } 4882 4883 var textEl = getTextEl(titleEl); 4884 var tl = gsap.timeline({ 4885 id: 'lrh-hall-teaser-title-' + idx, 4886 overwrite: true, 4887 }); 4888 4889 // Fallback if SplitText isn't present. 4890 if (!hasSplitText) { 4891 tl.fromTo(textEl, { 4892 autoAlpha: 0, 4893 y: 30, 4894 scale: 0.98, 4895 }, { 4896 autoAlpha: 1, 4897 y: 0, 4898 scale: 1, 4899 duration: 0.5, 4900 ease: Power3.easeOut, 4901 overwrite: true, 4902 }); 4903 return tl; 4904 } 4905 4906 ensureSplit(idx); 4907 addSlamTextInToTimeline(tl, textEl, { 4908 duration: 1.1, 4909 ease: 'power2.inOut', 4910 fallbackDuration: 0.5, 4911 autoSplit: false, // already split + cached by ensureSplit() 4912 }); 4913 4914 return tl; 4915 }; 4916 4917 var fadeTitleIn = function (idx) { 4918 var titleEl = titles[idx]; 4919 if (!titleEl) { 4920 return; 4921 } 4922 4923 var tl = gsap.timeline({ 4924 id: 'lrh-hall-teaser-title-fade-' + idx, 4925 overwrite: true, 4926 }); 4927 4928 // Animate each text element (line) letter-by-letter. 4929 var staggerAmount = 1; 4930 var charDuration = 1.5; 4931 4932 var nodeList = titleEl.querySelectorAll('.year-in-review-2026-subheading__medium'); 4933 var textEls = (nodeList && nodeList.length) ? Array.prototype.slice.call(nodeList) : [getTextEl(titleEl)]; 4934 4935 for (var i = 0; i < textEls.length; i++) { 4936 var el = textEls[i]; 4937 if (!el) { 4938 continue; 4939 } 4940 4941 // Ensure the container is visible; chars will be animated in. 4942 gsap.set(el, { autoAlpha: 1, y: 0, overwrite: true }); 4943 4944 // Cache SplitText per element to avoid double-wrapping on replays. 4945 if (!el._lrhFadeSplit) { 4946 try { 4947 el._lrhFadeSplit = new SplitText(el, { 4948 type: 'words,chars', 4949 wordsClass: 'word', 4950 charsClass: 'char' 4951 }); 4952 } catch (e) { 4953 console.error('SplitText init failed:', e); 4954 window.sentry && Sentry.captureException(e); 4955 el._lrhFadeSplit = null; 4956 } 4957 } 4958 4959 var chars = el.querySelectorAll ? el.querySelectorAll('.char') : null; 4960 gsap.set(chars, { autoAlpha: 0, overwrite: true }); 4961 tl.to(chars, { 4962 autoAlpha: 1, 4963 y: 0, 4964 duration: charDuration, 4965 ease: Power2.easeOut, 4966 overwrite: true, 4967 stagger: { amount: staggerAmount }, 4968 }, i > 0 ? "<+=1.25" : 0); 4969 } 4970 4971 return tl; 4972 }; 4973 4974 var activeIndex = -1; 4975 var killTitleTweens = function (idx) { 4976 var titleEl = titles[idx]; 4977 if (!titleEl) { 4978 return; 4979 } 4980 var textEl = getTextEl(titleEl); 4981 gsap.killTweensOf(titleEl); 4982 if (textEl) { 4983 gsap.killTweensOf(textEl); 4984 var chars = textEl.querySelectorAll && textEl.querySelectorAll('.char'); 4985 if (chars && chars.length) { 4986 gsap.killTweensOf(chars); 4987 } 4988 } 4989 }; 4990 4991 var resetAllTitles = function () { 4992 activeIndex = -1; 4993 for (var i = 0; i < titles.length; i++) { 4994 killTitleTweens(i); 4995 gsap.set(titles[i], { autoAlpha: 0, y: 0, scale: 1 }); 4996 } 4997 }; 4998 4999 var showIndex = function (nextIndex, forceReplay) { 5000 if (nextIndex < 0 || nextIndex >= titles.length) { 5001 return; 5002 } 5003 if (!forceReplay && nextIndex === activeIndex) { 5004 return; 5005 } 5006 5007 var prevIndex = activeIndex; 5008 activeIndex = nextIndex; 5009 5010 if (prevIndex > -1 && titles[prevIndex]) { 5011 killTitleTweens(prevIndex); 5012 gsap.set(titles[prevIndex], { autoAlpha: 0, y: 0, scale: 1 }); 5013 } 5014 5015 // Ensure a clean state before replaying. 5016 killTitleTweens(nextIndex); 5017 gsap.set(titles[nextIndex], { autoAlpha: 1, y: 0, scale: 1 }); 5018 if(nextIndex == 0) { 5019 animateTitleIn(nextIndex); 5020 } else { 5021 fadeTitleIn(nextIndex); 5022 } 5023 }; 5024 5025 // Provide continuous visual feedback while scrubbing. 5026 var applyScrubFeedback = function (st, idx) { 5027 if (!st || idx < 0 || idx >= titles.length) { 5028 return; 5029 } 5030 5031 var segSize = 1 / titles.length; 5032 var local = (st.progress - (idx * segSize)) / segSize; 5033 local = Math.max(0, Math.min(1, local)); 5034 5035 // Subtle parallax/scale as you scroll (reverses naturally when scrolling back). 5036 var y = gsap.utils.interpolate(14, -14, local); 5037 var scale = gsap.utils.interpolate(1.02, 0.98, local); 5038 5039 gsap.set(titles[idx], { 5040 y: y, 5041 scale: scale, 5042 overwrite: true, 5043 }); 5044 }; 5045 5046 var fadeTween; 5047 var fadeOutTeaser = function () { 5048 if (fadeTween) { 5049 fadeTween.kill(); 5050 } 5051 fadeTween = gsap.to('.year-in-review-2026--lrh-hall-stars, .year-in-review-2026-lrh-hall-teaser', { 5052 autoAlpha: 0, 5053 duration: 0.35, 5054 ease: Power2.easeOut, 5055 overwrite: true, 5056 }); 5057 }; 5058 5059 var fadeInTeaser = function () { 5060 if (fadeTween) { 5061 fadeTween.kill(); 5062 } 5063 fadeTween = gsap.to('.year-in-review-2026--lrh-hall-stars, .year-in-review-2026-lrh-hall-teaser', { 5064 autoAlpha: 1, 5065 duration: 0.25, 5066 ease: Power2.easeOut, 5067 overwrite: true, 5068 }); 5069 }; 5070 5071 // Scroll controls *when* each title animates. 5072 ScrollTrigger.create({ 5073 trigger: teaser, 5074 start: 'top top', 5075 // Give each title enough scroll distance (otherwise small scroll instantly advances). 5076 end: "bottom top", 5077 pin: true, 5078 pinSpacing: false, 5079 scrub: true, 5080 toggleActions: "play pause resume reverse", 5081 anticipatePin: 1, 5082 invalidateOnRefresh: true, 5083 onEnter: function () { 5084 // Always replay the first line when entering from the top. 5085 fadeInTeaser(); 5086 showIndex(0, true); 5087 }, 5088 onEnterBack: function () { 5089 // When coming back up into the teaser, ensure title is visible. 5090 fadeInTeaser(); 5091 if (activeIndex === -1) { 5092 showIndex(0, true); 5093 } 5094 }, 5095 onUpdate: function (st) { 5096 var idx = Math.min(titles.length - 1, Math.floor(st.progress * titles.length)); 5097 showIndex(idx); 5098 applyScrubFeedback(st, idx); 5099 }, 5100 onLeave: function () { 5101 // Keep final title visible. 5102 showIndex(titles.length - 1); 5103 fadeOutTeaser(); 5104 }, 5105 onLeaveBack: function () { 5106 // Reset state so the first title can replay next time you scroll back in. 5107 resetAllTitles(); 5108 fadeInTeaser(); 5109 } 5110 }); 5111 } 5112 5113 YEAR2026.prototype.updateLRHStopPoint = function () { 5114 $('.navigation-step__abs-lrh-hall').css('bottom', $('.year-in-review-2026--lrh-hall-image-wrapper').height() + 20 + 'px'); 5115 } 5116 YEAR2026.prototype.initLRHHall = function () { 5117 var self = this; 5118 var isMobile = this.isMobile(); 5119 5120 this.updateLRHStopPoint(); 5121 var updateLRHStopPointThrottled = _.throttle(function () { 5122 self.updateLRHStopPoint(); 5123 ScrollTrigger.refresh(); 5124 }, 250); 5125 window.addEventListener('resize', updateLRHStopPointThrottled); 5126 window.addEventListener('orientationchange', updateLRHStopPointThrottled); 5127 5128 var lrhStarsTimeLine = gsap.timeline({ 5129 scrollTrigger: { 5130 trigger: ".year-in-review-2026--lrh-hall-stars", 5131 start: 'top-=50px top', 5132 pin: ".year-in-review-2026--lrh-hall-stars__video", 5133 end: "bottom top", 5134 pinSpacing: false, 5135 toggleActions: "play none resume reverse", 5136 } 5137 }); 5138 5139 lrhStarsTimeLine.from(".year-in-review-2026--lrh-hall-stars__video", { 5140 autoAlpha: 0, 5141 duration: 1, 5142 }); 5143 5144 5145 $('.year-in-review-2026--video-autoplay--lrh-hall').each(function (index, wrapper) { 5146 5147 gsap.to(wrapper.querySelector('.year-in-review-2026--intro-video-dim'), { 5148 scrollTrigger: { 5149 trigger: wrapper.querySelector('.year-in-review-2026--video-autoplay-element'), 5150 start: 'top top', 5151 toggleActions: "play none resume reverse", 5152 }, 5153 autoAlpha: 0.7, 5154 duration: 0.5, 5155 ease: Power4.easeOut, 5156 }); 5157 5158 var videoEl = wrapper.querySelector('.video-js'); 5159 if (!videoEl || typeof videojs === 'undefined') { 5160 return; 5161 } 5162 5163 var parseVideoSrcAttr = function (attrName) { 5164 var raw = videoEl.getAttribute(attrName); 5165 if (!raw) { 5166 return null; 5167 } 5168 if (typeof raw !== 'string') { 5169 return raw; 5170 } 5171 try { 5172 return JSON.parse(raw); 5173 } catch (e) { 5174 console.error('Invalid JSON for ' + attrName, e); 5175 window.sentry && Sentry.captureException(e); 5176 return null; 5177 } 5178 }; 5179 5180 var useDesktopSrc = window.innerWidth > 1200;
5181 var videoSource = (useDesktopSrc && parseVideoSrcAttr('data-desktop-videosrc')) || parseVideoSrcAttr('data-videosrc'); 5182 if (!videoSource) { 5183 return; 5184 } 5185 5186 gsap.timeline({ 5187 scrollTrigger: { 5188 trigger: '.year-in-review-2026--lrh-hall-wrapper', 5189 start: 'top top', 5190 pin: '.year-in-review-2026--lrh-hall-wrapper-inner', 5191 pinSpacing: false, 5192 // endTrigger: '.year-in-review-2026--lrh-hall-end', 5193 // end: 'bottom bottom', 5194 endTrigger: '.navigation-step__abs-lrh-hall', 5195 end: 'top top', 5196 toggleActions: "play none resume reverse", 5197 invalidateOnRefresh: true, 5198 } 5199 }); 5200 5201 var lrhHallTl = gsap.timeline({ 5202 scrollTrigger: { 5203 trigger: '.year-in-review-2026--lrh-hall-end-animation', 5204 start: 'top top', 5205 toggleActions: "play none resume reverse", 5206 } 5207 }); 5208 5209 lrhHallTl.to(videoEl, { 5210 autoAlpha: 0, 5211 duration: 0.5, 5212 ease: Power4.easeOut, 5213 }) 5214 5215 var imgEl = document.querySelector('.year-in-review-2026--lrh-hall-img'); 5216 lrhHallTl.fromTo('.year-in-review-2026--lrh-masterpiece-title', { 5217 autoAlpha: 1, 5218 }, { 5219 autoAlpha: 0, 5220 duration: 0.25, 5221 }, "<"); 5222 5223 var hallNameEl = document.querySelector('.year-in-review-2026--lrh-hall-name .year-2026--text-scale--inner'); 5224 var hallNameTl = gsap.timeline({ overwrite: true }); 5225 addSlamTextInToTimeline(hallNameTl, hallNameEl, { 5226 duration: 1.1, 5227 ease: 'power2.inOut', 5228 fallbackDuration: 0.5, 5229 autoSplit: true, 5230 delay: 1, 5231 }); 5232 lrhHallTl.add(hallNameTl, "-=0.5"); 5233 5234 lrhHallTl.to('.year-in-review-2026-lrh-line-4', { 5235 autoAlpha: 0, 5236 }, "<") 5237 5238 lrhHallTl.fromTo(imgEl.querySelector('img'), { 5239 scale: 5, 5240 transformOrigin: "center", 5241 }, { 5242 scale: 1, 5243 duration: 4, 5244 ease: "Power4.easeOut", 5245 }, "<"); 5246 5247 lrhHallTl.fromTo(imgEl, { 5248 scale: 0.8, 5249 autoAlpha: 0, 5250 y: 20, 5251 }, { 5252 scale: 1, 5253 autoAlpha: 1, 5254 y: 0, 5255 duration: 4, 5256 ease: "Power4.easeOut", 5257 }, "<"); 5258 5259 5260 lrhHallTl.from('.year-in-review-2026--lrh-hall-image-wrapper .year-in-review-2026-subtitle', { 5261 autoAlpha: 0, 5262 y: 50, 5263 duration: 0.25, 5264 }, "-=1"); 5265 5266 // The button timeline may be created in a paused state (data-paused). 5267 // Explicitly restart it when this end-card sequence reaches this point. 5268 var m13BtnTl = self.globalTimelines && self.globalTimelines['m13-2025-button']; 5269 if (m13BtnTl) { 5270 lrhHallTl.add(function () { 5271 if (lrhHallTl.reversed()) { 5272 m13BtnTl.pause(0); 5273 return; 5274 } 5275 try { m13BtnTl.restart(); } catch (e) { } 5276 }, "-=0.5"); 5277 } 5278 5279 5280 videojs(videoEl, { controls: false, muted: true, loop: false }).ready(function () { 5281 var player = this; 5282 this.src(videoSource); 5283 player.muted(true); 5284 5285 var autoScrollTriggered = false; 5286 var tryScrollToNextInset = function () { 5287 var now = Date.now(); 5288 if (self._lastInsetScrollTime && (now - self._lastInsetScrollTime) < 15000 && wrapper.getAttribute('data-auto-scroll') === 'true') { 5289 self.scrollToNextInset(); 5290 } 5291 }; 5292 5293 player.on('timeupdate', function () { 5294 if (autoScrollTriggered) { 5295 return; 5296 } 5297 var duration = player.duration(); 5298 if (!duration || !isFinite(duration)) { 5299 return; 5300 } 5301 if (duration - player.currentTime() <= 1) { 5302 autoScrollTriggered = true; 5303 tryScrollToNextInset(); 5304 } 5305 }); 5306 5307 player.on('play', function () { 5308 autoScrollTriggered = false;
5309 }); 5310 5311 player.on('ended', function () { 5312 if (autoScrollTriggered) { 5313 return; 5314 } 5315 tryScrollToNextInset(); 5316 }); 5317 5318 gsap.timeline({ 5319 scrollTrigger: { 5320 trigger: wrapper.querySelector('.year-in-review-2026--video-autoplay--fade-trigger'), 5321 start: 'center center', 5322 end: 'bottom top', 5323 toggleActions: "play none resume reverse", 5324 onToggle: (st) => { 5325 var shouldPlay = st.direction == 1 || player.currentTime() < 0.5; 5326 if (st.isActive && shouldPlay) { 5327 player.play(); 5328 } 5329 }, 5330 onLeaveBack: function () { 5331 // Scrolling back out: reset video + lrhHall end-card animation 5332 try { player.pause(); } catch (e) { } 5333 try { player.currentTime(0); } catch (e) { } 5334 m13BtnTl && m13BtnTl.pause(0); 5335 5336 var autoplayEl = wrapper.querySelector('.year-in-review-2026--videoautoplay'); 5337 if (autoplayEl) { 5338 gsap.set(autoplayEl, { autoAlpha: 0 }); 5339 } 5340 }, 5341 }, 5342 }).fromTo(wrapper.querySelector('.year-in-review-2026--videoautoplay'), { 5343 autoAlpha: 0, 5344 }, { 5345 autoAlpha: 1, 5346 duration: 0.25, 5347 }); 5348 }); 5349 5350 }); 5351 } 5352 5353 YEAR2026.prototype.stickyVideoLastFrame = function () { 5354 $('.year-in-review-2026--video-autoplay, .year-in-review-2026--video-autoplay--lrh-hall').each(function (index, wrapper) { 5355 5356 var videoEl = wrapper.querySelector('.video-js'); 5357 if (!videoEl || typeof videojs === 'undefined') { 5358 return; 5359 } 5360 5361 var parseVideoSrcAttr = function (attrName) { 5362 var raw = videoEl.getAttribute(attrName); 5363 if (!raw) { 5364 return null; 5365 } 5366 if (typeof raw !== 'string') { 5367 return raw; 5368 } 5369 try { 5370 return JSON.parse(raw); 5371 } catch (e) { 5372 console.error('Invalid JSON for ' + attrName, e); 5373 window.sentry && Sentry.captureException(e); 5374 return null; 5375 } 5376 }; 5377 5378 var useDesktopSrc = window.innerWidth > 1200; 5379 var videoSource = (useDesktopSrc && parseVideoSrcAttr('data-desktop-videosrc')) || parseVideoSrcAttr('data-videosrc'); 5380 if (!videoSource) { 5381 return; 5382 } 5383 5384 videojs(videoEl, { controls: false, muted: true, loop: false }).ready(function () { 5385 var player = this; 5386 this.src(videoSource); 5387 player.muted(true); 5388 5389 // Seek to last frame and pause on init 5390 player.one('loadedmetadata', function () { 5391 var lastFrameTime = Math.max(player.duration() - 0.05, 0); 5392 player.currentTime(lastFrameTime); 5393 }); 5394 gsap.set(wrapper.querySelector('.year-in-review-2026--videoautoplay'), { autoAlpha: 1 }); 5395 5396 player.play(); 5397 }); 5398 }); 5399 } 5400 5401 5402 5403 YEAR2026.prototype.initVideoAutoplay = function () { 5404 var self = this; 5405 _.each($('.year-end-2022--playbutton-area'), function (el, index) { 5406 var $el = $(el); 5407 5408 var videoId = $el.data('videoid'); 5409 5410 var options = jQuery.extend({}, videoOptions); 5411 options.preload = "none"; 5412 options.controls = false; 5413 options.autoplay = false; 5414 options.sources = $el.data('videosrc'); 5415 options.loop = true; 5416 var videos = $el.find('.video-js'); 5417 if (videos.length == 0) { 5418 return; 5419 } 5420 var video = videos[0]; 5421 videojs(videos[0], options).ready(function () { 5422 var tween; 5423 5424 var player = this; 5425 5426 gsap.timeline({ 5427 scrollTrigger: { 5428 trigger: $el, 5429 start: 'top bottom', 5430 end: "bottom top", 5431 onEnter: function (progress, direction, isActive) { 5432 if (!video.muted) { 5433 return; 5434 } 5435 player.currentTime(0); 5436 player.one('play', function () { 5437 tween && tween.pause(); 5438 tween = gsap.to($el.find('.year-end-2022-videoautoplay'), 0.5, { autoAlpha: 1, ease: Power4.easeOut }); 5439 }); 5440 if (!$el.hasClass('year-end-2022-videoautoplay__inline')) {
5441 var vid = player.el().querySelector('video'); 5442 if (vid.currentTime < 3 && $(window).width() < 768) { 5443 player.src(player.currentSources()); 5444 } 5445 vid.controls = false; 5446 player.play(); 5447 } 5448 trackEvent("Video", "Visible", videoId); 5449 }, 5450 onEnterBack: function (progress, direction, isActive) { 5451 if (!video.muted) { 5452 return; 5453 } 5454 player.currentTime(0); 5455 player.one('play', function () { 5456 tween && tween.pause(); 5457 tween = gsap.to($el.find('.year-end-2022-videoautoplay'), 0.5, { autoAlpha: 1, ease: Power4.easeOut }); 5458 }); 5459 if (!$el.hasClass('year-end-2022-videoautoplay__inline')) { 5460 var vid = player.el().querySelector('video'); 5461 if (vid.currentTime < 3 && $(window).width() < 768) { 5462 player.src(player.currentSources()); 5463 } 5464 vid.controls = false; 5465 player.play(); 5466 } 5467 trackEvent("Video", "Visible", videoId); 5468 }, 5469 onLeave: function (progress, direction, isActive) { 5470 // video started playing by user interaction 5471 if (!video.muted) { 5472 return; 5473 } 5474 tween && tween.pause(); 5475 tween = TweenMax.to($el.find('.year-end-2022-videoautoplay'), 0.5, { autoAlpha: 0, ease: Power4.easeOut }); 5476 video.pause(); 5477 }, 5478 onLeaveBack: function (progress, direction, isActive) { 5479 // video started playing by user interaction 5480 if (!video.muted) { 5481 return; 5482 } 5483 if (direction === "reverse") { 5484 tween && tween.pause(); 5485 tween = TweenMax.to($el.find('.year-end-2022-videoautoplay'), 0.5, { autoAlpha: 0, ease: Power4.easeOut }); 5486 video.pause(); 5487 } 5488 } 5489 }, 5490 }); 5491 }); 5492 5493 }); 5494 } 5495 5496 5497 5498 /** 5499 * Calculates the minimum scale factor to rotate a background without revealing edges. 5500 * 5501 * @param {number} width - The width of the viewport (e.g., screen width). 5502 * @param {number} height - The height of the viewport (e.g., screen height). 5503 * @param {number} angle - The rotation angle in degrees. 5504 * @returns {number} - The minimum scale factor. 5505 */ 5506 5507 function addSlamTextInToTimeline(tl, textEl, options) { 5508 if (typeof gsap === 'undefined') { 5509 return tl; 5510 } 5511 5512 options = options || {}; 5513 var duration = typeof options.duration === 'number' ? options.duration : 1.1; 5514 var ease = options.ease || 'power2.inOut'; 5515 var fallbackDuration = typeof options.fallbackDuration === 'number' ? options.fallbackDuration : 0.5; 5516 var autoSplit = (typeof options.autoSplit === 'boolean') ? options.autoSplit : true; 5517 5518 if (!tl) { 5519 tl = gsap.timeline({ overwrite: true }); 5520 } 5521 if (!textEl) { 5522 return tl; 5523 } 5524 5525 var hasSplitText = (typeof SplitText !== 'undefined'); 5526 5527 // Split (once) if requested and available. 5528 if (hasSplitText && autoSplit) { 5529 var alreadySplit = textEl.querySelector && (textEl.querySelector('.char') || textEl.querySelector('.line')); 5530 if (!alreadySplit && !textEl._slamSplitText) { 5531 try { 5532 textEl._slamSplitText = new SplitText(textEl, { 5533 type: 'lines,chars', 5534 linesClass: 'line', 5535 charsClass: 'char' 5536 }); 5537 } catch (e) { 5538 console.error('SplitText init failed:', e); 5539 window.sentry && Sentry.captureException(e); 5540 } 5541 } 5542 } 5543 5544 // Fallback if SplitText isn't present / failed. 5545 if (!hasSplitText) { 5546 tl.fromTo(textEl, { 5547 autoAlpha: 0, 5548 y: 30, 5549 scale: 0.98, 5550 }, { 5551 autoAlpha: 1, 5552 y: 0, 5553 scale: 1, 5554 duration: fallbackDuration, 5555 ease: Power3.easeOut, 5556 overwrite: true, 5557 }); 5558 return tl; 5559 } 5560 5561 var lines = textEl.querySelectorAll ? textEl.querySelectorAll('.line') : null; 5562 if (!lines || lines.length === 0) { 5563 // SplitText may have produced chars without lines, or not at all. 5564 var charsFallback = textEl.querySelectorAll ? textEl.querySelectorAll('.char') : null; 5565 if (!charsFallback || charsFallback.length === 0) { 5566 tl.fromTo(textEl, { 5567 autoAlpha: 0, 5568 y: 30, 5569 }, { 5570 autoAlpha: 1, 5571 y: 0, 5572 duration: fallbackDuration, 5573 ease: Power3.easeOut, 5574 overwrite: true, 5575 }); 5576 return tl; 5577 } 5578 // Treat as a single âlineâ. 5579 lines = [{ querySelectorAll: function () { return charsFallback; } }]; 5580 } 5581 5582 Array.prototype.forEach.call(lines, function (line) { 5583 var chars = line && line.querySelectorAll ? line.querySelectorAll('.char') : null; 5584 if (!chars || chars.length === 0) { 5585 tl.fromTo(textEl, { 5586 autoAlpha: 0, 5587 y: 30, 5588 }, { 5589 autoAlpha: 1, 5590 y: 0, 5591 duration: fallbackDuration, 5592 ease: Power3.easeOut, 5593 overwrite: true, 5594 }); 5595 return; 5596 } 5597 5598 var charsArr = Array.prototype.slice.call(chars); 5599 var charsTotal = charsArr.length; 5600 5601 charsArr.forEach(function (char) { 5602 if (char && char.parentNode) { 5603 gsap.set(char.parentNode, { perspective: 1000 }); 5604 } 5605 }); 5606 5607 tl.fromTo(charsArr, { 5608 willChange: 'transform,opacity', 5609 x: function (position) {
5610 var center = Math.ceil(charsTotal / 2); 5611 var factor = position < center ? position : center - Math.abs(Math.floor(charsTotal / 2) - position) - 1; 5612 var dist = (charsTotal % 2 ? Math.abs(center - 1 - factor) : Math.abs(center - factor)) * 45; 5613 return dist * (position < charsTotal / 2 ? -1 : 1); 5614 }, 5615 y: 12, 5616 rotationY: -180, 5617 autoAlpha: 0, 5618 y: position => { 5619 const factor = position < Math.ceil(charsTotal / 2) ? position : Math.ceil(charsTotal / 2) - Math.abs(Math.floor(charsTotal / 2) - position) - 1; 5620 return factor * 3; 5621 }, 5622 rotationZ: position => { 5623 const factor = position < Math.ceil(charsTotal / 2) ? position : Math.ceil(charsTotal / 2) - Math.abs(Math.floor(charsTotal / 2) - position) - 1; 5624 return position < charsTotal / 2 ? Math.abs(factor - charsTotal / 2) * 8 : -1 * Math.abs(factor - charsTotal / 2) * 8; 5625 } 5626 }, { 5627 x: 0, 5628 y: 0, 5629 rotationY: 0, 5630 rotationZ: 0, 5631 autoAlpha: 1, 5632 duration: duration, 5633 ease: ease, 5634 overwrite: true, 5635 }, options.delay || 0); 5636 }); 5637 5638 return tl; 5639 } 5640 5641 function calculateScale(width, height, angle) { 5642 // Convert angle from degrees to radians 5643 const theta = angle * (Math.PI / 180); 5644 5645 const cosTheta = Math.cos(theta); 5646 const sinTheta = Math.sin(theta); 5647 5648 // Calculate scale factors for width and height 5649 const scaleX = (width * cosTheta + height * sinTheta) / width; 5650 const scaleY = (width * sinTheta + height * cosTheta) / height; 5651 5652 // Return the maximum scale factor to ensure full coverage 5653 return Math.max(scaleX, scaleY); 5654 } 5655 5656 5657 // Helper function that lets you dynamically figure out a grid's rows/columns as well as further refine those with "odd" or "even" ones 5658 // https://greensock.com/forums/topic/34808-how-can-i-animate-the-odd-and-even-columns-rows-of-a-grid-with-gsapto/?do=findComment&comment=174346 5659 const getGrid = selector => { 5660 let elements = gsap.utils.toArray(selector), 5661 bounds, 5662 getSubset = (axis, dimension, alternating, merge) => { 5663 let a = [], 5664 subsets = {}, 5665 onlyEven = alternating === "even", 5666 p; 5667 bounds.forEach((b, i) => { 5668 let position = Math.round(b[axis] + b[dimension] / 2), 5669 subset = subsets[position]; 5670 subset || (subsets[position] = subset = []); 5671 subset.push(elements[i]); 5672 }); 5673 for (p in subsets) { 5674 a.push(subsets[p]); 5675 } 5676 if (onlyEven || alternating === "odd") { 5677 a = a.filter((el, i) => !(i % 2) === onlyEven); 5678 } 5679 if (merge) { 5680 let a2 = []; 5681 a.forEach(subset => a2.push(...subset)); 5682 return a2; 5683 } 5684 return a; 5685 }; 5686 elements.refresh = () => bounds = elements.map(el => el.getBoundingClientRect()); 5687 elements.columns = (alternating, merge) => getSubset("left", "width", alternating, merge); 5688 elements.rows = (alternating, merge) => getSubset("top", "height", alternating, merge); 5689 elements.refresh(); 5690 5691 return elements; 5692 } 5693 5694 var hasWebP = (function () { 5695 // some small (2x1 px) test images for each feature 5696 var images = { 5697 basic: "data:image/webp;base64,UklGRjIAAABXRUJQVlA4ICYAAACyAgCdASoCAAEALmk0mk0iIiIiIgBoSygABc6zbAAA/v56QAAAAA==", 5698 lossless: "data:image/webp;base64,UklGRh4AAABXRUJQVlA4TBEAAAAvAQAAAAfQ//73v/+BiOh/AAA=" 5699 }; 5700 5701 return function (feature) { 5702 var deferred = $.Deferred(); 5703 5704 $("<img>").on("load", function () { 5705 // the images should have these dimensions 5706 if (this.width === 2 && this.height === 1) { 5707 deferred.resolve(); 5708 } else { 5709 deferred.reject(); 5710 } 5711 }).on("error", function () { 5712 deferred.reject(); 5713 }).attr("src", images[feature || "basic"]); 5714 5715 return deferred.promise(); 5716 } 5717 })(); 5718 5719 5720 return YEAR2026; 5721}())();
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.