File size: 4,701 Bytes
449f607
 
 
 
 
 
1af3855
 
 
 
 
 
 
 
 
 
 
 
 
 
449f607
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1af3855
 
 
 
 
 
 
0a23511
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1af3855
 
0a23511
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125

// 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 = `
        <div class="bg-gray-800 rounded-xl p-8 max-w-md w-full">
            <h2 class="text-2xl font-bold mb-4">Test Results</h2>
            <div class="space-y-4">
                <div class="flex justify-between">
                    <span class="text-gray-400">WPM:</span>
                    <span class="font-bold">${stats.wpm}</span>
                </div>
                <div class="flex justify-between">
                    <span class="text-gray-400">Accuracy:</span>
                    <span class="font-bold">${stats.accuracy}%</span>
                </div>
                <div class="flex justify-between">
                    <span class="text-gray-400">Time:</span>
                    <span class="font-bold">${formatTime(stats.timeElapsed)}</span>
                </div>
                <div class="flex justify-between">
                    <span class="text-gray-400">Characters:</span>
                    <span class="font-bold">${stats.totalChars}</span>
                </div>
            </div>
            <div class="mt-6 flex justify-end space-x-3">
                <button id="closeModal" class="px-4 py-2 bg-gray-700 rounded-lg hover:bg-gray-600">Close</button>
                <button id="tryAgain" class="px-4 py-2 bg-blue-600 rounded-lg hover:bg-blue-700">Try Again</button>
            </div>
        </div>
    `;
    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));
}