File size: 10,666 Bytes
993c9ec
af019c1
 
 
d4b0200
2ff6be1
993c9ec
0cb4699
 
993c9ec
 
0cb4699
 
 
 
 
 
 
2ff6be1
0cb4699
 
 
2ff6be1
 
 
0cb4699
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
af019c1
0cb4699
 
 
 
 
993c9ec
0cb4699
 
 
 
 
 
 
993c9ec
0cb4699
 
 
 
2ff6be1
993c9ec
0cb4699
 
2ff6be1
993c9ec
 
0cb4699
 
 
 
 
 
 
 
 
 
 
 
 
 
af019c1
0cb4699
 
 
 
993c9ec
 
0cb4699
 
 
 
993c9ec
 
0cb4699
 
 
 
 
 
 
 
 
 
993c9ec
 
0cb4699
 
 
993c9ec
 
0cb4699
 
 
 
 
 
 
 
993c9ec
 
0cb4699
 
 
 
 
 
 
 
 
 
 
95d824a
0cb4699
af019c1
 
 
 
 
993c9ec
 
 
 
 
 
 
 
 
 
 
c4ac914
993c9ec
 
 
 
 
 
 
 
 
c4ac914
 
e36c22c
 
c4ac914
e36c22c
af019c1
 
 
 
 
 
 
c4ac914
af019c1
 
 
c4ac914
993c9ec
 
 
 
 
 
 
 
 
 
 
 
 
e36c22c
993c9ec
 
 
 
 
 
 
 
 
 
 
 
2b9c1d8
993c9ec
2b9c1d8
 
993c9ec
2b9c1d8
 
 
 
 
 
 
 
993c9ec
2b9c1d8
993c9ec
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
628b3ee
993c9ec
 
 
628b3ee
993c9ec
 
 
 
 
 
e36c22c
 
993c9ec
 
628b3ee
 
 
e36c22c
 
628b3ee
 
e36c22c
 
0270f27
 
 
 
 
 
0cb4699
0270f27
bdf6428
 
 
0cb4699
 
 
bdf6428
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0270f27
bdf6428
 
0cb4699
0270f27
bdf6428
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
993c9ec
 
 
 
 
 
79c9f2f
 
 
 
 
 
 
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381

// Code snippets for typing test grouped by language
const codeSnippets = {
    html: [
`<!DOCTYPE html>
<html lang="en">
<head>
  <title>Document</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <h1>Hello World</h1>
  <script src="script.js"></script>
</body>
</html>`,

`<div class="container">
  <header class="header">
    <nav>
      <ul class="nav-list">
        <li><a href="/">Home</a></li>
        <li><a href="/about">About</a></li>
      </ul>
    </nav>
  </header>
  <main id="main-content"></main>
  <footer>&copy; ${new Date().getFullYear()}</footer>
</div>`,

`<form id="login-form">
  <div class="form-group">
    <label for="email">Email</label>
    <input type="email" id="email" required>
  </div>
  <div class="form-group">
    <label for="password">Password</label>
    <input type="password" id="password" required>
  </div>
  <button type="submit">Login</button>
</form>`
    ],
    javascript: [
`function calculateTotal(items) {
  return items.reduce((total, item) => {
    return total + (item.price * item.quantity);
  }, 0);
}

const cart = [
  { name: 'Shirt', price: 25, quantity: 2 },
  { name: 'Pants', price: 40, quantity: 1 }
];

const total = calculateTotal(cart);
console.log('Total:', total);`,

`class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  greet() {
    console.log(\`Hello, my name is \${this.name}\`);
  }
}

const person1 = new Person('Alice', 30);
person1.greet();`,

`const fetchData = async () => {
  try {
    const response = await fetch('api/data');
    const data = await response.json();
    return data;
  } catch (error) {
    console.error('Error:', error);
    return null;
  }
};`
    ],
    css: [
`.container {
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 20px;
}

.header {
  background: #333;
  color: white;
  padding: 1rem;
}

.nav-list {
  display: flex;
  gap: 1rem;
  list-style: none;
}`,

`body {
  font-family: 'Arial', sans-serif;
  line-height: 1.6;
  color: #333;
}

h1, h2, h3 {
  color: #222;
  margin-bottom: 1rem;
}

a {
  color: #0066cc;
  text-decoration: none;
}`,

`@keyframes fadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}

.modal {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0,0,0,0.5);
  display: flex;
  justify-content: center;
  align-items: center;
  animation: fadeIn 0.3s ease;
}`
    ]
};
// Get all snippets as one array for "all languages" option
function getAllSnippets() {
    return Object.values(codeSnippets).flat();
}
// DOM elements
const codeDisplay = document.getElementById('code-display');
const inputField = document.getElementById('input-field');
const timerElement = document.getElementById('timer');
const speedElement = document.getElementById('speed');
const resultsSection = document.getElementById('results');
const finalWPM = document.getElementById('final-wpm');
const finalAccuracy = document.getElementById('final-accuracy');
const finalTime = document.getElementById('final-time');
const tryAgainBtn = document.getElementById('try-again-btn');
// Game variables
let timeLeft = parseInt(document.getElementById('time-select').value);
let timer;
let isPlaying = false;
let startTime;
let endTime;
let correctChars = 0;
let totalChars = 0;

