/** * Footer Component - i18n-enabled * Shared footer for all Tractatus pages with language persistence */ (function() { 'use strict'; class TractatusFooter { constructor() { this.init(); } init() { console.log('[Footer] Initializing...'); // Listen for i18n initialization event (fired by i18n-simple.js) if (window.I18n && window.I18n.translations && window.I18n.translations.footer) { // i18n already loaded console.log('[Footer] i18n already loaded, rendering immediately'); this.render(); this.attachEventListeners(); } else { // Wait for i18nInitialized event console.log('[Footer] Waiting for i18nInitialized event...'); window.addEventListener('i18nInitialized', () => { console.log('[Footer] i18n initialized event received'); // Double-check translations loaded if (window.I18n && window.I18n.translations && window.I18n.translations.footer) { console.log('[Footer] Footer translations confirmed, rendering'); this.render(); this.attachEventListeners(); } else { console.error('[Footer] Event fired but no footer translations:', window.I18n?.translations); // Render anyway this.render(); this.attachEventListeners(); } }, { once: true }); } } render() { const currentYear = new Date().getFullYear(); // Create footer HTML with data-i18n attributes const footerHTML = ` `; // Insert footer at end of body const existingFooter = document.querySelector('footer[role="contentinfo"]'); if (existingFooter) { existingFooter.outerHTML = footerHTML; } else if (document.body) { document.body.insertAdjacentHTML('beforeend', footerHTML); } else { // If body not ready, wait for DOM document.addEventListener('DOMContentLoaded', () => { document.body.insertAdjacentHTML('beforeend', footerHTML); this.applyFooterTranslations(); }); return; // Exit early if DOM not ready } // Apply translations after DOM update this.applyFooterTranslations(); } applyFooterTranslations() { // Use double requestAnimationFrame to ensure DOM is fully painted requestAnimationFrame(() => { requestAnimationFrame(() => { if (window.I18n && window.I18n.applyTranslations) { console.log('[Footer] Applying translations...'); console.log('[Footer] Footer exists:', !!document.querySelector('footer[role="contentinfo"]')); console.log('[Footer] Elements with data-i18n:', document.querySelectorAll('footer[role="contentinfo"] [data-i18n]').length); window.I18n.applyTranslations(); console.log('[Footer] Translations applied'); // Verify a sample translation const aboutHeading = document.querySelector('footer [data-i18n="footer.about_heading"]'); if (aboutHeading) { console.log('[Footer] about_heading element text:', aboutHeading.innerHTML); } } else { console.warn('[Footer] I18n not available for translation'); } }); }); } attachEventListeners() { // Listen for language changes and re-render footer window.addEventListener('languageChanged', (event) => { console.log('[Footer] Language changed to:', event.detail.language); this.render(); }); // Contact modal functionality this.setupContactModal(); } setupContactModal() { const modal = document.getElementById('contact-modal'); const openBtn = document.getElementById('open-contact-modal'); const closeBtn = document.getElementById('close-contact-modal'); const cancelBtn = document.getElementById('cancel-contact'); const form = document.getElementById('contact-form'); const successMsg = document.getElementById('contact-success'); const errorMsg = document.getElementById('contact-error'); const errorText = document.getElementById('contact-error-message'); const submitBtn = document.getElementById('contact-submit'); if (!modal || !openBtn || !form) { console.warn('[Footer] Contact modal elements not found'); return; } const openModal = () => { modal.classList.remove('hidden'); // Re-apply translations to modal when opening if (window.I18n && window.I18n.applyTranslations) { window.I18n.applyTranslations(); } document.getElementById('contact-name')?.focus(); }; const closeModal = () => { modal.classList.add('hidden'); form.reset(); successMsg.classList.add('hidden'); errorMsg.classList.add('hidden'); }; // Event listeners openBtn.addEventListener('click', (e) => { e.preventDefault(); openModal(); }); closeBtn?.addEventListener('click', closeModal); cancelBtn?.addEventListener('click', closeModal); // Close on backdrop click modal.addEventListener('click', (e) => { if (e.target === modal) { closeModal(); } }); // Form submission form.addEventListener('submit', async (e) => { e.preventDefault(); // Reset messages successMsg.classList.add('hidden'); errorMsg.classList.add('hidden'); const formData = { type: document.getElementById('contact-type').value, name: document.getElementById('contact-name').value, email: document.getElementById('contact-email').value, organization: document.getElementById('contact-organization').value || null, subject: document.getElementById('contact-subject').value || null, message: document.getElementById('contact-message').value }; // Disable submit button submitBtn.disabled = true; // Use translation if available, fallback to English const sendingText = window.I18n?.translations?.contact_modal?.submitting || 'Sending...'; submitBtn.textContent = sendingText; try { // Get CSRF token from cookie const csrfToken = document.cookie .split('; ') .find(row => row.startsWith('csrf-token=')) ?.split('=')[1]; const response = await fetch('/api/contact/submit', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': csrfToken || '' }, body: JSON.stringify(formData) }); const data = await response.json(); if (response.ok && data.success) { // Show success message successMsg.classList.remove('hidden'); form.reset(); // Close modal after 2 seconds setTimeout(() => { closeModal(); }, 2000); } else { // Show error message errorText.textContent = data.message || data.error || 'Failed to send message. Please try again.'; errorMsg.classList.remove('hidden'); } } catch (error) { console.error('Contact form error:', error); errorText.textContent = 'Network error. Please check your connection and try again.'; errorMsg.classList.remove('hidden'); } finally { // Re-enable submit button submitBtn.disabled = false; // Use translation if available, fallback to English const submitText = window.I18n?.translations?.contact_modal?.submit_button || 'Send Message'; submitBtn.textContent = submitText; } }); } } // Auto-initialize when DOM is ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => new TractatusFooter()); } else { new TractatusFooter(); } })();