Harshasnade's picture
Initialize clean space deployment
ee00155
// ==================== ENHANCED LOADER SYSTEM ====================
// Countdown Timer and Real/Fake Status Cycling (Minimum 5 seconds)
const initLoader = () => {
console.log("Initializing Loader...");
const loaderWrapper = document.getElementById('loader-wrapper');
const loaderPercent = document.getElementById('loader-percent');
const detectionStatus = document.getElementById('detectionStatus');
const statusText = detectionStatus?.querySelector('.status-text');
const loaderTimestamp = document.getElementById('loaderTimestamp');
const loaderQuote = document.getElementById('loader-quote');
// Global flag for backend readiness (default false)
window.isBackendReady = false;
// Expose status update function
window.updateLoaderStatus = (message) => {
if (statusText) {
statusText.textContent = message;
// Clear animations/colors to show this is a sticky state
if (detectionStatus) {
detectionStatus.classList.remove('analyzing', 'real', 'fake');
detectionStatus.classList.add('analyzing');
}
}
};
if (!loaderWrapper) {
console.error("Loader wrapper not found!");
return;
}
// Force visibility at start
loaderWrapper.style.display = 'flex';
loaderWrapper.style.opacity = '1';
// Prevent double initialization
if (loaderWrapper.dataset.initialized) return;
loaderWrapper.dataset.initialized = "true";
let progress = 0;
const targetProgress = 100;
let currentStatus = 0;
const startTime = Date.now();
const minimumDuration = 3500; // 3.5 seconds (Optimized for better UX)
// Safety Timeout - Force remove loader after 2 minutes (120000ms) to allow for cold starts
// If backend is completely dead, this ensures user isn't stuck forever.
setTimeout(() => {
if (loaderWrapper && loaderWrapper.style.display !== 'none' && !loaderWrapper.classList.contains('loaded')) {
console.warn("Loader safety timeout triggered - forcing removal.");
loaderWrapper.classList.add('loaded');
setTimeout(() => {
loaderWrapper.style.display = 'none';
}, 800);
}
}, 120000);
// Real/Fake Status Messages
const statusMessages = [
{ text: 'ANALYZING...', class: 'analyzing' },
{ text: 'SCANNING PATTERNS...', class: 'analyzing' },
{ text: 'REAL?', class: 'real' },
{ text: 'CHECKING AUTHENTICITY...', class: 'analyzing' },
{ text: 'FAKE?', class: 'fake' },
{ text: 'VERIFYING DATA...', class: 'analyzing' }
];
// Update timestamp
const updateTimestamp = () => {
const now = new Date();
const timeStr = now.toLocaleTimeString('en-US', { hour12: false });
if (loaderTimestamp) {
loaderTimestamp.textContent = `SYSTEM TIME: ${timeStr}`;
}
};
updateTimestamp();
const timeInterval = setInterval(updateTimestamp, 1000);
// Cycle through status messages
let statusInterval;
const cycleStatus = () => {
// If external system says we are waiting (via explicit message override), stop cycling
// We'll use a property on the statusText or just check text content if it matches our "Starting..." message?
// Simpler: Just rely on the animation loop. If we are paused at 99%, we stop cycling.
if (!statusText || !detectionStatus) return;
// If we are waiting for backend (progress capped at 99), stop cycling status text
if (progress >= 99 && !window.isBackendReady) {
return;
}
const status = statusMessages[currentStatus];
statusText.textContent = status.text;
// Remove all status classes
detectionStatus.classList.remove('analyzing', 'real', 'fake');
// Add current class
detectionStatus.classList.add(status.class);
currentStatus = (currentStatus + 1) % statusMessages.length;
};
// Start cycling status every 800ms
cycleStatus();
statusInterval = setInterval(cycleStatus, 800);
// Countdown Timer Animation - Smooth progression from 0 to 100
let lastFrameTime = startTime;
// Pre-process loader quotes for typing effect
let allChars = [];
if (loaderQuote && !loaderQuote.dataset.processed) {
loaderQuote.dataset.processed = "true";
const processNode = (node) => {
if (node.nodeType === Node.TEXT_NODE) {
const text = node.textContent;
// Skip empty text nodes that are just whitespace to avoid weird spacing gaps if flex/grid were used,
// but for standard flow, whitespace is needed. However, large blocks of whitespace can be ignored.
if (text.trim().length === 0 && text.length > 0) {
// Keep the whitespace node as is
return;
}
const fragment = document.createDocumentFragment();
const map = text.split('');
map.forEach(char => {
const span = document.createElement('span');
span.textContent = char;
span.className = 'char-waiting'; // Start in waiting state
fragment.appendChild(span);
allChars.push(span);
});
node.replaceWith(fragment);
} else if (node.nodeType === Node.ELEMENT_NODE) {
if (node.tagName !== 'BR') {
Array.from(node.childNodes).forEach(processNode);
}
}
};
Array.from(loaderQuote.childNodes).forEach(processNode);
// Reveal the quote container after processing spans
// Synchronous update to prevent frame flicker
loaderQuote.style.opacity = '1';
}
function animateLoader() {
const now = Date.now();
const elapsed = now - startTime;
// Calculate exact progress based on time (0 to 100 over 5 seconds)
let exactProgress = Math.min((elapsed / minimumDuration) * 100, 100);
// BLOCKING: If backend is not ready, cap progress at 99%
if (!window.isBackendReady && exactProgress >= 99) {
// TIMEOUT FALLBACK: If we've been waiting too long (> 8 seconds), just let them in.
if (elapsed > 8000) {
console.warn("Backend check timed out - proceeding anyway.");
window.isBackendReady = true;
} else {
exactProgress = 99;
// Update status text if it hasn't been updated yet to show waiting state
if (statusText && statusText.textContent !== "STARTING SERVER..." && statusText.textContent !== "WAITING FOR BACKEND...") {
// We rely on script.js calling updateLoaderStatus, but we can also set a default here if stuck
// But let's let script.js drive the specific message
}
}
}
// Update progress value directly (no interpolation to avoid jumps)
progress = exactProgress;
if (loaderPercent) {
loaderPercent.textContent = Math.floor(progress);
}
if (allChars.length > 0) {
const totalChars = allChars.length;
// Calculate how many characters should be lit up based on progress
// We want all chars lit by 100%
const charsToLight = Math.floor((progress / 100) * totalChars);
allChars.forEach((charSpan, index) => {
if (index < charsToLight) {
charSpan.className = 'char-typed';
} else if (index === charsToLight && index < totalChars) {
charSpan.className = 'char-current';
} else {
charSpan.className = 'char-waiting';
}
});
} else if (loaderQuote) {
// Fallback if processing failed
loaderQuote.style.opacity = progress / 100;
}
// Only finish when minimum time has elapsed AND backend is ready
if (elapsed >= minimumDuration && progress >= 100 && window.isBackendReady === true) {
// Ensure we show 100%
if (loaderPercent) {
loaderPercent.textContent = '100';
}
// Reached 100% - Exit loader
setTimeout(() => {
console.log("Loader finished.");
clearInterval(statusInterval);
clearInterval(timeInterval);
loaderWrapper.classList.add('loaded'); // CSS transform
// Remove from DOM after animation
setTimeout(() => {
loaderWrapper.style.display = 'none';
}, 800); // Wait for CSS transition
}, 500); // Brief pause at 100%
} else {
requestAnimationFrame(animateLoader);
}
}
// Start countdown
animateLoader();
};
// Robust initialization
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initLoader);
} else {
// DOM already ready, run immediately
initLoader();
}