ItsJayKee commited on
Commit
1222789
·
verified ·
1 Parent(s): 0a23511

Manual changes saved

Browse files
Files changed (1) hide show
  1. typing-test.html +140 -809
typing-test.html CHANGED
@@ -4,827 +4,158 @@
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>CodeTyper Racer - Challenge</title>
7
- <link rel="stylesheet" href="style.css">
 
8
  <script src="https://cdn.tailwindcss.com"></script>
9
- <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
 
10
  <script src="https://unpkg.com/feather-icons"></script>
11
  </head>
12
- <body class="bg-gray-900 text-gray-100 min-h-screen">
13
- <header class="bg-gray-800 py-4 shadow-lg">
14
- <div class="container mx-auto px-4 flex justify-between items-center">
15
- <h1 class="text-xl font-bold">CodeTyper Racer 🚀</h1>
16
- <div class="flex items-center space-x-4">
17
- <a href="leaderboard.html" class="flex items-center space-x-1 bg-gray-700 px-3 py-2 rounded-lg hover:bg-gray-600 transition">
18
- <i data-feather="award"></i>
19
- <span>Leaderboard</span>
20
- </a>
21
- <div id="timerDisplay" class="bg-gray-700 px-4 py-2 rounded-lg font-mono">
22
- 00:00
23
- </div>
24
- <button id="leaderboardBtn" class="flex items-center space-x-1 bg-gray-700 px-3 py-2 rounded-lg hover:bg-gray-600 transition">
25
- <i data-feather="bar-chart-2"></i>
26
- <span>Leaderboard</span>
27
- </button>
28
- </div>
29
- </div>
30
- </header>
31
-
32
- <main class="container mx-auto px-4 py-8">
33
- <div class="flex flex-col lg:flex-row gap-8">
34
- <!-- Settings Panel -->
35
- <div class="lg:w-1/4 bg-gray-800 p-6 rounded-xl shadow-lg">
36
- <div class="mb-6">
37
- <h2 class="text-lg font-semibold mb-3">Language</h2>
38
- <div class="space-y-2">
39
- <button data-language="html" class="language-btn w-full bg-blue-600 text-white py-2 rounded-lg transition hover:bg-blue-700">
40
- HTML
41
- </button>
42
- <button data-language="css" class="language-btn w-full bg-blue-600 text-white py-2 rounded-lg transition hover:bg-blue-700">
43
- CSS
44
- </button>
45
- <button data-language="javascript" class="language-btn w-full bg-blue-600 text-white py-2 rounded-lg transition hover:bg-blue-700">
46
- JavaScript
47
- </button>
48
- </div>
49
- </div>
50
-
51
- <div class="mb-6">
52
- <h2 class="text-lg font-semibold mb-3">Timer</h2>
53
- <div class="grid grid-cols-2 gap-2">
54
- <button data-time="1" class="time-btn bg-gray-700 py-2 rounded-lg hover:bg-gray-600 transition">
55
- 1 min
56
- </button>
57
- <button data-time="5" class="time-btn bg-gray-700 py-2 rounded-lg hover:bg-gray-600 transition">
58
- 5 min
59
- </button>
60
- <button data-time="10" class="time-btn bg-gray-700 py-2 rounded-lg hover:bg-gray-600 transition">
61
- 10 min
62
- </button>
63
- <button data-time="30" class="time-btn bg-gray-700 py-2 rounded-lg hover:bg-gray-600 transition">
64
- 30 min
65
- </button>
66
- </div>
67
- </div>
68
-
69
- <div class="mb-6">
70
- <h2 class="text-lg font-semibold mb-3">Difficulty</h2>
71
- <div class="grid grid-cols-3 gap-2">
72
- <button data-difficulty="easy" class="difficulty-btn bg-green-600 py-2 rounded-lg hover:bg-green-700 transition">
73
- Easy
74
- </button>
75
- <button data-difficulty="medium" class="difficulty-btn bg-yellow-600 py-2 rounded-lg hover:bg-yellow-700 transition">
76
- Medium
77
- </button>
78
- <button data-difficulty="hard" class="difficulty-btn bg-red-600 py-2 rounded-lg hover:bg-red-700 transition">
79
- Hard
80
- </button>
81
- </div>
82
- </div>
83
 
