10fastfingers.com - Typing speed

Shows typing speed in wpm.

// ==UserScript==
// @name         10fastfingers.com - Typing speed
// @namespace    10fastfingers.com
// @version      0.8.1.4
// @description  Shows typing speed in wpm.
// @author       puzzle
// @match        https://10fastfingers.com/typing-test/*
// @match        https://10fastfingers.com/advanced-typing-test/*
// @match        https://10fastfingers.com/widget*/typingtest*
// @require      https://cdn.jsdelivr.net/npm/canvas-gauges@2.1.7/gauge.min.js
// @run-at       document-end
// @grant        none
// @noframes
// ==/UserScript==

// jshint esnext: true
/* globals RadialGauge */

(async function() {
    'use strict';
    let page = null;

    if (location.pathname.includes('typingtest')) {
       page = 'custom';
    } else if (location.pathname.includes('typing-test')) {
       page = 'regular';
    }

    const WORD_LENGTH = 5,
          SPACE = 1,
          SPEED_TIME_UNIT = 60,
          UPDATE_INTERVAL = 1000;

    let appContainer = null;

    if (page === 'regular') {
        appContainer = document.querySelector('#speedtest-main');
    } else if (page === 'custom') {
        appContainer = document.querySelector('#settings-container');
    }



    const appHtml = `
<style>
#_app {
  text-align: center;
  position: relative;
  z-index: 99999;
  color: #1474ed;
  background: transparent;
  text-shadow: currentcolor 0 0 10px;
}
</style>
<div id='_app'>
  <canvas id='gauge'></canvas>
</div>
`;

    if (page === 'regular') {
       appContainer.insertAdjacentHTML('afterBegin', appHtml);
    } else if (page === 'custom') {
       appContainer.insertAdjacentHTML('beforeBegin', appHtml);
    }
    

    let correctChars = 0,
        prevWordPointer = 0;


    document.addEventListener('keyup', e => {
        if (e.keyCode === 32) {

            if (prevWordPointer == window.word_pointer) return;
            prevWordPointer = window.word_pointer;

            let match = window.user_input_stream.match(/([^\s]*)\s$/),
                lastTypedWord = match && match[1],
                lastCorrectWord = window.words[window.word_pointer-1];

            if (window.countdown && lastTypedWord === lastCorrectWord) {
                correctChars += lastCorrectWord.length + SPACE;
            }

        }
    });

    // all config options can be found here - https://canvas-gauges.com/documentation/user-guide/configuration

    window.gauge = new RadialGauge({
        renderTo: 'gauge',

        width: 250,
        height: 250,

        units: "WPM",
        minValue: 0,
        maxValue: 220,
        majorTicks: [ "0", "20", "40", "60", "80", "100", "120", "140", "160", "180", "200", "220" ],
        minorTicks: 2,
        majorTicksDec: 5,
        strokeTicks: true,
        highlights: [
            {
                "from": 160,
                "to": 220,
                "color": "rgba(200, 50, 50, .75)"
            }
        ],        
        borderShadowWidth: 0,
        borders: false,
        needleType: "arrow",
        needleWidth: 2,
        needleCircleSize: 7,
        needleCircleOuter: true,
        needleCircleInner: false,

        animation: true,
        animationRule: "linear",
        animationDuration: UPDATE_INTERVAL - 100,
        animatedValue: true,

        valueBox: true,
        valueBoxBorderRadius: 1.5,
        valueBoxStroke: 5,
        valueBoxWidth: 40,
        valueInt: 3,
        valueDec: 2,

        colorPlate: "white",
        //colorNeedle: 'blue',
        //colorNeedleEnd: 'blue'

        fontNumbersWeight: 700

    }).draw();

    function updateGauge(value) {
        window.gauge.value = value;
        /*window.gauge.update({
            valueText: value.toFixed(2).padStart(6,0)
        });*/
    }


    function resetValues() {
        prevWordPointer = 0;
        correctChars = 0;
        window.gauge.value = 0;
        /*window.gauge.update({
            valueText: "000.00"
        });*/
    }


    function waitUntilVarAssigned(v) {
        return new Promise( (res,rej) => {
            setTimeout(function check() {
                if (Array.isArray(v)) {
                    if (v.every( i => !!window[i] )) {
                        res();
                        return;
                    }
                } else {
                    if (window[v]) {
                        res();
                        return;
                    }
                }

                setTimeout(check, 500)

            },0);
        })
    }


    let resetDone = false;
    let zeroCountdownOnceDone = false;
    let avgSpeed = 0;

    if (page === 'regular') {
       await waitUntilVarAssigned('countdown');
    } else if (page === 'custom') {
       await waitUntilVarAssigned(['param_duration','countdown']);
    }

    setInterval(function() {
        let duration = null;

        if (page === 'regular') {
            duration = 60;
        } else if (page === 'custom') {
            duration = window.param_duration;
        }

        if (window.countdown == duration) {
            if (!resetDone) {
                resetValues();
                resetDone = true;
                zeroCountdownOnceDone = false;
            }
        } else {
            if (window.countdown === 0 && zeroCountdownOnceDone) return;

            resetDone = false;

            avgSpeed = correctChars*SPEED_TIME_UNIT/(duration-window.countdown)/WORD_LENGTH;

            updateGauge(avgSpeed);

            if (window.countdown === 0 && !zeroCountdownOnceDone) {
                zeroCountdownOnceDone = true;
            }
        }
    }, UPDATE_INTERVAL);

})();