1// Logos slider - infinite seamless loop 2document.addEventListener("DOMContentLoaded", () => { 3 const prefersReducedMotion = 4 window.matchMedia && 5 window.matchMedia("(prefers-reduced-motion: reduce)").matches; 6 const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection; 7 const saveData = !!(connection && connection.saveData); 8 const slowConnection = 9 !!(connection && typeof connection.effectiveType === "string" && /(^|-)2g$|3g/.test(connection.effectiveType)); 10 const shouldAutoplayHeroVideo = 11 window.innerWidth >= 1024 && !prefersReducedMotion && !saveData && !slowConnection; 12 13 const logosContainer = document.querySelector(".logos"); 14 const logosSlide = document.querySelector(".logos-slide"); 15 16 if (logosContainer && logosSlide) { 17 const wrapper = document.createElement("div"); 18 wrapper.className = "logos-track"; 19 20 const clone = logosSlide.cloneNode(true); 21 clone.setAttribute("aria-hidden", "true"); 22 23 wrapper.appendChild(logosSlide); 24 wrapper.appendChild(clone); 25 logosContainer.appendChild(wrapper); 26 } 27 28 // Hero slider video: start video for a label (load from data-src only when needed, show video, hide poster, play from start) 29 function startVideoForLabel(label) { 30 const video = label.querySelector("video"); 31 if (!video || !video.dataset.src) return; 32 if (!video.src || video.src === window.location.href) { 33 video.src = video.dataset.src; 34 video.load(); 35 } 36 const poster = label.querySelector(".video-poster"); 37 if (poster) poster.style.display = "none"; 38 video.style.display = "block"; 39 video.currentTime = 0; // always start from beginning when switching to this slide 40 video.play().catch(() => {}); 41 const btn = label.querySelector(".start-video"); 42 if (btn) btn.classList.add("playing"); 43 } 44 45 function pauseVideoForLabel(label) { 46 const video = label.querySelector("video"); 47 if (video) video.pause(); 48 const btn = label.querySelector(".start-video"); 49 if (btn) btn.classList.remove("playing"); 50 } 51 52 // On initial load: play video only for the checked hero slide (good for performance) 53 const checkedInput = document.querySelector( 54 'input[name="hero-slider"]:checked' 55 ); 56 if (checkedInput && shouldAutoplayHeroVideo) { 57 const label = document.querySelector(`label[for="${checkedInput.id}"]`); 58 if (label) startVideoForLabel(label); 59 } 60 61 // Hero indicators + swipe (mobile): same behaviour as generic slider-nav 62 const heroSlidesWrap = document.querySelector(".hero-slider-slides"); 63 const heroIndicators = document.querySelector("[data-hero-indicators]"); 64 const heroPrevButton = document.querySelector("[data-hero-prev]"); 65 const heroNextButton = document.querySelector("[data-hero-next]"); 66 const heroInputs = document.querySelectorAll('input[name="hero-slider"]'); 67 const totalHeroSlides = heroInputs.length; 68 69 function syncHeroIndicators() { 70 if (!heroIndicators) return; 71 const checked = document.querySelector( 72 'input[name="hero-slider"]:checked' 73 ); 74 const idx = checked ? Array.from(heroInputs).indexOf(checked) : 0; 75 heroIndicators.querySelectorAll("li").forEach((li, i) => { 76 li.classList.toggle("is-active", i === idx); 77 }); 78 } 79 80 function goToHeroSlide(index) { 81 const i = Math.max(0, Math.min(index, totalHeroSlides - 1)); 82 const input = heroInputs[i]; 83 if (input) { 84 input.checked = true; 85 input.dispatchEvent(new Event("change", { bubbles: true })); 86 } 87 } 88 89 function stepHeroSlide(direction) { 90 if (!totalHeroSlides) return; 91 const checked = document.querySelector('input[name="hero-slider"]:checked'); 92 const currentIndex = checked ? Array.from(heroInputs).indexOf(checked) : 0; 93 const nextIndex = (currentIndex + direction + totalHeroSlides) % totalHeroSlides; 94 goToHeroSlide(nextIndex); 95 } 96 97 const heroForm = document.querySelector(".hero-slider-section form"); 98 if (heroForm) { 99 heroForm.addEventListener("change", (e) => { 100 if (e.target.name !== "hero-slider" || e.target.type !== "radio") 101 return; 102 syncHeroIndicators(); 103 const newLabel = document.querySelector( 104 `label[for="${e.target.id}"]` 105 ); 106 if (newLabel && shouldAutoplayHeroVideo) startVideoForLabel(newLabel); 107 document 108 .querySelectorAll(".hero-slider-section label") 109 .forEach((l) => { 110 if (l !== newLabel) pauseVideoForLabel(l); 111 }); 112 }); 113 } 114 115 if (heroIndicators) { 116 syncHeroIndicators(); 117 heroIndicators.addEventListener("click", (e) => { 118 const btn = e.target.closest("button[data-page]"); 119 if (btn) { 120 const page = parseInt(btn.dataset.page, 10); 121 goToHeroSlide(page); 122 } 123 }); 124 } 125 126 if (heroPrevButton) { 127 heroPrevButton.addEventListener("click", () => stepHeroSlide(-1)); 128 } 129 130 if (heroNextButton) { 131 heroNextButton.addEventListener("click", () => stepHeroSlide(1)); 132 } 133 134 // Touch swipe on hero (mobile): swipe left = next, swipe right = prev 135 if (heroSlidesWrap && totalHeroSlides > 1) { 136 let touchStartX = 0; 137 heroSlidesWrap.addEventListener( 138 "touchstart", 139 (e) => { 140 touchStartX = e.changedTouches[0].clientX; 141 }, 142 { passive: true } 143 ); 144 heroSlidesWrap.addEventListener( 145 "touchend", 146 (e) => { 147 const touchEndX = e.changedTouches[0].clientX; 148 const diff = touchStartX - touchEndX; 149 const threshold = 50; 150 if (diff > threshold) stepHeroSlide(1); 151 else if (diff < -threshold) stepHeroSlide(-1); 152 }, 153 { passive: true } 154 ); 155 } 156 157 const videoButtons = document.querySelectorAll(".start-video"); 158 videoButtons.forEach((button) => { 159 button.addEventListener("click", (event) => { 160 event.preventDefault(); 161 event.stopPropagation(); 162 163 const label = button.closest("label"); 164 if (!label) return; 165 166 const video = label.querySelector("video"); 167 if (!video) return; 168 169 if (!video.src && video.dataset.src) { 170 video.src = video.dataset.src; 171 video.load(); 172 } 173 174 if (video.style.display === "none") { 175 video.style.display = "block"; 176 } 177 const poster = label.querySelector(".video-poster"); 178 if (poster) poster.style.display = "none"; 179 180 if (video.paused) { 181 video.play(); 182 } else { 183 video.pause(); 184 } 185 }); 186 187 const label = button.closest("label"); 188 if (!label) return; 189 const video = label.querySelector("video"); 190 if (!video) return; 191 192 video.addEventListener("play", () => button.classList.add("playing")); 193 video.addEventListener("pause", () => 194 button.classList.remove("playing") 195 ); 196 video.addEventListener("ended", () => 197 button.classList.remove("playing") 198 ); 199 }); 200});
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.