84
- <button id="startTestBtn" class="w-full bg-gradient-to-r from-purple-600 to-blue-500 text-white py-3 rounded-lg font-semibold shadow-lg hover:from-purple-700 hover:to-blue-600 transition">
85
- Start Test
86
- </button>
87
- </div>
88
-
89
- <!-- Typing Area -->
90
- <div class="lg:w-3/4">
91
- <div id="codeDisplay" class="bg-gray-800 p-6 rounded-xl shadow-lg font-mono text-gray-300 mb-6 h-64 overflow-y-auto">
92
- <p class="text-gray-500 italic">Select a language and click "Start Test" to begin</p>
93
- </div>
94
-
95
- <div class="relative">
96
- <textarea id="typingArea" disabled class="w-full bg-gray-800 border border-gray-700 rounded-lg p-4 font-mono text-gray-300 h-48 focus:outline-none focus:ring-2 focus:ring-blue-500 resize-none" placeholder="Start typing here when the test begins..."></textarea>
97
- <div id="progressBar" class="h-1 bg-gray-700 rounded-full mt-2">
98
- <div id="progressFill" class="h-full bg-blue-500 rounded-full" style="width: 0%"></div>
99
- </div>
100
- </div>
101
-
102
- <div class="flex justify-between items-center mt-4">
103
- <div id="wpmDisplay" class="text-gray-400">WPM: 0</div>
104
- <div id="accuracyDisplay" class="text-gray-400">Accuracy: 0%</div>
105
- <button id="submitTestBtn" disabled class="bg-green-600 px-4 py-2 rounded-lg hover:bg-green-700 transition">
106
- Submit Code
107
- </button>
108
- </div>
109
- </div>
110
- </div>
111
- </main>
112
-
113
- <!-- Leaderboard Modal -->
114
- <div id="leaderboardModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden">
115
- <div class="bg-gray-800 rounded-xl p-6 w-full max-w-2xl max-h-[80vh] overflow-y-auto">
116
- <div class="flex justify-between items-center mb-4">
117
- <h2 class="text-xl font-bold">Leaderboard 🏆</h2>
118
- <button id="closeLeaderboardBtn" class="text-gray-400 hover:text-white">
119
- <i data-feather="x"></i>
120
- </button>
121
- </div>
122
- <table class="w-full">
123
- <thead>
124
- <tr class="border-b border-gray-700">
125
- <th class="text-left py-2">Rank</th>
126
- <th class="text-left py-2">Name</th>
127
- <th class="text-left py-2">Grade</th>
128
- <th class="text-left py-2">Section</th>
129
- <th class="text-right py-2">WPM</th>
130
- <th class="text-right py-2">Accuracy</th>
131
- </tr>
132
- </thead>
133
- <tbody id="leaderboardBody">
134
- <!-- Leaderboard data will be inserted here -->
135
- </tbody>
136
- </table>
137
- </div>
138
- </div>
139
 
140
- <!-- Results Modal -->
141
- <div id="resultsModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden">
142
- <div class="bg-gray-800 rounded-xl p-6 w-full max-w-2xl">
143
- <div class="flex justify-between items-center mb-4">
144
- <h2 class="text-xl font-bold">Your Results 🎉</h2>
145
- <button id="closeResultsBtn" class="text-gray-400 hover:text-white">
146
- <i data-feather="x"></i>
147
- </button>
148
- </div>
149
- <div class="grid grid-cols-2 gap-4 mb-6">
150
- <div class="bg-gray-700 p-4 rounded-lg">
151
- <div class="text-gray-400">Words Per Minute</div>
152
- <div id="resultWPM" class="text-3xl font-bold">0</div>
153
- </div>
154
- <div class="bg-gray-700 p-4 rounded-lg">
155
- <div class="text-gray-400">Accuracy</div>
156
- <div id="resultAccuracy" class="text-3xl font-bold">0%</div>
157
- </div>
158
- <div class="bg-gray-700 p-4 rounded-lg">
159
- <div class="text-gray-400">Time</div>
160
- <div id="resultTime" class="text-3xl font-bold">00:00</div>
161
- </div>
162
- <div class="bg-gray-700 p-4 rounded-lg">
163
- <div class="text-gray-400">Characters</div>
164
- <div id="resultChars" class="text-3xl font-bold">0</div>
165
- </div>
166
- </div>
167
- <button id="viewCodeBtn" class="w-full bg-blue-600 py-3 rounded-lg hover:bg-blue-700 transition">
168
- View Your Code
169
  </button>
170
  </div>
171
  </div>
