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