| |
| |
|
|
| 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'); |
|
|
| |
| window.isBackendReady = false; |
|
|
| |
| window.updateLoaderStatus = (message) => { |
| if (statusText) { |
| statusText.textContent = message; |
| |
| if (detectionStatus) { |
| detectionStatus.classList.remove('analyzing', 'real', 'fake'); |
| detectionStatus.classList.add('analyzing'); |
| } |
| } |
| }; |
|
|
| if (!loaderWrapper) { |
| console.error("Loader wrapper not found!"); |
| return; |
| } |
|
|
| |
| loaderWrapper.style.display = 'flex'; |
| loaderWrapper.style.opacity = '1'; |
|
|
| |
| 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; |
|
|
| |
| |
| 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); |
|
|
| |
| 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' } |
| ]; |
|
|
| |
| 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); |
|
|
| |
| let statusInterval; |
| const cycleStatus = () => { |
| |
| |
| |
|
|
| if (!statusText || !detectionStatus) return; |
|
|
| |
| if (progress >= 99 && !window.isBackendReady) { |
| return; |
| } |
|
|
| const status = statusMessages[currentStatus]; |
| statusText.textContent = status.text; |
|
|
| |
| detectionStatus.classList.remove('analyzing', 'real', 'fake'); |
| |
| detectionStatus.classList.add(status.class); |
|
|
| currentStatus = (currentStatus + 1) % statusMessages.length; |
| }; |
|
|
| |
| cycleStatus(); |
| statusInterval = setInterval(cycleStatus, 800); |
|
|
| |
| let lastFrameTime = startTime; |
|
|
| |
| let allChars = []; |
| if (loaderQuote && !loaderQuote.dataset.processed) { |
| loaderQuote.dataset.processed = "true"; |
|
|
| const processNode = (node) => { |
| if (node.nodeType === Node.TEXT_NODE) { |
| const text = node.textContent; |
| |
| |
| if (text.trim().length === 0 && text.length > 0) { |
| |
| return; |
| } |
|
|
| const fragment = document.createDocumentFragment(); |
| const map = text.split(''); |
| map.forEach(char => { |
| const span = document.createElement('span'); |
| span.textContent = char; |
| span.className = 'char-waiting'; |
| 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); |
|
|
| |
| |
| loaderQuote.style.opacity = '1'; |
| } |
|
|
| function animateLoader() { |
| const now = Date.now(); |
| const elapsed = now - startTime; |
|
|
| |
| let exactProgress = Math.min((elapsed / minimumDuration) * 100, 100); |
|
|
| |
| if (!window.isBackendReady && exactProgress >= 99) { |
| |
| if (elapsed > 8000) { |
| console.warn("Backend check timed out - proceeding anyway."); |
| window.isBackendReady = true; |
| } else { |
| exactProgress = 99; |
| |
| if (statusText && statusText.textContent !== "STARTING SERVER..." && statusText.textContent !== "WAITING FOR BACKEND...") { |
| |
| |
| } |
| } |
| } |
|
|
| |
| progress = exactProgress; |
|
|
| if (loaderPercent) { |
| loaderPercent.textContent = Math.floor(progress); |
| } |
|
|
| if (allChars.length > 0) { |
| const totalChars = allChars.length; |
| |
| |
| 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) { |
| |
| loaderQuote.style.opacity = progress / 100; |
| } |
|
|
| |
| if (elapsed >= minimumDuration && progress >= 100 && window.isBackendReady === true) { |
| |
| if (loaderPercent) { |
| loaderPercent.textContent = '100'; |
| } |
|
|
| |
| setTimeout(() => { |
| console.log("Loader finished."); |
| clearInterval(statusInterval); |
| clearInterval(timeInterval); |
|
|
| loaderWrapper.classList.add('loaded'); |
|
|
| |
| setTimeout(() => { |
| loaderWrapper.style.display = 'none'; |
| }, 800); |
| }, 500); |
| } else { |
| requestAnimationFrame(animateLoader); |
| } |
| } |
|
|
| |
| animateLoader(); |
| }; |
|
|
| |
| if (document.readyState === 'loading') { |
| document.addEventListener('DOMContentLoaded', initLoader); |
| } else { |
| |
| initLoader(); |
| } |
|
|