172
-
173
- <!-- Code Display Modal -->
174
- <div id="codeModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden">
175
- <div class="bg-gray-800 rounded-xl p-6 w-full max-w-4xl max-h-[80vh] overflow-y-auto">
176
- <div class="flex justify-between items-center mb-4">
177
- <h2 class="text-xl font-bold">Your Code 💻</h2>
178
- <button id="closeCodeBtn" class="text-gray-400 hover:text-white">
179
- <i data-feather="x"></i>
180
- </button>
181
- </div>
182
- <pre id="displayedCode" class="bg-gray-900 p-4 rounded-lg overflow-x-auto"></pre>
183
- </div>
184
- </div>
185
-
186
- <script src="script.js"></script>
187
- <script>
188
- feather.replace();
189
-
190
- // Sample code snippets for each language
191
- const codeSnippets = {
192
- html: {
193
- easy: `<div class="container">
194
- <h1>Welcome to my website</h1>
195
- <p>This is a paragraph of text</p>
196
- <button class="btn">Click me</button>
197
- </div>`,
198
- medium: `<!DOCTYPE html>
199
- <html lang="en">
200
- <head>
201
- <meta charset="UTF-8">
202
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
203
- <title>Document</title>
204
- <link rel="stylesheet" href="styles.css">
205
- </head>
206
- <body>
207
- <header>
208
- <nav class="navbar">
209
- <ul class="nav-links">
210
- <li><a href="#">Home</a></li>
211
- <li><a href="#">About</a></li>
212
- <li><a href="#">Contact</a></li>
213
- </ul>
214
- </nav>
215
- </header>
216
- </body>
217
- </html>`,
218
- hard: `<!DOCTYPE html>
219
- <html lang="en">
220
- <head>
221
- <meta charset="UTF-8">
222
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
223
- <title>Interactive Form</title>
224
- <style>
225
- .form-container {
226
- max-width: 600px;
227
- margin: 0 auto;
228
- padding: 20px;
229
- background: #f9f9f9;
230
- border-radius: 8px;
231
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
232
- }
233
- .form-group {
234
- margin-bottom: 15px;
235
- }
236
- label {
237
- display: block;
238
- margin-bottom: 5px;
239
- font-weight: bold;
240
- }
241
- input, select, textarea {
242
- width: 100%;
243
- padding: 8px;
244
- border: 1px solid #ddd;
245
- border-radius: 4px;
246
- }
247
- button {
248
- background: #4CAF50;
249
- color: white;
250
- padding: 10px 15px;
251
- border: none;
252
- border-radius: 4px;
253
- cursor: pointer;
254
- }
255
- </style>
256
- </head>
257
- <body>
258
- <div class="form-container">
259
- <h1>Contact Us</h1>
260
- <form id="contactForm">
261
- <div class="form-group">
262
- <label for="name">Full Name:</label>
263
- <input type="text" id="name" name="name" required>
264
- </div>
265
- <div class="form-group">
266
- <label for="email">Email:</label>
267
- <input type="email" id="email" name="email" required>
268
- </div>
269
- <div class="form-group">
270
- <label for="message">Message:</label>
271
- <textarea id="message" name="message" rows="5" required></textarea>
272
- </div>
273
- <button type="submit">Submit</button>
274
- </form>
275
  </div>
276
- <script>
277
- document.getElementById('contactForm').addEventListener('submit', function(e) {
278
- e.preventDefault();
279
- alert('Form submitted successfully!');
280
- this.reset();
281
- });
282
- </script>
283
- </body>
284
- </html>`
285
- },
286
- css: {
287
- easy: `body {
288
- font-family: Arial, sans-serif;
289
- background-color: #f4f4f4;
290
- margin: 0;
291
- padding: 20px;
292
- }
293
-
294
- .container {
295
- max-width: 800px;
296
- margin: 0 auto;
297
- background: white;
298
- padding: 20px;
299
- border-radius: 5px;
300
- box-shadow: 0 2px 5px rgba(0,0,0,0.1);
301
- }`,
302
- medium: `/* Button Styles */
303
- .btn {
304
- display: inline-block;
305
- padding: 10px 20px;
306
- font-size: 16px;
307
- text-align: center;
308
- text-decoration: none;
309
- border-radius: 5px;
310
- transition: all 0.3s ease;
311
- cursor: pointer;
312
- }
313
-
314
- .btn-primary {
315
- background-color: #3498db;
316
- color: white;
317
- border: 2px solid #3498db;
318
- }
319
-
320
- .btn-primary:hover {
321
- background-color: #2980b9;
322
- border-color: #2980b9;
323
- }
324
-
325
- .btn-outline {
326
- background-color: transparent;
327
- border: 2px solid #3498db;
328
- color: #3498db;
329
- }
330
-
331
- .btn-outline:hover {
332
- background-color: #3498db;
333
- color: white;
334
- }`,
335
- hard: `/* Responsive Grid Layout */
336
- .grid-container {
337
- display: grid;
338
- grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
339
- gap: 20px;
340
- padding: 20px;
341
- }
342
-
343
- .card {
344
- background: white;
345
- border-radius: 8px;
346
- box-shadow: 0 4px 6px rgba(0,0,0,0.1);
347
- overflow: hidden;
348
- transition: transform 0.3s ease, box-shadow 0.3s ease;
349
- }
350
-
351
- .card:hover {
352
- transform: translateY(-5px);
353
- box-shadow: 0 10px 20px rgba(0,0,0,0.1);
354
- }
355
-
356
- .card-header {
357
- padding: 15px;
358
- background: #3498db;
359
- color: white;
360
- font-weight: bold;
361
- }
362
-
363
- .card-body {
364
- padding: 15px;
365
- }
366
 
367
- @media (max-width: 768px) {
368
- .grid-container {
369
- grid-template-columns: 1fr;
370
- }
371
- }
372
 
373
- /* Animation */
374
- @keyframes fadeIn {
375
- from {
376
- opacity: 0;
377
- transform: translateY(20px);
378
- }
379
- to {
380
- opacity: 1;
381
- transform: translateY(0);
382
- }
383
- }
384
-
385
- .card {
386
- animation: fadeIn 0.5s ease forwards;
387
- }
388
-
389
- /* CSS Variables */
390
- :root {
391
- --primary-color: #3498db;
392
- --secondary-color: #2ecc71;
393
- --text-color: #333;
394
- --light-gray: #f5f5f5;
395
- }
396
-
397
- body {
398
- color: var(--text-color);
399
- background: var(--light-gray);
400
- }
401
-
402
- .btn-primary {
403
- background-color: var(--primary-color);
404
- }`
405
- },
406
- javascript: {
407
- easy: `// Function to greet user
408
- function greet(name) {
409
- return 'Hello, ' + name + '!';
410
- }
411
-
412
- // Calculate area of a rectangle
413
- function calculateArea(width, height) {
414
- return width * height;
415
- }
416
-
417
- // Call functions
418
- console.log(greet('Alice'));
419
- console.log('Area:', calculateArea(5, 3));`,
420
- medium: `// DOM Manipulation
421
- document.addEventListener('DOMContentLoaded', function() {
422
- const button = document.getElementById('myButton');
423
- const output = document.getElementById('output');
424
-
425
- button.addEventListener('click', function() {
426
- const name = prompt('What is your name?');
427
- if (name) {
428
- output.textContent = 'Hello, ' + name + '!';
429
- }
430
- });
431
- });
432
-
433
- // Fetch API example
434
- async function fetchData(url) {
435
- try {
436
- const response = await fetch(url);
437
- if (!response.ok) {
438
- throw new Error('Network response was not ok');
439
- }
440
- const data = await response.json();
441
- console.log(data);
442
- return data;
443
- } catch (error) {
444
- console.error('Error:', error);
445
- }
446
- }
447
 
448
- // Example usage
449
- fetchData('https://api.example.com/data');`,
450
- hard: `// Todo List Application
451
- class TodoApp {
452
- constructor() {
453
- this.todos = [];
454
- this.initElements();
455
- this.bindEvents();
456
- this.loadFromLocalStorage();
457
- this.render();
458
- }
459
-
460
- initElements() {
461
- this.todoForm = document.getElementById('todoForm');
462
- this.todoInput = document.getElementById('todoInput');
463
- this.todoList = document.getElementById('todoList');
464
- }
465
-
466
- bindEvents() {
467
- this.todoForm.addEventListener('submit', (e) => this.handleSubmit(e));
468
- this.todoList.addEventListener('click', (e) => this.handleListClick(e));
469
- }
470
-
471
- handleSubmit(e) {
472
- e.preventDefault();
473
- const todoText = this.todoInput.value.trim();
474
- if (todoText) {
475
- this.addTodo(todoText);
476
- this.todoInput.value = '';
477
- }
478
- }
479
-
480
- handleListClick(e) {
481
- if (e.target.classList.contains('delete-btn')) {
482
- const id = parseInt(e.target.parentElement.dataset.id);
483
- this.removeTodo(id);
484
- } else if (e.target.tagName === 'LI') {
485
- const id = parseInt(e.target.dataset.id);
486
- this.toggleTodo(id);
487
- }
488
- }
489
-
490
- addTodo(text) {
491
- const newTodo = {
492
- id: Date.now(),
493
- text,
494
- completed: false
495
- };
496
- this.todos.push(newTodo);
497
- this.saveToLocalStorage();
498
- this.render();
499
- }
500
-
501
- removeTodo(id) {
502
- this.todos = this.todos.filter(todo => todo.id !== id);
503
- this.saveToLocalStorage();
504
- this.render();
505
- }
506
-
507
- toggleTodo(id) {
508
- this.todos = this.todos.map(todo =>
509
- todo.id === id ? {...todo, completed: !todo.completed} : todo
510
- );
511
- this.saveToLocalStorage();
512
- this.render();
513
- }
514
-
515
- saveToLocalStorage() {
516
- localStorage.setItem('todos', JSON.stringify(this.todos));
517
- }
518
-
519
- loadFromLocalStorage() {
520
- const savedTodos = localStorage.getItem('todos');
521
- if (savedTodos) {
522
- this.todos = JSON.parse(savedTodos);
523
- }
524
- }
525
-
526
- render() {
527
- this.todoList.innerHTML = this.todos.map(todo => \`
528
- <li data-id="\${todo.id}" class="\${todo.completed ? 'completed' : ''}">
529
- \${todo.text}
530
- <button class="delete-btn">×</button>
531
- </li>
532
- \`).join('');
533
  }
534
- }
535
-
536
- // Initialize the app
537
- document.addEventListener('DOMContentLoaded', () => {
538
- new TodoApp();
539
- });`
540
- }
541
- };
542
-
543
- // DOM elements
544
- const codeDisplay = document.getElementById('codeDisplay');
545
- const typingArea = document.getElementById('typingArea');
546
- const timerDisplay = document.getElementById('timerDisplay');
547
- const startTestBtn = document.getElementById('startTestBtn');
548
- const submitTestBtn = document.getElementById('submitTestBtn');
549
- const progressFill = document.getElementById('progressFill');
550
- const wpmDisplay = document.getElementById('wpmDisplay');
551
- const accuracyDisplay = document.getElementById('accuracyDisplay');
552
- const leaderboardBtn = document.getElementById('leaderboardBtn');
553
- const leaderboardModal = document.getElementById('leaderboardModal');
554
- const closeLeaderboardBtn = document.getElementById('closeLeaderboardBtn');
555
- const leaderboardBody = document.getElementById('leaderboardBody');
556
- const resultsModal = document.getElementById('resultsModal');
557
- const closeResultsBtn = document.getElementById('closeResultsBtn');
558
- const resultWPM = document.getElementById('resultWPM');
559
- const resultAccuracy = document.getElementById('resultAccuracy');
560
- const resultTime = document.getElementById('resultTime');
561
- const resultChars = document.getElementById('resultChars');
562
- const viewCodeBtn = document.getElementById('viewCodeBtn');
563
- const codeModal = document.getElementById('codeModal');
564
- const displayedCode = document.getElementById('displayedCode');
565
- const closeCodeBtn = document.getElementById('closeCodeBtn');
566
-
567
- // Test state variables
568
- let selectedLanguage = 'html';
569
- let selectedTime = 1;
570
- let selectedDifficulty = 'easy';
571
- let timerInterval;
572
- let startTime;
573
- let endTime;
574
- let correctChars = 0;
575
- let totalChars = 0;
576
- let testActive = false;
577
- let currentCode = '';
578
- let userTypedCode = '';
579
-
580
- // Set up language buttons
581
- document.querySelectorAll('.language-btn').forEach(btn => {
582
- btn.addEventListener('click', () => {
583
- document.querySelectorAll('.language-btn').forEach(b => {
584
- b.classList.remove('bg-blue-700');
585
- b.classList.add('bg-blue-600');
586
- });
587
- btn.classList.remove('bg-blue-600');
588
- btn.classList.add('bg-blue-700');
589
- selectedLanguage = btn.dataset.language;
590
- });
591
- });
592
-
593
- // Set up time buttons
594
- document.querySelectorAll('.time-btn').forEach(btn => {
595
- btn.addEventListener('click', () => {
596
- document.querySelectorAll('.time-btn').forEach(b => {
597
- b.classList.remove('bg-gray-600');
598
- b.classList.add('bg-gray-700');
599
- });
600
- btn.classList.remove('bg-gray-700');
601
- btn.classList.add('bg-gray-600');
602
- selectedTime = parseInt(btn.dataset.time);
603
- });
604
- });
605
-
606
- // Set up difficulty buttons
607
- document.querySelectorAll('.difficulty-btn').forEach(btn => {
608
- btn.addEventListener('click', () => {
609
- document.querySelectorAll('.difficulty-btn').forEach(b => {
610
- if (b.dataset.difficulty === 'easy') {
611
- b.classList.remove('bg-green-700');
612
- b.classList.add('bg-green-600');
613
- } else if (b.dataset.difficulty === 'medium') {
614
- b.classList.remove('bg-yellow-700');
615
- b.classList.add('bg-yellow-600');
616
- } else {
617
- b.classList.remove('bg-red-700');
618
- b.classList.add('bg-red-600');
619
- }
620
- });
621
-
622
- if (btn.dataset.difficulty === 'easy') {
623
- btn.classList.remove('bg-green-600');
624
- btn.classList.add('bg-green-700');
625
- } else if (btn.dataset.difficulty === 'medium') {
626
- btn.classList.remove('bg-yellow-600');
627
- btn.classList.add('bg-yellow-700');
628
- } else {
629
- btn.classList.remove('bg-red-600');
630
- btn.classList.add('bg-red-700');
631
- }
632
-
633
- selectedDifficulty = btn.dataset.difficulty;
634
- });
635
- });
636
-
637
- // Start test button
638
- startTestBtn.addEventListener('click', startTest);
639
-
640
- // Submit test button
641
- submitTestBtn.addEventListener('click', endTest);
642
-
643
- // Typing area events
644
- typingArea.addEventListener('input', updateTypingStats);
645
-
646
- // Leaderboard modal
647
- leaderboardBtn.addEventListener('click', showLeaderboard);
648
- closeLeaderboardBtn.addEventListener('click', () => {
649
- leaderboardModal.classList.add('hidden');
650
- });
651
-
652
- // Results modal
653
- closeResultsBtn.addEventListener('click', () => {
654
- resultsModal.classList.add('hidden');
655
- });
656
-
657
- // View code button
658
- viewCodeBtn.addEventListener('click', () => {
659
- resultsModal.classList.add('hidden');
660
- displayedCode.textContent = userTypedCode;
661
- codeModal.classList.remove('hidden');
662
- });
663
-
664
- // Close code modal
665
- closeCodeBtn.addEventListener('click', () => {
666
- codeModal.classList.add('hidden');
667
- });
668
-
669
- // Format time as MM:SS
670
- function formatTime(seconds) {
671
- const mins = Math.floor(seconds / 60);
672
- const secs = seconds % 60;
673
- return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
674
- }
675
-
676
- // Start the typing test
677
- function startTest() {
678
- // Get the code to type
679
- currentCode = codeSnippets[selectedLanguage][selectedDifficulty];
680
- codeDisplay.innerHTML = currentCode
681
- .replace(/</g, '&lt;')
682
- .replace(/>/g, '&gt;')
683
- .replace(/\n/g, '<br>')
684
- .replace(/ /g, '&nbsp;');
685
-
686
- // Reset stats
687
- correctChars = 0;
688
- totalChars = 0;
689
- testActive = true;
690
- typingArea.disabled = false;
691
- typingArea.value = '';
692
- typingArea.focus();
693
- progressFill.style.width = '0%';
694
- wpmDisplay.textContent = 'WPM: 0';
695
- accuracyDisplay.textContent = 'Accuracy: 0%';
696
-
697
- // Start timer
698
- const totalSeconds = selectedTime * 60;
699
- let secondsLeft = totalSeconds;
700
- timerDisplay.textContent = formatTime(secondsLeft);
701
-
702
- startTime = new Date();
703
- timerInterval = setInterval(() => {
704
- secondsLeft--;
705
- timerDisplay.textContent = formatTime(secondsLeft);
706
-
707
- if (secondsLeft <= 0) {
708
- clearInterval(timerInterval);
709
- timerDisplay.textContent = "00:00";
710
- // Timer ends but test continues until all code is typed
711
- }
712
- }, 1000);
713
-
714
- // Update UI
715
- startTestBtn.disabled = true;
716
- submitTestBtn.disabled = false;
717
- }
718
-
719
- // Update typing statistics
720
- function updateTypingStats() {
721
- if (!testActive) return;
722
-
723
- userTypedCode = typingArea.value;
724
- totalChars = userTypedCode.length;
725
-
726
- // Calculate correct characters
727
- let correct = 0;
728
- for (let i = 0; i < totalChars; i++) {
729
- if (i < currentCode.length && userTypedCode[i] === currentCode[i]) {
730
- correct++;
731
- }
732
- }
733
- correctChars = correct;
734
-
735
- // Calculate progress
736
- const progress = Math.min((totalChars / currentCode.length) * 100, 100);
737
- progressFill.style.width = `${progress}%`;
738
-
739
- // Calculate accuracy
740
- const accuracy = totalChars > 0 ? Math.round((correctChars / totalChars) * 100) : 0;
741
- accuracyDisplay.textContent = `Accuracy: ${accuracy}%`;
742
-
743
- // Calculate WPM (assuming 5 chars = 1 word)
744
- const timeElapsed = (new Date() - startTime) / 1000 / 60; // in minutes
745
- const wpm = timeElapsed > 0 ? Math.round((correctChars / 5) / timeElapsed) : 0;
746
- wpmDisplay.textContent = `WPM: ${wpm}`;
747
-
748
- // Check if all code is typed
749
- if (totalChars >= currentCode.length) {
750
- endTest();
751
- }
752
- }
753
-
754
- // End the typing test
755
- function endTest() {
756
- if (!testActive) return;
757
-
758
- testActive = false;
759
- clearInterval(timerInterval);
760
- endTime = new Date();
761
- typingArea.disabled = true;
762
- submitTestBtn.disabled = true;
763
- startTestBtn.disabled = false;
764
-
765
- // Calculate final stats
766
- const timeElapsed = (endTime - startTime) / 1000;
767
- const wpm = Math.round((correctChars / 5) / (timeElapsed / 60));
768
- const accuracy = totalChars > 0 ? Math.round((correctChars / totalChars) * 100) : 0;
769
-
770
- // Show results
771
- resultWPM.textContent = wpm;
772
- resultAccuracy.textContent = `${accuracy}%`;
773
- resultTime.textContent = formatTime(Math.round(timeElapsed));
774
- resultChars.textContent = totalChars;
775
- resultsModal.classList.remove('hidden');
776
-
777
- // Save to leaderboard
778
- saveToLeaderboard(wpm, accuracy);
779
- }
780
-
781
- // Show leaderboard
782
- function showLeaderboard() {
783
- leaderboardBody.innerHTML = '';
784
-
785
- // Get leaderboard from local storage
786
- let leaderboard = JSON.parse(localStorage.getItem('leaderboard')) || [];
787
-
788
- // Sort by WPM descending
789
- leaderboard.sort((a, b) => b.wpm - a.wpm);
790
-
791
- // Display top 10
792
- const top10 = leaderboard.slice(0, 10);
793
- top10.forEach((entry, index) => {
794
- const row = document.createElement('tr');
795
- row.className = 'border-b border-gray-700';
796
- row.innerHTML = `
797
- <td class="py-2">${index + 1}</td>
798
- <td class="py-2">${entry.name}</td>
799
- <td class="py-2">${entry.gradeLevel}</td>
800
- <td class="py-2">${entry.section}</td>
801
- <td class="py-2 text-right">${entry.wpm}</td>
802
- <td class="py-2 text-right">${entry.accuracy}%</td>
803
- `;
804
- leaderboardBody.appendChild(row);
805
- });
806
-
807
- leaderboardModal.classList.remove('hidden');
808
- }
809
 
