let wordsList = [];
let currentWordIndex = 0;
let currentWordElementIndex = 0;
let inputHistory = [];
let currentInput = "";
let time = 0;
let timers = [];
let testActive = false;
let testStart, testEnd;
let wpmHistory = [];
let rawHistory = [];
let restartCount = 0;
let incompleteTestSeconds = 0;
let currentTestLine = 0;
let pageTransition = false;
let keypressPerSecond = [];
let currentKeypressCount = 0;
let afkDetected = false;
let errorsPerSecond = [];
let currentErrorCount = 0;
let resultVisible = false;
let activeWordTopBeforeJump = 0;
let activeWordTop = 0;
let activeWordJumped = false;
let sameWordset = false;
let quotes = [];
let focusState = false;
let themeColors = {
bg: "#323437",
main: "#e2b714",
caret: "#e2b714",
sub: "#646669",
text: "#d1d0c5",
error: "#ca4754",
errorExtra: "#7e2a33",
colorfulError: "#ca4754",
colorfulErrorExtra: "#7e2a33",
};
let accuracyStats = {
correct: 0,
incorrect: 0,
};
let keypressStats = {
spacing: {
current: -1,
array: [],
},
duration: {
current: -1,
array: [],
},
};
let customText = "The quick brown fox jumps over the lazy dog".split(" ");
let customTextIsRandom = false;
let customTextWordCount = 1;
let randomQuote = null;
const testCompleted = firebase.functions().httpsCallable("testCompleted");
const addTag = firebase.functions().httpsCallable("addTag");
const editTag = firebase.functions().httpsCallable("editTag");
const removeTag = firebase.functions().httpsCallable("removeTag");
const updateResultTags = firebase.functions().httpsCallable("updateResultTags");
const saveConfig = firebase.functions().httpsCallable("saveConfig");
const generatePairingCode = firebase
.functions()
.httpsCallable("generatePairingCode");
function smooth(arr, windowSize, getter = (value) => value, setter) {
const get = getter;
const result = [];
for (let i = 0; i < arr.length; i += 1) {
const leftOffeset = i - windowSize;
const from = leftOffeset >= 0 ? leftOffeset : 0;
const to = i + windowSize + 1;
let count = 0;
let sum = 0;
for (let j = from; j < to && j < arr.length; j += 1) {
sum += get(arr[j]);
count += 1;
}
result[i] = setter ? setter(arr[i], sum / count) : sum / count;
}
return result;
}
function stdDev(array) {
try {
const n = array.length;
const mean = array.reduce((a, b) => a + b) / n;
return Math.sqrt(
array.map((x) => Math.pow(x - mean, 2)).reduce((a, b) => a + b) / n
);
} catch (e) {
return 0;
}
}
function mean(array) {
try {
return (
array.reduce((previous, current) => (current += previous)) / array.length
);
} catch (e) {
return 0;
}
}
function refreshThemeColorObject() {
let st = getComputedStyle(document.body);
themeColors.bg = st.getPropertyValue("--bg-color").replace(" ", "");
themeColors.main = st.getPropertyValue("--main-color").replace(" ", "");
themeColors.caret = st.getPropertyValue("--caret-color").replace(" ", "");
themeColors.sub = st.getPropertyValue("--sub-color").replace(" ", "");
themeColors.text = st.getPropertyValue("--text-color").replace(" ", "");
themeColors.error = st.getPropertyValue("--error-color").replace(" ", "");
themeColors.errorExtra = st
.getPropertyValue("--error-extra-color")
.replace(" ", "");
themeColors.colorfulError = st
.getPropertyValue("--colorful-error-color")
.replace(" ", "");
themeColors.colorfulErrorExtra = st
.getPropertyValue("--colorful-error-extra-color")
.replace(" ", "");
}
function showNotification(text, time) {
let noti = $(".notification");
noti.text(text);
noti.css("top", `-${noti.outerHeight()}px`);
noti.stop(true, true).animate(
{
top: "1rem",
},
250,
"swing",
() => {
noti.stop(true, true).animate(
{
opacity: 1,
},
time,
() => {
noti.stop(true, true).animate(
{
top: `-${noti.outerHeight()}px`,
},
250,
"swing"
);
}
);
}
);
}
function copyResultToClipboard() {
if (navigator.userAgent.toLowerCase().indexOf("firefox") > -1) {
showNotification("Sorry, this feature is not supported in Firefox", 4000);
} else {
$(".pageTest .ssWatermark").removeClass("hidden");
$(".pageTest .buttons").addClass("hidden");
let src = $("#middle");
var sourceX = src.position().left; /*X position from div#target*/
var sourceY = src.position().top; /*Y position from div#target*/
var sourceWidth = src.width(); /*clientWidth/offsetWidth from div#target*/
var sourceHeight = src.height(); /*clientHeight/offsetHeight from div#target*/
try {
html2canvas(document.body, {
backgroundColor: themeColors.bg,
height: sourceHeight + 50,
width: sourceWidth + 50,
x: sourceX - 25,
y: sourceY - 25,
}).then(function (canvas) {
// document.body.appendChild(canvas);
canvas.toBlob(function (blob) {
navigator.clipboard
.write([
new ClipboardItem(
Object.defineProperty({}, blob.type, {
value: blob,
enumerable: true,
})
),
])
.then((f) => {
showNotification("Copied to clipboard", 1000);
$(".pageTest .ssWatermark").addClass("hidden");
$(".pageTest .buttons").removeClass("hidden");
})
.catch((f) => {
showNotification("Error saving image to clipboard", 2000);
$(".pageTest .ssWatermark").addClass("hidden");
$(".pageTest .buttons").removeClass("hidden");
});
});
});
} catch (e) {
showNotification("Error creating image", 2000);
$(".pageTest .ssWatermark").addClass("hidden");
$(".pageTest .buttons").removeClass("hidden");
}
}
}
function getReleasesFromGitHub() {
$.getJSON(
"https://api.github.com/repos/Miodec/monkey-type/releases",
(data) => {
$("#bottom .version").text(data[0].name).css("opacity", 1);
$("#versionHistory .releases").empty();
data.forEach((release) => {
if (!release.draft && !release.prerelease) {
$("#versionHistory .releases").append(`
`;
if (input !== wordsList[index]) {
wordEl = `
`;
}
let loop;
if (input.length > wordsList[index].length) {
//input is longer - extra characters possible (loop over input)
loop = input.length;
} else {
//input is shorter or equal (loop over word list)
loop = wordsList[index].length;
}
for (let c = 0; c < loop; c++) {
// input.forEach((inputLetter, inputLetterIndex) => {
if (wordsList[index][c] !== undefined) {
if (input[c] === wordsList[index][c]) {
wordEl +=
'' + wordsList[index][c] + "";
} else {
if (input[c] === currentInput || input[c] === undefined) {
wordEl += "" + wordsList[index][c] + "";
} else {
wordEl +=
'' + wordsList[index][c] + "";
}
}
} else {
wordEl += '";
}
}
wordEl += "
";
$("#words").append(wordEl);
});
$("#showWordHistoryButton").addClass("loaded");
}
function flipTestColors(tf) {
if (tf) {
$("#words").addClass("flipped");
} else {
$("#words").removeClass("flipped");
}
}
function applyColorfulMode(tc) {
if (tc) {
$("#words").addClass("colorfulMode");
} else {
$("#words").removeClass("colorfulMode");
}
}
function showEditTags(action, id, name) {
if (action === "add") {
$("#tagsWrapper #tagsEdit").attr("action", "add");
$("#tagsWrapper #tagsEdit .title").html("Add new tag");
$("#tagsWrapper #tagsEdit .button").html(`
`);
$("#tagsWrapper #tagsEdit input").val("");
$("#tagsWrapper #tagsEdit input").removeClass("hidden");
} else if (action === "edit") {
$("#tagsWrapper #tagsEdit").attr("action", "edit");
$("#tagsWrapper #tagsEdit").attr("tagid", id);
$("#tagsWrapper #tagsEdit .title").html("Edit tag name");
$("#tagsWrapper #tagsEdit .button").html(`
`);
$("#tagsWrapper #tagsEdit input").val(name);
$("#tagsWrapper #tagsEdit input").removeClass("hidden");
} else if (action === "remove") {
$("#tagsWrapper #tagsEdit").attr("action", "remove");
$("#tagsWrapper #tagsEdit").attr("tagid", id);
$("#tagsWrapper #tagsEdit .title").html("Remove tag " + name);
$("#tagsWrapper #tagsEdit .button").html(`
`);
$("#tagsWrapper #tagsEdit input").addClass("hidden");
}
if ($("#tagsWrapper").hasClass("hidden")) {
$("#tagsWrapper")
.stop(true, true)
.css("opacity", 0)
.removeClass("hidden")
.animate({ opacity: 1 }, 100, (e) => {
$("#tagsWrapper #tagsEdit input").focus();
});
}
}
function hideEditTags() {
if (!$("#tagsWrapper").hasClass("hidden")) {
$("#tagsWrapper #tagsEdit").attr("action", "");
$("#tagsWrapper #tagsEdit").attr("tagid", "");
$("#tagsWrapper")
.stop(true, true)
.css("opacity", 1)
.animate(
{
opacity: 0,
},
100,
(e) => {
$("#tagsWrapper").addClass("hidden");
}
);
}
}
function showBackgroundLoader() {
$("#backgroundLoader").stop(true, true).fadeIn(125);
}
function hideBackgroundLoader() {
$("#backgroundLoader").stop(true, true).fadeOut(125);
}
function updateTestModesNotice() {
let anim = false;
if ($(".pageTest #testModesNotice").text() === "") anim = true;
$(".pageTest #testModesNotice").empty();
if (config.difficulty === "expert") {
$(".pageTest #testModesNotice").append(
`
expert
`
);
} else if (config.difficulty === "master") {
$(".pageTest #testModesNotice").append(
`
master
`
);
}
if (config.blindMode) {
$(".pageTest #testModesNotice").append(
`
blind
`
);
}
if (config.confidenceMode === "on") {
$(".pageTest #testModesNotice").append(
`
confidence
`
);
}
if (config.confidenceMode === "max") {
$(".pageTest #testModesNotice").append(
`
max confidence
`
);
}
if (config.stopOnError) {
$(".pageTest #testModesNotice").append(
`
stop on error
`
);
}
if (config.layout !== "default") {
$(".pageTest #testModesNotice").append(
`
${config.layout}
`
);
}
tagsString = "";
// $.each($('.pageSettings .section.tags .tagsList .tag'), (index, tag) => {
// if($(tag).children('.active').attr('active') === 'true'){
// tagsString += $(tag).children('.title').text() + ', ';
// }
// })
try {
dbSnapshot.tags.forEach((tag) => {
if (tag.active === true) {
tagsString += tag.name + ", ";
}
});
if (tagsString !== "") {
$(".pageTest #testModesNotice").append(
`
${tagsString.substring(
0,
tagsString.length - 2
)}
`
);
}
} catch (e) {}
if (anim) {
$(".pageTest #testModesNotice")
.css("transition", "none")
.css("opacity", 0)
.animate(
{
opacity: 1,
},
125,
(e) => {
$(".pageTest #testModesNotice").css("transition", ".125s");
}
);
}
}
$("#tagsWrapper").click((e) => {
if ($(e.target).attr("id") === "tagsWrapper") {
hideEditTags();
}
});
$("#tagsWrapper #tagsEdit .button").click((e) => {
tagsEdit();
});
$("#tagsWrapper #tagsEdit input").keypress((e) => {
if (e.keyCode == 13) {
tagsEdit();
}
});
function tagsEdit() {
let action = $("#tagsWrapper #tagsEdit").attr("action");
let inputVal = $("#tagsWrapper #tagsEdit input").val();
let tagid = $("#tagsWrapper #tagsEdit").attr("tagid");
hideEditTags();
if (action === "add") {
showBackgroundLoader();
addTag({ uid: firebase.auth().currentUser.uid, name: inputVal }).then(
(e) => {
hideBackgroundLoader();
let status = e.data.resultCode;
if (status === 1) {
showNotification("Tag added", 2000);
dbSnapshot.tags.push({
name: inputVal,
id: e.data.id,
});
updateResultEditTagsPanelButtons();
updateSettingsPage();
updateFilterTags();
} else if (status === -1) {
showNotification("Invalid tag name", 3000);
} else if (status < -1) {
showNotification("Unknown error", 3000);
}
}
);
} else if (action === "edit") {
showBackgroundLoader();
editTag({
uid: firebase.auth().currentUser.uid,
name: inputVal,
tagid: tagid,
}).then((e) => {
hideBackgroundLoader();
let status = e.data.resultCode;
if (status === 1) {
showNotification("Tag updated", 2000);
dbSnapshot.tags.forEach((tag) => {
if (tag.id === tagid) {
tag.name = inputVal;
}
});
updateResultEditTagsPanelButtons();
updateSettingsPage();
updateFilterTags();
} else if (status === -1) {
showNotification("Invalid tag name", 3000);
} else if (status < -1) {
showNotification("Unknown error", 3000);
}
});
} else if (action === "remove") {
showBackgroundLoader();
removeTag({ uid: firebase.auth().currentUser.uid, tagid: tagid }).then(
(e) => {
hideBackgroundLoader();
let status = e.data.resultCode;
if (status === 1) {
showNotification("Tag removed", 2000);
dbSnapshot.tags.forEach((tag, index) => {
if (tag.id === tagid) {
dbSnapshot.tags.splice(index, 1);
}
});
updateResultEditTagsPanelButtons();
updateSettingsPage();
updateFilterTags();
updateActiveTags();
} else if (status < -1) {
showNotification("Unknown error", 3000);
}
}
);
}
}
function showCapsWarning() {
if ($("#capsWarning").hasClass("hidden")) {
$("#capsWarning").removeClass("hidden");
}
}
function hideCapsWarning() {
if (!$("#capsWarning").hasClass("hidden")) {
$("#capsWarning").addClass("hidden");
}
}
function showCustomTextPopup() {
if ($("#customTextPopupWrapper").hasClass("hidden")) {
$("#customTextPopupWrapper")
.stop(true, true)
.css("opacity", 0)
.removeClass("hidden")
.animate({ opacity: 1 }, 100, (e) => {
$("#customTextPopup textarea").val(customText.join(" "));
$("#customTextPopup .wordcount input").val(customTextWordCount);
$("#customTextPopup textarea").focus();
});
}
}
function hideCustomTextPopup() {
if (!$("#customTextPopupWrapper").hasClass("hidden")) {
$("#customTextPopupWrapper")
.stop(true, true)
.css("opacity", 1)
.animate(
{
opacity: 0,
},
100,
(e) => {
$("#customTextPopupWrapper").addClass("hidden");
}
);
}
}
$("#customTextPopupWrapper").click((e) => {
if ($(e.target).attr("id") === "customTextPopupWrapper") {
hideCustomTextPopup();
}
});
$("#customTextPopup .button").click((e) => {
let text = $("#customTextPopup textarea").val();
text = text.trim();
text = text.replace(/[\n\r\t ]/gm, " ");
text = text.replace(/ +/gm, " ");
text = text.split(" ");
if (text.length >= 10000) {
showNotification("Custom text cannot be longer than 10000 words.", 4000);
changeMode("time");
text = "The quick brown fox jumped over the lazy dog".split(" ");
} else {
customText = text;
customTextIsRandom = $("#customTextPopup .check input").prop("checked");
// if (customTextIsRandom && customText.length < 3) {
// showNotification("Random custom text requires at least 3 words", 4000);
// customTextIsRandom = false;
// }
customTextWordCount = $("#customTextPopup .wordcount input").val();
restartTest();
}
hideCustomTextPopup();
});
$(document).on("click", "#top .logo", (e) => {
changePage("test");
});
$(document).on("click", "#top .config .wordCount .text-button", (e) => {
wrd = $(e.currentTarget).attr("wordCount");
if (wrd == "custom") {
let newWrd = prompt("Custom word amount");
if (newWrd !== null && !isNaN(newWrd) && newWrd > 0 && newWrd <= 10000) {
changeWordCount(newWrd);
if (newWrd > 2000) {
showNotification(
"Very long tests can cause performance issues or crash the website on some machines!",
5000
);
}
} else {
showNotification(
"Custom word amount can only be set between 1 and 10000",
3000
);
}
} else {
changeWordCount(wrd);
}
restartTest();
});
$(document).on("click", "#top .config .time .text-button", (e) => {
time = $(e.currentTarget).attr("timeConfig");
if (time == "custom") {
let newTime = prompt("Custom time in seconds");
if (newTime !== null && !isNaN(newTime) && newTime > 0 && newTime <= 3600) {
changeTimeConfig(newTime);
if (newTime >= 1800) {
showNotification(
"Very long tests can cause performance issues or crash the website on some machines!",
5000
);
}
} else {
showNotification("Custom time can only be set between 1 and 3600", 3000);
}
} else {
changeTimeConfig(time);
}
restartTest();
});
$(document).on("click", "#top .config .customText .text-button", (e) => {
// changeCustomText();
// restartTest();
showCustomTextPopup();
});
$(document).on("click", "#top .config .punctuationMode .text-button", (e) => {
togglePunctuation();
restartTest();
});
$("#words").click((e) => {
focusWords();
});
$(document).on("click", "#top .config .mode .text-button", (e) => {
if ($(e.currentTarget).hasClass("active")) return;
mode = e.currentTarget.innerHTML;
changeMode(mode);
restartTest();
});
$(document).on("click", "#top #menu .icon-button", (e) => {
if ($(e.currentTarget).hasClass("discord")) return;
if ($(e.currentTarget).hasClass("leaderboards")) {
showLeaderboards();
} else {
href = $(e.currentTarget).attr("href");
changePage(href.replace("/", ""));
}
});
$(window).on("popstate", (e) => {
let state = e.originalEvent.state;
if (state == "" || state == "/") {
// show test
changePage("test");
} else if (state == "about") {
// show about
changePage("about");
} else if (state == "account" || state == "login") {
if (firebase.auth().currentUser) {
changePage("account");
} else {
changePage("login");
}
}
});
$(document).on("keypress", "#restartTestButton", (event) => {
if (event.keyCode == 13) {
if (testActive && !afkDetected) {
let testNow = Date.now();
let testSeconds = roundTo2((testNow - testStart) / 1000);
incompleteTestSeconds += testSeconds;
restartCount++;
}
restartTest();
}
});
$(document.body).on("click", "#restartTestButton", (event) => {
restartTest();
});
$(document).on("keypress", "#showWordHistoryButton", (event) => {
if (event.keyCode == 13) {
toggleResultWordsDisplay();
}
});
$(document.body).on("click", "#showWordHistoryButton", (event) => {
toggleResultWordsDisplay();
});
$(document.body).on("click", "#restartTestButtonWithSameWordset", (event) => {
restartTest(true);
});
$(document).on("keypress", "#restartTestButtonWithSameWordset", (event) => {
if (event.keyCode == 13) {
restartTest(true);
}
});
$(document.body).on("click", "#copyResultToClipboardButton", (event) => {
copyResultToClipboard();
});
$(document.body).on("click", ".version", (event) => {
$("#versionHistoryWrapper")
.css("opacity", 0)
.removeClass("hidden")
.animate({ opacity: 1 }, 125);
});
$(document.body).on("click", "#versionHistoryWrapper", (event) => {
$("#versionHistoryWrapper")
.css("opacity", 1)
.animate({ opacity: 0 }, 125, () => {
$("#versionHistoryWrapper").addClass("hidden");
});
});
$("#wordsInput").keypress((event) => {
event.preventDefault();
});
$("#wordsInput").on("focus", (event) => {
showCaret();
});
$("#wordsInput").on("focusout", (event) => {
hideCaret();
});
$(window).resize(() => {
updateCaretPosition();
});
$(document).mousemove(function (event) {
if (
$("#top").hasClass("focus") &&
(event.originalEvent.movementX > 0 || event.originalEvent.movementY > 0)
) {
setFocus(false);
}
});
//keypresses for the test, using different method to be more responsive
$(document).keypress(function (event) {
event = emulateLayout(event);
if (!$("#wordsInput").is(":focus")) return;
if (event["keyCode"] == 13) return;
if (event["keyCode"] == 32) return;
if (event["keyCode"] == 27) return;
if (event.key == "ContextMenu") return;
//start the test
if (currentInput == "" && inputHistory.length == 0 && !testActive) {
try {
if (firebase.auth().currentUser != null) {
firebase.analytics().logEvent("testStarted");
} else {
firebase.analytics().logEvent("testStartedNoLogin");
}
} catch (e) {
console.log("Analytics unavailable");
}
testActive = true;
testStart = Date.now();
// if (config.mode == "time") {
restartTimer();
showTimer();
$("#liveWpm").text("0");
showLiveWpm();
// }
updateActiveElement();
updateTimer();
clearIntervals();
keypressStats = {
spacing: {
current: -1,
array: [],
},
duration: {
current: -1,
array: [],
},
};
timers.push(
setInterval(function () {
time++;
if (config.mode === "time") {
updateTimer();
}
// console.time("livewpm");
// let wpm = liveWPM();
// updateLiveWpm(wpm);
// showLiveWpm();
// wpmHistory.push(wpm);
// rawHistory.push(liveRaw());
let wpmAndRaw = liveWpmAndRaw();
updateLiveWpm(wpmAndRaw.wpm);
wpmHistory.push(wpmAndRaw.wpm);
rawHistory.push(wpmAndRaw.raw);
// console.timeEnd("livewpm");
keypressPerSecond.push(currentKeypressCount);
currentKeypressCount = 0;
errorsPerSecond.push(currentErrorCount);
currentErrorCount = 0;
if (
keypressPerSecond[time - 1] == 0 &&
keypressPerSecond[time - 2] == 0 &&
keypressPerSecond[time - 3] == 0 &&
keypressPerSecond[time - 4] == 0 &&
keypressPerSecond[time - 5] == 0 &&
keypressPerSecond[time - 6] == 0 &&
!afkDetected
) {
showNotification("AFK detected", 3000);
afkDetected = true;
}
if (config.mode == "time") {
if (time >= config.time) {
//times up
clearIntervals();
hideCaret();
testActive = false;
inputHistory.push(currentInput);
showResult();
}
}
}, 1000)
);
} else {
if (!testActive) return;
}
let thisCharCorrect;
if (
wordsList[currentWordIndex].substring(
currentInput.length,
currentInput.length + 1
) != event["key"]
) {
accuracyStats.incorrect++;
currentErrorCount++;
thisCharCorrect = false;
} else {
accuracyStats.correct++;
thisCharCorrect = true;
}
currentKeypressCount++;
if (config.stopOnError && !thisCharCorrect) {
if (config.difficulty == "master") {
//failed due to master diff when pressing a key
showResult(true);
if (!afkDetected) {
let testNow = Date.now();
let testSeconds = roundTo2((testNow - testStart) / 1000);
incompleteTestSeconds += testSeconds;
restartCount++;
}
return;
} else {
return;
}
}
currentInput += event["key"];
setFocus(true);
activeWordTopBeforeJump = activeWordTop;
compareInput(!config.blindMode);
// let newActiveTop = $("#words .word.active").position().top;
// console.time("offcheck1");
let newActiveTop = document.querySelector("#words .word.active").offsetTop;
if (activeWordTopBeforeJump != newActiveTop) {
activeWordJumped = true;
}
// console.timeEnd("offcheck2");
if (config.keymapMode === "react") {
flashPressedKeymapKey(event.key, thisCharCorrect);
} else if (config.keymapMode === "next") {
updateHighlightedKeymapKey();
}
updateCaretPosition();
});
$(document).keydown((event) => {
keypressStats.duration.current = performance.now();
if ($("#wordsInput").is(":focus")) {
try {
if (event.originalEvent.getModifierState("CapsLock")) {
showCapsWarning();
} else {
hideCapsWarning();
}
} catch (e) {}
}
});
$(document).keyup((event) => {
let now = performance.now();
let diff = Math.abs(keypressStats.duration.current - now);
if (keypressStats.duration.current !== -1) {
keypressStats.duration.array.push(diff);
}
keypressStats.duration.current = now;
});
//handle keyboard events
$(document).keydown((event) => {
let now = performance.now();
let diff = Math.abs(keypressStats.spacing.current - now);
if (keypressStats.spacing.current !== -1) {
keypressStats.spacing.array.push(diff);
}
keypressStats.spacing.current = now;
//tab
if (event["keyCode"] == 9) {
if (config.quickTab) {
event.preventDefault();
if ($(".pageTest").hasClass("active")) {
if (testActive && !afkDetected) {
let testNow = Date.now();
let testSeconds = roundTo2((testNow - testStart) / 1000);
incompleteTestSeconds += testSeconds;
restartCount++;
}
restartTest();
} else {
changePage("test");
}
}
}
//only for the typing test
if ($("#wordsInput").is(":focus")) {
//backspace
if (event["keyCode"] == 8) {
event.preventDefault();
if (!testActive) return;
if (currentInput == "" && inputHistory.length > 0) {
if (
(inputHistory[currentWordIndex - 1] ==
wordsList[currentWordIndex - 1] &&
!config.freedomMode) ||
$($(".word")[currentWordIndex - 1]).hasClass("hidden")
) {
return;
} else {
if (config.confidenceMode === "on" || config.confidenceMode === "max")
return;
if (event["ctrlKey"] || event["altKey"]) {
currentInput = "";
inputHistory.pop();
} else {
currentInput = inputHistory.pop();
}
currentWordIndex--;
currentWordElementIndex--;
updateActiveElement();
compareInput(!config.blindMode);
}
} else {
if (config.confidenceMode === "max") return;
if (event["ctrlKey"]) {
currentInput = "";
} else {
currentInput = currentInput.substring(0, currentInput.length - 1);
}
compareInput(!config.blindMode);
}
// currentKeypressCount++;
if (config.keymapMode === "react") {
flashPressedKeymapKey(event.code, true);
} else if (config.keymapMode === "next") {
updateHighlightedKeymapKey();
}
updateCaretPosition();
}
//space
if (event["keyCode"] == 32 || event.key === " ") {
if (!testActive) return;
if (currentInput == "") return;
event.preventDefault();
let currentWord = wordsList[currentWordIndex];
// if (config.mode == "time") {
if (!config.showAllLines || config.mode == "time") {
// let currentTop = Math.floor($($("#words .word")[currentWordIndex]).position().top);
// let nextTop = Math.floor($($("#words .word")[currentWordIndex + 1]).position().top);
let currentTop = Math.floor(
document.querySelectorAll("#words .word")[currentWordElementIndex]
.offsetTop
);
let nextTop;
try {
nextTop = Math.floor(
document.querySelectorAll("#words .word")[
currentWordElementIndex + 1
].offsetTop
);
} catch (e) {
nextTop = 0;
}
if (nextTop > currentTop || activeWordJumped) {
//last word of the line
if (currentTestLine > 0) {
let hideBound = currentTop;
if (activeWordJumped) {
hideBound = activeWordTopBeforeJump;
}
activeWordJumped = false;
let toHide = [];
let wordElements = $("#words .word");
for (let i = 0; i < currentWordElementIndex + 1; i++) {
if ($(wordElements[i]).hasClass("hidden")) continue;
// let forWordTop = Math.floor($(wordElements[i]).position().top);
let forWordTop = Math.floor(wordElements[i].offsetTop);
if (forWordTop < hideBound) {
// $($("#words .word")[i]).addClass("hidden");
toHide.push($($("#words .word")[i]));
}
}
toHide.forEach((el) => el.remove());
currentWordElementIndex -= toHide.length;
}
currentTestLine++;
}
}
// }
if (config.blindMode) $("#words .word.active letter").addClass("correct");
// document
// .querySelector("#words .word.active")
// .setAttribute("input", currentInput);
if (currentWord == currentInput) {
inputHistory.push(currentInput);
currentInput = "";
currentWordIndex++;
currentWordElementIndex++;
updateActiveElement();
updateCaretPosition();
currentKeypressCount++;
} else {
if (config.stopOnError) {
if (config.difficulty == "expert" || config.difficulty == "master") {
//failed due to diff when pressing space
showResult(true);
if (!afkDetected) {
let testNow = Date.now();
let testSeconds = roundTo2((testNow - testStart) / 1000);
incompleteTestSeconds += testSeconds;
restartCount++;
}
return;
}
return;
}
inputHistory.push(currentInput);
highlightBadWord(currentWordElementIndex, !config.blindMode);
currentInput = "";
currentWordIndex++;
currentWordElementIndex++;
if (currentWordIndex == wordsList.length) {
//submitted last word that is incorrect
showResult();
return;
} else if (
config.difficulty == "expert" ||
config.difficulty == "master"
) {
//submitted last word incorrect and failed test
showResult(true);
if (!afkDetected) {
let testNow = Date.now();
let testSeconds = roundTo2((testNow - testStart) / 1000);
incompleteTestSeconds += testSeconds;
restartCount++;
}
return;
}
updateActiveElement();
updateCaretPosition();
currentKeypressCount++;
}
if (config.keymapMode === "react") {
flashPressedKeymapKey(event.code, true);
} else if (config.keymapMode === "next") {
updateHighlightedKeymapKey();
}
if (
config.mode === "words" ||
config.mode === "custom" ||
config.mode === "quote"
) {
updateTimer();
}
if (config.showAllLines) {
if (config.mode == "time") {
addWord();
}
} else {
if (config.mode == "time" || config.mode == "words") {
addWord();
}
}
}
}
});
loadConfigFromCookie();
getReleasesFromGitHub();
if (firebase.app().options.projectId === "monkey-type-dev-67af4") {
$("#top .logo .bottom").text("monkey-dev");
$("head title").text("Monkey Dev");
$("body").append(`
DEV
DEV
`);
}
if (window.location.hostname === "localhost") {
window.onerror = function (error) {
this.showNotification(error, 3000);
};
$("#top .logo .top").text("localhost");
$("head title").text($("head title").text() + " (localhost)");
firebase.functions().useFunctionsEmulator("http://localhost:5001");
$("body").append(`
local
local
`);
}
$(document).on("mouseenter", "#words .word", (e) => {
if (resultVisible) {
let input = $(e.currentTarget).attr("input");
if (input != undefined)
$(e.currentTarget).append(`
${input}
`);
}
});
$(document).on("mouseleave", "#words .word", (e) => {
$(".wordInputAfter").remove();
});
$(document).ready(() => {
updateFavicon(32, 14);
$("body").css("transition", ".25s");
restartTest();
if (config.quickTab) {
$("#restartTestButton").addClass("hidden");
}
$("#centerContent")
.css("opacity", "0")
.removeClass("hidden")
.stop(true, true)
.animate({ opacity: 1 }, 250);
if (window.location.pathname === "/account") {
history.replaceState("/", null, "/");
} else if (window.location.pathname !== "/") {
let page = window.location.pathname.replace("/", "");
changePage(page);
}
});
let ctx = $("#wpmChart");
let wpmOverTimeChart = new Chart(ctx, {
type: "line",
data: {
labels: [],
datasets: [
{
label: "wpm",
data: [],
// backgroundColor: 'rgba(255, 255, 255, 0.25)',
borderColor: "rgba(125, 125, 125, 1)",
borderWidth: 2,
yAxisID: "wpm",
order: 2,
radius: 2,
},
{
label: "raw",
data: [],
// backgroundColor: 'rgba(255, 255, 255, 0.25)',
borderColor: "rgba(125, 125, 125, 1)",
borderWidth: 2,
yAxisID: "raw",
order: 3,
radius: 2,
},
{
label: "errors",
data: [],
// backgroundColor: 'rgba(255, 255, 255, 0.25)',
borderColor: "rgba(255, 125, 125, 1)",
borderWidth: 2,
order: 1,
yAxisID: "error",
// barPercentage: 0.1,
maxBarThickness: 10,
type: "scatter",
pointStyle: "crossRot",
radius: function (context) {
var index = context.dataIndex;
var value = context.dataset.data[index];
return value.y <= 0 ? 0 : 3;
},
pointHoverRadius: function (context) {
var index = context.dataIndex;
var value = context.dataset.data[index];
return value.y <= 0 ? 0 : 5;
},
},
],
},
options: {
tooltips: {
titleFontFamily: "Roboto Mono",
bodyFontFamily: "Roboto Mono",
mode: "index",
intersect: false,
},
legend: {
display: false,
labels: {
defaultFontFamily: "Roboto Mono",
},
},
responsive: true,
maintainAspectRatio: false,
// hover: {
// mode: 'x',
// intersect: false
// },
scales: {
xAxes: [
{
ticks: {
fontFamily: "Roboto Mono",
autoSkip: true,
autoSkipPadding: 40,
},
display: true,
scaleLabel: {
display: false,
labelString: "Seconds",
fontFamily: "Roboto Mono",
},
},
],
yAxes: [
{
id: "wpm",
display: true,
scaleLabel: {
display: true,
labelString: "Words per Minute",
fontFamily: "Roboto Mono",
},
ticks: {
fontFamily: "Roboto Mono",
beginAtZero: true,
min: 0,
autoSkip: true,
autoSkipPadding: 40,
},
gridLines: {
display: false,
},
},
{
id: "raw",
display: false,
scaleLabel: {
display: true,
labelString: "Raw Words per Minute",
fontFamily: "Roboto Mono",
},
ticks: {
fontFamily: "Roboto Mono",
beginAtZero: true,
min: 0,
autoSkip: true,
autoSkipPadding: 40,
},
gridLines: {
display: false,
},
},
{
id: "error",
display: true,
position: "right",
scaleLabel: {
display: true,
labelString: "Errors",
fontFamily: "Roboto Mono",
},
ticks: {
precision: 0,
fontFamily: "Roboto Mono",
beginAtZero: true,
autoSkip: true,
autoSkipPadding: 40,
},
gridLines: {
display: true,
},
},
],
},
annotation: {
annotations: [
{
enabled: false,
type: "line",
mode: "horizontal",
scaleID: "wpm",
value: "-30",
borderColor: "red",
borderWidth: 1,
borderDash: [2, 2],
label: {
// Background color of label, default below
backgroundColor: "blue",
fontFamily: "Roboto Mono",
// Font size of text, inherits from global
fontSize: 11,
// Font style of text, default below
fontStyle: "normal",
// Font color of text, default below
fontColor: "#fff",
// Padding of label to add left/right, default below
xPadding: 6,
// Padding of label to add top/bottom, default below
yPadding: 6,
// Radius of label rectangle, default below
cornerRadius: 3,
// Anchor position of label on line, can be one of: top, bottom, left, right, center. Default below.
position: "center",
// Whether the label is enabled and should be displayed
enabled: true,
// Text to display in label - default is null. Provide an array to display values on a new line
content: "PB",
},
},
],
},
},
});