// Initialize the game
function initGame() {
    // Get selected time
    const timeSelect = document.getElementById('time-select');
    const selectedTime = parseInt(timeSelect.value);
    timeLeft = selectedTime;
    timerElement.textContent = `${timeLeft}s`;
// Get selected language
    const languageSelect = document.getElementById('language-select');
    const selectedLanguage = languageSelect.value;
    
    // Get appropriate snippets array
    const snippets = selectedLanguage === 'all' 
        ? getAllSnippets() 
        : codeSnippets[selectedLanguage] || getAllSnippets();
    
    // Select a random code snippet from the filtered list
    const randomSnippet = snippets[Math.floor(Math.random() * snippets.length)];
totalChars = randomSnippet.length;
// Display the code with spans for each character
    codeDisplay.innerHTML = '';
    randomSnippet.split('').forEach(char => {
        const span = document.createElement('span');
        span.textContent = char;
        codeDisplay.appendChild(span);
    });
    
    // Highlight the first character
    codeDisplay.children[0].classList.add('current');
    // Reset game state
    inputField.value = '';
    inputField.disabled = false;
    inputField.focus();
    timeLeft = parseInt(document.getElementById('time-select').value);
    timerElement.textContent = `${timeLeft}s`;
    correctChars = 0;
    isPlaying = false;
    resultsSection.classList.add('hidden');
}

// Start the game
function startGame() {
    if (isPlaying) return;
    
    isPlaying = true;
    startTime = new Date();
    let lastUpdate = Date.now();
    timer = setInterval(() => {
        const now = Date.now();
        const elapsed = now - lastUpdate;
        
        if (elapsed >= 1000) {
            timeLeft--;
            timerElement.textContent = `${timeLeft}s`;
            lastUpdate = now;
            
            if (timeLeft <= 0) {
                endGame();
            }
        }
    }, 100);
}