810
- // Save result to leaderboard
811
- function saveToLeaderboard(wpm, accuracy) {
812
- const userData = JSON.parse(localStorage.getItem('userData'));
813
- if (!userData) return;
814
-
815
- let leaderboard = JSON.parse(localStorage.getItem('leaderboard')) || [];
816
-
817
- leaderboard.push({
818
- name: userData.name,
819
- gradeLevel: userData.gradeLevel,
820
- section: userData.section,
821
- wpm,
822
- accuracy,
823
- timestamp: new Date().toISOString()
824
- });
825
-
826
- localStorage.setItem('leaderboard', JSON.stringify(leaderboard));
827
- }
828
- </script>
829
  </body>
830
- </html>
 
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>CodeTyper Racer - Challenge</title>
7
+
8
+ <!-- Tailwind -->
9
  <script src="https://cdn.tailwindcss.com"></script>
10
+
11
+ <!-- Feather Icons (ONLY ONCE – FIXED) -->
12
  <script src="https://unpkg.com/feather-icons"></script>
13
  </head>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
+ <body class="bg-gray-900 text-gray-100 min-h-screen">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
 
17
+ <!-- ================= HEADER ================= -->
18
+ <header class="bg-gray-800 py-4 shadow-lg">
19
+ <div class="container mx-auto px-4 flex justify-between items-center">
20
+ <h1 class="text-xl font-bold">CodeTyper Racer 🚀</h1>
21
+ <div class="flex items-center space-x-4">
22
+ <div id="timerDisplay" class="bg-gray-700 px-4 py-2 rounded-lg font-mono">00:00</div>
23
+ <button id="leaderboardBtn" class="flex items-center space-x-1 bg-gray-700 px-3 py-2 rounded-lg hover:bg-gray-600">
24
+ <i data-feather="bar-chart-2"></i>
25
+ <span>Leaderboard</span>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  </button>
27
  </div>
