/** * HVAC Event Manager JavaScript * * Minimal JavaScript for enhanced UX on event management pages * No JavaScript dependencies - progressive enhancement only * * @package HVAC_Community_Events * @since 3.0.0 */ (function() { 'use strict'; // Wait for DOM to be ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } function init() { // Only run on event management pages if (!isEventPage()) { return; } // Initialize enhancements initFormEnhancements(); initAccessibilityEnhancements(); initProgressiveEnhancements(); // Debug logging if enabled if (typeof hvac_event_manager !== 'undefined' && hvac_event_manager.debug) { console.log('[HVAC Event Manager] Initialized successfully'); } } /** * Check if we're on an event management page */ function isEventPage() { return document.querySelector('.hvac-event-wrapper, .hvac-edit-event-wrapper, .tribe-community-events-form') !== null; } /** * Initialize form enhancements */ function initFormEnhancements() { const forms = document.querySelectorAll('.tribe-community-events-form'); forms.forEach(function(form) { // Add loading states to form submissions addFormLoadingState(form); // Enhance form validation enhanceFormValidation(form); // Add character counters to textareas addCharacterCounters(form); }); } /** * Add loading state to form submissions */ function addFormLoadingState(form) { const submitButtons = form.querySelectorAll('input[type="submit"], .tribe-events-submit'); submitButtons.forEach(function(button) { button.addEventListener('click', function() { // Add loading class to form form.classList.add('hvac-loading'); // Disable submit button to prevent double submission button.disabled = true; // Store original button text const originalText = button.value || button.textContent; // Update button text if (button.tagName === 'INPUT') { button.value = 'Saving...'; } else { button.textContent = 'Saving...'; } // Remove loading state after 30 seconds (timeout) setTimeout(function() { form.classList.remove('hvac-loading'); button.disabled = false; if (button.tagName === 'INPUT') { button.value = originalText; } else { button.textContent = originalText; } }, 30000); }); }); } /** * Enhance form validation with real-time feedback */ function enhanceFormValidation(form) { const requiredFields = form.querySelectorAll('input[required], textarea[required], select[required]'); requiredFields.forEach(function(field) { // Add validation on blur field.addEventListener('blur', function() { validateField(field); }); // Add validation on input for immediate feedback field.addEventListener('input', function() { // Clear previous validation state clearFieldValidation(field); // Validate on input with debounce clearTimeout(field.validationTimeout); field.validationTimeout = setTimeout(function() { validateField(field); }, 500); }); }); } /** * Validate a single field */ function validateField(field) { const isValid = field.checkValidity(); const fieldRow = field.closest('.tribe-events-form-row') || field.parentElement; // Remove existing validation classes fieldRow.classList.remove('validation-error', 'validation-success'); // Remove existing validation messages const existingMessage = fieldRow.querySelector('.validation-message'); if (existingMessage) { existingMessage.remove(); } if (!isValid) { // Add error styling fieldRow.classList.add('validation-error'); // Add error message const message = document.createElement('div'); message.className = 'validation-message validation-error-message'; message.textContent = field.validationMessage || 'This field is required'; message.setAttribute('role', 'alert'); fieldRow.appendChild(message); } else if (field.value.trim()) { // Add success styling for non-empty valid fields fieldRow.classList.add('validation-success'); } } /** * Clear field validation state */ function clearFieldValidation(field) { const fieldRow = field.closest('.tribe-events-form-row') || field.parentElement; fieldRow.classList.remove('validation-error', 'validation-success'); const existingMessage = fieldRow.querySelector('.validation-message'); if (existingMessage) { existingMessage.remove(); } } /** * Add character counters to textareas */ function addCharacterCounters(form) { const textareas = form.querySelectorAll('textarea'); textareas.forEach(function(textarea) { // Skip if already has a counter if (textarea.nextElementSibling && textarea.nextElementSibling.classList.contains('character-counter')) { return; } // Create counter element const counter = document.createElement('div'); counter.className = 'character-counter'; counter.setAttribute('aria-live', 'polite'); // Insert after textarea textarea.parentNode.insertBefore(counter, textarea.nextSibling); // Update counter function function updateCounter() { const length = textarea.value.length; const maxLength = textarea.getAttribute('maxlength'); if (maxLength) { counter.textContent = length + ' / ' + maxLength + ' characters'; if (length > maxLength * 0.9) { counter.classList.add('character-counter-warning'); } else { counter.classList.remove('character-counter-warning'); } } else { counter.textContent = length + ' characters'; } } // Initialize counter updateCounter(); // Update on input textarea.addEventListener('input', updateCounter); }); } /** * Initialize accessibility enhancements */ function initAccessibilityEnhancements() { // Add ARIA labels to form sections addAriaLabels(); // Enhance keyboard navigation enhanceKeyboardNavigation(); // Add skip links for forms addSkipLinks(); } /** * Add ARIA labels to form sections */ function addAriaLabels() { // Label form sections const venueSection = document.querySelector('.tribe-events-venue-form'); if (venueSection) { venueSection.setAttribute('aria-labelledby', 'venue-section-title'); const title = venueSection.querySelector('h4'); if (title) { title.id = 'venue-section-title'; } } const organizerSection = document.querySelector('.tribe-events-organizer-form'); if (organizerSection) { organizerSection.setAttribute('aria-labelledby', 'organizer-section-title'); const title = organizerSection.querySelector('h4'); if (title) { title.id = 'organizer-section-title'; } } // Add aria-describedby to fields with help text const helpTexts = document.querySelectorAll('.tribe-events-help-text, .description'); helpTexts.forEach(function(helpText, index) { const helpId = 'help-text-' + index; helpText.id = helpId; const field = helpText.previousElementSibling; if (field && (field.tagName === 'INPUT' || field.tagName === 'TEXTAREA' || field.tagName === 'SELECT')) { field.setAttribute('aria-describedby', helpId); } }); } /** * Enhance keyboard navigation */ function enhanceKeyboardNavigation() { // Make form sections focusable for keyboard users const formSections = document.querySelectorAll('.tribe-events-venue-form, .tribe-events-organizer-form, .tribe-datetime-block'); formSections.forEach(function(section) { section.setAttribute('tabindex', '-1'); }); // Add keyboard shortcuts for common actions document.addEventListener('keydown', function(e) { // Ctrl/Cmd + S to save form if ((e.ctrlKey || e.metaKey) && e.key === 's') { e.preventDefault(); const submitButton = document.querySelector('.tribe-community-events-form input[type="submit"], .tribe-community-events-form .tribe-events-submit'); if (submitButton) { submitButton.click(); } } }); } /** * Add skip links for better navigation */ function addSkipLinks() { const form = document.querySelector('.tribe-community-events-form'); if (!form) return; const skipNav = document.createElement('nav'); skipNav.className = 'hvac-skip-links'; skipNav.setAttribute('aria-label', 'Form navigation'); const skipList = document.createElement('ul'); // Add skip links for major form sections const sections = [ { selector: '.tribe-events-venue-form', text: 'Skip to venue details' }, { selector: '.tribe-events-organizer-form', text: 'Skip to organizer details' }, { selector: '.tribe-datetime-block', text: 'Skip to date and time' }, { selector: 'input[type="submit"], .tribe-events-submit', text: 'Skip to save button' } ]; sections.forEach(function(section) { const element = form.querySelector(section.selector); if (element) { const skipItem = document.createElement('li'); const skipLink = document.createElement('a'); skipLink.href = '#'; skipLink.textContent = section.text; skipLink.className = 'screen-reader-text'; skipLink.addEventListener('click', function(e) { e.preventDefault(); element.focus(); }); skipItem.appendChild(skipLink); skipList.appendChild(skipItem); } }); if (skipList.children.length > 0) { skipNav.appendChild(skipList); form.insertBefore(skipNav, form.firstChild); } } /** * Initialize progressive enhancements */ function initProgressiveEnhancements() { // Auto-save draft functionality (if supported) initAutoSave(); // Enhanced date/time pickers enhanceDateTimePickers(); // Smart field suggestions initSmartSuggestions(); } /** * Initialize auto-save functionality */ function initAutoSave() { // Only enable if browser supports localStorage if (typeof Storage === 'undefined') return; const form = document.querySelector('.tribe-community-events-form'); if (!form) return; const autoSaveKey = 'hvac_event_autosave_' + (new Date().getTime()); let autoSaveTimeout; // Save form data function saveFormData() { const formData = new FormData(form); const data = {}; for (let [key, value] of formData.entries()) { data[key] = value; } try { localStorage.setItem(autoSaveKey, JSON.stringify(data)); } catch (e) { // Storage quota exceeded or not available console.warn('[HVAC Event Manager] Auto-save failed:', e); } } // Auto-save on input changes (debounced) form.addEventListener('input', function() { clearTimeout(autoSaveTimeout); autoSaveTimeout = setTimeout(saveFormData, 2000); }); // Clear auto-save on successful submission form.addEventListener('submit', function() { try { localStorage.removeItem(autoSaveKey); } catch (e) { // Ignore errors } }); } /** * Enhance date/time pickers */ function enhanceDateTimePickers() { const datePickers = document.querySelectorAll('input[type="date"], input[type="time"]'); datePickers.forEach(function(picker) { // Add helper text for date format if (picker.type === 'date' && !picker.getAttribute('aria-describedby')) { const helpText = document.createElement('div'); helpText.className = 'date-format-help'; helpText.textContent = 'Format: YYYY-MM-DD'; helpText.id = 'date-help-' + Math.random().toString(36).substr(2, 9); picker.setAttribute('aria-describedby', helpText.id); picker.parentNode.appendChild(helpText); } }); } /** * Initialize smart field suggestions */ function initSmartSuggestions() { // This could be expanded to provide intelligent suggestions // based on user's previous entries, location, etc. // For now, just add placeholder enhancements const titleField = document.querySelector('input[name="post_title"], input[name="EventTitle"]'); if (titleField && !titleField.placeholder) { titleField.placeholder = 'Enter a descriptive title for your event'; } const descriptionField = document.querySelector('textarea[name="post_content"], textarea[name="EventDescription"]'); if (descriptionField && !descriptionField.placeholder) { descriptionField.placeholder = 'Provide details about your event, including what attendees will learn...'; } } })();