'use strict'; (() => { const monthImage = document.getElementById('month-image'); const monthPeriod = document.getElementById('month-period'); const decadeDay = document.getElementById('decade-day'); const republicanDate = document.getElementById('republican-date'); const republicanYear = document.getElementById('republican-year'); const sextileBadge = document.getElementById('sextile-badge'); const gregorianEquivalentValue = document.getElementById('gregorian-equivalent-value'); const monthName = document.getElementById('month-name'); const monthSeasonSuffix = document.getElementById('month-season-suffix'); const monthEtymology = document.getElementById('month-etymology'); const monthDescription = document.getElementById('month-description'); const dateInput = document.getElementById('date-input'); const convertForm = document.getElementById('convert-form'); const todayButton = document.getElementById('today-button'); const formError = document.getElementById('form-error'); const viewHome = document.getElementById('view-home'); const viewAbout = document.getElementById('view-about'); const aboutTitle = document.getElementById('about-title'); const aboutContent = document.getElementById('about-content'); const menuToggle = document.getElementById('menu-toggle'); const mainNav = document.getElementById('main-nav'); const navBackdrop = document.getElementById('nav-backdrop'); const langSwitch = document.getElementById('lang-switch'); const ABOUT_ROUTES = { 'about/history': 'history', 'about/leap-years': 'leapYears', 'about/complementary-days': 'complementaryDays', }; let lastRepublicanResult = null; function pad(value) { return String(value).padStart(2, '0'); } function toIsoDate({ year, month, day }) { return `${String(year).padStart(4, '0')}-${pad(month)}-${pad(day)}`; } function formatGregorian({ year, month, day }, weekdayKey) { const weekdayLabel = I18N.t(`weekdays.${weekdayKey}`, ''); return `${weekdayLabel} ${pad(day)}/${pad(month)}/${year}`.trim(); } function closeMenu() { mainNav.classList.remove('open'); mainNav.setAttribute('aria-hidden', 'true'); navBackdrop.hidden = true; menuToggle.setAttribute('aria-expanded', 'false'); } function openMenu() { mainNav.classList.add('open'); mainNav.setAttribute('aria-hidden', 'false'); navBackdrop.hidden = false; menuToggle.setAttribute('aria-expanded', 'true'); } function renderRepublicanResult(result) { lastRepublicanResult = result; const months = I18N.months.months || {}; const decadeDays = I18N.months.decadeDays || {}; const complementary = I18N.months.complementaryDays || { days: {} }; sextileBadge.hidden = !result.isSextile; gregorianEquivalentValue.textContent = formatGregorian(result.gregorianDate, result.gregorianWeekdayKey); republicanYear.textContent = `${I18N.t('home.yearLabel')} ${result.republicanYearRoman} (${result.republicanYear})`; if (result.isComplementaryDay) { const day = complementary.days[result.complementaryDayKey] || {}; decadeDay.textContent = complementary.title || ''; republicanDate.textContent = day.name || ''; monthImage.src = 'assets/months/complementary.svg'; monthImage.alt = day.name || ''; monthPeriod.textContent = ''; monthName.textContent = day.name || ''; monthSeasonSuffix.textContent = ''; monthEtymology.textContent = ''; monthDescription.textContent = day.description || ''; } else { const month = months[result.monthKey] || {}; const seasonSuffix = (I18N.months.seasonSuffix || {})[month.season] || ''; decadeDay.textContent = decadeDays[result.decadeDayKey] || ''; republicanDate.textContent = `${result.dayOfMonth} ${month.name || ''}`.trim(); monthImage.src = `assets/months/${result.monthKey}.svg`; monthImage.alt = month.name || ''; monthPeriod.textContent = month.period || ''; monthName.textContent = month.name || ''; monthSeasonSuffix.textContent = seasonSuffix; monthEtymology.textContent = month.etymology || ''; monthDescription.textContent = month.description || ''; } } function showFormError(messageKey) { formError.textContent = I18N.t(messageKey); formError.hidden = false; } function hideFormError() { formError.hidden = true; formError.textContent = ''; } async function fetchAndRender(url) { hideFormError(); const response = await fetch(url); const payload = await response.json(); if (!payload.ok) { if (payload.error === 'BEFORE_EPOCH') { showFormError('form.errorBeforeEpoch'); } else if (payload.error === 'INVALID_DATE') { showFormError('form.errorInvalid'); } else { showFormError('form.errorGeneric'); } return; } renderRepublicanResult(payload.republicanDate); dateInput.value = toIsoDate(payload.republicanDate.gregorianDate); } async function loadToday() { try { await fetchAndRender('/api/today'); } catch (error) { showFormError('form.errorGeneric'); } } async function handleConvertSubmit(event) { event.preventDefault(); const value = dateInput.value; // yyyy-mm-dd from if (!value) return; const [year, month, day] = value.split('-').map((part) => Number.parseInt(part, 10)); try { await fetchAndRender(`/api/convert?year=${year}&month=${month}&day=${day}`); } catch (error) { showFormError('form.errorGeneric'); } } function renderAboutView(sectionKey) { const section = I18N.about[sectionKey]; if (!section) return; aboutTitle.textContent = section.title; aboutContent.innerHTML = ''; (section.sections || []).forEach((block) => { const heading = document.createElement('h2'); heading.textContent = block.heading; aboutContent.appendChild(heading); (block.paragraphs || []).forEach((paragraph) => { const p = document.createElement('p'); p.textContent = paragraph; aboutContent.appendChild(p); }); }); } function route() { const hash = window.location.hash.replace(/^#\/?/, ''); closeMenu(); document.querySelectorAll('.nav-link').forEach((link) => link.classList.remove('active')); if (hash in ABOUT_ROUTES) { viewHome.hidden = true; viewAbout.hidden = false; renderAboutView(ABOUT_ROUTES[hash]); const activeLink = document.querySelector(`.nav-link[href="#/${hash}"]`); if (activeLink) activeLink.classList.add('active'); } else { viewHome.hidden = false; viewAbout.hidden = true; const activeLink = document.querySelector('.nav-link[href="#/"]'); if (activeLink) activeLink.classList.add('active'); } } function bindEvents() { menuToggle.addEventListener('click', () => { if (mainNav.classList.contains('open')) { closeMenu(); } else { openMenu(); } }); navBackdrop.addEventListener('click', closeMenu); window.addEventListener('hashchange', route); convertForm.addEventListener('submit', handleConvertSubmit); todayButton.addEventListener('click', () => { loadToday(); }); langSwitch.addEventListener('click', async () => { const nextLang = I18N.currentLang === 'fr' ? 'en' : 'fr'; await I18N.setLang(nextLang); }); I18N.onChange((lang) => { langSwitch.textContent = lang.toUpperCase(); if (lastRepublicanResult) { renderRepublicanResult(lastRepublicanResult); } if (!viewAbout.hidden) { const hash = window.location.hash.replace(/^#\/?/, ''); if (hash in ABOUT_ROUTES) renderAboutView(ABOUT_ROUTES[hash]); } }); } async function init() { bindEvents(); const initialLang = I18N.detectInitialLang(); await I18N.load(initialLang); langSwitch.textContent = initialLang.toUpperCase(); route(); await loadToday(); } init(); })();