28
  </div>
29
+ </header>
30
+
31
+ <!-- ================= MAIN ================= -->
32
+ <main class="container mx-auto px-4 py-8">
33
+ <div class="flex flex-col lg:flex-row gap-8">
34
+
35
+ <!-- SETTINGS -->
36
+ <div class="lg:w-1/4 bg-gray-800 p-6 rounded-xl shadow-lg">
37
+ <h2 class="text-lg font-semibold mb-3">Language</h2>
38
+ <button data-language="html" class="language-btn w-full bg-blue-600 py-2 rounded mb-2">HTML</button>
39
+ <button data-language="css" class="language-btn w-full bg-blue-600 py-2 rounded mb-2">CSS</button>
40
+ <button data-language="javascript" class="language-btn w-full bg-blue-600 py-2 rounded">JavaScript</button>
41
+
42
+ <h2 class="text-lg font-semibold mt-6 mb-3">Timer</h2>
43
+ <button data-time="1" class="time-btn w-full bg-gray-700 py-2 rounded mb-2">1 min</button>
44
+ <button data-time="5" class="time-btn w-full bg-gray-700 py-2 rounded mb-2">5 min</button>
45
+ <button data-time="10" class="time-btn w-full bg-gray-700 py-2 rounded">10 min</button>
46
+
47
+ <h2 class="text-lg font-semibold mt-6 mb-3">Difficulty</h2>
48
+ <button data-difficulty="easy" class="difficulty-btn w-full bg-green-600 py-2 rounded mb-2">Easy</button>
49
+ <button data-difficulty="medium" class="difficulty-btn w-full bg-yellow-600 py-2 rounded mb-2">Medium</button>
50
+ <button data-difficulty="hard" class="difficulty-btn w-full bg-red-600 py-2 rounded">Hard</button>
51
+
52
+ <button id="startTestBtn" class="mt-6 w-full bg-purple-600 py-3 rounded font-bold">Start Test</button>
53
+ </div>
54
+
55
+ <!-- TYPING AREA -->
56
+ <div class="lg:w-3/4">
57
+ <div id="codeDisplay" class="bg-gray-800 p-6 rounded-xl font-mono h-64 overflow-y-auto mb-4">
58
+ <p class="italic text-gray-500">Select settings and start</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
 
