Spaces:
Running
Running
Download shared/layout.js from dsuacademy/DSUdemy-Main: direct link, hf CLI and curl.
- Browser
- Download file 21.8 kB
-
https://huggingface.co/spaces/dsuacademy/DSUdemy-Main/resolve/main/shared/layout.js
- Command line
-
hf download hf://spaces/dsuacademy/DSUdemy-Main/shared/layout.js
-
curl -L -o layout.js https://huggingface.co/spaces/dsuacademy/DSUdemy-Main/resolve/main/shared/layout.js
21.8 kB
| /* ========================================================= | |
| layout.js: ส่วนหัวและส่วนท้ายชุดเดียวของทุกหน้าใน dsudemy.net | |
| สร้างจากข้อมูลใน data/site.js และ data/units.js แก้เมนูหรือส่วนท้ายที่ไฟล์นี้ที่เดียว | |
| วิธีใช้ในหน้าใหม่ (ดู templates/subpage.html) | |
| - <body data-root="../" data-page="guidance"> data-root คือเส้นทางกลับรากของเว็บ | |
| หน้าหลักใช้ data-root="" และ data-page="home" | |
| - <header class="site-header" id="siteHeader"></header> และ <footer class="footer" id="contact"></footer> | |
| - โหลดไฟล์ข้อมูลก่อน แล้วโหลดไฟล์นี้ แล้วจึงโหลดสคริปต์ของหน้า | |
| - หน้าหลักใส่ data-search="on" ที่ body เพื่อแสดงปุ่มค้นหา | |
| ========================================================= */ | |
| (function () { | |
| const D = window.DSU = window.DSU || {}; | |
| const body = document.body; | |
| const ROOT = body.dataset.root || ''; | |
| const PAGE = body.dataset.page || 'home'; | |
| const HOME = PAGE === 'home'; | |
| const SITE = D.site || { contact: {}, links: {} }; | |
| const UNITS = D.units || []; | |
| /* ---------- เครื่องมือกลาง ---------- */ | |
| const TH_MON = ['ม.ค.', 'ก.พ.', 'มี.ค.', 'เม.ย.', 'พ.ค.', 'มิ.ย.', 'ก.ค.', 'ส.ค.', 'ก.ย.', 'ต.ค.', 'พ.ย.', 'ธ.ค.']; | |
| const TH_MON_FULL = ['มกราคม', 'กุมภาพันธ์', 'มีนาคม', 'เมษายน', 'พฤษภาคม', 'มิถุนายน', 'กรกฎาคม', 'สิงหาคม', 'กันยายน', 'ตุลาคม', 'พฤศจิกายน', 'ธันวาคม']; | |
| const TH_DAY = ['อาทิตย์', 'จันทร์', 'อังคาร', 'พุธ', 'พฤหัสบดี', 'ศุกร์', 'เสาร์']; | |
| const esc = s => String(s == null ? '' : s).replace(/[&<>"']/g, c => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c])); | |
| const has = v => Array.isArray(v) ? v.length > 0 : (typeof v === 'string' ? v.trim() !== '' : !!v); | |
| const isExternal = h => /^(https?:|mailto:|tel:)/.test(h || ''); | |
| const isPending = h => !h || h.startsWith('['); | |
| /* Hugging Face Spaces (*.hf.space) ไม่เปิด index.html ให้เองเมื่อ URL ลงท้ายด้วย / จึงเติมให้เฉพาะบนโดเมนนั้น | |
| บน dsudemy.net ลิงก์คงเป็น URL สะอาดตาม docs/SITE_MAP.md */ | |
| const EXPLICIT_INDEX = /\.hf\.space$/.test(location.hostname); | |
| function withIndex(h) { | |
| if (!EXPLICIT_INDEX || !h || isExternal(h) || isPending(h)) return h; | |
| const i = h.indexOf('#'), path = i < 0 ? h : h.slice(0, i), hash = i < 0 ? '' : h.slice(i); | |
| if (path === '' || /^[a-z]+:|^\/\//i.test(path)) return h; | |
| if (path.endsWith('/')) return path + 'index.html' + hash; | |
| if (path === '.' || path === '..') return path + '/index.html' + hash; | |
| return h; | |
| } | |
| /* แปลงลิงก์ในไฟล์ข้อมูลให้ถูกระดับโฟลเดอร์ */ | |
| function href(h) { | |
| if (!h) return ''; | |
| if (isExternal(h)) return h; | |
| if (h.startsWith('#')) return HOME ? h : withIndex(ROOT + h); | |
| return withIndex(ROOT + h); | |
| } | |
| const store = { | |
| get(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }, | |
| set(k, v) { try { localStorage.setItem(k, v); } catch (e) {} } | |
| }; | |
| function todayISO() { | |
| try { return new Intl.DateTimeFormat('en-CA', { timeZone: 'Asia/Bangkok', year: 'numeric', month: '2-digit', day: '2-digit' }).format(new Date()); } | |
| catch (e) { const d = new Date(); return d.getFullYear() + '-' + String(d.getMonth() + 1).padStart(2, '0') + '-' + String(d.getDate()).padStart(2, '0'); } | |
| } | |
| const parts = iso => { const [y, m, d] = iso.split('-').map(Number); return { y, m, d }; }; | |
| const weekday = iso => { const p = parts(iso); return new Date(Date.UTC(p.y, p.m - 1, p.d)).getUTCDay(); }; | |
| const daysBetween = (a, b) => { const p = parts(a), q = parts(b); return Math.round((Date.UTC(q.y, q.m - 1, q.d) - Date.UTC(p.y, p.m - 1, p.d)) / 86400000); }; | |
| const fmtDate = (iso, year = true) => { const p = parts(iso); return p.d + ' ' + TH_MON[p.m - 1] + (year ? ' ' + (p.y + 543) : ''); }; | |
| function fmtRange(s, e, year = true) { | |
| if (!e || e === s) return fmtDate(s, year); | |
| const a = parts(s), b = parts(e); | |
| if (a.y === b.y && a.m === b.m) return a.d + ' ถึง ' + b.d + ' ' + TH_MON[b.m - 1] + (year ? ' ' + (b.y + 543) : ''); | |
| if (a.y === b.y) return a.d + ' ' + TH_MON[a.m - 1] + ' ถึง ' + fmtDate(e, year); | |
| return fmtDate(s) + ' ถึง ' + fmtDate(e); | |
| } | |
| const inRange = (iso, s, e) => iso >= s && iso <= (e || s); | |
| const ICONS = { | |
| calendar: '<rect x="3" y="5" width="18" height="16" rx="2"/><path d="M3 10h18M8 3v4M16 3v4"/>', | |
| book: '<path d="M4 5a2 2 0 0 1 2-2h12v16H6a2 2 0 0 0-2 2z"/><path d="M4 21V5M8 7h6"/>', | |
| file: '<path d="M14 3H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"/><path d="M14 3v6h6M8 14h8M8 18h5"/>', | |
| people: '<circle cx="8" cy="8" r="3"/><circle cx="17" cy="9" r="2.5"/><path d="M2.5 20a5.5 5.5 0 0 1 11 0M13.5 19a4 4 0 0 1 8 0"/>', | |
| table: '<rect x="3" y="4" width="18" height="16" rx="2"/><path d="M3 9h18M3 14.5h18M9 9v11"/>', | |
| compass: '<circle cx="12" cy="12" r="9"/><path d="M15.5 8.5l-2 5-5 2 2-5z"/>', | |
| layers: '<path d="M12 3l9 5-9 5-9-5z"/><path d="M3 13l9 5 9-5"/>', | |
| flask: '<path d="M9 3h6M10 3v6l-5 9a2 2 0 0 0 1.7 3h10.6a2 2 0 0 0 1.7-3l-5-9V3"/><path d="M7.5 15h9"/>', | |
| badge: '<circle cx="12" cy="9" r="6"/><path d="M8.5 13.5L7 21l5-3 5 3-1.5-7.5"/>', | |
| grid: '<rect x="3" y="3" width="7" height="7" rx="2"/><rect x="14" y="3" width="7" height="7" rx="2"/><rect x="3" y="14" width="7" height="7" rx="2"/><rect x="14" y="14" width="7" height="7" rx="2"/>', | |
| external: '<path d="M14 4h6v6M20 4l-9 9"/><path d="M18 14v5a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h5"/>', | |
| arrow: '<path d="M5 12h14M13 6l6 6-6 6"/>' | |
| }; | |
| const icon = (name, size) => '<svg viewBox="0 0 24 24" width="' + (size || 20) + '" height="' + (size || 20) + '" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' + (ICONS[name] || ICONS.file) + '</svg>'; | |
| /* ลิงก์ภายนอกเปิดแท็บใหม่ และบอกโปรแกรมอ่านหน้าจอ */ | |
| const extAttrs = h => isExternal(h) && !/^(mailto:|tel:)/.test(h) ? ' target="_blank" rel="noopener"' : ''; | |
| const extNote = h => isExternal(h) && !/^(mailto:|tel:)/.test(h) ? '<span class="sr-only"> (เปิดในแท็บใหม่)</span>' : ''; | |
| /* ปุ่มช่องทางติดต่อของหน่วยงาน (หน้าแนะแนวและหน้าศูนย์) แสดงเฉพาะช่องที่มีข้อมูล | |
| LINE ที่ไม่มีลิงก์แสดงเป็นป้ายธรรมดา เพราะกดไปที่ใดไม่ได้ */ | |
| function contactActions(c, facebook, fbLabel) { | |
| c = c || {}; | |
| const out = []; | |
| if (has(c.email)) out.push('<a class="link-btn" href="mailto:' + esc(c.email) + '">อีเมล ' + esc(c.email) + '</a>'); | |
| if (has(c.phone)) out.push('<a class="link-btn ghost" href="tel:' + esc(String(c.phone).replace(/[^\d+]/g, '')) + '">โทร ' + esc(c.phone) + '</a>'); | |
| if (has(c.line)) out.push(has(c.lineUrl) | |
| ? '<a class="link-btn ghost" href="' + esc(c.lineUrl) + '" target="_blank" rel="noopener">LINE ' + esc(c.line) + extNote(c.lineUrl) + '</a>' | |
| : '<span class="contact-chip">LINE ' + esc(c.line) + '</span>'); | |
| if (has(facebook)) out.push('<a class="link-btn ghost" href="' + esc(facebook) + '" target="_blank" rel="noopener">' + esc(fbLabel || 'Facebook') + icon('external', 16) + '<span class="sr-only"> (เปิดในแท็บใหม่)</span></a>'); | |
| return out.join(''); | |
| } | |
| /* รายการกำหนดการแยกตามเดือน รายการที่เริ่มก่อนวันนี้จัดไว้ในเดือนปัจจุบัน | |
| tagsFn(e) คืน HTML ของป้ายใต้ชื่อรายการ (ไม่บังคับ) คืน '' เมื่อไม่มีรายการ ผู้เรียกแสดงข้อความว่างเอง */ | |
| function monthList(events, tagsFn) { | |
| const today = todayISO(); | |
| let html = '', cur = ''; | |
| events.forEach(e => { | |
| const p = parts(e.start < today ? today : e.start), key = p.y + '-' + p.m; | |
| if (key !== cur) { if (cur) html += '</ul>'; html += '<h3 class="cal-month">' + TH_MON_FULL[p.m - 1] + ' ' + (p.y + 543) + '</h3><ul class="cal-list">'; cur = key; } | |
| const tags = tagsFn ? tagsFn(e) : ''; | |
| html += '<li><span class="cal-when tnum">' + esc(fmtRange(e.start, e.end)) + '</span><span>' + esc(e.title) + (tags ? '<span class="cal-meta">' + tags + '</span>' : '') + '</span></li>'; | |
| }); | |
| return cur ? html + '</ul>' : ''; | |
| } | |
| D.util = { TH_MON, TH_MON_FULL, TH_DAY, esc, has, href, withIndex, isExternal, isPending, store, todayISO, parts, weekday, daysBetween, fmtDate, fmtRange, inRange, icon, extAttrs, extNote, contactActions, monthList, ROOT, PAGE, HOME }; | |
| /* ---------- ส่วนหัว ---------- */ | |
| const NAV = [ | |
| { id: 'top', label: 'หน้าแรก', home: '#top', page: '' }, | |
| { id: 'quick', label: 'บริการวิชาการ', home: '#quick' }, | |
| { id: 'programs', label: 'หลักสูตรพิเศษ', home: '#programs', page: 'programs/' }, | |
| { id: 'units', label: 'ศูนย์และหน่วยงาน', home: '#units' }, | |
| { id: 'hub', label: 'DSUdemy Hub', home: '#hub' }, | |
| { id: 'about', label: 'เกี่ยวกับฝ่าย', home: '#about' }, | |
| { id: 'contact', label: 'ติดต่อ', home: '#contact', local: true } | |
| ]; | |
| const C = SITE.contact || {}; | |
| const HAS_CONTACT = ['email', 'phone', 'line'].some(k => has(C[k])); | |
| const navHref = n => HOME || n.local ? n.home : (n.page !== undefined ? (ROOT + n.page || './') : ROOT + n.home); | |
| /* หน้าของหน่วยงาน (ยกเว้นหลักสูตรที่มีเมนูของตัวเอง) ให้เมนู "ศูนย์และหน่วยงาน" เป็นเมนูปัจจุบัน */ | |
| const NAV_CUR = NAV.some(n => n.id === PAGE) ? PAGE : (UNITS.some(u => u.id === PAGE) ? 'units' : PAGE); | |
| const navLinks = NAV.filter(n => n.id !== 'contact' || HAS_CONTACT).map(n => | |
| '<a href="' + esc(navHref(n) || './') + '" data-spy="' + n.id + '"' + (!HOME && n.id === NAV_CUR ? ' aria-current="page"' : '') + '>' + n.label + '</a>').join(''); | |
| const header = document.getElementById('siteHeader'); | |
| if (header) { | |
| header.innerHTML = | |
| '<div class="wrap"><div class="bar">' + | |
| '<a class="brand" href="' + (HOME ? '#top' : (ROOT || './')) + '" aria-label="ฝ่ายวิชาการและนวัตกรรมการเรียนรู้ หน้าแรก">' + | |
| '<span class="logo" aria-hidden="true"><img src="' + ROOT + 'assets/dsu-logo.png" width="42" height="42" alt=""></span>' + | |
| '<span class="brand-text"><span class="brand-name"><span class="nw">ฝ่ายวิชาการและ</span><wbr><span class="nw">นวัตกรรมการเรียนรู้</span></span>' + | |
| '<span class="brand-sub">โรงเรียนสาธิต มหาวิทยาลัยศิลปากร (มัธยมศึกษา)</span></span></a>' + | |
| '<nav class="nav" aria-label="เมนูหลัก" id="desktopNav">' + navLinks + '</nav>' + | |
| '<div class="tools">' + | |
| (body.dataset.search === 'on' ? '<button class="icon-btn" type="button" id="openSearch" aria-label="ค้นหาบริการ หน่วยงาน หลักสูตร และกำหนดการ" title="ค้นหา (Ctrl+K)"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="M20 20l-4-4"/></svg></button>' : '') + | |
| '<button class="icon-btn" type="button" id="themeBtn" aria-label="สลับโหมดสว่างและมืด"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/></svg></button>' + | |
| '<button class="icon-btn menu-btn" type="button" id="menuBtn" aria-label="เปิดเมนู" aria-expanded="false" aria-controls="mobileNav"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M4 7h16M4 12h16M4 17h10"/></svg></button>' + | |
| '</div></div>' + | |
| '<nav class="mobile-nav" id="mobileNav" aria-label="เมนูหลัก (มือถือ)" hidden>' + navLinks + | |
| /* จอแคบมากย้ายปุ่มสลับธีมมาไว้ในเมนู เพื่อให้ชื่อฝ่ายในแคปซูลไม่ถูกปุ่มบัง */ | |
| '<button class="mobile-theme" type="button" id="themeBtnMobile"></button></nav></div>'; | |
| } | |
| /* ---------- ส่วนท้าย ---------- */ | |
| const footer = document.getElementById('contact'); | |
| if (footer) { | |
| const L = SITE.links || {}; | |
| const rows = []; | |
| if (has(C.email)) rows.push('<span class="copy-row">อีเมล <span id="siteEmail">' + esc(C.email) + '</span> <button class="copy-btn" type="button" id="copyEmail">คัดลอก</button></span>'); | |
| if (has(C.phone)) rows.push('<span>' + esc(C.phone) + '</span>'); | |
| if (has(C.line)) rows.push('<span>LINE ' + (has(C.lineUrl) ? '<a href="' + esc(C.lineUrl) + '" target="_blank" rel="noopener">' + esc(C.line) + '</a>' : esc(C.line)) + '</span>'); | |
| const links = [['school', 'เว็บไซต์โรงเรียน'], ['university', 'มหาวิทยาลัยศิลปากร'], ['studio', 'DSUdemy Studio']] | |
| .filter(([k]) => has(L[k])).map(([k, label]) => '<a href="' + esc(L[k]) + '" target="_blank" rel="noopener">' + label + '<span class="sr-only"> (เปิดในแท็บใหม่)</span></a>' + (k === 'studio' ? '<span class="footer-note">ระบบสร้างแอปสำหรับทีมพัฒนา ต้องเข้าสู่ระบบ</span>' : '')); | |
| const channels = UNITS.filter(u => has(u.facebook)).map(u => '<a href="' + esc(u.facebook) + '" target="_blank" rel="noopener">' + esc(u.display) + ' (Facebook)<span class="sr-only"> (เปิดในแท็บใหม่)</span></a>'); | |
| footer.innerHTML = | |
| '<div class="wrap footer-grid">' + | |
| '<div class="footer-brand"><span class="footer-name">ฝ่ายวิชาการและนวัตกรรมการเรียนรู้</span><span>โรงเรียนสาธิต มหาวิทยาลัยศิลปากร (มัธยมศึกษา)</span>' + (has(C.address) ? '<span>' + esc(C.address) + '</span>' : '') + '</div>' + | |
| (rows.length ? '<div><h2>ติดต่อฝ่าย</h2>' + rows.join('') + '</div>' : '') + | |
| (channels.length ? '<div><h2>ช่องทางของศูนย์</h2>' + channels.join('') + '</div>' : '') + | |
| '<div><h2>ลิงก์ที่เกี่ยวข้อง</h2>' + (HOME ? '' : '<a href="' + (ROOT || './') + '">หน้าหลัก dsudemy.net</a>') + links.join('') + '</div>' + | |
| '</div><div class="wrap footer-bottom">พ.ศ. 2569 ฝ่ายวิชาการและนวัตกรรมการเรียนรู้ โรงเรียนสาธิต มหาวิทยาลัยศิลปากร (มัธยมศึกษา)</div>'; | |
| const copy = document.getElementById('copyEmail'); | |
| if (copy) copy.addEventListener('click', async () => { | |
| try { await navigator.clipboard.writeText(C.email); toast('คัดลอกอีเมลแล้ว'); } | |
| catch (e) { const r = document.createRange(); r.selectNodeContents(document.getElementById('siteEmail')); const s = getSelection(); s.removeAllRanges(); s.addRange(r); toast('เลือกข้อความอีเมลไว้แล้ว กดคัดลอกจากแป้นพิมพ์ได้'); } | |
| }); | |
| } | |
| /* ลิงก์ที่เขียนไว้ใน HTML ตรง ๆ และในส่วนหัวส่วนท้าย (ลิงก์ที่หน้าสร้างภายหลังผ่าน href() แล้ว) */ | |
| if (EXPLICIT_INDEX) document.querySelectorAll('a[href]').forEach(a => { | |
| const h = a.getAttribute('href'), n = withIndex(h); | |
| if (n !== h) a.setAttribute('href', n); | |
| }); | |
| /* ---------- ข้อความแจ้งสั้น และลิงก์ที่ยังไม่มีปลายทาง ---------- */ | |
| let toastEl = document.getElementById('toast'); | |
| if (!toastEl) { toastEl = document.createElement('div'); toastEl.className = 'toast'; toastEl.id = 'toast'; toastEl.setAttribute('role', 'status'); toastEl.hidden = true; body.appendChild(toastEl); } | |
| let toastTimer; | |
| function toast(msg) { toastEl.textContent = msg; toastEl.hidden = false; clearTimeout(toastTimer); toastTimer = setTimeout(() => { toastEl.hidden = true; }, 2800); } | |
| D.util.toast = toast; | |
| document.addEventListener('click', e => { | |
| const a = e.target.closest('a[href]'); | |
| if (a && isPending(a.getAttribute('href'))) { e.preventDefault(); toast('ลิงก์ "' + (a.dataset.label || a.textContent.trim()).slice(0, 40) + '" ยังรอเชื่อมต่อ'); } | |
| }); | |
| /* ---------- ธีม ---------- */ | |
| const root = document.documentElement; | |
| const saved = store.get('theme'); | |
| if (saved === 'dark' || saved === 'light') root.dataset.theme = saved; | |
| const themeBtn = document.getElementById('themeBtn'), themeBtnMobile = document.getElementById('themeBtnMobile'); | |
| const currentTheme = () => root.dataset.theme || (matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'); | |
| const syncTheme = () => { | |
| const label = currentTheme() === 'dark' ? 'เปลี่ยนเป็นโหมดสว่าง' : 'เปลี่ยนเป็นโหมดมืด'; | |
| if (themeBtn) themeBtn.setAttribute('aria-label', label); | |
| if (themeBtnMobile) themeBtnMobile.textContent = label; | |
| }; | |
| const toggleTheme = () => { const n = currentTheme() === 'dark' ? 'light' : 'dark'; root.dataset.theme = n; store.set('theme', n); syncTheme(); }; | |
| [themeBtn, themeBtnMobile].forEach(b => b && b.addEventListener('click', toggleTheme)); | |
| syncTheme(); | |
| /* ---------- เมนูมือถือ ---------- */ | |
| const menuBtn = document.getElementById('menuBtn'), mobileNav = document.getElementById('mobileNav'); | |
| function setMenu(open) { mobileNav.hidden = !open; menuBtn.setAttribute('aria-expanded', open); menuBtn.setAttribute('aria-label', open ? 'ปิดเมนู' : 'เปิดเมนู'); } | |
| if (menuBtn && mobileNav) { | |
| menuBtn.addEventListener('click', () => setMenu(mobileNav.hidden)); | |
| mobileNav.addEventListener('click', e => { if (e.target.closest('a')) setMenu(false); }); | |
| document.addEventListener('keydown', e => { if (e.key === 'Escape' && !mobileNav.hidden) { setMenu(false); menuBtn.focus(); } }); | |
| /* ปิดเมื่อแตะนอกแผงเมนู หรือเมื่อขยายจอจนเมนูเต็มแสดงแล้ว */ | |
| document.addEventListener('click', e => { if (!mobileNav.hidden && !e.target.closest('#mobileNav, #menuBtn')) setMenu(false); }); | |
| matchMedia('(min-width: 1201px)').addEventListener('change', e => { if (e.matches) setMenu(false); }); | |
| } | |
| /* ---------- ปุ่มกลับขึ้นด้านบน ---------- */ | |
| const toTop = document.createElement('button'); | |
| toTop.className = 'to-top'; toTop.type = 'button'; toTop.hidden = true; toTop.setAttribute('aria-label', 'กลับขึ้นด้านบน'); | |
| toTop.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 19V5M5 12l7-7 7 7"/></svg>'; | |
| body.appendChild(toTop); | |
| addEventListener('scroll', () => { toTop.hidden = scrollY < 700; }, { passive: true }); | |
| toTop.addEventListener('click', () => { scrollTo({ top: 0 }); const b = document.querySelector('.brand'); if (b) b.focus(); }); | |
| /* ซ่อนส่วนในหน้าพร้อมเมนูของส่วนนั้น */ | |
| D.util.hideSection = id => { | |
| const el = document.getElementById(id); if (el) el.hidden = true; | |
| document.querySelectorAll('[data-spy="' + id + '"]').forEach(a => { a.hidden = true; }); | |
| }; | |
| })(); |