document.addEventListener('DOMContentLoaded', () => { initTheme(); initFooterYear(); initMobileMenu(); fetchPageData(); }); /* -------------------------------------------------------------------------- */ /* Theme / Dark Mode Management */ /* -------------------------------------------------------------------------- */ function initTheme() { const isDarkStored = localStorage.theme === 'dark'; const noThemeStored = !('theme' in localStorage); const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; if (isDarkStored || (noThemeStored && prefersDark)) { document.documentElement.classList.add('dark'); } else { document.documentElement.classList.remove('dark'); } updateThemeUI(); document.querySelectorAll('.theme-toggle').forEach(button => { button.addEventListener('click', toggleDarkMode); }); } function toggleDarkMode() { const isDark = document.documentElement.classList.toggle('dark'); localStorage.theme = isDark ? 'dark' : 'light'; updateThemeUI(); } function updateThemeUI() { const isDark = document.documentElement.classList.contains('dark'); document.querySelectorAll('.icon-sun').forEach(el => el.classList.toggle('hidden', !isDark)); document.querySelectorAll('.icon-moon').forEach(el => el.classList.toggle('hidden', isDark)); document.querySelectorAll('.theme-text').forEach(el => { el.textContent = isDark ? '☀️ Light Mode' : '🌙 Dark Mode'; }); } /* -------------------------------------------------------------------------- */ /* Footer Year Handler */ /* -------------------------------------------------------------------------- */ function initFooterYear() { const yearEl = document.getElementById('current-year'); if (yearEl) { yearEl.textContent = new Date().getFullYear(); } } /* -------------------------------------------------------------------------- */ /* Mobile Menu Toggle */ /* -------------------------------------------------------------------------- */ function initMobileMenu() { const menuToggleBtn = document.getElementById('menu-toggle'); const mobileMenu = document.getElementById('mobile-menu'); const iconHamburger = document.getElementById('icon-hamburger'); const iconClose = document.getElementById('icon-close'); if (!menuToggleBtn || !mobileMenu) return; menuToggleBtn.setAttribute('aria-expanded', 'false'); menuToggleBtn.addEventListener('click', () => { const isHidden = mobileMenu.classList.toggle('hidden'); menuToggleBtn.setAttribute('aria-expanded', String(!isHidden)); iconHamburger.classList.toggle('hidden', !isHidden); iconClose.classList.toggle('hidden', isHidden); }); } /* -------------------------------------------------------------------------- */ /* Async Data Fetching (Pages, Banner & Exam Schedule) */ /* -------------------------------------------------------------------------- */ async function fetchPageData() { const currentPath = window.location.pathname; // 1. Fetch Banner Data try { const bannerResp = await fetch(`/assets/banner.json?t=${Date.now()}`); if (bannerResp.ok) { const banner = await bannerResp.json(); if (banner && banner.title && banner.content) { document.getElementById('banner-title').textContent = banner.title; document.getElementById('banner-content').textContent = banner.content; document.getElementById('banner').classList.remove('hidden'); } } } catch (err) { console.error('Failed to load banner data:', err); } // 2. Fetch Navigation Links try { const navResp = await fetch('/assets/pages.json'); if (navResp.ok) { const pages = await navResp.json(); renderNavigation(pages, currentPath); } } catch (err) { console.error('Failed to load navigation data:', err); } // 3. Fetch Exam Schedule (only runs if the #exams-list element exists) const examsContainer = document.getElementById('exams-list'); if (examsContainer) { try { const examResp = await fetch(`/assets/exams.json?t=${Date.now()}`); if (examResp.ok) { const exams = await examResp.json(); renderExams(exams, examsContainer); } } catch (err) { console.error('Failed to load exam data:', err); } } // 4. Fetch Net Check-In Data (only runs on pages containing #callsigns-list or #weekly-list) const callsignsContainer = document.getElementById('callsigns-list'); const weeklyContainer = document.getElementById('weekly-list'); if (callsignsContainer || weeklyContainer) { try { const netResp = await fetch('/net/net.json'); if (netResp.ok) { const data = await netResp.json(); renderNetStats(data, callsignsContainer, weeklyContainer); } } catch (err) { console.error('Failed to load net statistics data:', err); } } } function renderNavigation(pages, currentPath) { const desktopContainer = document.getElementById('desktop-links'); const mobileContainer = document.getElementById('mobile-links'); if (!desktopContainer || !mobileContainer) return; desktopContainer.innerHTML = ''; mobileContainer.innerHTML = ''; pages.forEach(page => { const desktopLink = document.createElement('a'); desktopLink.href = page.path; desktopLink.textContent = page.name; if (currentPath === page.path) desktopLink.classList.add('active'); desktopContainer.appendChild(desktopLink); const mobileLink = document.createElement('a'); mobileLink.href = page.path; mobileLink.textContent = page.name; if (currentPath === page.path) mobileLink.classList.add('active'); mobileContainer.appendChild(mobileLink); }); } function renderExams(exams, container) { container.innerHTML = ''; if (Array.isArray(exams) && exams.length > 0) { exams.forEach(exam => { const li = document.createElement('li'); li.append(`${exam.date}, ${exam.time} – ${exam.location} – `); const link = document.createElement('a'); link.href = exam.register; link.className = 'link'; link.target = '_blank'; link.rel = 'noopener'; link.textContent = 'Register here'; li.appendChild(link); container.appendChild(li); }); } else { const li = document.createElement('li'); li.textContent = "We don't have any sessions scheduled right now, but if you're ready to test let us know and we'll set one up!"; container.appendChild(li); } }