1(function($) { 2 'use strict'; 3 4 $(document).ready(function() { 5 $('.hero-slideshow__slideshow').each(function() { 6 const $slideshow = $(this); 7 const $slides = $slideshow.find('.hero-slideshow__slide'); 8 const $dots = $slideshow.find('.hero-slideshow__dot'); 9 const $prevBtn = $slideshow.find('.hero-slideshow__control--prev'); 10 const $nextBtn = $slideshow.find('.hero-slideshow__control--next'); 11 const autoRotate = $slideshow.data('auto-rotate') === true; 12 const rotationSpeed = parseInt($slideshow.data('rotation-speed')) || 5000; 13 let currentSlide = 0; 14 let autoRotateInterval = null; 15 let isAnimating = false; 16 17 function hydrateSlide($slide) { 18 const bg = $slide.attr('data-bg'); 19 if (bg) { 20 $slide.css('background-image', 'url("' + bg + '")').removeAttr('data-bg'); 21 } 22 } 23 24 $(window).on('load', function() { 25 $slides.each(function() { 26 hydrateSlide($(this)); 27 }); 28 }); 29 30 if ($slides.length <= 1) { 31 return; 32 } 33 34 function goToSlide(index, direction = 'next') { 35 if (isAnimating) return; 36 37 isAnimating = true; 38 const $currentSlide = $slides.eq(currentSlide); 39 const $targetSlide = $slides.eq(index); 40 41 hydrateSlide($targetSlide); 42 43 $currentSlide.removeClass('active'); 44 $targetSlide.addClass('active'); 45 46 $dots.eq(currentSlide).removeClass('active'); 47 $dots.eq(index).addClass('active'); 48 49 currentSlide = index; 50 51 setTimeout(function() { 52 isAnimating = false; 53 }, 800); 54 55 if (autoRotate) { 56 resetAutoRotate(); 57 } 58 } 59 60 function nextSlide() { 61 const next = (currentSlide + 1) % $slides.length; 62 goToSlide(next, 'next'); 63 } 64 65 function prevSlide() { 66 const prev = (currentSlide - 1 + $slides.length) % $slides.length; 67 goToSlide(prev, 'prev'); 68 } 69 70 function startAutoRotate() { 71 if (autoRotate && !autoRotateInterval) { 72 autoRotateInterval = setInterval(nextSlide, rotationSpeed); 73 } 74 } 75 76 function stopAutoRotate() { 77 if (autoRotateInterval) { 78 clearInterval(autoRotateInterval); 79 autoRotateInterval = null; 80 } 81 } 82 83 function resetAutoRotate() { 84 stopAutoRotate(); 85 startAutoRotate(); 86 } 87 88 $nextBtn.on('click', function(e) { 89 e.preventDefault(); 90 nextSlide(); 91 }); 92 93 $prevBtn.on('click', function(e) { 94 e.preventDefault(); 95 prevSlide(); 96 }); 97 98 $dots.on('click', function(e) { 99 e.preventDefault(); 100 const index = parseInt($(this).data('slide')); 101 if (index !== currentSlide) { 102 goToSlide(index); 103 } 104 }); 105 106 $slideshow.on('mouseenter', function() { 107 stopAutoRotate(); 108 }); 109 110 $slideshow.on('mouseleave', function() { 111 if (autoRotate) { 112 startAutoRotate(); 113 } 114 }); 115 116 // Keyboard navigation 117 $(document).on('keydown', function(e) { 118 if (!$slideshow.is(':visible')) return; 119 120 if (e.key === 'ArrowLeft') { 121 prevSlide(); 122 } else if (e.key === 'ArrowRight') { 123 nextSlide(); 124 } 125 }); 126 127 // Touch/swipe support 128 let touchStartX = 0; 129 let touchEndX = 0; 130 131 $slideshow.on('touchstart', function(e) { 132 touchStartX = e.changedTouches[0].screenX; 133 }); 134 135 $slideshow.on('touchend', function(e) { 136 touchEndX = e.changedTouches[0].screenX; 137 handleSwipe(); 138 }); 139 140 function handleSwipe() { 141 const swipeThreshold = 50; 142 const diff = touchStartX - touchEndX; 143 144 if (Math.abs(diff) > swipeThreshold) { 145 if (diff > 0) { 146 nextSlide(); 147 } else { 148 prevSlide(); 149 } 150 } 151 } 152 153 startAutoRotate(); 154 155 // Animate inline stats numbers when the first slide loads 156 const $firstSlideStats = $slides.eq(0).find('.hero-slideshow__inline-stats'); 157 if ($firstSlideStats.length) { 158 // Animate immediately for the first slide 159 $firstSlideStats.find('.hero-slideshow__inline-stat-number').each(function() { 160 animateNumber(this); 161 }); 162 } 163 164 function animateNumber(element) { 165 const $element = $(element); 166 const finalText = $element.text(); 167 168 let numericValue, suffix = ''; 169 if (finalText.includes('K')) { 170 numericValue = parseFloat(finalText.replace('K', '')) * 1000; 171 suffix = 'K'; 172 } else if (finalText.includes('M')) { 173 numericValue = parseFloat(finalText.replace('M', '')) * 1000000; 174 suffix = 'M'; 175 } else { 176 numericValue = parseInt(finalText.replace(/,/g, ''), 10); 177 } 178 179 if (isNaN(numericValue)) return; 180 181 const duration = 2000; 182 const startTime = performance.now(); 183 184 function easeOutExpo(t) { 185 return t === 1 ? 1 : 1 - Math.pow(2, -10 * t); 186 } 187 188 function formatNumber(num) { 189 if (suffix === 'K') { 190 return Math.round(num / 1000) + 'K'; 191 } else if (suffix === 'M') { 192 return (num / 1000000).toFixed(1) + 'M'; 193 }
194 return Math.round(num).toLocaleString(); 195 } 196 197 function update(currentTime) { 198 const elapsed = currentTime - startTime; 199 const progress = Math.min(elapsed / duration, 1); 200 const easedProgress = easeOutExpo(progress); 201 const current = numericValue * easedProgress; 202 203 $element.text(formatNumber(current)); 204 205 if (progress < 1) { 206 requestAnimationFrame(update); 207 } else { 208 $element.text(formatNumber(numericValue)); 209 } 210 } 211 212 requestAnimationFrame(update); 213 } 214 }); 215 }); 216 217})(jQuery); 218
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.