const dropzone = document.getElementById('dropzone'); const fileInput = document.getElementById('fileInput'); const scanStage = document.getElementById('scanStage'); const previewImg = document.getElementById('previewImg'); const scanLabel = document.getElementById('scanLabel'); const rescanBtn = document.getElementById('rescanBtn'); const result = document.getElementById('result'); const errorBox = document.getElementById('errorBox'); const verdictIcon = document.getElementById('verdictIcon'); const verdictLabel = document.getElementById('verdictLabel'); const verdictSub = document.getElementById('verdictSub'); const realPct = document.getElementById('realPct'); const screenPct = document.getElementById('screenPct'); const meterFillReal = document.getElementById('meterFillReal'); const meterFillScreen = document.getElementById('meterFillScreen'); const MIN_SCAN_MS = 1700; const SCAN_MESSAGES = ['Reading pixels…', 'Checking bezel shadows…', 'Measuring color temperature…', 'Scoring saturation profile…']; dropzone.addEventListener('click', () => fileInput.click()); dropzone.addEventListener('dragover', (e) => { e.preventDefault(); dropzone.classList.add('drag-over'); }); dropzone.addEventListener('dragleave', () => dropzone.classList.remove('drag-over')); dropzone.addEventListener('drop', (e) => { e.preventDefault(); dropzone.classList.remove('drag-over'); if (e.dataTransfer.files.length) handleFile(e.dataTransfer.files[0]); }); fileInput.addEventListener('change', () => { if (fileInput.files.length) handleFile(fileInput.files[0]); }); rescanBtn.addEventListener('click', resetUI); function resetUI() { fileInput.value = ''; dropzone.classList.remove('hidden'); scanStage.classList.add('hidden'); scanStage.classList.remove('scanning'); rescanBtn.classList.add('hidden'); result.classList.add('hidden'); errorBox.classList.add('hidden'); meterFillReal.style.width = '0%'; meterFillScreen.style.width = '0%'; } function handleFile(file) { if (!file.type.startsWith('image/')) { showError('That file does not look like an image.'); return; } const url = URL.createObjectURL(file); previewImg.src = url; dropzone.classList.add('hidden'); scanStage.classList.remove('hidden'); scanStage.classList.add('scanning'); rescanBtn.classList.add('hidden'); result.classList.add('hidden'); errorBox.classList.add('hidden'); let msgIdx = 0; scanLabel.textContent = SCAN_MESSAGES[0]; const msgTimer = setInterval(() => { msgIdx = (msgIdx + 1) % SCAN_MESSAGES.length; scanLabel.textContent = SCAN_MESSAGES[msgIdx]; }, 600); const started = Date.now(); const formData = new FormData(); formData.append('file', file); fetch('/api/predict', { method: 'POST', body: formData }) .then(async (res) => { const body = await res.json().catch(() => ({})); if (!res.ok) throw new Error(body.detail || 'Prediction failed.'); return body; }) .then((data) => { const elapsed = Date.now() - started; const wait = Math.max(0, MIN_SCAN_MS - elapsed); setTimeout(() => { clearInterval(msgTimer); finishScan(); showResult(data); }, wait); }) .catch((err) => { clearInterval(msgTimer); const elapsed = Date.now() - started; const wait = Math.max(0, 600 - elapsed); setTimeout(() => { finishScan(); showError(err.message || 'Something went wrong while analyzing the image.'); }, wait); }); } function finishScan() { scanStage.classList.remove('scanning'); rescanBtn.classList.remove('hidden'); } function showResult(data) { result.classList.remove('hidden'); const isScreen = data.label === 'screen_recapture'; verdictIcon.className = 'verdict-icon ' + (isScreen ? 'screen' : 'real'); verdictIcon.textContent = isScreen ? '⚠' : '✓'; verdictLabel.className = 'verdict-label ' + (isScreen ? 'screen' : 'real'); verdictLabel.textContent = isScreen ? 'Screen Recapture' : 'Real Photo'; verdictSub.textContent = `${data.confidence}% confidence`; realPct.textContent = `${data.real_pct}%`; screenPct.textContent = `${data.screen_pct}%`; requestAnimationFrame(() => { meterFillReal.style.width = `${data.real_pct}%`; meterFillScreen.style.width = `${data.screen_pct}%`; }); } function showError(message) { result.classList.remove('hidden'); errorBox.textContent = message; errorBox.classList.remove('hidden'); verdictIcon.className = 'verdict-icon screen'; verdictIcon.textContent = '!'; verdictLabel.className = 'verdict-label screen'; verdictLabel.textContent = 'Could not analyze image'; verdictSub.textContent = ''; realPct.textContent = '0%'; screenPct.textContent = '0%'; meterFillReal.style.width = '0%'; meterFillScreen.style.width = '0%'; }