// modified from https://stackoverflow.com/a/14051331 // Issue is keycode conversion for non-alphanumeric keys (e.g. period) // is not accurate function convertKeyCode(evt) { var chara = ""; var keyCode = (evt.which) ? evt.which : evt.keyCode; var shift = evt.shiftKey; if (keyCode == 19) chara = "pause/break"; if (keyCode == 48) chara = (shift) ? ")" : "0"; if (keyCode == 49) chara = (shift) ? "!" : "1"; if (keyCode == 50) chara = (shift) ? "@" : "2"; if (keyCode == 51) chara = (shift) ? "#" : "3"; if (keyCode == 52) chara = (shift) ? "$" : "4"; if (keyCode == 53) chara = (shift) ? "%" : "5"; if (keyCode == 54) chara = (shift) ? "^" : "6"; if (keyCode == 55) chara = (shift) ? "&" : "7"; if (keyCode == 56) chara = (shift) ? "*" : "8"; if (keyCode == 57) chara = (shift) ? "(" : "9"; if (keyCode == 65) chara = (shift) ? "A" : "a"; if (keyCode == 66) chara = (shift) ? "B" : "b"; if (keyCode == 67) chara = (shift) ? "C" : "c"; if (keyCode == 68) chara = (shift) ? "D" : "d"; if (keyCode == 69) chara = (shift) ? "E" : "e"; if (keyCode == 70) chara = (shift) ? "F" : "f"; if (keyCode == 71) chara = (shift) ? "G" : "g"; if (keyCode == 72) chara = (shift) ? "H" : "h"; if (keyCode == 73) chara = (shift) ? "I" : "i"; if (keyCode == 74) chara = (shift) ? "J" : "j"; if (keyCode == 75) chara = (shift) ? "K" : "k"; if (keyCode == 76) chara = (shift) ? "L" : "l"; if (keyCode == 77) chara = (shift) ? "M" : "m"; if (keyCode == 78) chara = (shift) ? "N" : "n"; if (keyCode == 79) chara = (shift) ? "O" : "o"; if (keyCode == 80) chara = (shift) ? "P" : "p"; if (keyCode == 81) chara = (shift) ? "Q" : "q"; if (keyCode == 82) chara = (shift) ? "R" : "r"; if (keyCode == 83) chara = (shift) ? "S" : "s"; if (keyCode == 84) chara = (shift) ? "T" : "t"; if (keyCode == 85) chara = (shift) ? "U" : "u"; if (keyCode == 86) chara = (shift) ? "V" : "v"; if (keyCode == 87) chara = (shift) ? "W" : "w"; if (keyCode == 88) chara = (shift) ? "X" : "x"; if (keyCode == 89) chara = (shift) ? "Y" : "y"; if (keyCode == 90) chara = (shift) ? "Z" : "z"; if (keyCode == 93) chara = "select key"; if (keyCode == 186 || keyCode == 59) chara = ";"; if (keyCode == 187 || keyCode == 61) chara = "="; if (keyCode == 188) chara = ","; if (keyCode == 189 || keyCode == 173) chara = "-"; if (keyCode == 190) chara = "."; if (keyCode == 191) chara = "/"; if (keyCode == 192) chara = "`"; if (keyCode == 219) chara = (shift) ? "{" : "["; if (keyCode == 220) chara = "\\"; if (keyCode == 221) chara = (shift) ? "}" : "]"; if (keyCode == 222) chara = "'"; return chara; } const wordDisplay = document.getElementById("word"); const input = document.getElementById("input"); const timer = document.getElementById("timer"); const hintDisplay = document.getElementById("hint"); const leftShiftCode = 32; // space const rightShiftCode = 16; // shift key let leftShift = false; let rightShift = false; addEventListener("keydown", event => { switch(event.keyCode) { case leftShiftCode: leftShift = true; event.preventDefault(); break; case rightShiftCode: rightShift = true; event.preventDefault(); break; } }); addEventListener("keyup", event => { switch(event.keyCode) { case leftShiftCode: leftShift = false; break; case rightShiftCode: rightShift = false; break; } }); const check = () => { if (input.value == word) { clearTimeout(timeout); wordDisplay.style.color = "green"; wordDisplay.innerHTML += " ✅"; setTimeout(() => { wordDisplay.style.color = ""; reset(); }, 1000); } }; addEventListener("compositionend", event => check()); input.addEventListener("keydown", e => { const pressed = convertKeyCode(e).toLowerCase(); // String.fromCharCode(e.keyCode).toLowerCase(); if (keymap[pressed] !== undefined) { let valueIndex = 0; if (leftShift && keymap[pressed][1]) { valueIndex = 1; } else if (rightShift && keymap[pressed][2]) { valueIndex = 2; } input.value += keymap[pressed][valueIndex]; check(); e.preventDefault(); } else if (pressed == " ") { e.preventDefault(); } }); // window.requestAnimationFrame() might be better suited // https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame let typeTimePerKana = 2; // in seconds const typeTimePerKanaInput = document.getElementById("timePerKana"); typeTimePerKanaInput.value = typeTimePerKana; typeTimePerKanaInput.addEventListener("change", (e) => { typeTimePerKana = typeTimePerKanaInput.value; }); const timerFPS = 60; let timerTimeout = null; let timeout = null; let word = null; const reset = () => { input.value = ""; hint.innerHTML = ""; timer.value = 1; word = words[Math.round(Math.random() * words.length)]; timeout = wordDisplay.innerHTML = word; const typeTime = typeTimePerKana * word.length; setTimeout(() => { for (let i = 0; i < word.length; i++) { const letter = word[i]; let key = null; let index = null; for (let otherKey in keymap) { for (let i = 0; i < keymap[otherKey].length && !key; i++) { if (keymap[otherKey][i] == letter) { key = otherKey; index = i; } } } let t = `

${letter.toUpperCase()}:`; if (index > 0) { t += ""; if (index == 1) { t += "←"; } else if (index == 2) { t += "→"; } else if (index == 3) { t += "↑"; } t += " + "; } t += `${key}

`; hint.innerHTML += t; } }, typeTime * 1000); clearInterval(timerTimeout); timerTimeout = setInterval(() => { timer.value -= (1 / timerFPS) / typeTime; }, 1000 / timerFPS); }; reset();