fixing wpm and correctedHistory for Korean (#3474) neezacoto

* fixing wpm and correct history for korean

* pretty fix
This commit is contained in:
Christian Rudder 2022-09-05 07:09:35 -04:00 committed by GitHub
parent 1a9e2c4134
commit e541fbaf53
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 150 additions and 44 deletions

View file

@ -295,6 +295,7 @@
// transition: .25s;
.wordInputAfter {
opacity: 1;
white-space: nowrap;
position: absolute;
background: var(--sub-color);
color: var(--bg-color);

View file

@ -32,6 +32,8 @@ import { navigate } from "./route-controller";
let dontInsertSpace = false;
let correctShiftUsed = true;
let isKoCompiling = false;
let isBackspace: boolean;
const wordsInput = document.getElementById("wordsInput") as HTMLInputElement;
const koInputVisual = document.getElementById("koInputVisual") as HTMLElement;
@ -291,7 +293,8 @@ function isCharCorrect(char: string, charIndex: number): boolean {
return true;
}
if (Config.language.startsWith("korean")) {
//Checking for Korean char
if (TestInput.input.getKoreanStatus()) {
//disassembles Korean current Test word to check against char Input
const koWordArray: string[] = Hangul.disassemble(
TestWords.words.getCurrent()
@ -366,7 +369,7 @@ function handleChar(
if (TestUI.resultCalculating || TestUI.resultVisible) {
return;
}
const isCharKorean: boolean = TestInput.input.getKoreanStatus();
if (char === "…") {
for (let i = 0; i < 3; i++) {
handleChar(".", charIndex + i);
@ -420,7 +423,9 @@ function handleChar(
let resultingWord: string;
if (thisCharCorrect && Config.mode !== "zen") {
char = TestWords.words.getCurrent().charAt(charIndex);
char = !isCharKorean
? TestWords.words.getCurrent().charAt(charIndex)
: Hangul.disassemble(TestWords.words.getCurrent())[charIndex];
}
if (!thisCharCorrect && char === "\n") {
@ -432,7 +437,7 @@ function handleChar(
TestInput.setBurstStart(performance.now());
}
if (!Config.language.startsWith("korean")) {
if (!isCharKorean && !Config.language.startsWith("korean")) {
resultingWord =
TestInput.input.current.substring(0, charIndex) +
char +
@ -487,10 +492,14 @@ function handleChar(
//update current corrected version. if its empty then add the current char. if its not then replace the last character with the currently pressed one / add it
if (TestInput.corrected.current === "") {
TestInput.corrected.current += resultingWord;
TestInput.corrected.current += !isCharKorean
? resultingWord
: Hangul.disassemble(resultingWord).join("");
} else {
if (charIndex >= TestInput.corrected.current.length) {
TestInput.corrected.current += char;
TestInput.corrected.current += !isCharKorean
? char
: Hangul.disassemble(char).concat();
} else if (!thisCharCorrect) {
TestInput.corrected.current =
TestInput.corrected.current.substring(0, charIndex) +
@ -891,6 +900,8 @@ $(document).keydown(async (event) => {
TestUI.scrollTape();
}
}
isBackspace = event.key === "Backspace" || event.key === "delete";
});
$("#wordsInput").keyup((event) => {
@ -918,33 +929,59 @@ $("#wordsInput").on("beforeinput", (event) => {
}
});
let avg = 0;
$("#wordsInput").on("input", (event) => {
const start = performance.now();
if (!event.originalEvent?.isTrusted || TestUI.testRestarting) {
(event.target as HTMLInputElement).value = " ";
return;
}
const popupVisible = Misc.isAnyPopupVisible();
if (popupVisible) return;
TestInput.setKeypressNotAfk();
const isLangKorean: boolean = Config.language.startsWith("korean");
if (
(event.target as HTMLInputElement).value
.normalize()
.match(
/[\uac00-\ud7af]|[\u1100-\u11ff]|[\u3130-\u318f]|[\ua960-\ua97f]|[\ud7b0-\ud7ff]/g
)
) {
TestInput.input.setKoreanStatus(true);
}
const containsKorean = TestInput.input.getKoreanStatus();
//Hangul.disassemble breaks down Korean characters into its components
//allowing it to be treated as normal latin characters
//Hangul.disassemble('한글') //['ㅎ','ㅏ','ㄴ','ㄱ','ㅡ','ㄹ']
//Hangul.disassemble('한글',true) //[['ㅎ','ㅏ','ㄴ'],['ㄱ','ㅡ','ㄹ']]
const realInputValue = (event.target as HTMLInputElement).value.normalize();
const inputValue = isLangKorean
const inputValue = containsKorean
? Hangul.disassemble(realInputValue).join("").slice(1)
: realInputValue.slice(1);
const currTestInput = isLangKorean
const currTestInput = containsKorean
? Hangul.disassemble(TestInput.input.current).join("")
: TestInput.input.current;
//checks to see if a korean word has compiled into two characters.
//inputs: ㄱ, 가, 갇, 가다
//what it actually reads: ㄱ, 가, 갇, , 가, 가다
//this skips this part (, , 가,)
if (containsKorean && !isBackspace) {
if (
isKoCompiling ||
(realInputValue.slice(1).length < TestInput.input.current.length &&
Hangul.disassemble(TestInput.input.current.slice(-1)).length > 1)
) {
isKoCompiling = !isKoCompiling;
return;
}
}
// input will be modified even with the preventDefault() in
// beforeinput/keydown if it's part of a compose sequence. this undoes
// the effects of that and takes the input out of compose mode.
@ -960,7 +997,7 @@ $("#wordsInput").on("input", (event) => {
// fallback for when no Backspace keydown event (mobile)
backspaceToPrevious();
} else if (inputValue.length < currTestInput.length) {
if (!isLangKorean) {
if (!containsKorean) {
TestInput.input.current = inputValue;
} else {
const realInput = (event.target as HTMLInputElement).value
@ -1025,6 +1062,9 @@ $("#wordsInput").on("input", (event) => {
).selectionEnd = (event.target as HTMLInputElement).value.length;
}
}, 0);
const end = performance.now();
avg = Math.floor(end - start + avg) / 2;
console.log("input avg", avg, "live input", end - start);
});
$("#wordsInput").on("focus", (event) => {

View file

@ -23,12 +23,14 @@ class Input {
current: string;
history: string[];
historyLength: number;
koreanStatus: boolean;
length: number;
constructor() {
this.current = "";
this.history = [];
this.historyLength = 0;
this.length = 0;
this.koreanStatus = false;
}
reset(): void {
@ -47,6 +49,10 @@ class Input {
this.length = this.current.length;
}
setKoreanStatus(val: boolean): void {
this.koreanStatus = val;
}
appendCurrent(val: string): void {
this.current += val;
this.length = this.current.length;
@ -60,6 +66,10 @@ class Input {
return this.current;
}
getKoreanStatus(): boolean {
return this.koreanStatus;
}
pushHistory(): void {
this.history.push(this.current);
this.historyLength = this.history.length;

View file

@ -1,3 +1,4 @@
import Hangul from "hangul-js";
import Config from "../config";
import * as Misc from "../utils/misc";
import * as TestInput from "./test-input";
@ -188,18 +189,35 @@ export function calculateTestSeconds(now?: number): number {
return (now - start) / 1000;
}
}
let avg = 0;
export function calculateWpmAndRaw(): MonkeyTypes.WordsPerMinuteAndRaw {
const start = performance.now();
const containsKorean = TestInput.input.getKoreanStatus();
let chars = 0;
let correctWordChars = 0;
let spaces = 0;
const currTestInput = !containsKorean
? TestInput.input.current
: Hangul.disassemble(TestInput.input.current);
//check input history
for (let i = 0; i < TestInput.input.history.length; i++) {
const word =
const word: string = !containsKorean
? //english
Config.mode == "zen"
? (TestInput.input.getHistory(i) as string)
: TestWords.words.get(i)
: //korean
Config.mode == "zen"
? TestInput.input.getHistory(i)
: TestWords.words.get(i);
if (TestInput.input.getHistory(i) == word) {
? Hangul.disassemble(TestInput.input.getHistory(i) as string).join("")
: Hangul.disassemble(TestWords.words.get(i)).join("");
const historyWord: string = !containsKorean
? (TestInput.input.getHistory(i) as string)
: Hangul.disassemble(TestInput.input.getHistory(i) as string).join("");
if (historyWord === word) {
//the word is correct
//+1 for space
correctWordChars += word.length;
@ -210,13 +228,17 @@ export function calculateWpmAndRaw(): MonkeyTypes.WordsPerMinuteAndRaw {
spaces++;
}
}
chars += TestInput.input.getHistory(i).length;
chars += !containsKorean
? TestInput.input.getHistory(i).length
: Hangul.disassemble(TestInput.input.getHistory(i) as string).length;
}
if (TestInput.input.current !== "") {
if (currTestInput !== "") {
const word =
Config.mode == "zen"
? TestInput.input.current
: TestWords.words.getCurrent();
? currTestInput
: !containsKorean
? TestWords.words.getCurrent()
: Hangul.disassemble(TestWords.words.getCurrent() ?? "");
//check whats currently typed
const toAdd = {
correct: 0,
@ -224,9 +246,9 @@ export function calculateWpmAndRaw(): MonkeyTypes.WordsPerMinuteAndRaw {
missed: 0,
};
for (let c = 0; c < word.length; c++) {
if (c < TestInput.input.current.length) {
if (c < currTestInput.length) {
//on char that still has a word list pair
if (TestInput.input.current[c] == word[c]) {
if (currTestInput[c] === word[c]) {
toAdd.correct++;
} else {
toAdd.incorrect++;
@ -247,12 +269,15 @@ export function calculateWpmAndRaw(): MonkeyTypes.WordsPerMinuteAndRaw {
if (Config.funbox === "nospace" || Config.funbox === "arrows") {
spaces = 0;
}
chars += TestInput.input.current.length;
chars += currTestInput.length;
const testSeconds = calculateTestSeconds(performance.now());
const wpm = Math.round(
((correctWordChars + spaces) * (60 / testSeconds)) / 5
);
const raw = Math.round(((chars + spaces) * (60 / testSeconds)) / 5);
const end = performance.now();
avg = (end - start + avg) / 2;
console.log("wpm avg", avg, "live wpm", end - start);
return {
wpm: wpm,
raw: raw,
@ -294,11 +319,17 @@ export function setLastSecondNotRound(): void {
}
export function calculateBurst(): number {
const containsKorean = TestInput.input.getKoreanStatus();
const timeToWrite = (performance.now() - TestInput.currentBurstStart) / 1000;
let wordLength: number;
wordLength = TestInput.input.current.length;
wordLength = !containsKorean
? TestInput.input.current.length
: Hangul.disassemble(TestInput.input.current).length;
if (wordLength == 0) {
wordLength = TestInput.input.getHistoryLast()?.length ?? 0;
wordLength = !containsKorean
? TestInput.input.getHistoryLast()?.length ?? 0
: Hangul.disassemble(TestInput.input.getHistoryLast() as string)
?.length ?? 0;
}
if (wordLength == 0) return 0;
const speed = Misc.roundTo2((wordLength * (60 / timeToWrite)) / 5);
@ -330,30 +361,41 @@ function countChars(): CharCount {
let spaces = 0;
let correctspaces = 0;
for (let i = 0; i < TestInput.input.history.length; i++) {
const word =
const containsKorean = TestInput.input.getKoreanStatus();
const word: string = !containsKorean
? //english
Config.mode == "zen"
? (TestInput.input.getHistory(i) as string)
: TestWords.words.get(i)
: //korean
Config.mode == "zen"
? TestInput.input.getHistory(i)
: TestWords.words.get(i);
? Hangul.disassemble(TestInput.input.getHistory(i) as string).join("")
: Hangul.disassemble(TestWords.words.get(i)).join("");
if (TestInput.input.getHistory(i) === "") {
//last word that was not started
continue;
}
if (TestInput.input.getHistory(i) == word) {
const historyWord: string = !containsKorean
? (TestInput.input.getHistory(i) as string)
: Hangul.disassemble(TestInput.input.getHistory(i) as string).join("");
if (historyWord == word) {
//the word is correct
correctWordChars += word.length;
correctChars += word.length;
if (
i < TestInput.input.history.length - 1 &&
Misc.getLastChar(TestInput.input.getHistory(i) as string) !== "\n"
Misc.getLastChar(historyWord as string) !== "\n"
) {
correctspaces++;
}
} else if (TestInput.input.getHistory(i).length >= word.length) {
} else if (historyWord.length >= word.length) {
//too many chars
for (let c = 0; c < TestInput.input.getHistory(i).length; c++) {
for (let c = 0; c < historyWord.length; c++) {
if (c < word.length) {
//on char that still has a word list pair
if (TestInput.input.getHistory(i)[c] == word[c]) {
if (historyWord[c] == word[c]) {
correctChars++;
} else {
incorrectChars++;
@ -371,9 +413,9 @@ function countChars(): CharCount {
missed: 0,
};
for (let c = 0; c < word.length; c++) {
if (c < TestInput.input.getHistory(i).length) {
if (c < historyWord.length) {
//on char that still has a word list pair
if (TestInput.input.getHistory(i)[c] == word[c]) {
if (historyWord[c] == word[c]) {
toAdd.correct++;
} else {
toAdd.incorrect++;

View file

@ -383,9 +383,9 @@ export function updateWordElement(showError = !Config.blindMode): void {
} else {
let correctSoFar = false;
const isLangKorean: boolean = Config.language.startsWith("korean");
const containsKorean = TestInput.input.getKoreanStatus();
if (!isLangKorean) {
if (!containsKorean) {
// slice earlier if input has trailing compose characters
const inputWithoutComposeLength = Misc.trailingComposeChars.test(input)
? input.search(Misc.trailingComposeChars)
@ -464,7 +464,7 @@ export function updateWordElement(showError = !Config.blindMode): void {
currentLetter !== undefined &&
CompositionState.getComposing() &&
i >= CompositionState.getStartPos() &&
!(isLangKorean && !correctSoFar)
!(containsKorean && !correctSoFar)
) {
ret += `<letter class="${
Config.highlightMode == "word" ? wordHighlightClassString : ""
@ -697,6 +697,13 @@ async function loadWordsHistory(): Promise<boolean> {
for (let i = 0; i < TestInput.input.history.length + 2; i++) {
const input = <string>TestInput.input.getHistory(i);
const word = TestWords.words.get(i);
const containsKorean =
input?.match(
/[\uac00-\ud7af]|[\u1100-\u11ff]|[\u3130-\u318f]|[\ua960-\ua97f]|[\ud7b0-\ud7ff]/g
) ||
word?.match(
/[\uac00-\ud7af]|[\u1100-\u11ff]|[\u3130-\u318f]|[\ua960-\ua97f]|[\ud7b0-\ud7ff]/g
);
let wordEl = "";
try {
if (input === "") throw new Error("empty input word");
@ -704,10 +711,12 @@ async function loadWordsHistory(): Promise<boolean> {
TestInput.corrected.getHistory(i) !== undefined &&
TestInput.corrected.getHistory(i) !== ""
) {
const correctedChar = !containsKorean
? TestInput.corrected.getHistory(i)
: Hangul.assemble(TestInput.corrected.getHistory(i).split(""));
wordEl = `<div class='word' burst="${
TestInput.burstHistory[i]
}" input="${TestInput.corrected
.getHistory(i)
}" input="${correctedChar
.replace(/"/g, "&quot;")
.replace(/ /g, "_")}">`;
} else {
@ -759,19 +768,23 @@ async function loadWordsHistory(): Promise<boolean> {
//input is shorter or equal (loop over word list)
loop = word.length;
}
for (let c = 0; c < loop; c++) {
let correctedChar;
try {
correctedChar = TestInput.corrected.getHistory(i)[c];
correctedChar = !containsKorean
? TestInput.corrected.getHistory(i)[c]
: Hangul.assemble(TestInput.corrected.getHistory(i).split(""))[c];
} catch (e) {
correctedChar = undefined;
}
let extraCorrected = "";
const historyWord: string = !containsKorean
? TestInput.corrected.getHistory(i)
: Hangul.assemble(TestInput.corrected.getHistory(i).split(""));
if (
c + 1 === loop &&
TestInput.corrected.getHistory(i) !== undefined &&
TestInput.corrected.getHistory(i).length > input.length
historyWord !== undefined &&
historyWord.length > input.length
) {
extraCorrected = "extraCorrected";
}