PageSourceSearch

https://tokyogrown.jp/themes/custom/tokyogrown/assets/front/front.js?tm5o7o

js tokyogrown.jp collected 2026-10-01 12:40:44 UTC 6,367 bytes, 152 lines download raw bytes

1/** Front-page behaviors. Lower pages never load this file. */
2(function (Drupal, once) {
3  'use strict';
4  const carouselSpeed = 450;
5  const carouselDelay = 3250;
6  function carousel(section, trackSelector, viewportSelector, prevSelector, nextSelector, delay, mobileOnly = false) {
7    if (!section) return;
8    const track = section.querySelector(trackSelector);
9    const viewport = section.querySelector(viewportSelector);
10    const previous = section.querySelector(prevSelector);
11    const next = section.querySelector(nextSelector);
12    const originals = [...track.children];
13    if (!originals.length) { section.hidden = true; return; }
14    const dots = section.querySelector('.pickup-dots');
15    const reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)');
16    const count = originals.length;
17    const cloneCard = card => {
18      const clone = card.cloneNode(true);
19      clone.removeAttribute('id');
20      clone.querySelectorAll('[id]').forEach(element => element.removeAttribute('id'));
21      return clone;
22    };
23    const before = originals.map(cloneCard);
24    const after = originals.map(cloneCard);
25    const normalize = value => (value % count + count) % count;
26    let index = 0, shown = 3, timer, settleTimer, startX, moving = false, looping = false;
27    const stop = () => window.clearInterval(timer);
28    const start = () => {
29      stop();
30      if (looping && !reducedMotion.matches && !document.hidden && !section.matches(':hover') && !section.contains(document.activeElement)) {
31        timer = window.setInterval(() => move(index + 1), delay);
32      }
33    };
34    const render = (animate = true) => {
35      const step = originals[0].getBoundingClientRect().width + (parseFloat(getComputedStyle(track).columnGap) || 0);
36      track.style.transition = animate && !reducedMotion.matches ? `transform ${carouselSpeed}ms ease` : 'none';
37      const position = looping ? count + index : 0;
38      track.style.transform = `translateX(${-position * step}px)`;
39      [...track.children].forEach((card, i) => {
40        const visible = i >= position && i < position + shown;
41        card.inert = !visible;
42        card.setAttribute('aria-hidden', String(!visible));
43      });
44      dots?.querySelectorAll('button').forEach((dot, i) => {
45        dot.classList.toggle('is-active', i === normalize(index));
46        dot.setAttribute('aria-current', String(i === normalize(index)));
47      });
48    };
49    const settle = () => {
50      window.clearTimeout(settleTimer);
51      index = normalize(index);
52      render(false);
53      moving = false;
54    };
55    track.addEventListener('transitionend', event => {
56      if (event.target === track && event.propertyName === 'transform' && moving) settle();
57    });
58    const move = (target) => {
59      if (moving || !looping) return;
60      index = target;
61      moving = true;
62      render();
63      // Reduced motion has no transitionend; the timeout also covers interrupted transitions.
64      if (reducedMotion.matches) settle();
65      else settleTimer = window.setTimeout(settle, carouselSpeed + 50);
66    };
67    const resize = () => {
68      window.clearTimeout(settleTimer);
69      moving = false;
70      const desktop = window.matchMedia('(min-width: 737px)').matches;
71      shown = desktop ? (mobileOnly ? count : 3) : 1;
72      looping = count > 1 && !(mobileOnly && desktop);
73      index = looping ? normalize(index) : 0;
74      track.replaceChildren(...(looping ? [...before, ...originals, ...after] : originals));
75      previous.hidden = next.hidden = !looping;
76      if (dots) {
77        dots.replaceChildren();
78        if (looping) for (let i = 0; i < count; i++) {
79          const dot = document.createElement('button'); dot.type = 'button';
80          dot.setAttribute('aria-label', `${i + 1}件目の記事を表示`);
81          dot.addEventListener('click', () => { move(i); start(); }); dots.append(dot);
82        }
83      }
84      render(false); start();
85    };
86    previous.addEventListener('click', () => { move(index - 1); start(); });
87    next.addEventListener('click', () => { move(index + 1); start(); });
88    section.addEventListener('mouseenter', stop);
89    section.addEventListener('mouseleave', start);
90    section.addEventListener('focusin', stop);
91    section.addEventListener('focusout', () => window.setTimeout(start, 0));
92    viewport.addEventListener('touchstart', e => { startX = e.changedTouches[0].clientX; stop(); }, { passive: true });
93    viewport.addEventListener('touchend', e => {
94      const distance = e.changedTouches[0].clientX - startX;
95      if (Math.abs(distance) > 40) move(index + (distance < 0 ? 1 : -1)); start();
96    }, { passive: true });
97    window.addEventListener('resize', resize);
98    document.addEventListener('visibilitychange', start);
99    reducedMotion.addEventListener('change', start);
100    resize();
101  }
102
103  function switchResponsiveImages(body) {
104    const update = () => {
105      const suffix = window.innerWidth > 736 ? '_pc.' : '_sp.';
106      body.querySelectorAll('img.jsSwitch').forEach(image => {
107        image.src = image.src.replace(/_(?:pc|sp)\./, suffix);
108        image.style.visibility = 'visible';
109      });
110    };
111    update();
112    window.addEventListener('resize', update);
113  }
114
115  function newsSwiper(body) {
116    const slider = body.querySelector('.swiperNews');
117    if (!slider || typeof window.Swiper !== 'function') return;
118    const slides = slider.querySelectorAll('.swiper-slide');
119    new window.Swiper(slider, {
120      slidesPerView: 'auto',
121      spaceBetween: 30,
122      centeredSlides: false,
123      loop: slides.length > 1,
124      speed: carouselSpeed,
125      autoplay: {
126        delay: carouselDelay,
127        pauseOnMouseEnter: true,
128        waitForTransition: false,
129      },
130      navigation: {
131        nextEl: body.querySelector('.swiper-button-next'),
132        prevEl: body.querySelector('.swiper-button-prev'),
133      },
134      breakpoints: {
135        812: {
136          spaceBetween: 30,
137          roundLengths: true,
138        },
139      },
140    });
141  }
142
143  Drupal.behaviors.front = {
144    attach(context) {
145      once('front', 'body.tg-front', context).forEach(body => {
146        switchResponsiveImages(body);
147        newsSwiper(body);
148        carousel(body.querySelector('.tg-purpose'), '.purpose-cards', '.purpose-viewport', '.purpose-slide-prev', '.purpose-slide-next', carouselDelay, true);
149      });
150    }
151  };
152})(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.