// Update the display as user types
function updateDisplay() {
    const inputText = inputField.value;
    const codeSpans = codeDisplay.children;
    
    // Reset all spans
    for (let i = 0; i < codeSpans.length; i++) {
        codeSpans[i].className = '';
    }
    
    // Mark correct/incorrect characters
    let allCorrect = true;
    for (let i = 0; i < inputText.length; i++) {
        if (i < codeSpans.length) {
            if (inputText[i] === codeSpans[i].textContent) {
                codeSpans[i].classList.add('correct');
                if (i === inputText.length - 1) {
                    codeSpans[i].classList.add('current');
                }
            } else {
                codeSpans[i].classList.add('incorrect');
                allCorrect = false;
            }
        }
    }
    
    // Highlight next character if all correct so far
    if (allCorrect && inputText.length < codeSpans.length) {
        codeSpans[inputText.length].classList.add('current');
    }
    
    // Calculate and display typing speed
    if (isPlaying) {
        const elapsedTime = (new Date() - startTime) / 60000; // in minutes
        const wordsTyped = inputText.length / 5; // standard word length
        const wpm = Math.round(wordsTyped / elapsedTime) || 0;
        speedElement.textContent = `${wpm} WPM`;
    }
    
    // Count correct characters for accuracy
    correctChars = 0;
    for (let i = 0; i < inputText.length && i < codeSpans.length; i++) {
        if (inputText[i] === codeSpans[i].textContent) {
            correctChars++;
        }
    }
    // End game if all characters are typed correctly
    if (inputText.length === codeSpans.length && allCorrect) {
        endGame(true);
    }
}
// End the game
function endGame(isCompleted = false) {
    clearInterval(timer);
    isPlaying = false;
    inputField.disabled = true;
    endTime = new Date();
    
    // Calculate results
    const timeTaken = Math.max(1, parseInt(document.getElementById('time-select').value) - timeLeft); // at least 1 second to avoid division by zero
const wordsTyped = correctChars / 5;
    const wpm = Math.round((wordsTyped / timeTaken) * 60);
    const accuracy = Math.round((correctChars / totalChars) * 100);
    
    // Only show results if code was completed or time ran out
    if (isCompleted || timeLeft <= 0) {
        const totalTime = parseInt(document.getElementById('time-select').value);
// Display results
        finalWPM.textContent = wpm;
        finalAccuracy.textContent = `${accuracy}%`;
        finalTime.textContent = `${totalTime - timeLeft}s`;
resultsSection.classList.remove('hidden');

        // Save results to database
        const language = document.getElementById('language-select').value;
        saveResults(language, wpm, accuracy, timeTaken);
    }
}
// Mock save results function for local testing
async function saveResults(language, wpm, accuracy, timeTaken) {
    const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}');
    
    const result = {
        language,
        wpm,
        accuracy,
        time_taken: timeTaken,
        name: userInfo.name || 'Anonymous',
        grade: userInfo.grade || 'N/A',
        section: userInfo.section || 'N/A',
        created_at: new Date().toISOString()
    };

    console.log('Results would be saved:', result);
    
    // In a real app, you would send this to your backend
    // await fetch('/api/results', {
    //     method: 'POST',
    //     headers: { 'Content-Type': 'application/json' },
    //     body: JSON.stringify(result)
    // });

    return { success: true, data: result };
}
// Mock leaderboard data for local testing
async function fetchLeaderboard() {
    // Try to get from localStorage if no backend
    const savedResults = JSON.parse(localStorage.getItem('savedResults') || '[]');
    
    // Add some sample data if empty
    if (savedResults.length === 0) {
        return [
            { 
                name: 'John Doe', 
                grade: '10', 
                section: 'A', 
                language: 'javascript', 
                wpm: 85, 
                accuracy: 98, 
                time_taken: 120, 
                created_at: new Date().toISOString() 
            },
            { 
                name: 'Jane Smith', 
                grade: '11', 
                section: 'B', 
                language: 'html', 
                wpm: 75, 
                accuracy: 95, 
                time_taken: 180, 
                created_at: new Date().toISOString() 
            },
            { 
                name: 'Alex Johnson', 
                grade: '9', 
                section: 'C', 
                language: 'css', 
                wpm: 65, 
                accuracy: 92, 
                time_taken: 150, 
                created_at: new Date().toISOString() 
            }
        ];
    }

    return savedResults;
}
// Event listeners
inputField.addEventListener('input', updateDisplay);
inputField.addEventListener('focus', startGame);
tryAgainBtn.addEventListener('click', initGame);

// Reset game when time selection changes
document.getElementById('time-select').addEventListener('change', initGame);
// Initialize the game on page load and when language changes
document.addEventListener('DOMContentLoaded', () => {
    initGame();
    document.getElementById('language-select').addEventListener('change', initGame);
});