document.addEventListener('DOMContentLoaded', () => { const form = document.getElementById("predictionForm"); const resultContainer = document.getElementById("result-container"); if (resultContainer) { resultContainer.style.display = 'none'; } // Validation helper const validateField = (input) => { const value = input.value.trim(); const min = parseFloat(input.min); const max = parseFloat(input.max); if (!value) return false; if (input.type === 'number') { const numValue = parseFloat(value); return !isNaN(numValue) && numValue >= min && numValue <= max; } return true; }; // Add validation feedback const addValidationFeedback = (input) => { const validateInput = () => { const formGroup = input.closest('.form-group'); const value = input.value.trim(); const isValid = value !== '' && validateField(input); formGroup.classList.remove('error', 'success'); formGroup.classList.add(isValid ? 'success' : 'error'); const existingMessage = formGroup.querySelector('.validation-message'); if (existingMessage) existingMessage.remove(); const message = document.createElement('div'); message.className = `validation-message ${isValid ? 'success' : 'error'}`; message.textContent = value === '' ? 'This field is required' : (isValid ? 'Valid input' : 'Please check the value'); formGroup.appendChild(message); return isValid; }; input.addEventListener('input', validateInput); input.addEventListener('blur', validateInput); }; document.querySelectorAll('input, select').forEach(addValidationFeedback); // Animate counter function animateCounter(element, target) { let start = 0; const duration = 1000; const stepTime = Math.abs(Math.floor(duration / target)); const timer = setInterval(() => { start++; element.textContent = start + '%'; if (start >= target) clearInterval(timer); }, stepTime); } // Update gauge SVG function updateGauge(elementId, percent, textId, tooltipId, tooltipMessages) { const gauge = document.querySelector(elementId); const text = document.querySelector(textId); const tooltip = document.querySelector(tooltipId); const radius = elementId.includes('radial') ? 40 : 54; const circumference = 2 * Math.PI * radius; const offset = circumference - (percent / 100) * circumference; gauge.style.strokeDasharray = `${circumference} ${circumference}`; gauge.style.strokeDashoffset = offset; if (text) text.textContent = `${Math.round(percent)}%`; if (tooltip && tooltipMessages) { if (percent >= 75) { tooltip.textContent = tooltipMessages.high; } else if (percent >= 50) { tooltip.textContent = tooltipMessages.medium; } else { tooltip.textContent = tooltipMessages.low; } } } // Mapping of recommendation keywords to professional resources const resourceLinks = { 'training': { url: 'https://www.acsm.org/docs/default-source/files-for-resource-library/acsms-guidelines-for-exercise-testing-and-prescription-(10th-ed.).pdf', source: 'American College of Sports Medicine' }, 'intensity': { url: 'https://www.mayoclinic.org/healthy-lifestyle/fitness/in-depth/exercise-intensity/art-20046887', source: 'Mayo Clinic' }, 'recovery': { url: 'https://www.ncbi.nlm.nih.gov/pmc/articles/PMC5592286/', source: 'PubMed - Sports Medicine Journal' }, 'rest': { url: 'https://www.sleepfoundation.org/physical-activity/athletic-performance-and-sleep', source: 'Sleep Foundation' }, 'injury': { url: 'https://orthoinfo.aaos.org/en/diseases--conditions/sports-injuries/', source: 'American Academy of Orthopaedic Surgeons' }, 'prevention': { url: 'https://www.nata.org/professional-interests/safe-sports-environments/injury-prevention', source: 'National Athletic Trainers\' Association' } }; // Enhanced recommendation processing function enhanceRecommendations(recommendations, factorData, featureImportance) { const categories = { 'Training Adjustments': [], 'Recovery Strategies': [], 'Injury Prevention': [] }; recommendations.forEach((rec, index) => { let category = 'Injury Prevention'; if (rec.toLowerCase().includes('training') || rec.toLowerCase().includes('intensity')) { category = 'Training Adjustments'; } else if (rec.toLowerCase().includes('recovery') || rec.toLowerCase().includes('rest')) { category = 'Recovery Strategies'; } const priority = calculatePriority(rec, factorData, featureImportance); // Find the most relevant resource link let resource = { url: 'https://www.healthline.com/health/sports-injuries', source: 'Healthline' }; for (const [keyword, link] of Object.entries(resourceLinks)) { if (rec.toLowerCase().includes(keyword)) { resource = link; break; } } categories[category].push({ id: `rec-${index}`, text: rec, priority: priority, details: generateDetails(rec, factorData), completed: false, resourceUrl: resource.url, resourceSource: resource.source }); }); Object.keys(categories).forEach(category => { categories[category].sort((a, b) => b.priority - a.priority); }); return categories; } function calculatePriority(recommendation, factorData, featureImportance) { let priority = 0.5; const keywords = { 'Training_Load_Score': ['training', 'intensity'], 'Fatigue_Level': ['fatigue', 'rest'], 'Recovery_Time_Between_Sessions': ['recovery', 'rest'], 'Previous_Injury_Count': ['injury', 'prevention'] }; Object.keys(keywords).forEach(key => { if (keywords[key].some(kw => recommendation.toLowerCase().includes(kw))) { const impact = (factorData[key] || 0) * (featureImportance[key] || 0); priority = Math.max(priority, impact); } }); return Math.min(1, Math.max(0, priority)); } function generateDetails(recommendation, factorData) { if (recommendation.toLowerCase().includes('training') && factorData.High_Intensity_Training_Hours > 10) { return `Reduce high-intensity sessions by 1-2 hours/week to lower training load.`; } else if (recommendation.toLowerCase().includes('recovery') && factorData.Recovery_Time_Between_Sessions < 12) { return `Increase rest periods to at least 12 hours between sessions.`; } else if (recommendation.toLowerCase().includes('injury') && factorData.Previous_Injury_Count > 2) { return `Consult a physiotherapist to address recurring injury risks.`; } return `Follow this recommendation consistently for optimal results.`; } function loadCompletedRecommendations() { const stored = localStorage.getItem('completedRecommendations'); return stored ? JSON.parse(stored) : {}; } function saveCompletedRecommendations(completed) { localStorage.setItem('completedRecommendations', JSON.stringify(completed)); } if (form) { form.addEventListener("submit", async (e) => { e.preventDefault(); document.querySelectorAll('.validation-message').forEach(msg => msg.remove()); document.querySelectorAll('.form-group').forEach(group => group.classList.remove('error')); const inputs = form.querySelectorAll('input, select'); let isValid = true; inputs.forEach(input => { const value = input.value.trim(); const formGroup = input.closest('.form-group'); if (value === '') { isValid = false; formGroup.classList.add('error'); const message = document.createElement('div'); message.className = 'validation-message error'; message.innerHTML = `Required: Please fill out this field`; formGroup.appendChild(message); input.classList.add('shake'); setTimeout(() => input.classList.remove('shake'), 500); input.style.borderColor = '#ef4444'; input.style.boxShadow = '0 0 10px rgba(239, 68, 68, 0.3)'; } }); if (!isValid) { const firstError = form.querySelector('.form-group.error'); if (firstError) { firstError.scrollIntoView({ behavior: 'smooth', block: 'center' }); } return; } const rawData = Object.fromEntries(new FormData(form).entries()); const formData = new FormData(form); const data = Object.fromEntries(formData.entries()); const genderMap = { "Male": 0, "Female": 1 }; const experienceMap = { "Beginner": 0, "Intermediate": 1, "Advanced": 2, "Professional": 3 }; const injuryTypeMap = { "None": 0, "Sprain": 1, "Ligament Tear": 2, "Tendonitis": 3, "Strain": 4, "Fracture": 5 }; const sportTypeMap = { "Football": 0, "Basketball": 1, "Swimming": 2, "Running": 3, "Tennis": 4, "Volleyball": 5 }; data.Gender = genderMap[data.Gender] ?? 0; data.Experience_Level = experienceMap[data.Experience_Level] ?? 0; data.Previous_Injury_Type = injuryTypeMap[data.Previous_Injury_Type] ?? 0; data.Sport_Type = sportTypeMap[data.Sport_Type] ?? 0; Object.keys(data).forEach(key => { data[key] = isNaN(data[key]) ? 0 : parseFloat(data[key]); }); const outlierWarnings = []; if (data.Age < 18 && data.Total_Weekly_Training_Hours > 40) { outlierWarnings.push('Unusual training hours for age under 18. Consider reducing to prevent burnout.'); } if (data.Sprint_Speed > 10 && data.Age > 50) { outlierWarnings.push('Sprint speed seems high for age over 50. Please verify this value.'); } if (data.Fatigue_Level > 8 && data.Recovery_Time_Between_Sessions < 12) { outlierWarnings.push('High fatigue with low recovery time may skew results. Consider adjusting inputs.'); } if (outlierWarnings.length > 0) { const warningDiv = document.createElement('div'); warningDiv.style.color = '#facc15'; warningDiv.style.marginBottom = '20px'; warningDiv.innerHTML = `Warning:
Error: ${err.message}. Please ensure the prediction server is running and try again.
`; form.appendChild(resultOutput); } }); } });