// At the beginning of your file, add this to ensure CLASS_DESCRIPTIONS is defined // This assumes your class descriptions are passed from the Flask backend const CLASS_DESCRIPTIONS = window.CLASS_DESCRIPTIONS || { 'akiec': { name: 'Actinic Keratosis', description: 'A precancerous growth caused by sun damage.' }, 'bcc': { name: 'Basal Cell Carcinoma', description: 'The most common type of skin cancer.' }, 'bkl': { name: 'Benign Keratosis', description: 'A non-cancerous growth on the skin.' }, 'df': { name: 'Dermatofibroma', description: 'A common benign skin growth.' }, 'mel': { name: 'Melanoma', description: 'The most serious form of skin cancer.' }, 'nv': { name: 'Melanocytic Nevus', description: 'A common mole.' }, 'vasc': { name: 'Vascular Lesion', description: 'An abnormality of blood vessels.' } }; // Define CONDITION_INFO if not already defined const CONDITION_INFO = window.CONDITION_INFO || { 'akiec': { severity: 'moderate', description: 'Actinic Keratosis is a precancerous growth caused by sun damage.', resources: [{ name: 'Mayo Clinic', url: 'https://www.mayoclinic.org/diseases-conditions/actinic-keratosis/symptoms-causes/syc-20354969' }] }, 'bcc': { severity: 'high', description: 'Basal Cell Carcinoma is the most common type of skin cancer.', resources: [{ name: 'Skin Cancer Foundation', url: 'https://www.skincancer.org/skin-cancer-information/basal-cell-carcinoma/' }] }, 'bkl': { severity: 'low', description: 'Benign Keratosis is a non-cancerous growth that appears as a waxy, scaly growth on the skin.', resources: [{ name: 'American Academy of Dermatology', url: 'https://www.aad.org/public/diseases/bumps-and-growths/seborrheic-keratoses' }] }, 'df': { severity: 'low', description: 'Dermatofibroma is a common benign skin growth that often appears as a small, firm bump.', resources: [{ name: 'DermNet NZ', url: 'https://dermnetnz.org/topics/dermatofibroma' }] }, 'mel': { severity: 'very high', description: 'Melanoma is the most serious form of skin cancer that develops in the cells that produce melanin.', resources: [{ name: 'American Cancer Society', url: 'https://www.cancer.org/cancer/melanoma-skin-cancer.html' }] }, 'nv': { severity: 'low', description: 'Melanocytic Nevus is a common mole that appears as a small, dark brown spot caused by clusters of pigmented cells.', resources: [{ name: 'Cleveland Clinic', url: 'https://my.clevelandclinic.org/health/diseases/21880-moles' }] }, 'vasc': { severity: 'moderate', description: 'Vascular Lesion is an abnormality of blood vessels that can appear as red or purple marks on the skin.', resources: [{ name: 'Stanford Health Care', url: 'https://stanfordhealthcare.org/medical-conditions/skin-hair-and-nails/vascular-malformations.html' }] } }; document.addEventListener('DOMContentLoaded', function() { // DOM Elements const uploadBox = document.getElementById('upload-box'); const fileInput = document.getElementById('file-input'); const previewContainer = document.getElementById('preview-container'); const previewImage = document.getElementById('image-preview') const removeImageBtn = document.getElementById('remove-image'); const analyzeBtn = document.getElementById('analyze-button'); const uploadForm = document.getElementById('upload-form'); const loadingIndicator = document.getElementById('loading'); const resultContainer = document.getElementById('result'); const errorMessage = document.getElementById('error-message'); const errorText = document.getElementById('error-text'); const modelSelect = document.getElementById('model-select'); const modelUsedBadge = document.getElementById('model-used-badge'); // Tab elements const tabButtons = document.querySelectorAll('.tab-button'); const tabPanes = document.querySelectorAll('.tab-pane'); // Result elements const resultImage = document.getElementById('result-image'); const predictionElement = document.getElementById('prediction'); const descriptionElement = document.getElementById('description'); const confidenceElement = document.getElementById('confidence'); const confidenceFill = document.getElementById('confidence-fill'); const probabilitiesContainer = document.getElementById('probabilities'); const severityIndicator = document.getElementById('severity-indicator'); const infoConditionName = document.getElementById('info-condition-name'); const conditionInformation = document.getElementById('condition-information'); const resourcesList = document.getElementById('resources-list'); const saveResultBtn = document.getElementById('save-result'); const newAnalysisBtn = document.getElementById('new-analysis'); // State let fileSelected = false; // Event Listeners uploadBox.addEventListener('click', function() { fileInput.click(); }); fileInput.addEventListener('change', handleFileSelect); removeImageBtn.addEventListener('click', function(e) { e.stopPropagation(); resetUpload(); }); uploadForm.addEventListener('submit', handleFormSubmit); tabButtons.forEach(button => { button.addEventListener('click', function() { const tabName = this.getAttribute('data-tab'); switchTab(tabName); }); }); newAnalysisBtn.addEventListener('click', resetAll); saveResultBtn.addEventListener('click', saveResults); // Functions function handleFileSelect(e) { const file = e.target.files[0]; if (!file) return; if (!file.type.match('image.*')) { showError('Please select an image file (JPEG, PNG, etc.)'); return; } const reader = new FileReader(); reader.onload = function(e) { previewImage.src = e.target.result; previewContainer.classList.remove('hidden'); removeImageBtn.classList.remove('hidden'); uploadBox.classList.add('has-image'); fileSelected = true; analyzeBtn.disabled = false; }; reader.readAsDataURL(file); } function resetUpload() { fileInput.value = ''; previewContainer.classList.add('hidden'); removeImageBtn.classList.add('hidden'); uploadBox.classList.remove('has-image'); fileSelected = false; analyzeBtn.disabled = true; } function handleFormSubmit(e) { e.preventDefault(); if (!fileSelected) { showError('Please select an image to analyze'); return; } // Hide any previous results or errors resultContainer.classList.add('hidden'); errorMessage.classList.add('hidden'); // Show loading indicator loadingIndicator.style.display = 'block'; // Get the selected model const selectedModel = modelSelect.value; // Create form data const formData = new FormData(); formData.append('file', fileInput.files[0]); formData.append('model', selectedModel); console.log('Uploading file:', fileInput.files[0].name); console.log('Selected model:', selectedModel); // Send the request fetch('/predict', { method: 'POST', body: formData }) .then(response => { if (!response.ok) { return response.json().then(data => { throw new Error(data.error || `Server error: ${response.status}`); }).catch(e => { // If we can't parse JSON, use the status text throw new Error(`Server error: ${response.status} ${response.statusText}`); }); } return response.json(); }) .then(data => { // Hide loading indicator loadingIndicator.style.display = 'none'; // Check if we have predictions if (!data.predictions || data.predictions.length === 0) { throw new Error('No predictions returned from the server'); } // Display results displayResults(data, selectedModel); }) .catch(error => { // Hide loading indicator loadingIndicator.style.display = 'none'; // Show error message showError(`Error: ${error.message}`); console.error('Error:', error); }); } function displayResults(data, modelName) { // Set the model badge modelUsedBadge.textContent = modelName; // Set the result image resultImage.src = previewImage.src; // Get the top prediction const topPrediction = data.predictions[0]; const predictionClass = topPrediction.class; const confidence = topPrediction.confidence; // Set prediction details predictionElement.textContent = CLASS_DESCRIPTIONS[predictionClass].name; descriptionElement.textContent = CLASS_DESCRIPTIONS[predictionClass].description; // Set confidence const confidencePercent = Math.round(confidence * 100); confidenceElement.textContent = confidencePercent + '%'; confidenceFill.style.width = confidencePercent + '%'; // Set confidence color based on value if (confidencePercent >= 80) { confidenceFill.style.backgroundColor = 'var(--success-color)'; } else if (confidencePercent >= 50) { confidenceFill.style.backgroundColor = 'var(--warning-color)'; } else { confidenceFill.style.backgroundColor = 'var(--danger-color)'; } // Display severity indicator const conditionInfo = CONDITION_INFO[predictionClass]; let severityHTML = ''; if (conditionInfo && conditionInfo.severity) { let severityClass = ''; let severityIcon = ''; switch (conditionInfo.severity) { case 'low': severityClass = 'severity-low'; severityIcon = 'fa-check-circle'; break; case 'moderate': severityClass = 'severity-moderate'; severityIcon = 'fa-exclamation-circle'; break; case 'high': severityClass = 'severity-high'; severityIcon = 'fa-exclamation-triangle'; break; case 'very high': severityClass = 'severity-very-high'; severityIcon = 'fa-radiation'; break; } severityHTML = `
This is based on typical characteristics of this condition.
`; } severityIndicator.innerHTML = severityHTML; // Display all probabilities probabilitiesContainer.innerHTML = ''; data.predictions.forEach(prediction => { const probabilityPercent = Math.round(prediction.confidence * 100); const probabilityHTML = `