Initial commit
This commit is contained in:
commit
a9a32378d0
4 changed files with 298 additions and 0 deletions
27
index.html
Normal file
27
index.html
Normal file
|
@ -0,0 +1,27 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>nicolator</title>
|
||||
<link rel="stylesheet" href="https://unpkg.com/sakura.css/css/normalize.css" type="text/css">
|
||||
<link rel="stylesheet" href="https://unpkg.com/sakura.css/css/sakura-pink.css" type="text/css">
|
||||
<script src="keymap.js"></script>
|
||||
<script src="words.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<details open>
|
||||
<summary>Keymap</summary>
|
||||
<img src="http://xahlee.info/kbd/i2/japanese_nicola_j_keyboard_layout.png" alt="Keymap">
|
||||
</details>
|
||||
<details>
|
||||
<summary>Settings ⚙️</summary>
|
||||
Seconds per kana <input type="number" id="timePerKana">
|
||||
</details>
|
||||
<h1 id="word" style="text-align: center"></h1>
|
||||
<input id="input" style="width: 100%" autocomplete="off"><br>
|
||||
<progress id="timer" value="1" style="width: 100%"></progress>
|
||||
<div id="hint"></div>
|
||||
<script src="nicolator.js"></script>
|
||||
</body>
|
||||
</html>
|
48
keymap.js
Normal file
48
keymap.js
Normal file
|
@ -0,0 +1,48 @@
|
|||
const keymap = {
|
||||
"1": ["1", "?", null, "!"],
|
||||
"2": ["2", "/", null, "”"],
|
||||
"3": ["3", null, null, "#"],
|
||||
"4": ["4", "「", null, "$"],
|
||||
"5": ["5", "」", null, "%"],
|
||||
"6": ["6", null, "[", "&"],
|
||||
"7": ["7", null, "]", "’"],
|
||||
"8": ["8", null, "(", "("],
|
||||
"9": ["9", null, ")", ")"],
|
||||
"0": ["0", null, null, "~"],
|
||||
"-": ["-", null, null, "="],
|
||||
"=": ["^", null, null, "~"],
|
||||
"q": ["。", "ぁ", null, "Q"],
|
||||
"w": ["か", "え", "が", "W"],
|
||||
"e": ["た", "り", "だ", "E"],
|
||||
"r": ["こ", "ゃ", "ご", "R"],
|
||||
"t": ["さ", "れ", "ざ", "T"],
|
||||
"y": ["ら", "ぱ", "よ", "Y"],
|
||||
"u": ["ち", "ぢ", "に", "U"],
|
||||
"i": ["く", "ぐ", "る", "I"],
|
||||
"o": ["つ", "づ", "ま", "O"],
|
||||
"p": [",", "ぴ", "ぇ", "P"],
|
||||
"[": ["、", null, null, "‘"],
|
||||
"]": ["゛", null, "゜", "{"],
|
||||
"\\": ["]", null, null, "}"],
|
||||
"a": ["う", "を", null, "A"],
|
||||
"s": ["し", "あ", "じ", "S"],
|
||||
"d": ["て", "な", "で", "D"],
|
||||
"f": ["け", "ゅ", "げ", "F"],
|
||||
"g": ["せ", "も", "ぜ", "G"],
|
||||
"h": ["は", "ば", "み", "H"],
|
||||
"j": ["と", "ど", "お", "J"],
|
||||
"k": ["き", "ぎ", "の", "K"],
|
||||
"l": ["い", "ぽ", "ょ", "L"],
|
||||
";": ["ん", null, "っ", "+"],
|
||||
"'": [":", null, null, "*"],
|
||||
"z": [".", "ぅ", null, "Z"],
|
||||
"x": ["ひ", "ー", "び", "X"],
|
||||
"c": ["す", "ろ", "ず", "C"],
|
||||
"v": ["ふ", "や", "ぶ", "V"],
|
||||
"b": ["へ", "ぃ", "べ", "B"],
|
||||
"n": ["め", "ぷ", "ぬ", "N"],
|
||||
"m": ["そ", "ぞ", "ゆ", "M"],
|
||||
",": ["ね", "ぺ", "む", "<"],
|
||||
".": ["ほ", "ぼ", "わ", ">"],
|
||||
"/": ["・", null, "ぉ", "?"],
|
||||
};
|
222
nicolator.js
Normal file
222
nicolator.js
Normal file
|
@ -0,0 +1,222 @@
|
|||
// 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;
|
||||
let inComposition = 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;
|
||||
}
|
||||
});
|
||||
addEventListener("compositionstart", event => inComposition = true);
|
||||
addEventListener("compositionend", event => inComposition = false);
|
||||
input.addEventListener("keydown", e => {
|
||||
if (inComposition) {
|
||||
return;
|
||||
}
|
||||
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];
|
||||
if (input.value == word) {
|
||||
clearTimeout(timeout);
|
||||
wordDisplay.style.color = "green";
|
||||
wordDisplay.innerHTML += " ✅";
|
||||
setTimeout(() => {
|
||||
wordDisplay.style.color = "";
|
||||
reset();
|
||||
}, 1000);
|
||||
}
|
||||
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 = `<h3>${letter.toUpperCase()}:`;
|
||||
if (index > 0) {
|
||||
t += "<kbd>";
|
||||
if (index == 1) {
|
||||
t += "←";
|
||||
} else if (index == 2) {
|
||||
t += "→";
|
||||
} else if (index == 3) {
|
||||
t += "↑";
|
||||
}
|
||||
t += "</kbd> + ";
|
||||
}
|
||||
t += `<kbd>${key}</kbd></h3>`;
|
||||
hint.innerHTML += t;
|
||||
}
|
||||
}, typeTime * 1000);
|
||||
clearInterval(timerTimeout);
|
||||
timerTimeout = setInterval(() => {
|
||||
timer.value -= (1 / timerFPS) / typeTime;
|
||||
}, 1000 / timerFPS);
|
||||
};
|
||||
reset();
|
1
words.js
Normal file
1
words.js
Normal file
File diff suppressed because one or more lines are too long
Reference in a new issue