|
|
|
|
// 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(incorrectTimeout);
|
|
|
|
|
clearTimeout(timerTimeout);
|
|
|
|
|
wordDisplay.style.color = "green";
|
|
|
|
|
wordDisplay.innerHTML = `${word} ⭕`;
|
|
|
|
|
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 incorrectTimeout = null;
|
|
|
|
|
let timerTimeout = null;
|
|
|
|
|
let word = null;
|
|
|
|
|
let repeats = 0;
|
|
|
|
|
const reset = () => {
|
|
|
|
|
input.value = "";
|
|
|
|
|
hint.innerHTML = "";
|
|
|
|
|
timer.value = 1;
|
|
|
|
|
if (repeats == 0) {
|
|
|
|
|
word = words[Math.round(Math.random() * words.length)];
|
|
|
|
|
}
|
|
|
|
|
wordDisplay.innerHTML = word;
|
|
|
|
|
const typeTime = typeTimePerKana * word.length;
|
|
|
|
|
incorrectTimeout = setTimeout(() => {
|
|
|
|
|
repeats += repeats == 0 ? 3 : 1;
|
|
|
|
|
wordDisplay.innerHTML = `${word} ❌`;
|
|
|
|
|
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 = `<h3>${letter.toUpperCase()}:<kbd`;
|
|
|
|
|
if (index > 0) {
|
|
|
|
|
t += ' style="background: ';
|
|
|
|
|
if (index == 1) {
|
|
|
|
|
t += "yellow";
|
|
|
|
|
} else if (index == 2) {
|
|
|
|
|
t += "cyan";
|
|
|
|
|
} else if (index == 3) {
|
|
|
|
|
t += "magenta";
|
|
|
|
|
}
|
|
|
|
|
t += '"';
|
|
|
|
|
}
|
|
|
|
|
t += `>${key}</kbd></h3>`;
|
|
|
|
|
hint.innerHTML += t;
|
|
|
|
|
}
|
|
|
|
|
}, typeTime * 1000);
|
|
|
|
|
clearInterval(timerTimeout);
|
|
|
|
|
timerTimeout = setInterval(() => {
|
|
|
|
|
timer.value -= (1 / timerFPS) / typeTime;
|
|
|
|
|
}, 1000 / timerFPS);
|
|
|
|
|
if (repeats > 0) {
|
|
|
|
|
repeats -= 1;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
reset();
|