diff --git a/gulpfile.js b/gulpfile.js index dbbba2fc8..ae853a534 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -90,6 +90,8 @@ const refactoredSrc = [ "./src/js/dom-util.js", "./src/js/cloud-functions.js", "./src/js/misc.js", + "./src/js/layouts.js", + "./src/js/monkey.js", ]; //legacy files @@ -98,7 +100,6 @@ const globalSrc = [ "./src/js/global-dependencies.js", "./src/js/simple-popups.js", "./src/js/words.js", - "./src/js/layouts.js", "./src/js/userconfig.js", "./src/js/commandline.js", "./src/js/leaderboards.js", diff --git a/src/js/account.js b/src/js/account.js index a33816a49..9bbd457fa 100644 --- a/src/js/account.js +++ b/src/js/account.js @@ -368,8 +368,9 @@ function getAccountDataAndInit() { accountIconLoading(false); } if (config.paceCaret === "pb") { - // initPaceCaret(true); - $("#paceCaret").addClass("hidden"); + if (!testActive) { + initPaceCaret(true); + } } try { if ( diff --git a/src/js/commandline.js b/src/js/commandline.js index bf8c56310..baaf2cfa0 100644 --- a/src/js/commandline.js +++ b/src/js/commandline.js @@ -687,6 +687,14 @@ let commands = { }, 150); }, }, + { + id: "toggleMonkey", + display: "Toggle Monkey", + visible: false, + exec: () => { + $("#monkey").toggleClass("hidden"); + }, + }, ], }; diff --git a/src/js/db.js b/src/js/db.js index 2fce48365..a5ea6540d 100644 --- a/src/js/db.js +++ b/src/js/db.js @@ -103,7 +103,7 @@ export async function db_getUserResults() { .get() .then((data) => { dbSnapshot.results = []; - data.docs.forEach((doc, index) => { + data.docs.forEach((doc) => { let result = doc.data(); result.id = doc.id; dbSnapshot.results.push(result); @@ -181,6 +181,7 @@ export async function db_getLocalPB( let retval; if (dbSnapshot == null) { + retval = 0; } else { retval = cont(); } @@ -249,8 +250,7 @@ export async function db_saveLocalPB( } } - if (dbSnapshot == null) { - } else { + if (dbSnapshot != null) { cont(); } } diff --git a/src/js/global-dependencies.js b/src/js/global-dependencies.js index 1539f3cab..7a23021f9 100644 --- a/src/js/global-dependencies.js +++ b/src/js/global-dependencies.js @@ -22,3 +22,5 @@ import { import { showBackgroundLoader, hideBackgroundLoader } from "./dom-util"; import * as Misc from "./misc"; import * as CloudFunctions from "./cloud-functions"; +import layouts from "./layouts"; +import * as Monkey from "./monkey"; diff --git a/src/js/layouts.js b/src/js/layouts.js index 9505db1bb..43befd36a 100644 --- a/src/js/layouts.js +++ b/src/js/layouts.js @@ -236,3 +236,4 @@ const layouts = { ] }, } +export default layouts; diff --git a/src/js/misc.js b/src/js/misc.js index 11a0088a1..f9bf207b8 100644 --- a/src/js/misc.js +++ b/src/js/misc.js @@ -1,4 +1,52 @@ import { showBackgroundLoader, hideBackgroundLoader } from "./dom-util"; + +function hexToHSL(H) { + // Convert hex to RGB first + let r = 0, + g = 0, + b = 0; + if (H.length == 4) { + r = "0x" + H[1] + H[1]; + g = "0x" + H[2] + H[2]; + b = "0x" + H[3] + H[3]; + } else if (H.length == 7) { + r = "0x" + H[1] + H[2]; + g = "0x" + H[3] + H[4]; + b = "0x" + H[5] + H[6]; + } + // Then to HSL + r /= 255; + g /= 255; + b /= 255; + let cmin = Math.min(r, g, b), + cmax = Math.max(r, g, b), + delta = cmax - cmin, + h = 0, + s = 0, + l = 0; + + if (delta == 0) h = 0; + else if (cmax == r) h = ((g - b) / delta) % 6; + else if (cmax == g) h = (b - r) / delta + 2; + else h = (r - g) / delta + 4; + + h = Math.round(h * 60); + + if (h < 0) h += 360; + + l = (cmax + cmin) / 2; + s = delta == 0 ? 0 : delta / (1 - Math.abs(2 * l - 1)); + s = +(s * 100).toFixed(1); + l = +(l * 100).toFixed(1); + + return { + hue: h, + sat: s, + lgt: l, + string: "hsl(" + h + "," + s + "%," + l + "%)", + }; +} + let themesList = null; export async function getThemesList() { if (themesList == null) { @@ -96,8 +144,40 @@ export async function getChallengeList() { } } -let currentLanguage = null; +export function showNotification(text, time) { + let noti = $(".notification"); + noti.text(text); + noti.css("top", `-${noti.outerHeight()}px`); + noti.stop(true, false).animate( + { + top: "1rem", + }, + 250, + "swing", + () => { + noti.stop(true, false).animate( + { + opacity: 1, + }, + time, + () => { + noti.stop(true, false).animate( + { + top: `-${noti.outerHeight()}px`, + }, + 250, + "swing", + () => { + noti.text(""); + } + ); + } + ); + } + ); +} +let currentLanguage = null; export function getCurrentLanguage() { return currentLanguage; } @@ -149,7 +229,7 @@ export function sendVerificationEmail() { showBackgroundLoader(); let cu = firebase.auth().currentUser; cu.sendEmailVerification() - .then((e) => { + .then(() => { hideBackgroundLoader(); showNotification("Email sent to " + cu.email, 4000); }) @@ -204,39 +284,6 @@ export function mean(array) { } } -export function showNotification(text, time) { - let noti = $(".notification"); - noti.text(text); - noti.css("top", `-${noti.outerHeight()}px`); - noti.stop(true, false).animate( - { - top: "1rem", - }, - 250, - "swing", - () => { - noti.stop(true, false).animate( - { - opacity: 1, - }, - time, - () => { - noti.stop(true, false).animate( - { - top: `-${noti.outerHeight()}px`, - }, - 250, - "swing", - () => { - noti.text(""); - } - ); - } - ); - } - ); -} - export function getReleasesFromGitHub() { $.getJSON( "https://api.github.com/repos/Miodec/monkeytype/releases", @@ -291,53 +338,6 @@ export function kogasa(cov) { ); } -function hexToHSL(H) { - // Convert hex to RGB first - let r = 0, - g = 0, - b = 0; - if (H.length == 4) { - r = "0x" + H[1] + H[1]; - g = "0x" + H[2] + H[2]; - b = "0x" + H[3] + H[3]; - } else if (H.length == 7) { - r = "0x" + H[1] + H[2]; - g = "0x" + H[3] + H[4]; - b = "0x" + H[5] + H[6]; - } - // Then to HSL - r /= 255; - g /= 255; - b /= 255; - let cmin = Math.min(r, g, b), - cmax = Math.max(r, g, b), - delta = cmax - cmin, - h = 0, - s = 0, - l = 0; - - if (delta == 0) h = 0; - else if (cmax == r) h = ((g - b) / delta) % 6; - else if (cmax == g) h = (b - r) / delta + 2; - else h = (r - g) / delta + 4; - - h = Math.round(h * 60); - - if (h < 0) h += 360; - - l = (cmax + cmin) / 2; - s = delta == 0 ? 0 : delta / (1 - Math.abs(2 * l - 1)); - s = +(s * 100).toFixed(1); - l = +(l * 100).toFixed(1); - - return { - hue: h, - sat: s, - lgt: l, - string: "hsl(" + h + "," + s + "%," + l + "%)", - }; -} - export function roundTo2(num) { return Math.round((num + Number.EPSILON) * 100) / 100; } diff --git a/src/js/monkey.js b/src/js/monkey.js new file mode 100644 index 000000000..777ec24bc --- /dev/null +++ b/src/js/monkey.js @@ -0,0 +1,44 @@ +let left = false; +let right = false; +let elements = { + "00": document.querySelector("#monkey .up"), + 10: document.querySelector("#monkey .left"), + "01": document.querySelector("#monkey .right"), + 11: document.querySelector("#monkey .both"), +}; +let last = "right"; +// 0 up +// 1 down + +export function type() { + if (!left && last == "right") { + left = true; + last = "left"; + } else if (!right) { + right = true; + last = "right"; + } + update(); +} + +export function stop() { + if (left) { + left = false; + } else if (right) { + right = false; + } + update(); +} + +function update() { + if (!document.querySelector("#monkey").classList.contains("hidden")) { + Object.keys(elements).forEach((key) => { + elements[key].classList.add("hidden"); + }); + + let id = left ? "1" : "0"; + id += right ? "1" : "0"; + + elements[id].classList.remove("hidden"); + } +} diff --git a/src/js/script.js b/src/js/script.js index 70b27e2d8..e4a898457 100644 --- a/src/js/script.js +++ b/src/js/script.js @@ -20,6 +20,7 @@ let lineTransition = false; let keypressPerSecond = []; let currentKeypress = { count: 0, + mod: 0, words: [], }; let errorsPerSecond = []; @@ -331,6 +332,11 @@ async function activateFunbox(funbox, mode) { settingsGroups.keymapMode.updateButton(); restartTest(); } + + if (funbox === "read_ahead") { + setHighlightMode("letter", true); + restartTest(); + } } else if (mode === "script") { if (funbox === "tts") { $("#funBoxTheme").attr("href", `funbox/simon_says.css`); @@ -1020,7 +1026,9 @@ function compareInput(showError) { } let testNow = performance.now(); let testSeconds = Misc.roundTo2((testNow - testStart) / 1000); - let afkseconds = keypressPerSecond.filter((x) => x.count == 0).length; + let afkseconds = keypressPerSecond.filter( + (x) => x.count == 0 && x.mod == 0 + ).length; incompleteTestSeconds += testSeconds - afkseconds; restartCount++; } @@ -1075,7 +1083,9 @@ function compareInput(showError) { } let testNow = performance.now(); let testSeconds = Misc.roundTo2((testNow - testStart) / 1000); - let afkseconds = keypressPerSecond.filter((x) => x.count == 0).length; + let afkseconds = keypressPerSecond.filter( + (x) => x.count == 0 && x.mod == 0 + ).length; incompleteTestSeconds += testSeconds - afkseconds; restartCount++; } @@ -1143,15 +1153,19 @@ function showTimer() { { opacity: op, }, - 250 + 125 ); } else if (config.timerStyle === "text") { - $("#timerNumber").stop(true, true).removeClass("hidden").animate( - { - opacity: op, - }, - 250 - ); + $("#timerNumber") + .stop(true, true) + .removeClass("hidden") + .css("opacity", 0) + .animate( + { + opacity: op, + }, + 125 + ); } else if (config.timerStyle === "mini") { if (op > 0) { $("#miniTimerAndLiveWpm .time") @@ -1161,7 +1175,7 @@ function showTimer() { { opacity: op, }, - 250 + 125 ); } } @@ -1711,12 +1725,6 @@ function showCrown() { let resultCalculating = false; function showResult(difficultyFailed = false) { - console.log(keypressPerSecond); - console.log(errorsPerSecond); - console.log(wpmHistory); - console.log(rawHistory); - console.log("-"); - resultCalculating = true; resultVisible = true; testEnd = performance.now(); @@ -1745,9 +1753,12 @@ function showResult(difficultyFailed = false) { } clearTimeout(timer); let testtime = stats.time; - let afkseconds = keypressPerSecond.filter((x) => x.count == 0).length; + let afkseconds = keypressPerSecond.filter((x) => x.count == 0 && x.mod == 0) + .length; let afkSecondsPercent = Misc.roundTo2((afkseconds / testtime) * 100); + wpmOverTimeChart.options.annotation.annotations = []; + $("#result #resultWordsHistory").addClass("hidden"); if (config.alwaysShowDecimalPlaces) { @@ -1864,6 +1875,7 @@ function showResult(difficultyFailed = false) { rawHistory.push(wpmAndRaw.raw); keypressPerSecond.push(currentKeypress); currentKeypress = { + mod: 0, count: 0, words: [], }; @@ -1902,11 +1914,6 @@ function showResult(difficultyFailed = false) { wpmOverTimeChart.data.labels = labels; - console.log(keypressPerSecond); - console.log(errorsPerSecond); - console.log(wpmHistory); - console.log(rawHistory); - let rawWpmPerSecondRaw = keypressPerSecond.map((f) => Math.round((f.count / 5) * 60) ); @@ -1953,13 +1960,6 @@ function showResult(difficultyFailed = false) { wpmOverTimeChart.data.datasets[1].pointBackgroundColor = themeColors.sub; wpmOverTimeChart.data.datasets[1].data = rawWpmPerSecond; - wpmOverTimeChart.options.annotation.annotations[0].borderColor = - themeColors.sub; - wpmOverTimeChart.options.annotation.annotations[0].label.backgroundColor = - themeColors.sub; - wpmOverTimeChart.options.annotation.annotations[0].label.fontColor = - themeColors.bg; - let maxChartVal = Math.max( ...[Math.max(...rawWpmPerSecond), Math.max(...wpmHistory)] ); @@ -2126,9 +2126,29 @@ function showResult(difficultyFailed = false) { localPb = true; } if (lpb > 0) { - wpmOverTimeChart.options.annotation.annotations[0].value = lpb; - wpmOverTimeChart.options.annotation.annotations[0].label.content = - "PB: " + lpb; + wpmOverTimeChart.options.annotation.annotations.push({ + enabled: false, + type: "line", + mode: "horizontal", + scaleID: "wpm", + value: lpb, + borderColor: themeColors.sub, + borderWidth: 1, + borderDash: [2, 2], + label: { + backgroundColor: themeColors.sub, + fontFamily: "Roboto Mono", + fontSize: 11, + fontStyle: "normal", + fontColor: themeColors.bg, + xPadding: 6, + yPadding: 6, + cornerRadius: 3, + position: "center", + enabled: true, + content: `PB: ${lpb}`, + }, + }); if (maxChartVal >= lpb - 15 && maxChartVal <= lpb + 15) { maxChartVal = lpb + 15; } @@ -2165,6 +2185,30 @@ function showResult(difficultyFailed = false) { "+" + Misc.roundTo2(stats.wpm - tpb) ); console.log("new pb for tag " + tag.name); + } else { + wpmOverTimeChart.options.annotation.annotations.push({ + enabled: false, + type: "line", + mode: "horizontal", + scaleID: "wpm", + value: tpb, + borderColor: themeColors.sub, + borderWidth: 1, + borderDash: [2, 2], + label: { + backgroundColor: themeColors.sub, + fontFamily: "Roboto Mono", + fontSize: 11, + fontStyle: "normal", + fontColor: themeColors.bg, + xPadding: 6, + yPadding: 6, + cornerRadius: 3, + position: "center", + enabled: true, + content: `${tag.name} PB: ${tpb}`, + }, + }); } }); @@ -2676,6 +2720,7 @@ function startTest() { keypressPerSecond.push(currentKeypress); currentKeypress = { + mod: 0, count: 0, words: [], }; @@ -2766,6 +2811,7 @@ function restartTest(withSameWordset = false, nosave = false) { keypressPerSecond = []; lastSecondNotRound = false; currentKeypress = { + mod: 0, count: 0, words: [], }; @@ -2869,7 +2915,6 @@ function restartTest(withSameWordset = false, nosave = false) { hideCrown(); clearTimeout(timer); if ($("#commandLineWrapper").hasClass("hidden")) focusWords(); - wpmOverTimeChart.options.annotation.annotations[0].value = "-30"; wpmOverTimeChart.update(); updateTestModesNotice(); } @@ -3051,13 +3096,13 @@ function liveWpmAndRaw() { //the word is correct //+1 for space correctWordChars += wordsList[i].length; - if (i < inputHistory.length - 1) { + if (i < inputHistory.length) { spaces++; } } chars += inputHistory[i].length; } - if (wordsList[currentWordIndex] === currentInput) { + if (wordsList[currentWordIndex] == currentInput) { correctWordChars += currentInput.length; } if (activeFunBox === "nospace") { @@ -3110,15 +3155,20 @@ function showLiveWpm() { if (!testActive) return; if (config.timerStyle === "mini") { // $("#miniTimerAndLiveWpm .wpm").css("opacity", config.timerOpacity); - $("#miniTimerAndLiveWpm .wpm").removeClass("hidden").animate( - { - opacity: config.timerOpacity, - }, - 125 - ); + if (!$("#miniTimerAndLiveWpm .wpm").hasClass("hidden")) return; + $("#miniTimerAndLiveWpm .wpm") + .removeClass("hidden") + .css("opacity", 0) + .animate( + { + opacity: config.timerOpacity, + }, + 125 + ); } else { // $("#liveWpm").css("opacity", config.timerOpacity); - $("#liveWpm").removeClass("hidden").animate( + if (!$("#liveWpm").hasClass("hidden")) return; + $("#liveWpm").removeClass("hidden").css("opacity", 0).animate( { opacity: config.timerOpacity, }, @@ -3155,15 +3205,20 @@ function showLiveAcc() { if (!testActive) return; if (config.timerStyle === "mini") { // $("#miniTimerAndLiveWpm .wpm").css("opacity", config.timerOpacity); - $("#miniTimerAndLiveWpm .acc").removeClass("hidden").animate( - { - opacity: config.timerOpacity, - }, - 125 - ); + if (!$("#miniTimerAndLiveWpm .acc").hasClass("hidden")) return; + $("#miniTimerAndLiveWpm .acc") + .removeClass("hidden") + .css("opacity", 0) + .animate( + { + opacity: config.timerOpacity, + }, + 125 + ); } else { // $("#liveWpm").css("opacity", config.timerOpacity); - $("#liveAcc").removeClass("hidden").animate( + if (!$("#liveAcc").hasClass("hidden")) return; + $("#liveAcc").removeClass("hidden").css("opacity", 0).animate( { opacity: config.timerOpacity, }, @@ -4243,10 +4298,13 @@ $(document).on("keypress", "#restartTestButton", (event) => { if (testActive) { let testNow = performance.now(); let testSeconds = Misc.roundTo2((testNow - testStart) / 1000); - let afkseconds = keypressPerSecond.filter((x) => x.count == 0).length; + let afkseconds = keypressPerSecond.filter( + (x) => x.count == 0 && x.mod == 0 + ).length; incompleteTestSeconds += testSeconds - afkseconds; restartCount++; } + if (resultCalculating) return; restartTest(); } else { Misc.showNotification("Quick restart disabled for long tests", 2000); @@ -4256,6 +4314,7 @@ $(document).on("keypress", "#restartTestButton", (event) => { $(document.body).on("click", "#restartTestButton", (event) => { manualRestart = true; + if (resultCalculating) return; restartTest(); }); @@ -4418,6 +4477,8 @@ $(document).keydown((event) => { keypressStats.spacing.current = now; } + Monkey.type(); + //tab if ( (event.key == "Tab" && !config.swapEscAndTab) || @@ -4450,8 +4511,9 @@ $(document).keydown((event) => { if (testActive) { let testNow = performance.now(); let testSeconds = Misc.roundTo2((testNow - testStart) / 1000); - let afkseconds = keypressPerSecond.filter((x) => x.count == 0) - .length; + let afkseconds = keypressPerSecond.filter( + (x) => x.count == 0 && x.mod == 0 + ).length; incompleteTestSeconds += testSeconds - afkseconds; restartCount++; } @@ -4534,7 +4596,10 @@ $(document).keydown((event) => { updateCaretPosition(); } //space - if (event.key === " ") { + if ( + event.key === " " || + (activeFunBox == "58008" && event.key === "Enter") + ) { if (!testActive) return; if (currentInput === "") return; event.preventDefault(); @@ -4614,8 +4679,9 @@ $(document).keydown((event) => { showResult(true); let testNow = performance.now(); let testSeconds = Misc.roundTo2((testNow - testStart) / 1000); - let afkseconds = keypressPerSecond.filter((x) => x.count == 0) - .length; + let afkseconds = keypressPerSecond.filter( + (x) => x.count == 0 && x.mod == 0 + ).length; incompleteTestSeconds += testSeconds - afkseconds; restartCount++; return; @@ -4635,7 +4701,9 @@ $(document).keydown((event) => { showResult(true); let testNow = performance.now(); let testSeconds = Misc.roundTo2((testNow - testStart) / 1000); - let afkseconds = keypressPerSecond.filter((x) => x.count == 0).length; + let afkseconds = keypressPerSecond.filter( + (x) => x.count == 0 && x.mod == 0 + ).length; incompleteTestSeconds += testSeconds - afkseconds; restartCount++; return; @@ -4803,8 +4871,10 @@ $(document).keydown(function (event) { "Unidentified", undefined, ].includes(event.key) - ) + ) { + currentKeypress.mod++; return; + } if (event.key === " ") { if (config.difficulty !== "normal" || config.strictSpace) { if (dontInsertSpace) { @@ -4916,7 +4986,9 @@ $(document).keydown(function (event) { showResult(true); let testNow = performance.now(); let testSeconds = Misc.roundTo2((testNow - testStart) / 1000); - let afkseconds = keypressPerSecond.filter((x) => x.count == 0).length; + let afkseconds = keypressPerSecond.filter( + (x) => x.count == 0 && x.mod == 0 + ).length; incompleteTestSeconds += testSeconds - afkseconds; restartCount++; return; @@ -4990,6 +5062,7 @@ $(document).keyup((event) => { keypressStats.duration.array.push(diff); } keypressStats.duration.current = now; + Monkey.stop(); }); window.addEventListener("beforeunload", (event) => { @@ -5362,50 +5435,7 @@ let wpmOverTimeChart = new Chart(ctx, { ], }, 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", - }, - }, - ], + annotations: [], }, }, }); diff --git a/src/js/userconfig.js b/src/js/userconfig.js index fc0d64499..b931498cd 100644 --- a/src/js/userconfig.js +++ b/src/js/userconfig.js @@ -810,8 +810,11 @@ function toggleShowLiveAcc() { } function setHighlightMode(mode, nosave) { - if (activeFunBox === "nospace" && mode === "word") { - Misc.showNotification("Can't use word highlight with nospace funbox", 3000); + if ( + mode === "word" && + (activeFunBox === "nospace" || activeFunBox === "read_ahead") + ) { + Misc.showNotification("Can't use word highlight with this funbox", 3000); return; } if (mode == null || mode == undefined) { diff --git a/src/sass/style.scss b/src/sass/style.scss index 228f5888c..cc590dfe4 100644 --- a/src/sass/style.scss +++ b/src/sass/style.scss @@ -3091,6 +3091,29 @@ key { opacity: 0; } +#monkey { + width: 308px; + height: 0; + margin: 0 auto; + div { + height: 200px; + width: 308px; + position: fixed; + } + .up { + background-image: url("../m3.png"); + } + .left { + background-image: url("../m1.png"); + } + .right { + background-image: url("../m2.png"); + } + .both { + background-image: url("../m4.png"); + } +} + .keymap { .keymap-split-spacer, .keymap-stagger-split-spacer, diff --git a/static/index.html b/static/index.html index e947d55a0..76c2292d3 100644 --- a/static/index.html +++ b/static/index.html @@ -962,6 +962,12 @@