PageSourceSearch

https://kalameh.com/themes/custom/kalameh_webapp/components/hero-slideshow/hero-slideshow.js?tlvv2d

js kalameh.com collected 2026-09-24 22:53:03 UTC 4,672 bytes, 128 lines download raw bytes

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.