/** * App loading overlay management * Prevents FOUC (Flash of Unstyled Content) during page initialization */ window.AppLoader = { initialized: false, readyChecks: [], /** * Initialize the loading system */ init() { if (this.initialized) return; this.initialized = true; // Add loading class to body document.body.classList.add('app-loading'); // Create loading overlay if it doesn't exist if (!document.querySelector('.app-loading-overlay')) { this.createOverlay(); } // Set up ready checks this.setupReadyChecks(); }, /** * Create the loading overlay element */ createOverlay() { const overlay = document.createElement('div'); overlay.className = 'app-loading-overlay'; overlay.innerHTML = `
Loading...
`; document.body.appendChild(overlay); }, /** * Add a ready check condition */ addReadyCheck(checkFn) { this.readyChecks.push(checkFn); }, /** * Setup default ready checks */ setupReadyChecks() { // Check if DOM is ready this.addReadyCheck(() => document.readyState === 'complete'); // Check if styles are loaded this.addReadyCheck(() => { const styles = document.querySelector('link[href*="styles.css"]'); return !styles || styles.sheet; }); // Check if theme is initialized this.addReadyCheck(() => { const computed = window.getComputedStyle(document.documentElement); return computed.getPropertyValue('--bg-primary').trim() !== ''; }); }, /** * Check if all conditions are met */ isReady() { if (this.readyChecks.length === 0) return true; return this.readyChecks.every(check => { try { return check(); } catch (e) { return false; } }); }, /** * Hide the loading overlay */ hide() { const overlay = document.querySelector('.app-loading-overlay'); if (overlay) { overlay.classList.add('fade-out'); setTimeout(() => { overlay.remove(); document.body.classList.remove('app-loading'); }, 300); } else { document.body.classList.remove('app-loading'); } }, /** * Wait for app to be ready then hide overlay */ async waitForReady(timeout = 5000) { const startTime = Date.now(); const checkReady = () => { if (this.isReady() || Date.now() - startTime > timeout) { this.hide(); } else { requestAnimationFrame(checkReady); } }; // Start checking after a minimum display time setTimeout(checkReady, 300); } }; // Auto-initialize on script load if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => AppLoader.init()); } else { AppLoader.init(); }