Spaces:
Running
Running
| // 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>© ${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); | |
| }); | |