@@ -29,12 +29,16 @@ class TypingTest {
2929 this.totalLinesTyped = 0
3030 // Metrics aggregation
3131 this.metrics = {}
32+ // For accurate WPM and Accuracy
33+ this.actualTypedCharacterKeystrokes = 0
34+ this.actualMistakesMade = 0
3235 }
3336
3437 /**
3538 * Bind UI event handlers and perform initial setup.
3639 */
3740 init() {
41+ $('#user-input').val('')
3842 this.resetText()
3943 this.formatVisibleText()
4044 this.bindEvents()
@@ -83,8 +87,10 @@ class TypingTest {
8387
8488 $('#user-input').on('keydown', (e) => this.handleKeydown(e))
8589 $('#user-input').on('input', () => this.handleInput())
90+ $('#user-input').on('copy cut paste', (e) => e.preventDefault())
8691
87- $('#reset-button, #new-test-button').on('click', () => this.resetTest())
92+ $('#reset-button').on('click', () => this.resetTest())
93+ $('#stats-new-test').on('click', () => window.location.reload())
8894
8995 $('#ancient').on('click', () => this.changeEra('Ancient'))
9096 $('#medieval').on('click', () => this.changeEra('Medieval'))
@@ -296,12 +302,23 @@ class TypingTest {
296302 * @private
297303 */
298304 handleKeydown(e) {
305+ const now = Date.now()
299306 const isChar = e.key.length === 1 || e.key === ' ' || e.key === 'Enter'
307+ const currentInputLength = $('#user-input').val().length
308+ const targetCharIndex = this.currentCharIndex + currentInputLength
309+ const targetChar = this.textToType[targetCharIndex]
310+
311+ if (isChar) {
312+ this.actualTypedCharacterKeystrokes++
313+ const typedChar = e.key === 'Enter' ? '\n' : e.key
314+ if (targetChar !== undefined && typedChar !== targetChar) {
315+ this.actualMistakesMade++
316+ }
317+ }
300318 if (!this.testActive && (isChar || e.key === 'Backspace')) {
301319 this.startTest()
302320 }
303321 if (this.testActive) {
304- const now = Date.now()
305322 const delay = this.lastKeystrokeTime ?
306323 now - this.lastKeystrokeTime :
307324 0
@@ -398,6 +415,8 @@ class TypingTest {
398415 this.currentCharIndex = 0
399416 this.typedCharIndex = 0
400417 this.totalLinesTyped = 0
418+ this.actualTypedCharacterKeystrokes = 0
419+ this.actualMistakesMade = 0
401420 this.metrics = {
402421 wpmHistory: [],
403422 lastWPMHistoryTime: this.startTime.getTime(),
@@ -465,18 +484,19 @@ class TypingTest {
465484 */
466485 calculateLiveStats() {
467486 if (!this.testActive) return
468- const input = $('#user-input').val()
469- const visible = $('.text-to-type .line').first().text() || ''
470- let correct = 0
471- for (let i = 0; i < Math.min(input.length, visible.length); i++) {
472- if (input[i] === visible[i]) correct++
487+
488+ // Live accuracy based on all keystrokes, including errors and corrections
489+ let liveAcc = 0
490+ if (this.actualTypedCharacterKeystrokes > 0) {
491+ liveAcc = Math.round(
492+ ((this.actualTypedCharacterKeystrokes - this.actualMistakesMade) /
493+ this.actualTypedCharacterKeystrokes) *
494+ 100
495+ )
473496 }
474- // Live accuracy
475- const acc = visible.length > 0 ?
476- Math.round((correct / Math.min(input.length, visible.length)) * 100) :
477- 0
478- $('.accuracy').text(`Accuracy: ${acc}%`)
479- // Live WPM
497+ $('.accuracy').text(`Accuracy: ${liveAcc}%`)
498+
499+ // Live WPM calculation
480500 const elapsedSec = (Date.now() - this.startTime) / 1000
481501 if (elapsedSec < 0.5) {
482502 $('.wpm').html(
@@ -486,28 +506,32 @@ class TypingTest {
486506 return
487507 }
488508 const mins = elapsedSec / 60
489- const rawChars = this.currentCharIndex + input.length
490- const rawWpm = Math.max(0, Math.round((rawChars / 5) / mins))
491- const totalCorrect = this.metrics.completedLinesStats.correctChars + correct
509+
510+ // Gross WPM
511+ const rawWpm = Math.max(0, Math.round((this.actualTypedCharacterKeystrokes / 5) / mins))
512+
513+ // Net WPM
492514 let correctWpm = 0
515+ const currentNetChars = this.actualTypedCharacterKeystrokes - this.actualMistakesMade
516+
493517 if (elapsedSec < 7) {
494518 const deltaSec = (Date.now() - this.metrics.lastUpdateTime) / 1000
495- const deltaChars = totalCorrect -
496- this.metrics.lastCorrectCharCountForSmoothing
519+ const deltaNetChars = currentNetChars - this.metrics.lastCorrectCharCountForSmoothing;
497520 const inst = deltaSec > 0.05 ?
498- Math.round(((deltaChars / 5) / (deltaSec / 60))) :
499- this.metrics.lastUpdateWPM
500- correctWpm = Math.round(inst * 0.4 + this.metrics.lastUpdateWPM * 0.6)
521+ Math.round(((deltaNetChars / 5) / (deltaSec / 60))) :
522+ this.metrics.lastUpdateWPM;
523+ correctWpm = Math.round(inst * 0.4 + this.metrics.lastUpdateWPM * 0.6);
501524 } else {
502- correctWpm = Math.round((totalCorrect / 5) / mins)
525+ correctWpm = Math.round((currentNetChars / 5) / mins);
503526 }
504527 correctWpm = Math.min(Math.max(0, correctWpm), 350)
528+
505529 $('.wpm').html(
506530 `WPM: <span class="text-gray-600">${rawWpm}</span> / ` +
507531 `<span class="text-green-600">${correctWpm}</span>`
508532 )
509533 // Update smoothing state
510- this.metrics.lastCorrectCharCountForSmoothing = totalCorrect
534+ this.metrics.lastCorrectCharCountForSmoothing = currentNetChars;
511535 this.metrics.lastUpdateWPM = correctWpm
512536 this.metrics.lastUpdateTime = Date.now()
513537 if (Date.now() - this.metrics.lastWPMHistoryTime > 3000) {
@@ -527,61 +551,55 @@ class TypingTest {
527551 let elapsed = (endTime - this.startTime) / 1000
528552 if (elapsed < 0.1) elapsed = 0.1
529553 const mins = elapsed / 60
530- const finalInput = $('#user-input').val()
531- const finalLine = $('.text-to-type .line').first().text() || ''
532- // Final line stats
533- let lastCorrect = 0,
534- lastIncorrect = 0,
535- lastExtra = 0
536- for (let i = 0; i < finalInput.length; i++) {
537- if (i < finalLine.length) {
538- finalInput[i] === finalLine[i] ? lastCorrect++ : lastIncorrect++
539- } else {
540- lastExtra++
541- }
542- }
543- const totalCorrect = this.metrics.completedLinesStats.correctChars +
544- lastCorrect
545- const totalIncorrect = this.metrics.completedLinesStats.incorrectChars +
546- lastIncorrect
547- const rawWpm = Math.max(
554+
555+ // WPM
556+ const grossWpmFinal = Math.max(
548557 0,
549- Math.round((( this.currentCharIndex + finalInput.length) / 5) / mins)
558+ Math.round((this.actualTypedCharacterKeystrokes / 5) / mins)
550559 )
551- const correctWpm = Math.max(
560+ const netWpmFinal = Math.max(
552561 0,
553- Math.round((totalCorrect / 5) / mins)
562+ Math.round(
563+ ((this.actualTypedCharacterKeystrokes - this.actualMistakesMade) /
564+ 5) /
565+ mins
566+ )
554567 )
555- const accDen = totalCorrect + totalIncorrect
556- const finalAcc = accDen > 0 ?
557- Math.round((totalCorrect / accDen) * 100) :
558- (totalCorrect > 0 ? 100 : 0)
568+ // Accuracy
569+ const finalAcc =
570+ this.actualTypedCharacterKeystrokes > 0 ?
571+ Math.round(
572+ ((this.actualTypedCharacterKeystrokes - this.actualMistakesMade) /
573+ this.actualTypedCharacterKeystrokes) *
574+ 100
575+ ) :
576+ 0
559577 // Consistency
560578 if (this.metrics.wpmHistory.length === 0 ||
561579 Date.now() - this.metrics.lastWPMHistoryTime > 1000) {
562- this.metrics.wpmHistory.push(correctWpm)
580+ this.metrics.wpmHistory.push(netWpmFinal) // Use netWPM for consistency history
563581 }
564582 const consistency = this.calculateConsistency(this.metrics.wpmHistory)
565583 // Display results
566584 $('#final-wpm').html(
567- `<span class="text-gray-600">${rawWpm }</span> / ` +
568- `<span class="text-green-600">${correctWpm }</span>`
585+ `<span class="text-gray-600">${grossWpmFinal }</span> / ` +
586+ `<span class="text-green-600">${netWpmFinal }</span>`
569587 )
570588 $('#final-accuracy').text(`${finalAcc}%`)
571589 $('#final-consistency').text(`${consistency}%`)
572590 $('.typing-test').attr('hidden', true)
573591 $('.result').attr('hidden', false)
574592 $('#test-screen').addClass('hidden')
575593 $('#stats-duration').text($('.timer').text())
576- $('#stats-correct-wpm').text(correctWpm )
594+ $('#stats-correct-wpm').text(netWpmFinal )
577595 $('#stats-accuracy').text(`${finalAcc}%`)
578596 $('#stats-consistency').text(`${consistency}%`)
579597 $('#stats-screen').removeClass('hidden')
580598 this.saveTestData(
581- correctWpm ,
582- rawWpm ,
599+ netWpmFinal ,
600+ grossWpmFinal ,
583601 finalAcc,
584- `${totalCorrect }/${totalIncorrect}/${lastExtra} `,
602+ `${this.actualTypedCharacterKeystrokes - this.actualMistakesMade }/${this.actualMistakesMade}/0 `,
585603 `${this.currentMode} ${this.currentGoal}`,
586604 consistency
587605 )
0 commit comments