// Shared utility functions and state const appState = { currentTest: null, activeButton: null }; // Format time as MM:SS function formatTime(seconds) { const mins = Math.floor(seconds / 60); const secs = seconds % 60; return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`; } // Calculate typing statistics function calculateTypingStats(startTime, endTime, correctChars, totalChars) { const timeElapsed = (endTime - startTime) / 1000; const wpm = Math.round((correctChars / 5) / (timeElapsed / 60)); const accuracy = totalChars > 0 ? Math.round((correctChars / totalChars) * 100) : 0; return { wpm, accuracy, timeElapsed, totalChars }; } // Highlight active button function setActiveButton(button, category) { if (appState.activeButton) { appState.activeButton.classList.remove('active-button'); } button.classList.add('active-button'); appState.activeButton = button; appState.currentTest = { ...appState.currentTest, [category]: button.dataset[category] }; } // Show modal with results function showResultsModal(stats) { const modal = document.createElement('div'); modal.className = 'fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50'; modal.innerHTML = `

Test Results

WPM: ${stats.wpm}
Accuracy: ${stats.accuracy}%
Time: ${formatTime(stats.timeElapsed)}
Characters: ${stats.totalChars}
`; document.body.appendChild(modal); modal.querySelector('#closeModal').addEventListener('click', () => { modal.remove(); window.location.href = 'leaderboard.html'; }); modal.querySelector('#tryAgain').addEventListener('click', () => { modal.remove(); window.location.href = 'typing-test.html'; }); } // Save result to leaderboard function saveToLeaderboard(result) { const userData = JSON.parse(localStorage.getItem('userData')); if (!userData) return; let leaderboard = JSON.parse(localStorage.getItem('leaderboard')) || []; // Check if user already has an entry and update if new score is higher const existingEntryIndex = leaderboard.findIndex( entry => entry.name === userData.name && entry.gradeLevel === userData.gradeLevel && entry.section === userData.section ); if (existingEntryIndex >= 0) { // Update only if new score is better if (result.wpm > leaderboard[existingEntryIndex].wpm) { leaderboard[existingEntryIndex] = { ...leaderboard[existingEntryIndex], wpm: result.wpm, accuracy: result.accuracy, time: result.timeElapsed, chars: result.totalChars, timestamp: new Date().toISOString() }; } } else { // Add new entry leaderboard.push({ name: userData.name, gradeLevel: userData.gradeLevel, section: userData.section, wpm: result.wpm, accuracy: result.accuracy, time: result.timeElapsed, chars: result.totalChars, timestamp: new Date().toISOString() }); } // Sort by WPM descending leaderboard.sort((a, b) => b.wpm - a.wpm); // Keep only top 100 entries if (leaderboard.length > 100) { leaderboard = leaderboard.slice(0, 100); } localStorage.setItem('leaderboard', JSON.stringify(leaderboard)); }