1/** 2 * @file 3 * Home hero slideshow behaviour. 4 * Desktop/tablet: cross-fade between absolutely-positioned slides, autoplay 5 * every 6s, pause on hover/focus, dots + chevron arrows. 6 * Mobile (<=640px): horizontal scroll-snap carousel; the active dot syncs to 7 * the scrolled-to slide and autoplay is off. 8 */ 9((Drupal, once) => { 10 Drupal.behaviors.kalamehHeroSlideshow = { 11 attach(context) { 12 once('kal-hero', '[data-slideshow]', context).forEach((root) => { 13 const stage = root.closest('[data-hero-stage]'); 14 const slidesContainer = root.querySelector('.hero-slides'); 15 const slides = Array.from(root.querySelectorAll('.hero-slide')); 16 const dots = Array.from(root.querySelectorAll('.slide-dot')); 17 const prevBtn = root.querySelector('[data-prev]'); 18 const nextBtn = root.querySelector('[data-next]'); 19 if (slides.length < 2) { 20 return; 21 } 22 const AUTOPLAY_MS = 6000; 23 const mobileQ = window.matchMedia('(max-width: 640px)'); 24 const isMobile = () => mobileQ.matches; 25 let idx = 0; 26 let timer = null; 27 28 const getSlideBg = (slide) => { 29 const inline = slide.style.backgroundImage; 30 if (inline) { 31 return inline; 32 } 33 const computed = window.getComputedStyle(slide).backgroundImage; 34 return computed && computed !== 'none' ? computed : ''; 35 }; 36 37 const setActive = (next) => { 38 idx = ((next % slides.length) + slides.length) % slides.length; 39 slides.forEach((s, i) => s.classList.toggle('is-active', i === idx)); 40 dots.forEach((d, i) => { 41 d.classList.toggle('is-active', i === idx); 42 d.setAttribute('aria-selected', i === idx ? 'true' : 'false'); 43 }); 44 if (stage) { 45 const bg = getSlideBg(slides[idx]); 46 if (bg) { 47 stage.style.setProperty('--hero-bg', bg); 48 } 49 } 50 }; 51 52 const scrollToSlide = (i, smooth) => { 53 if (!slidesContainer || !slides[i]) { 54 return; 55 } 56 const delta = slides[i].getBoundingClientRect().left - slidesContainer.getBoundingClientRect().left; 57 slidesContainer.scrollTo({ left: slidesContainer.scrollLeft + delta, behavior: smooth ? 'smooth' : 'auto' }); 58 }; 59 60 const go = (next) => { 61 next = ((next % slides.length) + slides.length) % slides.length; 62 if (next === idx) { 63 return; 64 } 65 if (isMobile() && slidesContainer) { 66 scrollToSlide(next, true); 67 } else {
68 setActive(next); 69 } 70 }; 71 72 const stop = () => { 73 if (timer) { 74 clearInterval(timer); 75 timer = null; 76 } 77 }; 78 const start = () => { 79 stop(); 80 if (isMobile()) { 81 return; 82 } 83 timer = setInterval(() => go(idx + 1), AUTOPLAY_MS); 84 }; 85 86 let scrollDebounce = null; 87 if (slidesContainer) { 88 slidesContainer.addEventListener('scroll', () => { 89 if (!isMobile()) { 90 return; 91 } 92 clearTimeout(scrollDebounce); 93 scrollDebounce = setTimeout(() => { 94 const cr = slidesContainer.getBoundingClientRect(); 95 const center = cr.left + cr.width / 2; 96 let best = 0; 97 let bestDist = Infinity; 98 slides.forEach((s, i) => { 99 const r = s.getBoundingClientRect(); 100 const d = Math.abs(r.left + r.width / 2 - center); 101 if (d < bestDist) { 102 bestDist = d; 103 best = i; 104 } 105 }); 106 if (best !== idx) {
107 setActive(best); 108 } 109 }, 90); 110 }, { passive: true }); 111 } 112 113 if (prevBtn) prevBtn.addEventListener('click', () => { go(idx - 1); start(); }); 114 if (nextBtn) nextBtn.addEventListener('click', () => { go(idx + 1); start(); }); 115 dots.forEach((d, i) => d.addEventListener('click', () => { go(i); start(); })); 116 root.addEventListener('mouseenter', stop); 117 root.addEventListener('mouseleave', start); 118 root.addEventListener('focusin', stop); 119 root.addEventListener('focusout', start); 120 document.addEventListener('visibilitychange', () => { document.hidden ? stop() : start(); }); 121 mobileQ.addEventListener('change', () => { isMobile() ? (stop(), scrollToSlide(idx, false)) : start(); }); 122 123 setActive(0); 124 start(); 125 }); 126 }, 127 }; 128})(Drupal, once);
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.