1/** 2 * Homepage featured-stories slider â progressive enhancement. 3 * 4 * The markup (CSS scroll-snap carousel with anchor-link dots/arrows) is fully 5 * functional with zero JS; the first slide paints without this file. This only 6 * adds: auto-advance, pause-on-interaction, reduced-motion support, and keeping 7 * the dots' aria-current in sync with manual swiping. No jQuery / framework. 8 */ 9(function () { 10 'use strict'; 11 12 var AUTO_DELAY = 5000; 13 var reduceMotion = window.matchMedia && 14 window.matchMedia('(prefers-reduced-motion: reduce)').matches; 15 16 function initSlider(root) { 17 var track = root.querySelector('.hfs-track'); 18 if (!track) { 19 return; 20 } 21 22 var slides = Array.prototype.slice.call(track.querySelectorAll('.hfs-slide')); 23 var dots = Array.prototype.slice.call(root.querySelectorAll('.hfs-dot')); 24 if (slides.length < 2) { 25 return; 26 } 27 28 root.classList.add('hfs--js'); 29 30 var current = 0; 31 var timer = null; 32 var paused = false; 33 34 function setActive(i) { 35 dots.forEach(function (dot, di) { 36 if (di === i) { 37 dot.setAttribute('aria-current', 'true'); 38 } else { 39 dot.removeAttribute('aria-current'); 40 } 41 }); 42 } 43 44 function goTo(i) { 45 current = (i + slides.length) % slides.length; 46 // Instant snap (no glide) â a story "just changes" to the next. 47 track.scrollTo({ left: current * track.clientWidth, behavior: 'auto' }); 48 setActive(current); 49 } 50 51 // Dots + arrows are anchor links; hijack so the URL hash doesn't jump. 52 root.addEventListener('click', function (e) { 53 var link = e.target.closest && e.target.closest('a[href^="#hfs-slide-"]'); 54 if (!link) { 55 return; 56 } 57 var target = document.getElementById(link.getAttribute('href').slice(1)); 58 var idx = slides.indexOf(target); 59 if (idx === -1) { 60 return; 61 } 62 e.preventDefault(); 63 goTo(idx); 64 restart(); 65 }); 66 67 // Keep aria-current in sync with manual swipe / native scroll. 68 var scrollRaf = null; 69 track.addEventListener('scroll', function () { 70 if (scrollRaf) { 71 return; 72 } 73 scrollRaf = window.requestAnimationFrame(function () { 74 scrollRaf = null; 75 var nearest = Math.round(track.scrollLeft / track.clientWidth); 76 nearest = Math.max(0, Math.min(slides.length - 1, nearest)); 77 if (nearest !== current) { 78 current = nearest; 79 setActive(current); 80 } 81 }); 82 }, { passive: true }); 83 84 function tick() { 85 if (!paused) { 86 goTo(current + 1); 87 } 88 } 89 90 function start() { 91 if (reduceMotion || timer) { 92 return; 93 } 94 timer = window.setInterval(tick, AUTO_DELAY); 95 } 96 97 function stop() { 98 if (timer) { 99 window.clearInterval(timer); 100 timer = null; 101 } 102 } 103 104 function restart() { 105 stop(); 106 start(); 107 } 108 109 // Pause on hover/focus and when the tab is hidden. 110 root.addEventListener('pointerenter', function () { paused = true; }); 111 root.addEventListener('pointerleave', function () { paused = false; }); 112 root.addEventListener('focusin', function () { paused = true; }); 113 root.addEventListener('focusout', function () { paused = false; }); 114 document.addEventListener('visibilitychange', function () { 115 paused = document.hidden; 116 }); 117 118 setActive(0); 119 start(); 120 } 121 122 function init() { 123 Array.prototype.slice.call(document.querySelectorAll('.hfs')).forEach(initSlider); 124 } 125 126 if (document.readyState === 'loading') { 127 document.addEventListener('DOMContentLoaded', init); 128 } else { 129 init(); 130 } 131})();
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.