PageSourceSearch

https://www.ksos-web.jp/js/index.js?v=20261001

js ksos-web.jp collected 2026-10-02 18:45:01 UTC 7,832 bytes, 251 lines download raw bytes

1(() => {
2  const menuToggle = document.querySelector("[data-menu-toggle]");
3
4  const setMenu = (open) => {
5    document.body.classList.toggle("is-menu-open", open);
6    menuToggle?.setAttribute("aria-expanded", String(open));
7    menuToggle?.setAttribute("aria-label", open ? "メニューを閉じる" : "メニューを開く");
8  };
9
10  menuToggle?.addEventListener("click", () => {
11    setMenu(!document.body.classList.contains("is-menu-open"));
12  });
13
14  document.addEventListener("keydown", (event) => {
15    if (event.key === "Escape") setMenu(false);
16  });
17
18  // Safari 対策
19  let menuLockedOnHide = false;
20
21  const releaseMenuScrollLock = () => {
22    const y = window.scrollY;
23    setMenu(false);
24    const root = document.documentElement;
25    const body = document.body;
26    root.style.setProperty("overflow", "auto", "important");
27    body.style.setProperty("overflow", "auto", "important");
28    void body.offsetHeight;
29    root.style.removeProperty("overflow");
30    body.style.removeProperty("overflow");
31    if (window.scrollY !== y) window.scrollTo(0, y);
32  };
33
34  window.addEventListener("pagehide", () => {
35    menuLockedOnHide = document.body.classList.contains("is-menu-open");
36    setMenu(false);
37  });
38
39  window.addEventListener("pageshow", (event) => {
40    const needsRelease = menuLockedOnHide || document.body.classList.contains("is-menu-open");
41    menuLockedOnHide = false;
42    if (event.persisted && needsRelease) releaseMenuScrollLock();
43  });
44
45  const pcQuery = window.matchMedia("(min-width: 768px)");
46  pcQuery.addEventListener("change", (event) => {
47    if (event.matches) setMenu(false);
48  });
49
50  const header = document.querySelector(".site-header");
51  const hero = document.querySelector(".hero");
52
53  const syncOverHero = () => {
54    if (!header) return;
55    let over = false;
56    if (document.body.classList.contains("page-top2026") && hero) {
57      const band = header.offsetHeight;
58      const heroRect = hero.getBoundingClientRect();
59      over = band > 0 && heroRect.bottom > 0 && heroRect.top < band;
60    }
61    header.classList.toggle("is-over-hero", over);
62  };
63
64  const scrollTargets = [".site-header", ".float-cta"]
65    .map((selector) => document.querySelector(selector))
66    .filter(Boolean);
67
68  if (scrollTargets.length || header) {
69    let lastY = window.scrollY;
70    let ticking = false;
71
72    const syncScrollClasses = () => {
73      ticking = false;
74      const y = window.scrollY;
75      const goingDown = y > lastY;
76      const goingUp = y < lastY;
77      lastY = y;
78
79      for (const el of scrollTargets) {
80        if (y <= 0) {
81          el.classList.remove("is-down", "is-up");
82          continue;
83        }
84        if (goingDown) {
85          el.classList.add("is-down");
86          el.classList.remove("is-up");
87        } else if (goingUp) {
88          el.classList.add("is-up");
89          el.classList.remove("is-down");
90        }
91      }
92
93      syncOverHero();
94    };
95
96    window.addEventListener("scroll", () => {
97      if (ticking) return;
98      ticking = true;
99      requestAnimationFrame(syncScrollClasses);
100    }, { passive: true });
101
102    window.addEventListener("resize", () => {
103      syncOverHero();
104    }, { passive: true });
105
106    window.addEventListener("pageshow", () => {
107      lastY = window.scrollY;
108      syncScrollClasses();
109    });
110
111    window.addEventListener("load", syncOverHero);
112    header.addEventListener("transitionend", (event) => {
113      if (event.propertyName === "translate") syncOverHero();
114    });
115
116    if (typeof ResizeObserver === "function") {
117      const ro = new ResizeObserver(syncOverHero);
118      ro.observe(header);
119      if (hero) ro.observe(hero);
120    }
121
122    syncScrollClasses();
123  }
124
125  const leadDialog = document.querySelector("#lead-dialog");
126  if (leadDialog && document.body.hasAttribute("data-lead-dialog")) {
127    const leadTitle = leadDialog.querySelector("#lead-dialog-title");
128    const leadText = leadDialog.querySelector("#lead-dialog-lead");
129    const leadCopy = {
130      estimate: {
131        title: "無料お見積り",
132        before: "ご希望の無料お見積りの種類を",
133      },
134      shiryo: {
135        title: "資料請求",
136        before: "ご希望の資料請求の種類を",
137      },
138    };
139
140    const fillLead = (kind) => {
141      const copy = leadCopy[kind];
142      if (!copy || !leadTitle || !leadText) return;
143      leadTitle.textContent = copy.title;
144      const breakLine = document.createElement("br");
145      breakLine.className = "sp-only";
146      leadText.replaceChildren(copy.before, breakLine, "お選びください。");
147      leadDialog.querySelectorAll("[data-audience]").forEach((link) => {
148        const href = link.getAttribute(`data-href-${kind}`);
149        if (href) link.href = href;
150      });
151    };
152
153    let leadOpener = null;
154
155    document.querySelectorAll("[data-lead]").forEach((trigger) => {
156      trigger.addEventListener("click", (event) => {
157        const kind = trigger.getAttribute("data-lead");
158        if (!leadCopy[kind]) return;
159        event.preventDefault();
160        leadOpener = trigger;
161        setMenu(false);
162        fillLead(kind);
163        if (typeof leadDialog.showModal === "function") leadDialog.showModal();
164      });
165    });
166
167    leadDialog.addEventListener("close", () => {
168      const opener = leadOpener;
169      leadOpener = null;
170      if (!opener?.closest(".site-menu") || document.body.classList.contains("is-menu-open")) return;
171      requestAnimationFrame(() => menuToggle?.focus());
172    });
173
174    if (!("closedBy" in HTMLDialogElement.prototype)) {
175      leadDialog.addEventListener("click", (event) => {
176        if (event.target !== leadDialog) return;
177        const rect = leadDialog.getBoundingClientRect();
178        const inside = (
179          rect.top <= event.clientY &&
180          event.clientY <= rect.top + rect.height &&
181          rect.left <= event.clientX &&
182          event.clientX <= rect.left + rect.width
183        );
184        if (!inside) leadDialog.close();
185      });
186    }
187  }
188
189  const collage = document.querySelector(".hero__collage");
190  if (collage) {
191    const whenDecoded = (img) => {
192      if (typeof img.decode === "function") {
193        return img.decode().catch(() => {});
194      }
195      if (img.complete) return Promise.resolve();
196      return new Promise((resolve) => {
197        img.addEventListener("load", resolve, { once: true });
198        img.addEventListener("error", resolve, { once: true });
199      });
200    };
201
202    const whenCssBackgroundReady = (el) => {
203      if (!el) return Promise.resolve();
204      const src = [...getComputedStyle(el).backgroundImage.matchAll(/url\((['"]?)(.*?)\1\)/g)]
205        .map((match) => match[2])
206        .find(Boolean);
207      if (!src) return Promise.resolve();
208      const img = new Image();
209      img.src = src;
210      return whenDecoded(img);
211    };
212
213    const reveal = () => collage.classList.add("is-ready");
214    const ready = [
215      ...[...collage.querySelectorAll("img")].map(whenDecoded),
216      whenCssBackgroundReady(hero),
217    ];
218    Promise.all(ready).then(reveal);
219    setTimeout(reveal, 4000);
220  }
221
222  const bannerSwiperEl = document.querySelector(".banners-swiper");
223  if (bannerSwiperEl && typeof Swiper === "function") {
224    new Swiper(bannerSwiperEl, {
225      slidesPerView: "auto",
226      centeredSlides: true,
227      spaceBetween: 16,
228      loop: true,
229      speed: 500,
230      watchOverflow: true,
231      breakpoints: {
232        768: {
233          spaceBetween: 24,
234        },
235      },
236      navigation: {
237        prevEl: ".banners .carousel__prev",
238        nextEl: ".banners .carousel__next",
239      },
240      pagination: {
241        el: ".banners .carousel__dots",
242        clickable: true,
243      },
244      a11y: {
245        prevSlideMessage: "前のバナー",
246        nextSlideMessage: "次のバナー",
247        paginationBulletMessage: "{{index}}枚目のバナー",
248      },
249    });
250  }
251})();

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.