61
+ <textarea id="typingArea" disabled class="w-full h-48 bg-gray-800 p-4 rounded font-mono"></textarea>
 
 
 
 
62
 
63
+ <div class="h-2 bg-gray-700 rounded mt-2">
64
+ <div id="progressFill" class="h-full bg-blue-500 rounded" style="width:0%"></div>
65
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
 
67
+ <div class="flex justify-between mt-4">
68
+ <div id="wpmDisplay">WPM: 0</div>
69
+ <div id="accuracyDisplay">Accuracy: 0%</div>
70
+ <button id="submitTestBtn" disabled class="bg-green-600 px-4 py-2 rounded">Submit</button>
71
+ </div>
72
+ </div>
73
+
74
+ </div>
75
+ </main>
76
+
77
+ <!-- ================= SCRIPT ================= -->
78
+ <script>
79
+ feather.replace();
80
+
81
+ /* ---------- USER DATA ---------- */
82
+ if (!localStorage.getItem('userData')) {
83
+ localStorage.setItem('userData', JSON.stringify({
84
+ name: prompt("Name:") || "Anonymous",
85
+ gradeLevel: prompt("Grade:") || "-",
86
+ section: prompt("Section:") || "-"
87
+ }));
88
+ }
89
+
90
+ /* ---------- CODE SNIPPETS ---------- */
91
+ const codeSnippets = {
92
+ html:{easy:`<h1>Hello World</h1>`,medium:`<div><p>Sample</p></div>`,hard:`<!DOCTYPE html><html><body><h1>Hello</h1></body></html>`},
93
+ css:{easy:`body{margin:0}`,medium:`.box{padding:10px}`,hard:`@media(max-width:600px){body{background:red}}`},
94
+ javascript:{easy:`console.log("Hi")`,medium:`function add(a,b){return a+b}`,hard:`class App{constructor(){console.log("Start")}}`}
95
+ };
96
+
97
+ /* ---------- STATE ---------- */
98
+ let selectedLanguage="html",selectedTime=1,selectedDifficulty="easy";
99
+ let startTime,timer,testActive=false,currentCode="",correctChars=0;
100
+
101
+ /* ---------- DOM ---------- */
102
+ const codeDisplay=document.getElementById("codeDisplay");
103
+ const typingArea=document.getElementById("typingArea");
104
+ const timerDisplay=document.getElementById("timerDisplay");
105
+ const progressFill=document.getElementById("progressFill");
106
+ const wpmDisplay=document.getElementById("wpmDisplay");
107
+ const accuracyDisplay=document.getElementById("accuracyDisplay");
108
+
109
+ /* ---------- BUTTONS ---------- */
110
+ document.querySelectorAll(".language-btn").forEach(b=>b.onclick=()=>selectedLanguage=b.dataset.language);
111
+ document.querySelectorAll(".time-btn").forEach(b=>b.onclick=()=>selectedTime=+b.dataset.time);
112
+ document.querySelectorAll(".difficulty-btn").forEach(b=>b.onclick=()=>selectedDifficulty=b.dataset.difficulty);
113
+
114
+ /* ---------- START ---------- */
115
+ document.getElementById("startTestBtn").onclick=()=>{
116
+ currentCode=codeSnippets[selectedLanguage][selectedDifficulty];
117
+ codeDisplay.innerHTML=currentCode.replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/\n/g,"<br>");
118
+ typingArea.disabled=false;
119
+ typingArea.value="";
120
+ typingArea.focus();
121
+ startTime=Date.now();
122
+ testActive=true;
123
+
124
+ let sec=selectedTime*60;
125
+ timerDisplay.textContent=`00:${sec}`;
126
+ clearInterval(timer);
127
+ timer=setInterval(()=>{
128
+ sec--;
129
+ timerDisplay.textContent=`${Math.floor(sec/60).toString().padStart(2,"0")}:${(sec%60).toString().padStart(2,"0")}`;
130
+ if(sec<=0) endTest();
131
+ },1000);
132
+ };
133
+
134
+ /* ---------- TYPING ---------- */
135
+ typingArea.oninput=()=>{
136
+ if(!testActive)return;
137
+ let typed=typingArea.value;
138
+ correctChars=0;
139
+ for(let i=0;i<typed.length;i++){
140
+ if(typed[i]===currentCode[i]) correctChars++;
 
 
 
 
 
 
 
 
 
 
 
141
  }
142
+ let acc=Math.round((correctChars/typed.length)*100)||0;
143
+ let wpm=Math.round((correctChars/5)/((Date.now()-startTime)/60000))||0;
144
+ accuracyDisplay.textContent=`Accuracy: ${acc}%`;
145
+ wpmDisplay.textContent=`WPM: ${wpm}`;
146
+ progressFill.style.width=`${Math.min(typed.length/currentCode.length*100,100)}%`;
147
+ if(typed.length>=currentCode.length) endTest();
148
+ };
149
+
150
+ /* ---------- END ---------- */
151
+ function endTest(){
152
+ if(!testActive)return;
153
+ testActive=false;
154
+ clearInterval(timer);
155
+ typingArea.disabled=true;
156
+ alert("Test Finished!");
157
+ }
158
+ </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
  </body>
161
+ </html>