ai-detection / static /script.js
maddyrox's picture
Upload 23 files
6361699 verified
document.addEventListener('DOMContentLoaded', () => {
const dropZone = document.getElementById('drop-zone');
const fileInput = document.getElementById('file-input');
const previewContainer = document.getElementById('preview-container');
const imagePreview = document.getElementById('image-preview');
const removeBtn = document.getElementById('remove-btn');
const analyzeBtn = document.getElementById('analyze-btn');
const resultContainer = document.getElementById('result-container');
const resultBadge = document.getElementById('result-badge');
const tabDetector = document.getElementById('tab-detector');
const tabRisk = document.getElementById('tab-risk');
let currentMode = 'detector'; // 'detector' or 'risk'
const modelLabel = document.getElementById('model-label');
const btnText = analyzeBtn.querySelector('.btn-text');
const loader = analyzeBtn.querySelector('.loader');
let currentFile = null;
// --- Tab Switching ---
function switchTab(mode) {
currentMode = mode;
resultContainer.classList.add('hidden'); // Hide previous results
if (mode === 'detector') {
tabDetector.className = 'px-6 py-2 rounded-full font-medium transition-all duration-300 bg-white text-black shadow-[0_0_20px_-5px_rgba(255,255,255,0.5)]';
tabRisk.className = 'px-6 py-2 rounded-full font-medium transition-all duration-300 bg-white/5 text-brand-gray hover:bg-white/10 hover:text-white border border-white/10';
btnText.textContent = 'Analyze Authenticity';
} else {
tabRisk.className = 'px-6 py-2 rounded-full font-medium transition-all duration-300 bg-brand-green text-black shadow-[0_0_20px_-5px_rgba(0,220,130,0.5)]';
tabDetector.className = 'px-6 py-2 rounded-full font-medium transition-all duration-300 bg-white/5 text-brand-gray hover:bg-white/10 hover:text-white border border-white/10';
btnText.textContent = 'Check Identity Risk';
}
}
tabDetector.addEventListener('click', () => switchTab('detector'));
tabRisk.addEventListener('click', () => switchTab('risk'));
// --- Drag & Drop ---
dropZone.addEventListener('click', () => fileInput.click());
dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
dropZone.classList.add('dragover');
});
dropZone.addEventListener('dragleave', () => {
dropZone.classList.remove('dragover');
});
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
dropZone.classList.remove('dragover');
if (e.dataTransfer.files.length) {
handleFile(e.dataTransfer.files[0]);
}
});
fileInput.addEventListener('change', (e) => {
if (e.target.files.length) {
handleFile(e.target.files[0]);
}
});
// --- File Handling ---
function handleFile(file) {
if (!file.type.startsWith('image/')) {
alert('Please upload an image file (JPG, PNG).');
return;
}
currentFile = file;
const reader = new FileReader();
reader.onload = (e) => {
imagePreview.src = e.target.result;
dropZone.classList.add('hidden');
previewContainer.classList.remove('hidden');
analyzeBtn.disabled = false;
resultContainer.classList.add('hidden');
};
reader.readAsDataURL(file);
}
// --- Reset ---
removeBtn.addEventListener('click', () => {
currentFile = null;
fileInput.value = '';
dropZone.classList.remove('hidden');
previewContainer.classList.add('hidden');
analyzeBtn.disabled = true;
resultContainer.classList.add('hidden');
});
// --- Prediction ---
analyzeBtn.addEventListener('click', async () => {
if (!currentFile) return;
// UI Loading State
analyzeBtn.disabled = true;
btnText.textContent = currentMode === 'detector' ? 'Analysing...' : 'Checking Risk...';
loader.classList.remove('hidden');
resultContainer.classList.add('hidden');
const formData = new FormData();
formData.append('image', currentFile);
try {
const response = await fetch('/predict', {
method: 'POST',
body: formData
});
if (!response.ok) throw new Error('API Error');
const data = await response.json();
displayResult(data);
} catch (error) {
console.error(error);
alert('Error processing image. Please try again.');
} finally {
// UI Reset State
analyzeBtn.disabled = false;
btnText.textContent = currentMode === 'detector' ? 'Analyze Authenticity' : 'Check Identity Risk';
loader.classList.add('hidden');
}
});
function displayResult(data) {
resultContainer.classList.remove('hidden');
const badgeContainer = document.getElementById('result-badge-container');
// Remove existing detailed warnings/lists
const existingWarning = document.getElementById('risk-warning');
if (existingWarning) existingWarning.remove();
const existingList = document.getElementById('risk-details');
if (existingList) existingList.remove();
const confidenceWrapper = document.getElementById('confidence-wrapper');
const explanationsSection = document.getElementById('explanations-section');
const qualificationTag = document.getElementById('qualification-tag'); // [NEW]
if (currentMode === 'detector') {
// --- AI Detector Mode ---
if (confidenceWrapper) confidenceWrapper.classList.remove('hidden'); // Show in Detector Mode
modelLabel.textContent = 'AI Detection Result';
const label = data.prediction.toLowerCase();
resultBadge.textContent = label.toUpperCase();
// [NEW] Qualification Tag Logic
if (qualificationTag) {
if (data.classification_tag) {
qualificationTag.classList.remove('hidden');
qualificationTag.textContent = data.classification_tag;
// Dynamic Styling based on content
qualificationTag.className = "px-3 py-1 rounded-full text-[10px] font-bold uppercase tracking-wider border transition-colors duration-300";
if (data.classification_tag.includes("AI") || data.classification_tag.includes("Manipulation")) {
qualificationTag.classList.add("bg-red-500/10", "border-red-500/20", "text-red-200");
} else if (data.classification_tag.includes("Raw")) {
qualificationTag.classList.add("bg-brand-green/10", "border-brand-green/20", "text-brand-green");
} else {
qualificationTag.classList.add("bg-yellow-500/10", "border-yellow-500/20", "text-yellow-200");
}
} else {
qualificationTag.classList.add('hidden');
}
}
// Confidence Update
const confidenceBar = document.getElementById('confidence-bar');
const confidenceValue = document.getElementById('confidence-value');
const percent = Math.round(data.confidence * 100);
// Animate Bar
setTimeout(() => {
confidenceBar.style.width = `${percent}%`;
}, 100);
confidenceValue.textContent = `${percent}%`;
resultBadge.className = 'text-3xl font-bold tracking-tight transition-colors duration-300';
confidenceBar.className = 'h-full w-0 rounded-full transition-all duration-1000 ease-out'; // Reset base
if (label.includes('real')) {
resultBadge.classList.add('text-brand-green', 'drop-shadow-[0_0_15px_rgba(0,220,130,0.5)]');
confidenceBar.classList.add('bg-brand-green', 'shadow-[0_0_10px_rgba(0,220,130,0.5)]');
} else if (label.includes('fake') || label.includes('ai')) {
resultBadge.classList.add('text-red-500', 'drop-shadow-[0_0_15px_rgba(255,50,50,0.5)]');
confidenceBar.classList.add('bg-red-500', 'shadow-[0_0_10px_rgba(255,50,50,0.5)]');
} else {
resultBadge.classList.add('text-brand-gray');
confidenceBar.classList.add('bg-gray-500');
}
// [NEW] Analysis Points Logic
const explanationsSection = document.getElementById('explanations-section');
const reasonsList = document.getElementById('reasons-list');
reasonsList.innerHTML = ''; // Clear previous
if (data.analysis_points && data.analysis_points.length > 0) {
explanationsSection.classList.remove('hidden');
data.analysis_points.forEach(point => {
const li = document.createElement('li');
li.className = 'text-gray-300 text-sm flex items-start gap-2.5 leading-relaxed';
li.innerHTML = `
<i class="fa-solid fa-circle-check text-brand-green text-[10px] mt-1.5 opacity-80"></i>
<span class="opacity-90">${point}</span>
`;
// Change icon for AI points
if (label.includes('ai')) {
li.innerHTML = `
<i class="fa-solid fa-circle-exclamation text-red-500 text-[10px] mt-1.5 opacity-80"></i>
<span class="opacity-90">${point}</span>
`;
}
reasonsList.appendChild(li);
});
} else {
explanationsSection.classList.add('hidden');
}
// Old Warning for "Real" images in Detection Mode (optional, keeping minimal as per request)
if (data.risk_analysis && data.risk_analysis.is_high_risk && label.includes('real')) {
const warningDiv = document.createElement('div');
warningDiv.id = 'risk-warning';
warningDiv.className = 'mt-4 p-3 rounded-xl bg-orange-500/10 border border-orange-500/20 backdrop-blur-sm animate-fade-in-up';
warningDiv.innerHTML = `
<div class="flex items-center gap-3">
<i class="fa-solid fa-triangle-exclamation text-orange-500"></i>
<p class="text-orange-200/80 text-xs">
Identity Risk Detected. Switch to the <b>Identity Risk</b> tab for details.
</p>
</div>
`;
const resultCard = resultContainer.querySelector('div');
resultCard.appendChild(warningDiv);
}
} else {
// --- Identity Risk Mode ---
modelLabel.textContent = 'Identity Theft Risk Analysis';
// Hide Detector-specific elements
if (confidenceWrapper) confidenceWrapper.classList.add('hidden');
if (explanationsSection) explanationsSection.classList.add('hidden');
// Check if analysis actually ran
if (data.prediction !== "Real") {
// It's AI, so no risk analysis
resultBadge.textContent = "N/A (AI Detected)";
resultBadge.className = 'text-xl font-semibold text-brand-gray';
const infoDiv = document.createElement('div');
infoDiv.id = 'risk-details';
infoDiv.className = 'mt-4 text-brand-gray/60 text-sm text-center';
infoDiv.textContent = "Identity risk analysis is only performed on real images.";
const resultCard = resultContainer.querySelector('div');
resultCard.appendChild(infoDiv);
return;
}
if (!data.risk_analysis || data.risk_analysis.error) {
resultBadge.textContent = "Error";
return;
}
const isHighRisk = data.risk_analysis.is_high_risk;
resultBadge.textContent = isHighRisk ? "HIGH RISK" : "LOW RISK";
resultBadge.className = 'text-3xl font-bold tracking-tight transition-colors duration-300';
if (isHighRisk) {
resultBadge.classList.add('text-red-500', 'drop-shadow-[0_0_15px_rgba(255,50,50,0.5)]');
} else {
resultBadge.classList.add('text-brand-green', 'drop-shadow-[0_0_15px_rgba(0,220,130,0.5)]');
}
// Create Detailed List
const detailsDiv = document.createElement('div');
detailsDiv.id = 'risk-details';
detailsDiv.className = 'mt-6 pt-4 border-t border-white/10 animate-fade-in-up';
let html = '<div class="space-y-3">';
// Passed Criteria (Green Check) - Logic: "Passed" means it matched the ID criteria (so it IS risky)
// Wait, my passed_criteria list means "Matches ID standard".
// So if it's in passed_criteria, it contributes to High Risk.
const passed = data.risk_analysis.passed_criteria || [];
const failed = data.risk_analysis.details || []; // Reasons why it FAILED to be an ID photo (so good for safety)
// Show "Risk Factors" (The things that make it ID-like)
if (passed.length > 0) {
html += '<h5 class="text-xs font-semibold text-brand-gray uppercase mb-2">Risk Factors (ID Compliant)</h5>';
passed.forEach(item => {
html += `
<div class="flex items-center gap-2 text-sm text-red-200/80">
<i class="fa-solid fa-check text-red-500"></i>
<span>${item}</span>
</div>`;
});
}
// Show "Safety Factors" (The things that make it SAFE / Not ID-like)
if (failed.length > 0) {
html += '<h5 class="text-xs font-semibold text-brand-gray uppercase mt-4 mb-2">Safety Factors (Non-ID)</h5>';
failed.forEach(item => {
html += `
<div class="flex items-center gap-2 text-sm text-brand-green/80">
<i class="fa-solid fa-shield text-brand-green"></i>
<span>${item}</span>
</div>`;
});
}
html += '</div>';
detailsDiv.innerHTML = html;
const resultCard = resultContainer.querySelector('div');
resultCard.appendChild(detailsDiv);
}
// --- Frequency Map Logic (Shared) ---
// This runs after the mode-specific logic
// --- Advanced Analysis Logic (Tabs + Maps) ---
const advancedToggleContainer = document.getElementById('advanced-toggle-container');
const advancedSection = document.getElementById('advanced-analysis-section');
const advancedBtn = document.getElementById('advanced-btn');
const frequencyMapImg = document.getElementById('frequency-map-img');
const patternMapImg = document.getElementById('pattern-map-img');
const tabFreq = document.getElementById('tab-freq');
const tabPattern = document.getElementById('tab-pattern');
const panelFreq = document.getElementById('panel-freq');
const panelPattern = document.getElementById('panel-pattern');
if (advancedToggleContainer && advancedSection) {
// 1. Reset State
advancedToggleContainer.classList.add('hidden');
advancedSection.classList.add('hidden');
advancedBtn.innerHTML = '<i class="fa-solid fa-chart-simple mr-2"></i>View Advanced Frequency Analysis';
// 2. Check if we have data to show
if (currentMode === 'detector' && (data.frequency_analysis || data.pattern_analysis)) {
advancedToggleContainer.classList.remove('hidden');
// Populate Images
if (data.frequency_analysis && frequencyMapImg) frequencyMapImg.src = 'data:image/png;base64,' + data.frequency_analysis;
if (data.pattern_analysis && patternMapImg) patternMapImg.src = 'data:image/png;base64,' + data.pattern_analysis;
// 3. Toggle Section Visibility
const newBtn = advancedBtn.cloneNode(true);
advancedBtn.parentNode.replaceChild(newBtn, advancedBtn);
newBtn.addEventListener('click', () => {
const isHidden = advancedSection.classList.contains('hidden');
if (isHidden) {
advancedSection.classList.remove('hidden');
newBtn.innerHTML = '<i class="fa-solid fa-chevron-up mr-2"></i>Hide Advanced Analysis';
} else {
advancedSection.classList.add('hidden');
newBtn.innerHTML = '<i class="fa-solid fa-chart-simple mr-2"></i>View Advanced Frequency Analysis';
}
});
// 4. Tab Switching Logic
// Update References & remove old listeners
const newTabFreq = tabFreq.cloneNode(true);
if (tabFreq) tabFreq.parentNode.replaceChild(newTabFreq, tabFreq);
const newTabPattern = tabPattern.cloneNode(true);
if (tabPattern) tabPattern.parentNode.replaceChild(newTabPattern, tabPattern);
const setActive = (activeTab, activePanel) => {
// Reset all
[newTabFreq, newTabPattern].forEach(t => {
if (t) t.className = "relative px-4 py-2 text-xs font-medium text-brand-gray bg-black/40 rounded-t-lg transition-colors border-t border-x border-white/5 hover:bg-white/5 hover:text-white cursor-pointer";
});
if (panelFreq) panelFreq.classList.add('hidden');
if (panelPattern) panelPattern.classList.add('hidden');
// Set Active
if (activeTab) activeTab.className = "relative px-4 py-2 text-xs font-medium text-white bg-white/10 rounded-t-lg transition-colors border-t border-x border-white/10 hover:bg-white/20 z-10 cursor-default";
if (activePanel) activePanel.classList.remove('hidden');
};
if (newTabFreq) newTabFreq.addEventListener('click', () => setActive(newTabFreq, panelFreq));
if (newTabPattern) newTabPattern.addEventListener('click', () => setActive(newTabPattern, panelPattern));
// Default to Freq
setActive(newTabFreq, panelFreq);
}
}
}
});