// Code snippets for typing test grouped by language
const codeSnippets = {
html: [
`
Document
Hello World
`,
`
`,
``
],
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);
});