UNPKG

@uplink-protocol/calendar-controller

Version:

Flexible calendar API supporting both calendar and date-picker integrations for any JavaScript framework or library

961 lines (799 loc) 33.9 kB
/** * Comprehensive Date Picker Demo - Enhanced Documentation Version * Showcases all features of the Calendar Controller with modern UI */ // Global variables let calendar; let currentView = 'day'; let eventLogEntries = []; // Initialize the demo when the page loads document.addEventListener('DOMContentLoaded', initializeDemo); function initializeDemo() { try { // Create calendar controller with comprehensive configuration calendar = window.CalendarController({ initialSelectedDate: new Date(), firstDayOfWeek: 0, // Sunday locale: 'en-US', isRangeSelection: false, dateFormat: 'MM/DD/YYYY', yearRangeSize: 12 }); console.log('Initializing Calendar Controller with options:', calendar.options); // Verify calendar was created successfully if (!calendar || !calendar.bindings) { throw new Error('Failed to create calendar controller or bindings not available'); } console.log('Calendar created successfully:', calendar); console.log('Available methods:', Object.keys(calendar.methods).length); // Set up event listeners for all calendar events setupEventListeners(); // Test all methods are accessible testAllMethods(); // Initialize the display updateCalendarDisplay(); updateInfoDisplays(); // Expose calendar instance globally for floating calendar integration window.sharedCalendarController = calendar; // Make demo functions globally accessible window.calendarDemo = { // Basic Selection selectToday, selectSpecificDate, clearSelection, selectRandomDate, // Range Selection enableRangeSelection, disableRangeSelection, selectThisWeek, selectNextMonth, // Navigation goToPreviousMonth, goToNextMonth, goToPreviousYear, goToNextYear, goToToday, goToSpecificDate, jumpToFuture, testKeyboardNav, // Constraints setMinMaxDates, disableWeekends, disableHolidays, clearConstraints, // Internationalization changeLocale, changeFirstDayOfWeek, showDateFormats, // Accessibility testScreenReaderLabels, testKeyboardNavigation, testFocusManagement, generateAccessibilityReport, // Advanced Features showWeekNumbers, generateMonthView, generateYearView, calculateDateDifferences, // Events and Bindings setupEventListeners, triggerTestEvents, showEventLog, clearEventLog, // API Reference runMethodValidation, showMethodCategories, benchmarkMethods }; logEvent('Calendar Initialized', { initialDate: calendar.bindings.currentDate?.current, locale: 'en-US', methodCount: Object.keys(calendar.methods).length }); // Update method count displays updateMethodCountDisplays(); } catch (error) { console.error('Error initializing demo:', error); showError('Failed to initialize calendar demo: ' + error.message); } } // Test all methods are accessible function testAllMethods() { console.log('Testing all calendar methods...'); const methodsToTest = [ 'selectDate', 'selectDateRange', 'clearSelection', 'isSelected', 'goToNextMonth', 'goToPreviousMonth', 'goToNextYear', 'goToPreviousYear', 'goToDate', 'goToMonth', 'goToYear', 'goToToday', 'setLocale', 'getLocale', 'getMonthNames', 'getWeekdayNames', 'setFirstDayOfWeek', 'formatDate', 'getFormattedDate', 'setMinDate', 'setMaxDate', 'addDisabledDate', 'removeDisabledDate', 'getDisabledDates', 'clearDisabledDates', 'focusDate', 'moveFocusRight', 'moveFocusLeft', 'moveFocusUp', 'moveFocusDown', 'selectFocusedDate', 'getAccessibleDateLabel', 'getDateStateDescription', 'isToday', 'generateMonthView', 'generateCalendarDays', 'generateCalendarMonths', 'generateCalendarYears', 'getWeekNumber', 'getCurrentYearRange', 'getDateFormatOptions', 'setRangeSelectionMode' ]; const results = []; methodsToTest.forEach(methodName => { try { if (!calendar.methods[methodName]) { results.push({ method: methodName, status: 'MISSING' }); return; } results.push({ method: methodName, status: 'SUCCESS' }); } catch (error) { results.push({ method: methodName, status: 'ERROR', error: error.message }); } }); const successful = results.filter(r => r.status === 'SUCCESS').length; const total = results.length; console.log(`Method Test Results: ${successful}/${total} methods available`); updateMethodValidationDisplay(successful, total); } // Enhanced event listeners setup function setupEventListeners() { try { // Subscribe to all available bindings if (calendar.bindings.selectedDate?.subscribe) { calendar.bindings.selectedDate.subscribe(date => { updateSelectedDateDisplay(date); logEvent('Date Selected', { date: date?.toISOString() }); }); } if (calendar.bindings.selectedRange?.subscribe) { calendar.bindings.selectedRange.subscribe(range => { updateRangeDisplay(range); logEvent('Range Selected', { start: range?.start?.toISOString(), end: range?.end?.toISOString() }); }); } if (calendar.bindings.currentDate?.subscribe) { calendar.bindings.currentDate.subscribe(date => { updateCurrentDateDisplay(date); logEvent('Navigation', { date: date?.toISOString() }); }); } if (calendar.bindings.calendarDays?.subscribe) { calendar.bindings.calendarDays.subscribe(days => { renderCalendarDays(days); logEvent('Calendar Updated', { dayCount: days?.length }); }); } if (calendar.bindings.weekdays?.subscribe) { calendar.bindings.weekdays.subscribe(weekdays => { renderWeekdayHeaders(weekdays); }); } if (calendar.bindings.isRangeSelection?.subscribe) { calendar.bindings.isRangeSelection.subscribe(isRange => { updateSelectionModeDisplay(isRange); logEvent('Mode Changed', { isRangeSelection: isRange }); }); } console.log('Event listeners set up successfully'); } catch (error) { console.error('Error setting up event listeners:', error); logEvent('Error', { message: 'Failed to set up event listeners: ' + error.message }); } } // ============================================================================= // BASIC DATE SELECTION FUNCTIONS // ============================================================================= function selectToday() { const today = new Date(); calendar.methods.selectDate(today.getFullYear(), today.getMonth(), today.getDate()); logEvent('Select Today', { date: today.toISOString() }); } function selectSpecificDate() { calendar.methods.selectDate(2025, 11, 25); // Christmas 2025 logEvent('Select Specific Date', { date: '2025-12-25' }); } function selectRandomDate() { const year = 2025; const month = Math.floor(Math.random() * 12); const day = Math.floor(Math.random() * 28) + 1; // Safe day range calendar.methods.selectDate(year, month, day); logEvent('Select Random Date', { year, month, day }); } function clearSelection() { calendar.methods.clearSelection(); logEvent('Clear Selection'); } // ============================================================================= // RANGE SELECTION FUNCTIONS // ============================================================================= function enableRangeSelection() { calendar.methods.setRangeSelectionMode(true); logEvent('Enable Range Selection'); } function disableRangeSelection() { calendar.methods.setRangeSelectionMode(false); logEvent('Disable Range Selection'); } function selectThisWeek() { enableRangeSelection(); const today = new Date(); const startOfWeek = new Date(today); startOfWeek.setDate(today.getDate() - today.getDay()); const endOfWeek = new Date(startOfWeek); endOfWeek.setDate(startOfWeek.getDate() + 6); // Use selectDateRange if available, otherwise use individual selections if (calendar.methods.selectDateRange) { calendar.methods.selectDateRange(startOfWeek, endOfWeek); } else { calendar.methods.selectDate(startOfWeek.getFullYear(), startOfWeek.getMonth(), startOfWeek.getDate()); calendar.methods.selectDate(endOfWeek.getFullYear(), endOfWeek.getMonth(), endOfWeek.getDate()); } logEvent('Select This Week', { start: startOfWeek.toISOString(), end: endOfWeek.toISOString() }); } function selectNextMonth() { enableRangeSelection(); const nextMonth = new Date(); nextMonth.setMonth(nextMonth.getMonth() + 1); const startOfMonth = new Date(nextMonth.getFullYear(), nextMonth.getMonth(), 1); const endOfMonth = new Date(nextMonth.getFullYear(), nextMonth.getMonth() + 1, 0); if (calendar.methods.selectDateRange) { calendar.methods.selectDateRange(startOfMonth, endOfMonth); } else { calendar.methods.selectDate(startOfMonth.getFullYear(), startOfMonth.getMonth(), startOfMonth.getDate()); calendar.methods.selectDate(endOfMonth.getFullYear(), endOfMonth.getMonth(), endOfMonth.getDate()); } logEvent('Select Next Month', { start: startOfMonth.toISOString(), end: endOfMonth.toISOString() }); } // ============================================================================= // NAVIGATION FUNCTIONS // ============================================================================= function goToPreviousMonth() { calendar.methods.goToPreviousMonth(); logEvent('Navigate Previous Month'); } function goToNextMonth() { calendar.methods.goToNextMonth(); logEvent('Navigate Next Month'); } function goToPreviousYear() { calendar.methods.goToPreviousYear(); logEvent('Navigate Previous Year'); } function goToNextYear() { calendar.methods.goToNextYear(); logEvent('Navigate Next Year'); } function goToToday() { calendar.methods.goToToday(); logEvent('Navigate to Today'); } function goToSpecificDate() { calendar.methods.goToDate(new Date(2025, 11, 25)); // Christmas 2025 logEvent('Navigate to Specific Date', { date: '2025-12-25' }); } function jumpToFuture() { calendar.methods.goToYear(2030); logEvent('Jump to Future', { year: 2030 }); } function testKeyboardNav(direction) { // Focus a date first if none is focused if (!calendar.bindings.focusedDate?.current) { calendar.methods.focusDate(new Date()); } switch (direction) { case 'ArrowUp': calendar.methods.moveFocusUp(); break; case 'ArrowDown': calendar.methods.moveFocusDown(); break; case 'ArrowLeft': calendar.methods.moveFocusLeft(); break; case 'ArrowRight': calendar.methods.moveFocusRight(); break; } logEvent('Keyboard Navigation', { direction }); } // ============================================================================= // DATE CONSTRAINTS FUNCTIONS // ============================================================================= function setMinMaxDates() { const today = new Date(); const minDate = new Date(today.getFullYear(), today.getMonth() - 1, 1); const maxDate = new Date(today.getFullYear() + 1, today.getMonth(), 0); calendar.methods.setMinDate(minDate); calendar.methods.setMaxDate(maxDate); logEvent('Set Min/Max Dates', { min: minDate.toISOString(), max: maxDate.toISOString() }); } function disableWeekends() { // Clear existing disabled dates first if (calendar.methods.clearDisabledDates) { calendar.methods.clearDisabledDates(); } // Disable weekends for the next 3 months const today = new Date(); for (let i = 0; i < 90; i++) { const date = new Date(today); date.setDate(today.getDate() + i); if (date.getDay() === 0 || date.getDay() === 6) { // Sunday or Saturday calendar.methods.addDisabledDate(date); } } logEvent('Disable Weekends', { period: '90 days' }); } function disableHolidays() { const holidays = [ new Date(2025, 0, 1), // New Year's Day new Date(2025, 1, 14), // Valentine's Day new Date(2025, 6, 4), // Independence Day new Date(2025, 9, 31), // Halloween new Date(2025, 10, 28), // Thanksgiving new Date(2025, 11, 25), // Christmas new Date(2025, 11, 31) // New Year's Eve ]; holidays.forEach(date => { calendar.methods.addDisabledDate(date); }); logEvent('Disable Holidays', { count: holidays.length }); } function clearConstraints() { // Clear min/max dates calendar.methods.setMinDate(null); calendar.methods.setMaxDate(null); // Clear disabled dates if (calendar.methods.clearDisabledDates) { calendar.methods.clearDisabledDates(); } logEvent('Clear All Constraints'); } // ============================================================================= // INTERNATIONALIZATION FUNCTIONS // ============================================================================= function changeLocale(locale) { calendar.methods.setLocale(locale); updateLocaleDisplay(locale); logEvent('Change Locale', { locale }); } function changeFirstDayOfWeek(day) { calendar.methods.setFirstDayOfWeek(day); const dayName = day === 0 ? 'Sunday' : 'Monday'; updateFirstDayDisplay(dayName); logEvent('Change First Day', { day, dayName }); } function showDateFormats() { const formats = calendar.methods.getDateFormatOptions(); console.log('Date Format Options:', formats); logEvent('Show Date Formats', { formats }); } // ============================================================================= // ACCESSIBILITY FUNCTIONS // ============================================================================= function testScreenReaderLabels() { const today = new Date(); const label = calendar.methods.getAccessibleDateLabel(today); const description = calendar.methods.getDateStateDescription(today); console.log('Screen Reader Label:', label); console.log('Date State Description:', description); logEvent('Test Screen Reader', { label, description }); } function testKeyboardNavigation() { // Test all keyboard navigation methods const focusDate = new Date(); calendar.methods.focusDate(focusDate); setTimeout(() => calendar.methods.moveFocusRight(), 100); setTimeout(() => calendar.methods.moveFocusDown(), 200); setTimeout(() => calendar.methods.moveFocusLeft(), 300); setTimeout(() => calendar.methods.moveFocusUp(), 400); logEvent('Test Keyboard Navigation'); } function testFocusManagement() { const testDate = new Date(2025, 5, 15); // June 15, 2025 calendar.methods.focusDate(testDate); setTimeout(() => { calendar.methods.selectFocusedDate(); }, 500); logEvent('Test Focus Management', { date: testDate.toISOString() }); } function generateAccessibilityReport() { const report = { hasAriaSupport: true, hasKeyboardNav: true, hasFocusManagement: true, hasScreenReaderSupport: true, wcagCompliance: 'AA' }; console.log('Accessibility Report:', report); logEvent('Generate A11y Report', report); } // ============================================================================= // ADVANCED FEATURES FUNCTIONS // ============================================================================= function showWeekNumbers() { const today = new Date(); const weekNumber = calendar.methods.getWeekNumber(today); updateWeekNumberDisplay(weekNumber); logEvent('Show Week Numbers', { weekNumber }); } function generateMonthView() { const monthView = calendar.methods.generateMonthView(); console.log('Month View Data:', monthView); logEvent('Generate Month View', { monthView }); } function generateYearView() { const yearView = calendar.methods.generateCalendarYears(); console.log('Year View Data:', yearView); logEvent('Generate Year View', { yearView }); } function calculateDateDifferences() { const today = new Date(); const futureDate = new Date(today.getTime() + 30 * 24 * 60 * 60 * 1000); // 30 days from now const daysDiff = Math.ceil((futureDate - today) / (1000 * 60 * 60 * 24)); console.log(`Days between ${today.toDateString()} and ${futureDate.toDateString()}: ${daysDiff}`); logEvent('Calculate Date Differences', { daysDiff }); } // ============================================================================= // EVENTS AND BINDINGS FUNCTIONS // ============================================================================= function triggerTestEvents() { // Trigger a series of events to test the event system selectToday(); setTimeout(() => enableRangeSelection(), 100); setTimeout(() => goToNextMonth(), 200); setTimeout(() => changeLocale('es-ES'), 300); setTimeout(() => disableRangeSelection(), 400); logEvent('Trigger Test Events'); } function showEventLog() { const container = document.getElementById('eventLogContainer'); const display = document.getElementById('eventLogDisplay'); if (container && display) { container.classList.remove('hidden'); display.innerHTML = eventLogEntries.slice(-20).map(entry => `<div class="mb-1"><span class="text-blue-600">${entry.timestamp}</span> - ${entry.event}: ${JSON.stringify(entry.data || {})}</div>` ).join(''); } logEvent('Show Event Log'); } function clearEventLog() { eventLogEntries = []; const display = document.getElementById('eventLogDisplay'); if (display) { display.innerHTML = '<div class="text-gray-500">Event log cleared</div>'; } updateEventCountDisplay(0); } // ============================================================================= // API REFERENCE FUNCTIONS // ============================================================================= function runMethodValidation() { const allMethods = Object.keys(calendar.methods); const testResults = []; allMethods.forEach(methodName => { try { const method = calendar.methods[methodName]; if (typeof method === 'function') { testResults.push({ method: methodName, status: 'AVAILABLE' }); } else { testResults.push({ method: methodName, status: 'NOT_FUNCTION' }); } } catch (error) { testResults.push({ method: methodName, status: 'ERROR', error: error.message }); } }); const available = testResults.filter(r => r.status === 'AVAILABLE').length; const total = testResults.length; updateMethodValidationDisplay(available, total); logEvent('Method Validation', { available, total, successRate: (available/total*100).toFixed(1) + '%' }); } function showMethodCategories() { const categories = { navigation: ['goToNextMonth', 'goToPreviousMonth', 'goToNextYear', 'goToPreviousYear', 'goToDate', 'goToMonth', 'goToYear', 'goToToday'], selection: ['selectDate', 'selectDateRange', 'clearSelection', 'isSelected', 'setRangeSelectionMode'], internationalization: ['setLocale', 'getLocale', 'getMonthNames', 'getWeekdayNames', 'setFirstDayOfWeek', 'formatDate'], accessibility: ['getAccessibleDateLabel', 'getDateStateDescription', 'focusDate', 'moveFocusRight', 'moveFocusLeft', 'moveFocusUp', 'moveFocusDown', 'selectFocusedDate'], constraints: ['setMinDate', 'setMaxDate', 'addDisabledDate', 'removeDisabledDate', 'getDisabledDates', 'clearDisabledDates'], utilities: ['isToday', 'generateMonthView', 'generateCalendarDays', 'generateCalendarMonths', 'generateCalendarYears', 'getWeekNumber', 'getCurrentYearRange', 'getDateFormatOptions'] }; console.log('Method Categories:', categories); logEvent('Show Method Categories', { categories: Object.keys(categories) }); } function benchmarkMethods() { const methodsToTest = ['getMonthNames', 'generateCalendarDays', 'formatDate', 'isToday', 'getWeekNumber']; const results = {}; methodsToTest.forEach(methodName => { if (calendar.methods[methodName]) { const start = performance.now(); // Run method multiple times for better benchmarking for (let i = 0; i < 1000; i++) { try { switch (methodName) { case 'formatDate': case 'isToday': case 'getWeekNumber': calendar.methods[methodName](new Date()); break; default: calendar.methods[methodName](); } } catch (error) { // Ignore errors during benchmarking } } const end = performance.now(); results[methodName] = (end - start).toFixed(2) + 'ms'; } }); console.log('Benchmark Results (1000 iterations):', results); logEvent('Benchmark Methods', results); } // ============================================================================= // DISPLAY UPDATE FUNCTIONS // ============================================================================= function updateCalendarDisplay() { try { updateMonthYearDisplay(); updateWeekdayHeaders(); updateCalendarDays(); updateInfoDisplays(); } catch (error) { console.error('Error updating calendar display:', error); } } function updateMonthYearDisplay() { const element = document.getElementById('currentMonthYear'); if (element && calendar.bindings.currentDate?.current) { const date = calendar.bindings.currentDate.current; const monthNames = calendar.methods.getMonthNames(); element.textContent = `${monthNames[date.getMonth()]} ${date.getFullYear()}`; } } function updateWeekdayHeaders() { const container = document.getElementById('weekdayHeaders'); if (!container) return; try { const weekdays = calendar.methods.getWeekdayNames(true); // short names container.innerHTML = weekdays.map(day => `<div class="weekday-header">${day}</div>` ).join(''); } catch (error) { console.error('Error updating weekday headers:', error); } } function updateCalendarDays() { const container = document.getElementById('calendarDays'); if (!container) return; try { const days = calendar.methods.generateCalendarDays(); container.innerHTML = days.map(day => { const classes = ['date-cell']; if (day.isSelected) classes.push('selected'); if (day.isToday) classes.push('today'); if (day.isDisabled) classes.push('disabled'); if (day.isOtherMonth) classes.push('other-month'); if (day.isFocused) classes.push('focused'); if (day.isRangeStart) classes.push('range-start'); if (day.isRangeEnd) classes.push('range-end'); if (day.isInRange) classes.push('in-range'); return `<div class="${classes.join(' ')}" onclick="handleDateClick(${day.year}, ${day.month}, ${day.day})">${day.day}</div>`; }).join(''); } catch (error) { console.error('Error updating calendar days:', error); container.innerHTML = '<div class="col-span-7 text-center text-gray-500">Error loading calendar</div>'; } } function renderCalendarDays(days) { if (days && Array.isArray(days)) { updateCalendarDays(); } } function renderWeekdayHeaders(weekdays) { if (weekdays && Array.isArray(weekdays)) { updateWeekdayHeaders(); } } // Handle date clicks from the calendar window.handleDateClick = function(year, month, day) { calendar.methods.selectDate(year, month, day); logEvent('Date Clicked', { year, month, day }); }; function updateSelectedDateDisplay(date) { const element = document.getElementById('selectedDateDisplay'); const formattedElement = document.getElementById('formattedDateDisplay'); const isTodayElement = document.getElementById('isTodayDisplay'); if (element) { element.textContent = date ? date.toDateString() : 'None'; } if (formattedElement && date) { try { const formatted = calendar.methods.formatDate(date); formattedElement.textContent = formatted; } catch (error) { formattedElement.textContent = 'Error formatting'; } } if (isTodayElement && date) { try { const isToday = calendar.methods.isToday(date); isTodayElement.textContent = isToday.toString(); } catch (error) { isTodayElement.textContent = 'false'; } } } function updateRangeDisplay(range) { const startElement = document.getElementById('rangeStartDisplay'); const endElement = document.getElementById('rangeEndDisplay'); const daysElement = document.getElementById('rangeDaysDisplay'); if (startElement) { startElement.textContent = range?.start ? range.start.toDateString() : '--'; } if (endElement) { endElement.textContent = range?.end ? range.end.toDateString() : '--'; } if (daysElement && range?.start && range?.end) { const days = Math.ceil((range.end - range.start) / (1000 * 60 * 60 * 24)) + 1; daysElement.textContent = days.toString(); } else if (daysElement) { daysElement.textContent = '0'; } } function updateSelectionModeDisplay(isRange) { const element = document.getElementById('selectionModeDisplay'); if (element) { element.textContent = isRange ? 'Range' : 'Single'; } } function updateCurrentDateDisplay(date) { const element = document.getElementById('currentDateDisplay'); if (element && date) { element.textContent = date.toDateString(); } } function updateLocaleDisplay(locale) { const element = document.getElementById('currentLocaleDisplay'); if (element) { element.textContent = locale; } // Update month and weekday names try { const monthNames = calendar.methods.getMonthNames(); const weekdayNames = calendar.methods.getWeekdayNames(); const monthElement = document.getElementById('monthNamesDisplay'); const weekdayElement = document.getElementById('weekdayNamesDisplay'); if (monthElement) { monthElement.textContent = monthNames.slice(0, 3).join(', ') + '...'; } if (weekdayElement) { weekdayElement.textContent = weekdayNames.join(', '); } } catch (error) { console.error('Error updating locale display:', error); } } function updateFirstDayDisplay(dayName) { const element = document.getElementById('firstDayDisplay'); if (element) { element.textContent = dayName; } } function updateWeekNumberDisplay(weekNumber) { const element = document.getElementById('currentWeekNumber'); if (element) { element.textContent = weekNumber.toString(); } } function updateMethodCountDisplays() { const count = Object.keys(calendar.methods).length; const elements = [ 'methodCount', 'methodCountSidebar', 'totalMethodsCount' ]; elements.forEach(id => { const element = document.getElementById(id); if (element) { element.textContent = count.toString(); } }); } function updateMethodValidationDisplay(available, total) { const testedElement = document.getElementById('testedMethodsCount'); const successElement = document.getElementById('successRateDisplay'); if (testedElement) { testedElement.textContent = `${available}/${total}`; } if (successElement) { const rate = ((available / total) * 100).toFixed(1); successElement.textContent = `${rate}%`; } } function updateEventCountDisplay(count) { const element = document.getElementById('eventCount'); if (element) { element.textContent = count.toString(); } } function updateInfoDisplays() { // Update various info displays const currentDate = calendar.bindings.currentDate?.current; if (currentDate) { updateCurrentDateDisplay(currentDate); // Update days in month const daysElement = document.getElementById('daysInMonth'); if (daysElement) { const daysInMonth = new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 0).getDate(); daysElement.textContent = daysInMonth.toString(); } // Update year range try { const yearRange = calendar.methods.getCurrentYearRange(); const yearRangeElement = document.getElementById('yearRangeDisplay'); if (yearRangeElement && yearRange) { yearRangeElement.textContent = `${yearRange.start} - ${yearRange.end}`; } } catch (error) { console.error('Error getting year range:', error); } } // Update constraint displays updateConstraintDisplays(); } function updateConstraintDisplays() { try { const disabledDates = calendar.methods.getDisabledDates ? calendar.methods.getDisabledDates() : []; const disabledElement = document.getElementById('disabledDatesDisplay'); if (disabledElement) { disabledElement.textContent = Array.isArray(disabledDates) ? disabledDates.length.toString() : '0'; } } catch (error) { console.error('Error updating constraint displays:', error); } } // ============================================================================= // UTILITY FUNCTIONS // ============================================================================= function logEvent(eventName, data = {}) { const entry = { timestamp: new Date().toLocaleTimeString(), event: eventName, data: data }; eventLogEntries.push(entry); // Update event count display updateEventCountDisplay(eventLogEntries.length); // Update last event display const lastEventElement = document.getElementById('lastEventDisplay'); if (lastEventElement) { lastEventElement.textContent = eventName; } console.log(`Event: ${eventName}`, data); } function showError(message) { const errorDiv = document.createElement('div'); errorDiv.className = 'bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-4 mx-6 mt-4'; errorDiv.innerHTML = `<strong>Error:</strong> ${message}`; const container = document.querySelector('.doc-container'); if (container) { container.insertBefore(errorDiv, container.firstChild); // Auto-remove after 5 seconds setTimeout(() => { if (errorDiv.parentNode) { errorDiv.parentNode.removeChild(errorDiv); } }, 5000); } } // Initialize locale display on load document.addEventListener('DOMContentLoaded', () => { setTimeout(() => { if (calendar && calendar.methods) { updateLocaleDisplay('en-US'); updateFirstDayDisplay('Sunday'); showWeekNumbers(); } }, 100); });