PageSourceSearch

https://lg-rehabus.kbus.com.tw/js/layout.js

js kbus.com.tw collected 2026-10-01 15:21:59 UTC 2,850 bytes, 99 lines download raw bytes

1// public/js/layout.js
2
3async function loadFragment(targetId, url) {
4  const container = document.getElementById(targetId);
5  if (!container) return;
6
7  try {
8    const res = await fetch(url, { cache: 'no-store' });
9    if (!res.ok) return;
10    const html = await res.text();
11    container.innerHTML = html;
12
13    if (window.i18n) {
14      await window.i18n.ready;
15      window.i18n.applyTranslations(container);
16    }
17
18    if (targetId === 'app-header') {
19      setupHeaderNavActiveState();
20      setupHeaderNavToggle();
21      setupLangSwitch();
22      setupAdminNavVisibility();
23    }
24  } catch (err) {
25    console.error('loadFragment error', targetId, url, err);
26  }
27}
28
29async function setupAdminNavVisibility() {
30  const items = document.querySelectorAll('[data-admin-nav]');
31  if (!items.length) return;
32
33  try {
34    const res = await fetch('/api/public/admin-nav', { cache: 'no-store' });
35    if (!res.ok) return;
36    const data = await res.json();
37    if (data.ok && !data.showAdminLogin) {
38      items.forEach((el) => el.remove());
39    }
40  } catch (err) {
41    console.error('setupAdminNavVisibility error', err);
42  }
43}
44
45function setupLangSwitch() {
46  const buttons = document.querySelectorAll('.lang-btn[data-lang]');
47  if (!buttons.length) return;
48
49  const current = window.i18n ? window.i18n.lang : 'zh-Hant';
50  buttons.forEach((btn) => {
51    btn.classList.toggle('is-active', btn.dataset.lang === current);
52    btn.addEventListener('click', () => {
53      if (btn.dataset.lang === current) return;
54      window.i18n?.setLang(btn.dataset.lang);
55    });
56  });
57}
58
59function setupHeaderNavToggle() {
60  const toggle = document.querySelector('[data-nav-toggle]');
61  const nav = document.querySelector('.site-nav');
62  if (!toggle || !nav) return;
63
64  toggle.addEventListener('click', () => {
65    const expanded = toggle.getAttribute('aria-expanded') === 'true';
66    const next = !expanded;
67    toggle.setAttribute('aria-expanded', String(next));
68    if (next) {
69      nav.classList.add('is-open');
70    } else {
71      nav.classList.remove('is-open');
72    }
73  });
74}
75
76function setupHeaderNavActiveState() {
77  const path = window.location.pathname || '/';
78  const links = document.querySelectorAll('.site-nav a[href]');
79  links.forEach((link) => {
80    const href = link.getAttribute('href');
81    if (!href) return;
82
83    // match exact, or prefix match for sub-paths (with a '/' boundary so
84    // e.g. '/booking.html' doesn't false-match '/booking-history.html')
85    if (
86      href === path ||
87      (href !== '/' &&
88        path.startsWith(href) &&
89        (path.length === href.length || path[href.length] === '/'))
90    ) {
91      link.classList.add('is-active');
92    }
93  });
94}
95
96document.addEventListener('DOMContentLoaded', () => {
97  loadFragment('app-header', '/partials/app-header.html');
98  loadFragment('app-footer', '/partials/app-footer.html');
99});

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.