⌨️
🎯

हिंदी Remington Layout टाइपिंग स्पीड टेस्ट

Traditional Remington layout for Hindi/Marathi typing with Kruti Dev font

⌨️ Remington
🎯 Hindi Language
🔤 Non-unicode
Time
0:00
Gross WPM
0
Net WPM
0
Accuracy
100%

Passage to Type:

Hkkjr ds jk"Vªfirk MkW- ch-vkj- vEcsMdj us Hkkjrh; lafo/kku dh jpuk dh A mUgksaus lekt lq/kkj ds fy, vFkd iz;kl fd, A mUgksaus vLi`';rk ds fo:) yM+kbZ yM+h vkSj lkekftd U;k; ds fy, dke fd;k A muds ;ksxnku dks dHkh Hkqyk;k ugha tk ldrk A os lHkh ds fy, lekurk vkSj U;k; ds leFkZd Fks A

Type Here (Remington Layout layout):

Input: 0 / 284 chars Correct: 0 Incorrect: 0
// Toggle main keymap guide with improved accessibility document.getElementById('toggleMainKeymap')?.addEventListener('click', function () { const guide = document.getElementById('mainKeymapGuide'); const chevron = document.getElementById('mainChevron'); const isHidden = guide.classList.contains('hidden'); guide.classList.toggle('hidden'); chevron.classList.toggle('rotate-180'); // Update ARIA attributes for accessibility this.setAttribute('aria-expanded', !isHidden); guide.setAttribute('aria-hidden', isHidden); }); // CSRF Token const csrfToken = document.getElementById('csrfToken').value; // DOM Elements const typingInput = document.getElementById('typingInput'); const passageDisplay = document.getElementById('passageDisplay'); const timeDisplay = document.getElementById('timeDisplay'); const grossWpmDisplay = document.getElementById('grossWpmDisplay'); const netWpmDisplay = document.getElementById('netWpmDisplay'); const accuracyDisplay = document.getElementById('accuracyDisplay'); const charCount = document.getElementById('charCount'); const correctCount = document.getElementById('correctCount'); const incorrectCount = document.getElementById('incorrectCount'); const resultSummary = document.getElementById('resultSummary'); const originalPassage = document.getElementById('originalPassage').value; // Debug: Check if DOM elements are found console.log('DOM Elements Check:'); console.log('typingInput:', typingInput); console.log('passageDisplay:', passageDisplay); console.log('originalPassage:', originalPassage); // Test State let testState = { started: false, completed: false, startTime: null, timerInterval: null, originalText: originalPassage, correctChars: 0, incorrectChars: 0, totalChars: originalPassage.length, rawInput: '', lastUpdateTime: 0, updateThrottle: 16 // ~60fps for smooth updates }; // Performance optimization: Throttle function function throttle(func, limit) { let inThrottle; return function () { const args = arguments; const context = this; if (!inThrottle) { func.apply(context, args); inThrottle = true; setTimeout(() => inThrottle = false, limit); } } } // Prevent copy-paste with better user feedback ['paste', 'copy', 'cut'].forEach(event => { typingInput.addEventListener(event, (e) => { e.preventDefault(); if (event === 'paste') { // Show temporary visual feedback instead of alert showTemporaryMessage('⛔ Copy-paste is disabled! This is a typing practice tool.'); } }); }); // Show temporary message function function showTemporaryMessage(message) { const messageEl = document.createElement('div'); messageEl.className = 'fixed top-4 right-4 bg-red-500 text-white px-4 py-2 rounded-lg shadow-lg z-50 animate-pulse'; messageEl.textContent = message; document.body.appendChild(messageEl); setTimeout(() => { messageEl.remove(); }, 3000); } // Optimized input handler with throttling const handleInput = throttle(function (e) { if (!testState.started && !testState.completed) { startTest(); } testState.rawInput = e.target.value; updateProgress(); }, testState.updateThrottle); typingInput.addEventListener('input', handleInput); // Add input conversion for non-Remington layouts only // Remington layout: Direct typing with Kruti Dev font - no JavaScript conversion needed console.log('Remington layout: Direct typing enabled - no JavaScript conversion'); // Start test function startTest() { testState.started = true; testState.startTime = new Date(); testState.timerInterval = setInterval(updateTimer, 500); // Reduced frequency for better mobile performance // Add visual feedback for test start typingInput.classList.add('ring-2', 'ring-green-400'); setTimeout(() => { typingInput.classList.remove('ring-2', 'ring-green-400'); }, 1000); } // Update timer with reduced frequency function updateTimer() { if (!testState.started || testState.completed) return; const elapsed = Math.floor((new Date() - testState.startTime) / 1000); const minutes = Math.floor(elapsed / 60); const seconds = elapsed % 60; timeDisplay.textContent = `${minutes}:${seconds.toString().padStart(2, '0')}`; calculateWPM(); } // Optimized progress update with incremental DOM updates function updateProgress() { const rawInput = testState.rawInput; const originalText = testState.originalText; let correct = 0; let incorrect = 0; // Use DocumentFragment for better performance const fragment = document.createDocumentFragment(); // Character-by-character comparison with optimized DOM manipulation for (let i = 0; i < originalText.length; i++) { const originalChar = originalText[i]; const typedChar = rawInput[i]; const span = document.createElement('span'); if (typedChar === undefined) { // Character not yet typed - show as pending span.className = 'char-pending'; span.innerHTML = originalChar === ' ' ? ' ' : originalChar; } else if (typedChar === originalChar) { // Correct character span.className = 'char-correct'; span.innerHTML = originalChar === ' ' ? ' ' : originalChar; correct++; } else { // Incorrect character span.className = 'char-incorrect'; if (originalChar === ' ') { span.innerHTML = '␣'; // Use visible space symbol for incorrect spaces } else { span.innerHTML = originalChar; } incorrect++; } fragment.appendChild(span); } // Single DOM update for better performance passageDisplay.innerHTML = ''; passageDisplay.appendChild(fragment); testState.correctChars = correct; testState.incorrectChars = incorrect; // Update counters charCount.textContent = rawInput.length; correctCount.textContent = correct; incorrectCount.textContent = incorrect; const totalTyped = rawInput.length; const accuracy = totalTyped > 0 ? ((correct / totalTyped) * 100).toFixed(1) : 100; accuracyDisplay.textContent = accuracy + '%'; if (rawInput.length >= originalText.length) { completeTest(); } } // Calculate WPM function calculateWPM() { if (!testState.started) return; const elapsedMinutes = (new Date() - testState.startTime) / 60000; if (elapsedMinutes === 0) return; const typedChars = testState.rawInput.length; const words = typedChars / 5; const grossWpm = Math.round(words / elapsedMinutes); grossWpmDisplay.textContent = grossWpm; const netWpm = Math.round((words - (testState.incorrectChars / 5)) / elapsedMinutes); netWpmDisplay.textContent = Math.max(0, netWpm); } // Complete test function completeTest() { if (testState.completed) return; testState.completed = true; testState.endTime = new Date(); clearInterval(testState.timerInterval); typingInput.disabled = true; const timeTaken = Math.floor((testState.endTime - testState.startTime) / 1000); const minutes = Math.floor(timeTaken / 60); const seconds = timeTaken % 60; const elapsedMinutes = timeTaken / 60; const words = testState.originalText.length / 5; const grossWpm = (words / elapsedMinutes).toFixed(2); const netWpm = Math.max(0, ((words - (testState.incorrectChars / 5)) / elapsedMinutes)).toFixed(2); const accuracy = ((testState.correctChars / testState.totalChars) * 100).toFixed(2); document.getElementById('finalTime').textContent = `${minutes}:${seconds.toString().padStart(2, '0')}`; document.getElementById('finalWpm').textContent = netWpm + ' WPM'; document.getElementById('finalAccuracy').textContent = accuracy + '%'; resultSummary.classList.remove('hidden'); saveResult({ passage_id: document.getElementById('passageId').value, time_taken: timeTaken, total_characters: testState.totalChars, correct_characters: testState.correctChars, incorrect_characters: testState.incorrectChars }); } // Save result using AJAX function saveResult(data) { console.log('Saving result via AJAX:', data); makeAjaxRequest('https://writingschool.in/typing-test/store', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken }, body: JSON.stringify(data) }) .then(result => { if (result.success) { console.log('Result saved successfully!'); } else { console.error('Failed to save result:', result.message); } }) .catch(error => { console.error('Error saving result:', error); }); } // Reset test function resetTest() { clearInterval(testState.timerInterval); // Get the current passage content (might be updated by loadNewPassage) const currentPassage = document.getElementById('originalPassage').value; testState = { started: false, completed: false, startTime: null, timerInterval: null, originalText: currentPassage, correctChars: 0, incorrectChars: 0, totalChars: currentPassage.length, rawInput: '', lastUpdateTime: 0, updateThrottle: 16 }; typingInput.value = ''; typingInput.disabled = false; passageDisplay.innerHTML = currentPassage; timeDisplay.textContent = '0:00'; grossWpmDisplay.textContent = '0'; netWpmDisplay.textContent = '0'; accuracyDisplay.textContent = '100%'; charCount.textContent = '0'; correctCount.textContent = '0'; incorrectCount.textContent = '0'; resultSummary.classList.add('hidden'); typingInput.focus(); console.log('Test reset with passage length:', currentPassage.length); } // AJAX Helper Function function makeAjaxRequest(url, options = {}) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); const method = options.method || 'GET'; const timeout = options.timeout || 15000; // Reduced timeout for mobile xhr.open(method, url, true); xhr.timeout = timeout; // Set headers xhr.setRequestHeader('Accept', 'application/json'); xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); if (options.headers) { Object.keys(options.headers).forEach(key => { xhr.setRequestHeader(key, options.headers[key]); }); } xhr.onreadystatechange = function () { if (xhr.readyState === XMLHttpRequest.DONE) { if (xhr.status >= 200 && xhr.status < 300) { try { const response = JSON.parse(xhr.responseText); resolve(response); } catch (e) { reject(new Error('Invalid JSON response')); } } else { reject(new Error(`HTTP ${xhr.status}: ${xhr.statusText}`)); } } }; xhr.onerror = () => reject(new Error('Network error')); xhr.ontimeout = () => reject(new Error('Request timeout')); xhr.send(options.body || null); }); } // Load new passage using AJAX function loadNewPassage() { const difficulty = document.getElementById('difficultySelect').value; const language = document.getElementById('currentLanguage').value; const layout = document.getElementById('currentLayout').value; // Add loading state const newPassageBtn = document.getElementById('newPassageBtn'); const originalText = newPassageBtn.textContent; newPassageBtn.textContent = 'Loading...'; newPassageBtn.disabled = true; // Add visual loading indicator newPassageBtn.innerHTML = 'Loading...'; console.log('Loading new passage via AJAX:', { difficulty, language, layout }); const url = `https://writingschool.in/typing-test/get-passage?difficulty=${encodeURIComponent(difficulty)}&language=${encodeURIComponent(language)}&layout=${encodeURIComponent(layout)}`; makeAjaxRequest(url) .then(result => { console.log('AJAX Response:', result); if (result.success && result.passage) { // Update passage data document.getElementById('originalPassage').value = result.passage.content; document.getElementById('passageId').value = result.passage.id; document.getElementById('totalChars').textContent = result.passage.character_count; // Update the global testState testState.originalText = result.passage.content; testState.totalChars = result.passage.character_count; // Reset the test with new passage resetTest(); console.log('New passage loaded successfully via AJAX'); // Show success feedback newPassageBtn.innerHTML = 'Loaded!'; setTimeout(() => { newPassageBtn.textContent = originalText; }, 1000); } else { throw new Error(result.message || 'Failed to load passage'); } }) .catch(error => { console.error('AJAX Error:', error); showTemporaryMessage(`Error loading passage: ${error.message}`); }) .finally(() => { // Restore button state setTimeout(() => { newPassageBtn.textContent = originalText; newPassageBtn.disabled = false; }, 1000); }); } // Event listeners document.getElementById('resetBtn').addEventListener('click', resetTest); document.getElementById('newPassageBtn').addEventListener('click', loadNewPassage); document.getElementById('tryAgainBtn')?.addEventListener('click', resetTest); document.getElementById('difficultySelect').addEventListener('change', loadNewPassage); window.addEventListener('load', () => { // Double-check DOM elements are available const elements = { typingInput: document.getElementById('typingInput'), passageDisplay: document.getElementById('passageDisplay'), originalPassage: document.getElementById('originalPassage') }; console.log('Elements check on load:', elements); typingInput.focus(); console.log('Hindi typing test loaded successfully'); });