PageSourceSearch

https://www.swtoo.com/wp-content/themes/sw2top_v2/js/navi.js

js swtoo.com collected 2026-10-02 07:34:11 UTC 5,823 bytes, 192 lines download raw bytes

1(function () {
2  "use strict";
3
4  const ready = (fn) => {
5    if (document.readyState === "loading") {
6      document.addEventListener("DOMContentLoaded", fn, { once: true });
7    } else {
8      fn();
9    }
10  };
11
12  ready(() => {
13    const body = document.body;
14    const navBtn = document.getElementById("nav-btn");
15    const navBtnText = navBtn ? navBtn.querySelector(".nav-btn__text") : null;
16    const globalNav = document.getElementById("global-nav");
17
18    // Create overlay if the template hasn't been updated yet (prevents hard-fail).
19    let navOverlay = document.getElementById("nav-overlay");
20    if (!navOverlay) {
21      navOverlay = document.createElement("div");
22      navOverlay.id = "nav-overlay";
23      navOverlay.className = "nav-overlay";
24      navOverlay.setAttribute("hidden", "");
25      document.body.appendChild(navOverlay);
26    }
27
28    // Optional: search modal (may not exist on some templates)
29    const searchBtn = document.getElementById("search-btn");
30    const searchPanel = document.getElementById("search-panel");
31
32    if (!body || !navBtn || !globalNav) return;
33    const MOBILE_BREAKPOINT = 948;
34    const mqMobile = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT}px)`);
35    const isMobile = () => mqMobile.matches;
36
37    const setHidden = (el, hidden) => {
38      if (!el) return;
39      if (hidden) el.setAttribute("hidden", "");
40      else el.removeAttribute("hidden");
41    };
42
43    const setNavExpanded = (expanded) => {
44      navBtn.setAttribute("aria-expanded", expanded ? "true" : "false");
45      navBtn.classList.toggle("show", expanded); // keeps existing sprite swap working
46      navBtn.setAttribute(
47        "aria-label",
48        expanded ? "メニューを閉じる" : "メニュー"
49      );
50
51      // Visible label swap (Menu <-> Close)
52      if (navBtnText) {
53        navBtnText.textContent = expanded ? "" : "";
54      }
55    };
56
57    const openOverlay = () => setHidden(navOverlay, false);
58
59    const closeOverlayIfNothingOpen = () => {
60      const navOpen = body.classList.contains("is-nav-open");
61      const searchOpen = body.classList.contains("is-search-open");
62      if (!navOpen && !searchOpen) setHidden(navOverlay, true);
63    };
64
65    const closeNav = () => {
66      body.classList.remove("is-nav-open");
67      globalNav.classList.remove("open-menu");
68      setNavExpanded(false);
69      closeOverlayIfNothingOpen();
70
71      // Clean up any leftover inline styles from legacy toggles
72      const ul = globalNav.querySelector("ul");
73      if (ul && ul.getAttribute("style")) ul.removeAttribute("style");
74    };
75
76    const openNav = () => {
77      body.classList.add("is-nav-open");
78      globalNav.classList.add("open-menu");
79      setNavExpanded(true);
80      openOverlay();
81    };
82
83    const closeSearch = () => {
84      if (!searchPanel) return;
85      body.classList.remove("is-search-open");
86      if (searchBtn) searchBtn.setAttribute("aria-expanded", "false");
87      setHidden(searchPanel, true);
88      closeOverlayIfNothingOpen();
89    };
90
91    const openSearch = () => {
92      if (!searchPanel) return;
93      closeAll();
94      body.classList.add("is-search-open");
95      if (searchBtn) searchBtn.setAttribute("aria-expanded", "true");
96      setHidden(searchPanel, false);
97      openOverlay();
98
99      const input = searchPanel.querySelector(
100        'input[type="search"], input[type="text"]'
101      );
102      if (input) window.setTimeout(() => input.focus(), 0);
103    };
104
105    const closeAll = () => {
106      closeNav();
107      closeSearch();
108      setHidden(navOverlay, true);
109    };
110
111    // Menu button
112    navBtn.addEventListener("click", (e) => {
113      e.preventDefault();
114
115      // Mobile: drawer toggle
116      if (isMobile()) {
117        const willOpen = !body.classList.contains("is-nav-open");
118        closeAll();
119        if (willOpen) openNav();
120        return;
121      }
122
123      // Desktop/tablet: do nothing (nav is visible by default)
124    });
125
126    // Overlay click closes everything
127    navOverlay.addEventListener("click", closeAll);
128
129    // Close mobile nav when clicking a nav link
130    globalNav.addEventListener("click", (e) => {
131      if (!isMobile()) return;
132      const target = e.target;
133      if (!(target instanceof Element)) return;
134      const link = target.closest("a");
135      if (!link) return;
136      closeNav();
137    });
138
139    // Search button (optional)
140    if (searchBtn && searchPanel) {
141      searchBtn.addEventListener("click", (e) => {
142        e.preventDefault();
143        const willOpen = searchPanel.hasAttribute("hidden");
144        closeAll();
145        if (willOpen) openSearch();
146      });
147    }
148
149    // ESC closes
150    document.addEventListener("keydown", (e) => {
151      if (e.key === "Escape") closeAll();
152    });
153
154    // Breakpoint changes: reset state
155    // MediaQueryList change listener: addEventListener('change') is modern; addListener is legacy Safari.
156    if (typeof mqMobile.addEventListener === "function") {
157      mqMobile.addEventListener("change", closeAll);
158    } else if (typeof mqMobile.addListener === "function") {
159      mqMobile.addListener(closeAll);
160    }
161
162    // Front page: header band appears after scroll
163    const applyScrolledClass = () => {
164      if (!body.classList.contains("home")) return;
165      body.classList.toggle("is-scrolled", window.scrollY > 20);
166    };
167
168    window.addEventListener("scroll", applyScrolledClass, { passive: true });
169    applyScrolledClass();
170
171    // Initial state
172    setHidden(navOverlay, true);
173    setNavExpanded(false);
174  });
175})();
176
177(() => {
178  const map = {
179    "#purchase": "sw2-tab-1",
180    "#support": "sw2-tab-2"
181  };
182
183  const applyHash = () => {
184    const id = map[location.hash];
185    if (!id) return;
186    const radio = document.getElementById(id);
187    if (radio) radio.checked = true;
188  };
189
190  window.addEventListener("hashchange", applyHash, { passive: true });
191  applyHash();
192})();

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.