export function validateAgeLive(inputEl, minAge = 5, maxAge = 18) { const dob = inputEl.value.trim(); if (!dob) return false; const birthDate = parseDateOnly(dob); if (isNaN(birthDate.getTime())) { showFeedback(inputEl, false, "Invalid date format (Use YYYY-MM-DD)"); return false; } const registrationAgeDeadline = window.appConfig?.registrationAgeDeadline; const schoolYearAgeDeadline = window.appConfig?.schoolYearAgeDeadline || registrationAgeDeadline; const minimumAgeDeadline = parseDateOnlyOrToday(registrationAgeDeadline); const ageDeadline = parseDateOnlyOrToday(schoolYearAgeDeadline); const minBirthDate = new Date(Date.UTC(ageDeadline.getUTCFullYear() - maxAge - 1, ageDeadline.getUTCMonth(), ageDeadline.getUTCDate() + 1)); // oldest const maxBirthDate = new Date(Date.UTC(minimumAgeDeadline.getUTCFullYear() - minAge, minimumAgeDeadline.getUTCMonth(), minimumAgeDeadline.getUTCDate())); // youngest const isValidAge = birthDate >= minBirthDate && birthDate <= maxBirthDate; // Format date as MM-DD-YYYY for error message (use original deadline, not adjusted) const formatDateMMDDYYYY = (dateStr) => { if (!dateStr) return dateStr; // Parse as UTC to avoid timezone issues that could shift the date const date = new Date(dateStr + 'T00:00:00.000Z'); if (isNaN(date.getTime())) return dateStr; const month = String(date.getUTCMonth() + 1).padStart(2, '0'); const day = String(date.getUTCDate()).padStart(2, '0'); const year = date.getUTCFullYear(); return `${month}-${day}-${year}`; }; const errorMessage = isValidAge ? "" : `Must be at least ${minAge} years old by ${formatDateMMDDYYYY(registrationAgeDeadline)} and no older than ${maxAge} by ${formatDateMMDDYYYY(schoolYearAgeDeadline)}.`; showFeedback(inputEl, isValidAge, errorMessage); return isValidAge; } function normalize(date) { return new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate())); } function parseDateOnly(value) { const match = String(value || '').match(/^(\d{4})-(\d{2})-(\d{2})$/); if (!match) return new Date(NaN); const year = Number(match[1]); const monthIndex = Number(match[2]) - 1; const day = Number(match[3]); const date = new Date(Date.UTC(year, monthIndex, day)); if ( date.getUTCFullYear() !== year || date.getUTCMonth() !== monthIndex || date.getUTCDate() !== day ) { return new Date(NaN); } return date; } function parseDateOnlyOrToday(value) { const date = parseDateOnly(value); return isNaN(date.getTime()) ? normalize(new Date()) : date; } // Helper function (unchanged) function showFeedback(inputEl, isValid, message) { inputEl.classList.toggle("is-invalid", !isValid); const feedbackEl = inputEl.nextElementSibling || document.createElement("div"); feedbackEl.textContent = message; feedbackEl.className = `invalid-feedback ${isValid ? "d-none" : ""}`; inputEl.parentNode.appendChild(feedbackEl); } // Set max birthdate (2025 - minAge) window.addEventListener("DOMContentLoaded", () => { const minAge = 5; // Must match validateAgeLive's default const deadline = parseDateOnlyOrToday(window.appConfig?.schoolYearAgeDeadline); const registrationAgeDeadline = parseDateOnly(window.appConfig?.registrationAgeDeadline); const maxDeadline = isNaN(registrationAgeDeadline.getTime()) ? deadline : registrationAgeDeadline; const maxBirthYear = maxDeadline.getUTCFullYear() - minAge; const maxDateStr = `${maxBirthYear}-12-31`; // Latest allowed birthdate for age-5 registration grace document.querySelectorAll(".dob-input").forEach((input) => { input.setAttribute("max", maxDateStr); input.setAttribute( "aria-label", `Born before ${maxBirthYear + 1} (${minAge}+ years by ${maxDeadline.getUTCFullYear()})` ); }); });