mirror of
https://github.com/monkeytypegame/monkeytype.git
synced 2025-10-17 11:06:17 +08:00
Added configuration for typing speed unit, removed alwaysShowCPM (#4492) fehmer
* Added configuration for typing speed unit, removed alwaysShowCPM
* review comments
* fix live-burst, activityChart and results-pb label
* Added support for typing speed unit in account histogram chart
* trigger build
* Update account.ts
* Fix chart scaling and wpm/rawWpm hovers on result page
fix chart scaling and bucket size on account page
* refactor histogramChartData to support 0.5 steps
* Revert dynamic scaling on accounts/result graph
* Refactor histogramChartData to an int[]
* Fix cutoff in account history
* Fix labels on result page
* Limit result chart label to two decimals
* renamed show average wpm to show average speed
* fix scaling on accounts history graph (again), not adding an easteregg 🤫
* hiding by default
* fix scaling on accounts history graph episode three
* move typingSpeedUnit related functions out of Misc
* updating account page if typing speed unit changes
* updating result page if changing units on the result page
* missing buton change
---------
Co-authored-by: Miodec <jack@monkeytype.com>
This commit is contained in:
parent
d4a061400b
commit
1f4df9199d
20 changed files with 411 additions and 293 deletions
|
@ -106,7 +106,7 @@ const CONFIG_SCHEMA = joi.object({
|
|||
minWpmCustomSpeed: joi.number().min(0),
|
||||
highlightMode: joi.string().valid("off", "letter", "word"),
|
||||
tapeMode: joi.string().valid("off", "letter", "word"),
|
||||
alwaysShowCPM: joi.boolean(),
|
||||
typingSpeedUnit: joi.string().valid("wpm", "cpm", "wps", "cps", "wph"),
|
||||
enableAds: joi.string().valid("off", "on", "max"),
|
||||
ads: joi.string().valid("off", "result", "on", "sellout"),
|
||||
hideExtraLetters: joi.boolean(),
|
||||
|
@ -128,7 +128,7 @@ const CONFIG_SCHEMA = joi.object({
|
|||
burstHeatmap: joi.boolean(),
|
||||
britishEnglish: joi.boolean(),
|
||||
lazyMode: joi.boolean(),
|
||||
showAverage: joi.string().valid("off", "wpm", "acc", "both"),
|
||||
showAverage: joi.string().valid("off", "speed", "acc", "both"),
|
||||
});
|
||||
|
||||
export default CONFIG_SCHEMA;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import Config from "../config";
|
||||
import format from "date-fns/format";
|
||||
import * as Misc from "../utils/misc";
|
||||
import { get as getTypingSpeedUnit } from "../utils/typing-speed-units";
|
||||
|
||||
function clearTables(isProfile: boolean): void {
|
||||
const source = isProfile ? "Profile" : "Account";
|
||||
|
@ -127,9 +128,9 @@ function buildPbHtml(
|
|||
): string {
|
||||
let retval = "";
|
||||
let dateText = "";
|
||||
const multiplier = Config.alwaysShowCPM ? 5 : 1;
|
||||
const modeString = `${mode2} ${mode === "time" ? "seconds" : "words"}`;
|
||||
const wpmCpm = Config.alwaysShowCPM ? "cpm" : "wpm";
|
||||
const speedUnit = Config.typingSpeedUnit;
|
||||
const typingSpeedUnit = getTypingSpeedUnit(Config.typingSpeedUnit);
|
||||
try {
|
||||
const pbData = (pbs[mode][mode2] ?? []).sort((a, b) => b.wpm - a.wpm)[0];
|
||||
const date = new Date(pbData.timestamp);
|
||||
|
@ -137,15 +138,15 @@ function buildPbHtml(
|
|||
dateText = format(date, "dd MMM yyyy");
|
||||
}
|
||||
|
||||
let wpmString: number | string = pbData.wpm * multiplier;
|
||||
let speedString: number | string = typingSpeedUnit.convert(pbData.wpm);
|
||||
if (Config.alwaysShowDecimalPlaces) {
|
||||
wpmString = Misc.roundTo2(wpmString).toFixed(2);
|
||||
speedString = Misc.roundTo2(speedString).toFixed(2);
|
||||
} else {
|
||||
wpmString = Math.round(wpmString);
|
||||
speedString = Math.round(speedString);
|
||||
}
|
||||
wpmString += ` ${wpmCpm}`;
|
||||
speedString += ` ${speedUnit}`;
|
||||
|
||||
let rawString: number | string = pbData.raw * multiplier;
|
||||
let rawString: number | string = typingSpeedUnit.convert(pbData.raw);
|
||||
if (Config.alwaysShowDecimalPlaces) {
|
||||
rawString = Misc.roundTo2(rawString).toFixed(2);
|
||||
} else {
|
||||
|
@ -179,14 +180,14 @@ function buildPbHtml(
|
|||
|
||||
retval = `<div class="quick">
|
||||
<div class="test">${modeString}</div>
|
||||
<div class="wpm">${Math.round(pbData.wpm * multiplier)}</div>
|
||||
<div class="wpm">${Math.round(typingSpeedUnit.convert(pbData.wpm))}</div>
|
||||
<div class="acc">${
|
||||
pbData.acc === undefined ? "-" : Math.floor(pbData.acc) + "%"
|
||||
}</div>
|
||||
</div>
|
||||
<div class="fullTest">
|
||||
<div>${modeString}</div>
|
||||
<div>${wpmString}</div>
|
||||
<div>${speedString}</div>
|
||||
<div>${rawString}</div>
|
||||
<div>${accString}</div>
|
||||
<div>${conString}</div>
|
||||
|
|
|
@ -28,7 +28,7 @@ import SoundVolumeCommands from "./lists/sound-volume";
|
|||
import FlipTestColorsCommands from "./lists/flip-test-colors";
|
||||
import SmoothLineScrollCommands from "./lists/smooth-line-scroll";
|
||||
import AlwaysShowDecimalCommands from "./lists/always-show-decimal";
|
||||
import AlwaysShowCpmCommands from "./lists/always-show-cpm";
|
||||
import TypingSpeedUnitCommands from "./lists/typing-speed-unit";
|
||||
import StartGraphsAtZeroCommands from "./lists/start-graphs-at-zero";
|
||||
import LazyModeCommands from "./lists/lazy-mode";
|
||||
import ShowAllLinesCommands from "./lists/show-all-lines";
|
||||
|
@ -275,7 +275,7 @@ export const commands: MonkeyTypes.CommandsSubgroup = {
|
|||
...TapeModeCommands,
|
||||
...SmoothLineScrollCommands,
|
||||
...ShowAllLinesCommands,
|
||||
...AlwaysShowCpmCommands,
|
||||
...TypingSpeedUnitCommands,
|
||||
...AlwaysShowDecimalCommands,
|
||||
...StartGraphsAtZeroCommands,
|
||||
...FontSizeCommands,
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
import * as UpdateConfig from "../../config";
|
||||
|
||||
const subgroup: MonkeyTypes.CommandsSubgroup = {
|
||||
title: "Always show CPM...",
|
||||
configKey: "alwaysShowCPM",
|
||||
list: [
|
||||
{
|
||||
id: "setAlwaysShowCPMOff",
|
||||
display: "off",
|
||||
configValue: false,
|
||||
exec: (): void => {
|
||||
UpdateConfig.setAlwaysShowCPM(false);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "setAlwaysShowCPMOn",
|
||||
display: "on",
|
||||
configValue: true,
|
||||
exec: (): void => {
|
||||
UpdateConfig.setAlwaysShowCPM(true);
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const commands: MonkeyTypes.Command[] = [
|
||||
{
|
||||
id: "changeAlwaysShowCPM",
|
||||
display: "Always show CPM...",
|
||||
icon: "fa-tachometer-alt",
|
||||
subgroup,
|
||||
},
|
||||
];
|
||||
|
||||
export default commands;
|
|
@ -14,10 +14,10 @@ const subgroup: MonkeyTypes.CommandsSubgroup = {
|
|||
},
|
||||
{
|
||||
id: "setShowAverageSpeed",
|
||||
display: "wpm",
|
||||
configValue: "wpm",
|
||||
display: "speed",
|
||||
configValue: "speed",
|
||||
exec: (): void => {
|
||||
UpdateConfig.setShowAverage("wpm");
|
||||
UpdateConfig.setShowAverage("speed");
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
60
frontend/src/ts/commandline/lists/typing-speed-unit.ts
Normal file
60
frontend/src/ts/commandline/lists/typing-speed-unit.ts
Normal file
|
@ -0,0 +1,60 @@
|
|||
import * as UpdateConfig from "../../config";
|
||||
|
||||
const subgroup: MonkeyTypes.CommandsSubgroup = {
|
||||
title: "Typing speed unit...",
|
||||
configKey: "typingSpeedUnit",
|
||||
list: [
|
||||
{
|
||||
id: "setTypingSpeedUnitWpm",
|
||||
display: "wpm",
|
||||
configValue: "wpm",
|
||||
exec: (): void => {
|
||||
UpdateConfig.setTypingSpeedUnit("wpm");
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "setTypingSpeedUnitCpm",
|
||||
display: "cpm",
|
||||
configValue: "cpm",
|
||||
exec: (): void => {
|
||||
UpdateConfig.setTypingSpeedUnit("cpm");
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "setTypingSpeedUnitWps",
|
||||
display: "wps",
|
||||
configValue: "wps",
|
||||
exec: (): void => {
|
||||
UpdateConfig.setTypingSpeedUnit("wps");
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "setTypingSpeedUnitCps",
|
||||
display: "cps",
|
||||
configValue: "cps",
|
||||
exec: (): void => {
|
||||
UpdateConfig.setTypingSpeedUnit("cps");
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "setTypingSpeedUnitWph",
|
||||
display: "wph",
|
||||
configValue: "wph",
|
||||
visible: false,
|
||||
exec: (): void => {
|
||||
UpdateConfig.setTypingSpeedUnit("wph");
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const commands: MonkeyTypes.Command[] = [
|
||||
{
|
||||
id: "changeTypingSpeedUnit",
|
||||
display: "Typing speed unit...",
|
||||
icon: "fa-tachometer-alt",
|
||||
subgroup,
|
||||
},
|
||||
];
|
||||
|
||||
export default commands;
|
|
@ -409,12 +409,20 @@ export function setAlwaysShowDecimalPlaces(
|
|||
return true;
|
||||
}
|
||||
|
||||
export function setAlwaysShowCPM(val: boolean, nosave?: boolean): boolean {
|
||||
if (!isConfigValueValid("always show CPM", val, ["boolean"])) return false;
|
||||
|
||||
config.alwaysShowCPM = val;
|
||||
saveToLocalStorage("alwaysShowCPM", nosave);
|
||||
ConfigEvent.dispatch("alwaysShowCPM", config.alwaysShowCPM);
|
||||
export function setTypingSpeedUnit(
|
||||
val: MonkeyTypes.TypingSpeedUnit,
|
||||
nosave?: boolean
|
||||
): boolean {
|
||||
if (
|
||||
!isConfigValueValid("typing speed unit", val, [
|
||||
["wpm", "cpm", "wps", "cps", "wph"],
|
||||
])
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
config.typingSpeedUnit = val;
|
||||
saveToLocalStorage("typingSpeedUnit", nosave);
|
||||
ConfigEvent.dispatch("typingSpeedUnit", config.typingSpeedUnit, nosave);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -888,7 +896,9 @@ export function setShowAverage(
|
|||
nosave?: boolean
|
||||
): boolean {
|
||||
if (
|
||||
!isConfigValueValid("show average", value, [["off", "wpm", "acc", "both"]])
|
||||
!isConfigValueValid("show average", value, [
|
||||
["off", "speed", "acc", "both"],
|
||||
])
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1901,7 +1911,7 @@ export function apply(
|
|||
setNumbers(configObj.numbers, true);
|
||||
setPunctuation(configObj.punctuation, true);
|
||||
setHighlightMode(configObj.highlightMode, true);
|
||||
setAlwaysShowCPM(configObj.alwaysShowCPM, true);
|
||||
setTypingSpeedUnit(configObj.typingSpeedUnit, true);
|
||||
setHideExtraLetters(configObj.hideExtraLetters, true);
|
||||
setStartGraphsAtZero(configObj.startGraphsAtZero, true);
|
||||
setStrictSpace(configObj.strictSpace, true);
|
||||
|
@ -1976,6 +1986,16 @@ function replaceLegacyValues(
|
|||
configObj.quickRestart = "esc";
|
||||
}
|
||||
|
||||
//@ts-ignore
|
||||
if (configObj.alwaysShowCPM === true) {
|
||||
configObj.typingSpeedUnit = "cpm";
|
||||
}
|
||||
|
||||
//@ts-ignore
|
||||
if (configObj.showAverage === "wpm") {
|
||||
configObj.showAverage = "speed";
|
||||
}
|
||||
|
||||
return configObj;
|
||||
}
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@ export default <MonkeyTypes.Config>{
|
|||
minWpm: "off",
|
||||
minWpmCustomSpeed: 100,
|
||||
highlightMode: "letter",
|
||||
alwaysShowCPM: false,
|
||||
typingSpeedUnit: "wpm",
|
||||
ads: "result",
|
||||
hideExtraLetters: false,
|
||||
strictSpace: false,
|
||||
|
|
|
@ -343,6 +343,7 @@ export const accountHistory: ChartWithUpdateColors<
|
|||
},
|
||||
wpm: {
|
||||
axis: "y",
|
||||
type: "linear",
|
||||
beginAtZero: true,
|
||||
min: 0,
|
||||
ticks: {
|
||||
|
@ -478,7 +479,7 @@ export const accountHistory: ChartWithUpdateColors<
|
|||
tooltipItem.dataIndex
|
||||
] as MonkeyTypes.HistoryChartData;
|
||||
let label =
|
||||
`${Config.alwaysShowCPM ? "cpm" : "wpm"}: ${resultData.wpm}` +
|
||||
`${Config.typingSpeedUnit}: ${resultData.wpm}` +
|
||||
"\n" +
|
||||
`raw: ${resultData.raw}` +
|
||||
"\n" +
|
||||
|
@ -652,9 +653,9 @@ export const accountActivity: ChartWithUpdateColors<
|
|||
true
|
||||
)}\nTests Completed: ${resultData.amount}`;
|
||||
case 1:
|
||||
return `Average ${
|
||||
Config.alwaysShowCPM ? "Cpm" : "Wpm"
|
||||
}: ${Misc.roundTo2(resultData.y)}`;
|
||||
return `Average ${Config.typingSpeedUnit}: ${Misc.roundTo2(
|
||||
resultData.y
|
||||
)}`;
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
|
@ -755,7 +756,7 @@ export const accountHistogram: ChartWithUpdateColors<
|
|||
// )}\nTests Completed: ${resultData.amount}`;
|
||||
// case 1:
|
||||
// return `Average ${
|
||||
// Config.alwaysShowCPM ? "Cpm" : "Wpm"
|
||||
// Config.typingSpeedUnit
|
||||
// }: ${Misc.roundTo2(resultData.y)}`;
|
||||
// default:
|
||||
// return "";
|
||||
|
|
|
@ -2,6 +2,7 @@ import Ape from "../ape";
|
|||
import * as DB from "../db";
|
||||
import Config from "../config";
|
||||
import * as Misc from "../utils/misc";
|
||||
import { get as getTypingSpeedUnit } from "../utils/typing-speed-units";
|
||||
import * as Notifications from "./notifications";
|
||||
import format from "date-fns/format";
|
||||
import { Auth } from "../firebase";
|
||||
|
@ -150,6 +151,7 @@ function updateFooter(lb: LbKey): void {
|
|||
return;
|
||||
}
|
||||
|
||||
const typingSpeedUnit = getTypingSpeedUnit(Config.typingSpeedUnit);
|
||||
if (DB.getSnapshot()?.lbOptOut === true) {
|
||||
$(`#leaderboardsWrapper table.${side} tfoot`).html(`
|
||||
<tr>
|
||||
|
@ -185,14 +187,10 @@ function updateFooter(lb: LbKey): void {
|
|||
<tr>
|
||||
<td>${entry.rank}</td>
|
||||
<td><span class="top">You</span>${toppercent ? toppercent : ""}</td>
|
||||
<td class="alignRight">${(Config.alwaysShowCPM
|
||||
? entry.wpm * 5
|
||||
: entry.wpm
|
||||
).toFixed(2)}<br><div class="sub">${entry.acc.toFixed(2)}%</div></td>
|
||||
<td class="alignRight">${(Config.alwaysShowCPM
|
||||
? entry.raw * 5
|
||||
: entry.raw
|
||||
).toFixed(2)}<br><div class="sub">${
|
||||
<td class="alignRight">${typingSpeedUnit.convert(entry.wpm).toFixed(2)}<br>
|
||||
<div class="sub">${entry.acc.toFixed(2)}%</div></td>
|
||||
<td class="alignRight">${typingSpeedUnit.convert(entry.raw).toFixed(2)}<br>
|
||||
<div class="sub">${
|
||||
!entry.consistency || entry.consistency === "-"
|
||||
? "-"
|
||||
: entry.consistency.toFixed(2) + "%"
|
||||
|
@ -264,6 +262,7 @@ async function fillTable(lb: LbKey, prepend?: number): Promise<void> {
|
|||
);
|
||||
}
|
||||
|
||||
const typingSpeedUnit = getTypingSpeedUnit(Config.typingSpeedUnit);
|
||||
const loggedInUserName = DB.getSnapshot()?.name;
|
||||
|
||||
const snap = DB.getSnapshot();
|
||||
|
@ -342,14 +341,10 @@ async function fillTable(lb: LbKey, prepend?: number): Promise<void> {
|
|||
${entry.badgeId ? getBadgeHTMLbyId(entry.badgeId) : ""}
|
||||
</div>
|
||||
</td>
|
||||
<td class="alignRight">${(Config.alwaysShowCPM
|
||||
? entry.wpm * 5
|
||||
: entry.wpm
|
||||
).toFixed(2)}<br><div class="sub">${entry.acc.toFixed(2)}%</div></td>
|
||||
<td class="alignRight">${(Config.alwaysShowCPM
|
||||
? entry.raw * 5
|
||||
: entry.raw
|
||||
).toFixed(2)}<br><div class="sub">${
|
||||
<td class="alignRight">${typingSpeedUnit.convert(entry.wpm).toFixed(2)}<br>
|
||||
<div class="sub">${entry.acc.toFixed(2)}%</div></td>
|
||||
<td class="alignRight">${typingSpeedUnit.convert(entry.raw).toFixed(2)}<br>
|
||||
<div class="sub">${
|
||||
!entry.consistency || entry.consistency === "-"
|
||||
? "-"
|
||||
: entry.consistency.toFixed(2) + "%"
|
||||
|
@ -608,15 +603,9 @@ export function show(): void {
|
|||
"disabled"
|
||||
);
|
||||
}
|
||||
if (Config.alwaysShowCPM) {
|
||||
$("#leaderboards table thead tr td:nth-child(3)").html(
|
||||
'cpm<br><div class="sub">accuracy</div>'
|
||||
);
|
||||
} else {
|
||||
$("#leaderboards table thead tr td:nth-child(3)").html(
|
||||
'wpm<br><div class="sub">accuracy</div>'
|
||||
);
|
||||
}
|
||||
$("#leaderboards table thead tr td:nth-child(3)").html(
|
||||
Config.typingSpeedUnit + '<br><div class="sub">accuracy</div>'
|
||||
);
|
||||
$("#leaderboardsWrapper")
|
||||
.stop(true, true)
|
||||
.css("opacity", 0)
|
||||
|
|
|
@ -7,6 +7,7 @@ import * as TestWords from "../test/test-words";
|
|||
import * as ConfigEvent from "../observables/config-event";
|
||||
import { Auth } from "../firebase";
|
||||
import * as CustomTextState from "../states/custom-text-name";
|
||||
import { get as getTypingSpeedUnit } from "../utils/typing-speed-units";
|
||||
|
||||
ConfigEvent.subscribe((eventKey) => {
|
||||
if (
|
||||
|
@ -21,7 +22,7 @@ ConfigEvent.subscribe((eventKey) => {
|
|||
"confidenceMode",
|
||||
"layout",
|
||||
"showAverage",
|
||||
"alwaysShowCPM",
|
||||
"typingSpeedUnit",
|
||||
].includes(eventKey)
|
||||
) {
|
||||
update();
|
||||
|
@ -142,10 +143,10 @@ export async function update(): Promise<void> {
|
|||
}
|
||||
|
||||
if (Auth?.currentUser && avgWPM > 0) {
|
||||
const avgWPMText = ["wpm", "both"].includes(Config.showAverage)
|
||||
? Config.alwaysShowCPM
|
||||
? `${Math.round(avgWPM * 5)} cpm`
|
||||
: `${avgWPM} wpm`
|
||||
const avgWPMText = ["speed", "both"].includes(Config.showAverage)
|
||||
? getTypingSpeedUnit(Config.typingSpeedUnit).convertWithUnitSuffix(
|
||||
avgWPM
|
||||
)
|
||||
: "";
|
||||
|
||||
const avgAccText = ["acc", "both"].includes(Config.showAverage)
|
||||
|
|
|
@ -12,11 +12,14 @@ import * as TodayTracker from "../test/today-tracker";
|
|||
import * as Notifications from "../elements/notifications";
|
||||
import Page from "./page";
|
||||
import * as Misc from "../utils/misc";
|
||||
import { get as getTypingSpeedUnit } from "../utils/typing-speed-units";
|
||||
import * as Profile from "../elements/profile";
|
||||
import format from "date-fns/format";
|
||||
import * as ConnectionState from "../states/connection";
|
||||
import * as Skeleton from "../popups/skeleton";
|
||||
import type { ScaleChartOptions } from "chart.js";
|
||||
import type { ScaleChartOptions, LinearScaleOptions } from "chart.js";
|
||||
import * as ConfigEvent from "../observables/config-event";
|
||||
import * as ActivePage from "../states/active-page";
|
||||
import { Auth } from "../firebase";
|
||||
|
||||
let filterDebug = false;
|
||||
|
@ -32,6 +35,7 @@ let filteredResults: MonkeyTypes.Result<MonkeyTypes.Mode>[] = [];
|
|||
let visibleTableLines = 0;
|
||||
|
||||
function loadMoreLines(lineIndex?: number): void {
|
||||
const typingSpeedUnit = getTypingSpeedUnit(Config.typingSpeedUnit);
|
||||
if (!filteredResults || filteredResults.length === 0) return;
|
||||
let newVisibleLines;
|
||||
if (lineIndex && lineIndex > visibleTableLines) {
|
||||
|
@ -49,9 +53,7 @@ function loadMoreLines(lineIndex?: number): void {
|
|||
|
||||
let raw;
|
||||
try {
|
||||
raw = Config.alwaysShowCPM
|
||||
? (result.rawWpm * 5).toFixed(2)
|
||||
: result.rawWpm.toFixed(2);
|
||||
raw = typingSpeedUnit.convert(result.rawWpm).toFixed(2);
|
||||
if (raw === undefined) {
|
||||
raw = "-";
|
||||
}
|
||||
|
@ -159,7 +161,7 @@ function loadMoreLines(lineIndex?: number): void {
|
|||
$(".pageAccount .history table tbody").append(`
|
||||
<tr class="resultRow" id="result-${i}">
|
||||
<td>${pb}</td>
|
||||
<td>${(Config.alwaysShowCPM ? result.wpm * 5 : result.wpm).toFixed(2)}</td>
|
||||
<td>${typingSpeedUnit.convert(result.wpm).toFixed(2)}</td>
|
||||
<td>${raw}</td>
|
||||
<td>${result.acc.toFixed(2)}%</td>
|
||||
<td>${consistency}</td>
|
||||
|
@ -264,13 +266,9 @@ async function fillContent(): Promise<void> {
|
|||
};
|
||||
}
|
||||
|
||||
interface HistogramChartData {
|
||||
[key: string]: number;
|
||||
}
|
||||
|
||||
const activityChartData: ActivityChartData = {};
|
||||
|
||||
const histogramChartData: HistogramChartData = {};
|
||||
const histogramChartData: number[] = [];
|
||||
const typingSpeedUnit = getTypingSpeedUnit(Config.typingSpeedUnit);
|
||||
|
||||
filteredResults = [];
|
||||
$(".pageAccount .history table tbody").empty();
|
||||
|
@ -538,13 +536,18 @@ async function fillContent(): Promise<void> {
|
|||
};
|
||||
}
|
||||
|
||||
const bucket = Math.floor(Math.round(result.wpm) / 10) * 10;
|
||||
const bucketSize = typingSpeedUnit.histogramDataBucketSize;
|
||||
const bucket = Math.floor(
|
||||
typingSpeedUnit.convert(result.wpm) / bucketSize
|
||||
);
|
||||
|
||||
if (Object.keys(histogramChartData).includes(String(bucket))) {
|
||||
histogramChartData[bucket]++;
|
||||
} else {
|
||||
histogramChartData[bucket] = 1;
|
||||
//grow array if needed
|
||||
if (histogramChartData.length <= bucket) {
|
||||
for (let i = histogramChartData.length; i <= bucket; i++) {
|
||||
histogramChartData.push(0);
|
||||
}
|
||||
}
|
||||
histogramChartData[bucket]++;
|
||||
|
||||
let tt = 0;
|
||||
if (
|
||||
|
@ -616,8 +619,8 @@ async function fillContent(): Promise<void> {
|
|||
|
||||
chartData.push({
|
||||
x: filteredResults.length,
|
||||
y: Config.alwaysShowCPM ? Misc.roundTo2(result.wpm * 5) : result.wpm,
|
||||
wpm: Config.alwaysShowCPM ? Misc.roundTo2(result.wpm * 5) : result.wpm,
|
||||
y: Misc.roundTo2(typingSpeedUnit.convert(result.wpm)),
|
||||
wpm: Misc.roundTo2(typingSpeedUnit.convert(result.wpm)),
|
||||
acc: result.acc,
|
||||
mode: result.mode,
|
||||
mode2: result.mode2,
|
||||
|
@ -625,9 +628,7 @@ async function fillContent(): Promise<void> {
|
|||
language: result.language,
|
||||
timestamp: result.timestamp,
|
||||
difficulty: result.difficulty,
|
||||
raw: Config.alwaysShowCPM
|
||||
? Misc.roundTo2(result.rawWpm * 5)
|
||||
: result.rawWpm,
|
||||
raw: Misc.roundTo2(typingSpeedUnit.convert(result.rawWpm)),
|
||||
isPb: result.isPb ?? false,
|
||||
});
|
||||
|
||||
|
@ -656,11 +657,9 @@ async function fillContent(): Promise<void> {
|
|||
}
|
||||
);
|
||||
|
||||
if (Config.alwaysShowCPM) {
|
||||
$(".pageAccount .group.history table thead tr td:nth-child(2)").text("cpm");
|
||||
} else {
|
||||
$(".pageAccount .group.history table thead tr td:nth-child(2)").text("wpm");
|
||||
}
|
||||
$(".pageAccount .group.history table thead tr td:nth-child(2)").text(
|
||||
Config.typingSpeedUnit
|
||||
);
|
||||
|
||||
await Misc.sleep(0);
|
||||
loadMoreLines();
|
||||
|
@ -684,9 +683,7 @@ async function fillContent(): Promise<void> {
|
|||
activityChartData_avgWpm.push({
|
||||
x: dateInt,
|
||||
y: Misc.roundTo2(
|
||||
(Config.alwaysShowCPM
|
||||
? activityChartData[dateInt].totalWpm * 5
|
||||
: activityChartData[dateInt].totalWpm) /
|
||||
typingSpeedUnit.convert(activityChartData[dateInt].totalWpm) /
|
||||
activityChartData[dateInt].amount
|
||||
),
|
||||
});
|
||||
|
@ -697,11 +694,8 @@ async function fillContent(): Promise<void> {
|
|||
ChartController.accountActivity.options as ScaleChartOptions<"bar" | "line">
|
||||
).scales;
|
||||
|
||||
if (Config.alwaysShowCPM) {
|
||||
accountActivityScaleOptions["avgWpm"].title.text = "Average Cpm";
|
||||
} else {
|
||||
accountActivityScaleOptions["avgWpm"].title.text = "Average Wpm";
|
||||
}
|
||||
accountActivityScaleOptions["avgWpm"].title.text =
|
||||
"Average " + Config.typingSpeedUnit;
|
||||
|
||||
ChartController.accountActivity.data.datasets[0].data =
|
||||
activityChartData_time;
|
||||
|
@ -711,21 +705,17 @@ async function fillContent(): Promise<void> {
|
|||
const histogramChartDataBucketed: { x: number; y: number }[] = [];
|
||||
const labels: string[] = [];
|
||||
|
||||
const keys = Object.keys(histogramChartData);
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const bucket = parseInt(keys[i]);
|
||||
labels.push(`${bucket} - ${bucket + 9}`);
|
||||
const bucketSize = typingSpeedUnit.histogramDataBucketSize;
|
||||
const bucketSizeUpperBound = bucketSize - (bucketSize <= 1 ? 0.01 : 1);
|
||||
|
||||
histogramChartData.forEach((amount: number, i: number) => {
|
||||
const bucket = i * bucketSize;
|
||||
labels.push(`${bucket} - ${bucket + bucketSizeUpperBound}`);
|
||||
histogramChartDataBucketed.push({
|
||||
x: bucket,
|
||||
y: histogramChartData[bucket],
|
||||
y: amount,
|
||||
});
|
||||
if (bucket + 10 !== parseInt(keys[i + 1])) {
|
||||
for (let j = bucket + 10; j < parseInt(keys[i + 1]); j += 10) {
|
||||
histogramChartDataBucketed.push({ x: j, y: 0 });
|
||||
labels.push(`${j} - ${j + 9}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ChartController.accountHistogram.data.labels = labels;
|
||||
ChartController.accountHistogram.data.datasets[0].data =
|
||||
|
@ -735,11 +725,10 @@ async function fillContent(): Promise<void> {
|
|||
ChartController.accountHistory.options as ScaleChartOptions<"line">
|
||||
).scales;
|
||||
|
||||
if (Config.alwaysShowCPM) {
|
||||
accountHistoryScaleOptions["wpm"].title.text = "Characters per Minute";
|
||||
} else {
|
||||
accountHistoryScaleOptions["wpm"].title.text = "Words per Minute";
|
||||
}
|
||||
const accountHistoryWpmOptions = accountHistoryScaleOptions[
|
||||
"wpm"
|
||||
] as LinearScaleOptions;
|
||||
accountHistoryWpmOptions.title.text = typingSpeedUnit.fullUnitString;
|
||||
|
||||
if (chartData.length > 0) {
|
||||
// get pb points
|
||||
|
@ -803,26 +792,29 @@ async function fillContent(): Promise<void> {
|
|||
const wpms = chartData.map((r) => r.y);
|
||||
const minWpmChartVal = Math.min(...wpms);
|
||||
const maxWpmChartVal = Math.max(...wpms);
|
||||
const wpmStepSize = typingSpeedUnit.historyStepSize;
|
||||
const maxWpmChartValWithBuffer =
|
||||
Math.floor(maxWpmChartVal) +
|
||||
(wpmStepSize - (Math.floor(maxWpmChartVal) % wpmStepSize));
|
||||
|
||||
// let accuracies = accChartData.map((r) => r.y);
|
||||
accountHistoryScaleOptions["wpm"].max =
|
||||
Math.floor(maxWpmChartVal) + (10 - (Math.floor(maxWpmChartVal) % 10));
|
||||
accountHistoryScaleOptions["pb"].max =
|
||||
Math.floor(maxWpmChartVal) + (10 - (Math.floor(maxWpmChartVal) % 10));
|
||||
accountHistoryScaleOptions["wpmAvgTen"].max =
|
||||
Math.floor(maxWpmChartVal) + (10 - (Math.floor(maxWpmChartVal) % 10));
|
||||
accountHistoryScaleOptions["wpmAvgHundred"].max =
|
||||
Math.floor(maxWpmChartVal) + (10 - (Math.floor(maxWpmChartVal) % 10));
|
||||
accountHistoryWpmOptions.max = maxWpmChartValWithBuffer;
|
||||
|
||||
accountHistoryWpmOptions.ticks.stepSize = wpmStepSize;
|
||||
|
||||
accountHistoryScaleOptions["pb"].max = maxWpmChartValWithBuffer;
|
||||
accountHistoryScaleOptions["wpmAvgTen"].max = maxWpmChartValWithBuffer;
|
||||
accountHistoryScaleOptions["wpmAvgHundred"].max = maxWpmChartValWithBuffer;
|
||||
|
||||
if (!Config.startGraphsAtZero) {
|
||||
const minWpmChartValFloor = Math.floor(minWpmChartVal);
|
||||
|
||||
accountHistoryScaleOptions["wpm"].min = minWpmChartValFloor;
|
||||
accountHistoryWpmOptions.min = minWpmChartValFloor;
|
||||
accountHistoryScaleOptions["pb"].min = minWpmChartValFloor;
|
||||
accountHistoryScaleOptions["wpmAvgTen"].min = minWpmChartValFloor;
|
||||
accountHistoryScaleOptions["wpmAvgHundred"].min = minWpmChartValFloor;
|
||||
} else {
|
||||
accountHistoryScaleOptions["wpm"].min = 0;
|
||||
accountHistoryWpmOptions.min = 0;
|
||||
accountHistoryScaleOptions["pb"].min = 0;
|
||||
accountHistoryScaleOptions["wpmAvgTen"].min = 0;
|
||||
accountHistoryScaleOptions["wpmAvgHundred"].min = 0;
|
||||
|
@ -852,38 +844,31 @@ async function fillContent(): Promise<void> {
|
|||
Misc.secondsToString(Math.round(totalSecondsFiltered), true, true)
|
||||
);
|
||||
|
||||
let highestSpeed: number | string = topWpm;
|
||||
if (Config.alwaysShowCPM) {
|
||||
highestSpeed = topWpm * 5;
|
||||
}
|
||||
let highestSpeed: number | string = typingSpeedUnit.convert(topWpm);
|
||||
|
||||
if (Config.alwaysShowDecimalPlaces) {
|
||||
highestSpeed = Misc.roundTo2(highestSpeed).toFixed(2);
|
||||
} else {
|
||||
highestSpeed = Math.round(highestSpeed);
|
||||
}
|
||||
|
||||
const wpmCpm = Config.alwaysShowCPM ? "cpm" : "wpm";
|
||||
const speedUnit = Config.typingSpeedUnit;
|
||||
|
||||
$(".pageAccount .highestWpm .title").text(`highest ${wpmCpm}`);
|
||||
$(".pageAccount .highestWpm .title").text(`highest ${speedUnit}`);
|
||||
$(".pageAccount .highestWpm .val").text(highestSpeed);
|
||||
|
||||
let averageSpeed: number | string = totalWpm;
|
||||
if (Config.alwaysShowCPM) {
|
||||
averageSpeed = totalWpm * 5;
|
||||
}
|
||||
let averageSpeed: number | string = typingSpeedUnit.convert(totalWpm);
|
||||
if (Config.alwaysShowDecimalPlaces) {
|
||||
averageSpeed = Misc.roundTo2(averageSpeed / testCount).toFixed(2);
|
||||
} else {
|
||||
averageSpeed = Math.round(averageSpeed / testCount);
|
||||
}
|
||||
|
||||
$(".pageAccount .averageWpm .title").text(`average ${wpmCpm}`);
|
||||
$(".pageAccount .averageWpm .title").text(`average ${speedUnit}`);
|
||||
$(".pageAccount .averageWpm .val").text(averageSpeed);
|
||||
|
||||
let averageSpeedLast10: number | string = wpmLast10total;
|
||||
if (Config.alwaysShowCPM) {
|
||||
averageSpeedLast10 = wpmLast10total * 5;
|
||||
}
|
||||
let averageSpeedLast10: number | string =
|
||||
typingSpeedUnit.convert(wpmLast10total);
|
||||
if (Config.alwaysShowDecimalPlaces) {
|
||||
averageSpeedLast10 = Misc.roundTo2(averageSpeedLast10 / last10).toFixed(2);
|
||||
} else {
|
||||
|
@ -891,40 +876,33 @@ async function fillContent(): Promise<void> {
|
|||
}
|
||||
|
||||
$(".pageAccount .averageWpm10 .title").text(
|
||||
`average ${wpmCpm} (last 10 tests)`
|
||||
`average ${speedUnit} (last 10 tests)`
|
||||
);
|
||||
$(".pageAccount .averageWpm10 .val").text(averageSpeedLast10);
|
||||
|
||||
let highestRawSpeed: number | string = rawWpm.max;
|
||||
if (Config.alwaysShowCPM) {
|
||||
highestRawSpeed = rawWpm.max * 5;
|
||||
}
|
||||
let highestRawSpeed: number | string = typingSpeedUnit.convert(rawWpm.max);
|
||||
if (Config.alwaysShowDecimalPlaces) {
|
||||
highestRawSpeed = Misc.roundTo2(highestRawSpeed).toFixed(2);
|
||||
} else {
|
||||
highestRawSpeed = Math.round(highestRawSpeed);
|
||||
}
|
||||
|
||||
$(".pageAccount .highestRaw .title").text(`highest raw ${wpmCpm}`);
|
||||
$(".pageAccount .highestRaw .title").text(`highest raw ${speedUnit}`);
|
||||
$(".pageAccount .highestRaw .val").text(highestRawSpeed);
|
||||
|
||||
let averageRawSpeed: number | string = rawWpm.total;
|
||||
if (Config.alwaysShowCPM) {
|
||||
averageRawSpeed = rawWpm.total * 5;
|
||||
}
|
||||
let averageRawSpeed: number | string = typingSpeedUnit.convert(rawWpm.total);
|
||||
if (Config.alwaysShowDecimalPlaces) {
|
||||
averageRawSpeed = Misc.roundTo2(averageRawSpeed / rawWpm.count).toFixed(2);
|
||||
} else {
|
||||
averageRawSpeed = Math.round(averageRawSpeed / rawWpm.count);
|
||||
}
|
||||
|
||||
$(".pageAccount .averageRaw .title").text(`average raw ${wpmCpm}`);
|
||||
$(".pageAccount .averageRaw .title").text(`average raw ${speedUnit}`);
|
||||
$(".pageAccount .averageRaw .val").text(averageRawSpeed);
|
||||
|
||||
let averageRawSpeedLast10: number | string = rawWpm.last10Total;
|
||||
if (Config.alwaysShowCPM) {
|
||||
averageRawSpeedLast10 = rawWpm.last10Total * 5;
|
||||
}
|
||||
let averageRawSpeedLast10: number | string = typingSpeedUnit.convert(
|
||||
rawWpm.last10Total
|
||||
);
|
||||
if (Config.alwaysShowDecimalPlaces) {
|
||||
averageRawSpeedLast10 = Misc.roundTo2(
|
||||
averageRawSpeedLast10 / rawWpm.last10Count
|
||||
|
@ -936,7 +914,7 @@ async function fillContent(): Promise<void> {
|
|||
}
|
||||
|
||||
$(".pageAccount .averageRaw10 .title").text(
|
||||
`average raw ${wpmCpm} (last 10 tests)`
|
||||
`average raw ${speedUnit} (last 10 tests)`
|
||||
);
|
||||
$(".pageAccount .averageRaw10 .val").text(averageRawSpeedLast10);
|
||||
|
||||
|
@ -1029,11 +1007,8 @@ async function fillContent(): Promise<void> {
|
|||
|
||||
$(".pageAccount .group.chart .below .text").text(
|
||||
`Speed change per hour spent typing: ${
|
||||
plus +
|
||||
Misc.roundTo2(
|
||||
Config.alwaysShowCPM ? wpmChangePerHour * 5 : wpmChangePerHour
|
||||
)
|
||||
} ${Config.alwaysShowCPM ? "cpm" : "wpm"}`
|
||||
plus + Misc.roundTo2(typingSpeedUnit.convert(wpmChangePerHour))
|
||||
} ${Config.typingSpeedUnit}`
|
||||
);
|
||||
|
||||
$(".pageAccount .estimatedWordsTyped .val").text(totalEstimatedWords);
|
||||
|
@ -1312,6 +1287,12 @@ $(".pageAccount .profile").on("click", ".details .copyLink", () => {
|
|||
);
|
||||
});
|
||||
|
||||
ConfigEvent.subscribe((eventKey) => {
|
||||
if (ActivePage.get() === "account" && eventKey === "typingSpeedUnit") {
|
||||
update();
|
||||
}
|
||||
});
|
||||
|
||||
export const page = new Page(
|
||||
"account",
|
||||
$(".page.pageAccount"),
|
||||
|
|
|
@ -379,9 +379,9 @@ async function initGroups(): Promise<void> {
|
|||
UpdateConfig.setAlwaysShowDecimalPlaces,
|
||||
"button"
|
||||
) as SettingsGroup<MonkeyTypes.ConfigValues>;
|
||||
groups["alwaysShowCPM"] = new SettingsGroup(
|
||||
"alwaysShowCPM",
|
||||
UpdateConfig.setAlwaysShowCPM,
|
||||
groups["typingSpeedUnit"] = new SettingsGroup(
|
||||
"typingSpeedUnit",
|
||||
UpdateConfig.setTypingSpeedUnit,
|
||||
"button"
|
||||
) as SettingsGroup<MonkeyTypes.ConfigValues>;
|
||||
groups["customBackgroundSize"] = new SettingsGroup(
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import Config from "../config";
|
||||
import * as TestState from "../test/test-state";
|
||||
import * as ConfigEvent from "../observables/config-event";
|
||||
import { get as getTypingSpeedUnit } from "../utils/typing-speed-units";
|
||||
|
||||
export async function update(burst: number): Promise<void> {
|
||||
if (!Config.showLiveBurst) return;
|
||||
burst = Math.round(getTypingSpeedUnit(Config.typingSpeedUnit).convert(burst));
|
||||
(document.querySelector("#miniTimerAndLiveWpm .burst") as Element).innerHTML =
|
||||
burst.toString();
|
||||
(document.querySelector("#liveBurst") as Element).innerHTML =
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import Config from "../config";
|
||||
import * as TestState from "../test/test-state";
|
||||
import * as ConfigEvent from "../observables/config-event";
|
||||
import { get as getTypingSpeedUnit } from "../utils/typing-speed-units";
|
||||
|
||||
const liveWpmElement = document.querySelector("#liveWpm") as Element;
|
||||
const miniLiveWpmElement = document.querySelector(
|
||||
|
@ -12,9 +13,9 @@ export function update(wpm: number, raw: number): void {
|
|||
if (Config.blindMode) {
|
||||
number = raw;
|
||||
}
|
||||
if (Config.alwaysShowCPM) {
|
||||
number = Math.round(number * 5);
|
||||
}
|
||||
number = Math.round(
|
||||
getTypingSpeedUnit(Config.typingSpeedUnit).convert(number)
|
||||
);
|
||||
miniLiveWpmElement.innerHTML = number.toString();
|
||||
liveWpmElement.innerHTML = number.toString();
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import * as QuoteRatePopup from "../popups/quote-rate-popup";
|
|||
import * as GlarsesMode from "../states/glarses-mode";
|
||||
import * as SlowTimer from "../states/slow-timer";
|
||||
import * as Misc from "../utils/misc";
|
||||
import { get as getTypingSpeedUnit } from "../utils/typing-speed-units";
|
||||
import * as FunboxList from "./funbox/funbox-list";
|
||||
import * as PbCrown from "./pb-crown";
|
||||
import * as TestConfig from "./test-config";
|
||||
|
@ -24,6 +25,7 @@ import * as TestInput from "./test-input";
|
|||
import * as TestStats from "./test-stats";
|
||||
import * as TestUI from "./test-ui";
|
||||
import * as TodayTracker from "./today-tracker";
|
||||
import * as ConfigEvent from "../observables/config-event";
|
||||
|
||||
import confetti from "canvas-confetti";
|
||||
import type { AnnotationOptions } from "chartjs-plugin-annotation";
|
||||
|
@ -48,7 +50,9 @@ let resultScaleOptions = (
|
|||
).scales;
|
||||
|
||||
async function updateGraph(): Promise<void> {
|
||||
const typingSpeedUnit = getTypingSpeedUnit(Config.typingSpeedUnit);
|
||||
const labels = [];
|
||||
|
||||
for (let i = 1; i <= TestInput.wpmHistory.length; i++) {
|
||||
if (TestStats.lastSecondNotRound && i === TestInput.wpmHistory.length) {
|
||||
labels.push(Misc.roundTo2(result.testDuration).toString());
|
||||
|
@ -56,21 +60,20 @@ async function updateGraph(): Promise<void> {
|
|||
labels.push(i.toString());
|
||||
}
|
||||
}
|
||||
resultScaleOptions["wpm"].title.text = Config.alwaysShowCPM
|
||||
? "Characters per Minute"
|
||||
: "Words per Minute";
|
||||
resultScaleOptions["wpm"].title.text = typingSpeedUnit.fullUnitString;
|
||||
|
||||
const chartData1 = [
|
||||
...(Config.alwaysShowCPM
|
||||
? TestInput.wpmHistory.map((a) => a * 5)
|
||||
: TestInput.wpmHistory),
|
||||
...TestInput.wpmHistory.map((a) =>
|
||||
Misc.roundTo2(typingSpeedUnit.convert(a))
|
||||
),
|
||||
];
|
||||
|
||||
if (result.chartData === "toolong") return;
|
||||
|
||||
const chartData2 = [
|
||||
...(Config.alwaysShowCPM
|
||||
? result.chartData.raw.map((a) => a * 5)
|
||||
: result.chartData.raw),
|
||||
...result.chartData.raw.map((a) =>
|
||||
Misc.roundTo2(typingSpeedUnit.convert(a))
|
||||
),
|
||||
];
|
||||
|
||||
if (
|
||||
|
@ -92,14 +95,12 @@ async function updateGraph(): Promise<void> {
|
|||
ChartController.result.data.labels = labels;
|
||||
ChartController.result.data.datasets[0].data = chartData1;
|
||||
ChartController.result.data.datasets[1].data = smoothedRawData;
|
||||
|
||||
ChartController.result.data.datasets[0].label = Config.alwaysShowCPM
|
||||
? "cpm"
|
||||
: "wpm";
|
||||
ChartController.result.data.datasets[0].label = Config.typingSpeedUnit;
|
||||
|
||||
maxChartVal = Math.max(
|
||||
...[Math.max(...smoothedRawData), Math.max(...chartData1)]
|
||||
);
|
||||
|
||||
if (!Config.startGraphsAtZero) {
|
||||
const minChartVal = Math.min(
|
||||
...[Math.min(...smoothedRawData), Math.min(...chartData1)]
|
||||
|
@ -169,9 +170,8 @@ export async function updateGraphPBLine(): Promise<void> {
|
|||
result.funbox ?? "none"
|
||||
);
|
||||
if (lpb === 0) return;
|
||||
const chartlpb = Misc.roundTo2(Config.alwaysShowCPM ? lpb * 5 : lpb).toFixed(
|
||||
2
|
||||
);
|
||||
const typingSpeedUnit = getTypingSpeedUnit(Config.typingSpeedUnit);
|
||||
const chartlpb = Misc.roundTo2(typingSpeedUnit.convert(lpb)).toFixed(2);
|
||||
resultAnnotation.push({
|
||||
display: true,
|
||||
type: "line",
|
||||
|
@ -198,54 +198,49 @@ export async function updateGraphPBLine(): Promise<void> {
|
|||
content: `PB: ${chartlpb}`,
|
||||
},
|
||||
});
|
||||
const lpbRange = typingSpeedUnit.convert(20);
|
||||
if (
|
||||
maxChartVal >= parseFloat(chartlpb) - 20 &&
|
||||
maxChartVal <= parseFloat(chartlpb) + 20
|
||||
maxChartVal >= parseFloat(chartlpb) - lpbRange &&
|
||||
maxChartVal <= parseFloat(chartlpb) + lpbRange
|
||||
) {
|
||||
maxChartVal = parseFloat(chartlpb) + 15;
|
||||
maxChartVal = Math.round(parseFloat(chartlpb) + lpbRange);
|
||||
}
|
||||
resultScaleOptions["wpm"].max = Math.round(maxChartVal + 5);
|
||||
resultScaleOptions["raw"].max = Math.round(maxChartVal + 5);
|
||||
resultScaleOptions["wpm"].max = maxChartVal;
|
||||
resultScaleOptions["raw"].max = maxChartVal;
|
||||
}
|
||||
|
||||
function updateWpmAndAcc(): void {
|
||||
let inf = false;
|
||||
const typingSpeedUnit = getTypingSpeedUnit(Config.typingSpeedUnit);
|
||||
if (result.wpm >= 1000) {
|
||||
inf = true;
|
||||
}
|
||||
|
||||
if (Config.alwaysShowDecimalPlaces) {
|
||||
if (Config.alwaysShowCPM === false) {
|
||||
$("#result .stats .wpm .top .text").text("wpm");
|
||||
if (inf) {
|
||||
$("#result .stats .wpm .bottom").text("Infinite");
|
||||
} else {
|
||||
$("#result .stats .wpm .bottom").text(
|
||||
Misc.roundTo2(result.wpm).toFixed(2)
|
||||
);
|
||||
}
|
||||
$("#result .stats .raw .bottom").text(
|
||||
Misc.roundTo2(result.rawWpm).toFixed(2)
|
||||
$("#result .stats .wpm .top .text").text(Config.typingSpeedUnit);
|
||||
if (inf) {
|
||||
$("#result .stats .wpm .bottom").text("Infinite");
|
||||
} else {
|
||||
$("#result .stats .wpm .bottom").text(
|
||||
Misc.roundTo2(typingSpeedUnit.convert(result.wpm)).toFixed(2)
|
||||
);
|
||||
}
|
||||
$("#result .stats .raw .bottom").text(
|
||||
Misc.roundTo2(typingSpeedUnit.convert(result.rawWpm)).toFixed(2)
|
||||
);
|
||||
|
||||
if (Config.typingSpeedUnit != "wpm") {
|
||||
$("#result .stats .wpm .bottom").attr(
|
||||
"aria-label",
|
||||
Misc.roundTo2(result.wpm * 5).toFixed(2) + " cpm"
|
||||
result.wpm.toFixed(2) + " wpm"
|
||||
);
|
||||
$("#result .stats .raw .bottom").attr(
|
||||
"aria-label",
|
||||
result.rawWpm.toFixed(2) + " wpm"
|
||||
);
|
||||
} else {
|
||||
$("#result .stats .wpm .top .text").text("cpm");
|
||||
if (inf) {
|
||||
$("#result .stats .wpm .bottom").text("Infinite");
|
||||
} else {
|
||||
$("#result .stats .wpm .bottom").text(
|
||||
Misc.roundTo2(result.wpm * 5).toFixed(2)
|
||||
);
|
||||
}
|
||||
$("#result .stats .raw .bottom").text(
|
||||
Misc.roundTo2(result.rawWpm * 5).toFixed(2)
|
||||
);
|
||||
$("#result .stats .wpm .bottom").attr(
|
||||
"aria-label",
|
||||
Misc.roundTo2(result.wpm).toFixed(2) + " wpm"
|
||||
);
|
||||
$("#result .stats .wpm .bottom").removeAttr("aria-label");
|
||||
$("#result .stats .raw .bottom").removeAttr("aria-label");
|
||||
}
|
||||
|
||||
$("#result .stats .acc .bottom").text(
|
||||
|
@ -256,7 +251,6 @@ function updateWpmAndAcc(): void {
|
|||
time = Misc.secondsToString(Misc.roundTo2(result.testDuration));
|
||||
}
|
||||
$("#result .stats .time .bottom .text").text(time);
|
||||
$("#result .stats .raw .bottom").removeAttr("aria-label");
|
||||
// $("#result .stats .acc .bottom").removeAttr("aria-label");
|
||||
|
||||
$("#result .stats .acc .bottom").attr(
|
||||
|
@ -265,34 +259,27 @@ function updateWpmAndAcc(): void {
|
|||
);
|
||||
} else {
|
||||
//not showing decimal places
|
||||
if (Config.alwaysShowCPM === false) {
|
||||
$("#result .stats .wpm .top .text").text("wpm");
|
||||
$("#result .stats .wpm .bottom").attr(
|
||||
"aria-label",
|
||||
result.wpm + ` (${Misc.roundTo2(result.wpm * 5)} cpm)`
|
||||
);
|
||||
if (inf) {
|
||||
$("#result .stats .wpm .bottom").text("Infinite");
|
||||
} else {
|
||||
$("#result .stats .wpm .bottom").text(Math.round(result.wpm));
|
||||
}
|
||||
$("#result .stats .raw .bottom").text(Math.round(result.rawWpm));
|
||||
$("#result .stats .raw .bottom").attr("aria-label", result.rawWpm);
|
||||
} else {
|
||||
$("#result .stats .wpm .top .text").text("cpm");
|
||||
$("#result .stats .wpm .bottom").attr(
|
||||
"aria-label",
|
||||
Misc.roundTo2(result.wpm * 5) + ` (${Misc.roundTo2(result.wpm)} wpm)`
|
||||
);
|
||||
if (inf) {
|
||||
$("#result .stats .wpm .bottom").text("Infinite");
|
||||
} else {
|
||||
$("#result .stats .wpm .bottom").text(Math.round(result.wpm * 5));
|
||||
}
|
||||
$("#result .stats .raw .bottom").text(Math.round(result.rawWpm * 5));
|
||||
$("#result .stats .raw .bottom").attr("aria-label", result.rawWpm * 5);
|
||||
let wpmHover = typingSpeedUnit.convertWithUnitSuffix(result.wpm);
|
||||
let rawWpmHover = typingSpeedUnit.convertWithUnitSuffix(result.rawWpm);
|
||||
if (Config.typingSpeedUnit != "wpm") {
|
||||
wpmHover += " (" + result.wpm.toFixed(2) + " wpm)";
|
||||
rawWpmHover += " (" + result.rawWpm.toFixed(2) + " wpm)";
|
||||
}
|
||||
|
||||
$("#result .stats .wpm .top .text").text(Config.typingSpeedUnit);
|
||||
$("#result .stats .wpm .bottom").attr("aria-label", wpmHover);
|
||||
if (inf) {
|
||||
$("#result .stats .wpm .bottom").text("Infinite");
|
||||
} else {
|
||||
$("#result .stats .wpm .bottom").text(
|
||||
Math.round(typingSpeedUnit.convert(result.wpm))
|
||||
);
|
||||
}
|
||||
$("#result .stats .raw .bottom").text(
|
||||
Math.round(typingSpeedUnit.convert(result.rawWpm))
|
||||
);
|
||||
$("#result .stats .raw .bottom").attr("aria-label", rawWpmHover);
|
||||
|
||||
$("#result .stats .acc .bottom").text(Math.floor(result.acc) + "%");
|
||||
$("#result .stats .acc .bottom").attr(
|
||||
"aria-label",
|
||||
|
@ -424,10 +411,11 @@ export async function updateCrown(): Promise<void> {
|
|||
Config.lazyMode,
|
||||
Config.funbox
|
||||
);
|
||||
const typingSpeedUnit = getTypingSpeedUnit(Config.typingSpeedUnit);
|
||||
pbDiff = Math.abs(result.wpm - lpb);
|
||||
$("#result .stats .wpm .crown").attr(
|
||||
"aria-label",
|
||||
"+" + Misc.roundTo2(pbDiff)
|
||||
"+" + Misc.roundTo2(typingSpeedUnit.convert(pbDiff))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -482,6 +470,7 @@ async function updateTags(dontSave: boolean): Promise<void> {
|
|||
$("#result .stats .tags .bottom").append(`
|
||||
<div tagid="${tag._id}" aria-label="PB: ${tpb}" data-balloon-pos="up">${tag.display}<i class="fas fa-crown hidden"></i></div>
|
||||
`);
|
||||
const typingSpeedUnit = getTypingSpeedUnit(Config.typingSpeedUnit);
|
||||
if (
|
||||
Config.mode !== "quote" &&
|
||||
!dontSave &&
|
||||
|
@ -517,7 +506,7 @@ async function updateTags(dontSave: boolean): Promise<void> {
|
|||
type: "line",
|
||||
id: "tpb",
|
||||
scaleID: "wpm",
|
||||
value: Config.alwaysShowCPM ? tpb * 5 : tpb,
|
||||
value: typingSpeedUnit.convert(tpb),
|
||||
borderColor: themecolors["sub"],
|
||||
borderWidth: 1,
|
||||
borderDash: [2, 2],
|
||||
|
@ -537,7 +526,7 @@ async function updateTags(dontSave: boolean): Promise<void> {
|
|||
xAdjust: labelAdjust,
|
||||
enabled: true,
|
||||
content: `${tag.display} PB: ${Misc.roundTo2(
|
||||
Config.alwaysShowCPM ? tpb * 5 : tpb
|
||||
typingSpeedUnit.convert(tpb)
|
||||
).toFixed(2)}`,
|
||||
},
|
||||
});
|
||||
|
@ -888,3 +877,22 @@ $(".pageTest #favoriteQuoteButton").on("click", async () => {
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
ConfigEvent.subscribe(async (eventKey) => {
|
||||
if (eventKey === "typingSpeedUnit" && TestUI.resultVisible) {
|
||||
resultScaleOptions = (
|
||||
ChartController.result.options as ScaleChartOptions<"line" | "scatter">
|
||||
).scales;
|
||||
resultAnnotation = [];
|
||||
|
||||
updateWpmAndAcc();
|
||||
await updateGraph();
|
||||
await updateGraphPBLine();
|
||||
|
||||
((ChartController.result.options as PluginChartOptions<"line" | "scatter">)
|
||||
.plugins.annotation.annotations as AnnotationOptions<"line">[]) =
|
||||
resultAnnotation;
|
||||
ChartController.result.updateColors();
|
||||
ChartController.result.resize();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -9,6 +9,7 @@ import * as Caret from "./caret";
|
|||
import * as OutOfFocus from "./out-of-focus";
|
||||
import * as Replay from "./replay";
|
||||
import * as Misc from "../utils/misc";
|
||||
import { get as getTypingSpeedUnit } from "../utils/typing-speed-units";
|
||||
import * as SlowTimer from "../states/slow-timer";
|
||||
import * as CompositionState from "../states/composition";
|
||||
import * as ConfigEvent from "../observables/config-event";
|
||||
|
@ -1244,9 +1245,9 @@ $(".pageTest #resultWordsHistory").on("mouseenter", ".words .word", (e) => {
|
|||
.replace(/>/g, ">")}
|
||||
</div>
|
||||
<div class="speed">
|
||||
${Math.round(Config.alwaysShowCPM ? burst * 5 : burst)}${
|
||||
Config.alwaysShowCPM ? "cpm" : "wpm"
|
||||
}
|
||||
${Math.round(
|
||||
getTypingSpeedUnit(Config.typingSpeedUnit).convert(burst)
|
||||
)}${Config.typingSpeedUnit}
|
||||
</div>
|
||||
</div>`
|
||||
);
|
||||
|
|
13
frontend/src/ts/types/types.d.ts
vendored
13
frontend/src/ts/types/types.d.ts
vendored
|
@ -89,7 +89,7 @@ declare namespace MonkeyTypes {
|
|||
|
||||
type KeymapShowTopRow = "always" | "layout" | "never";
|
||||
|
||||
type ShowAverage = "off" | "wpm" | "acc" | "both";
|
||||
type ShowAverage = "off" | "speed" | "acc" | "both";
|
||||
|
||||
type SmoothCaretMode = "off" | "slow" | "medium" | "fast";
|
||||
|
||||
|
@ -469,7 +469,7 @@ declare namespace MonkeyTypes {
|
|||
minWpm: MinimumWordsPerMinute;
|
||||
minWpmCustomSpeed: number;
|
||||
highlightMode: HighlightMode;
|
||||
alwaysShowCPM: boolean;
|
||||
typingSpeedUnit: TypingSpeedUnit;
|
||||
ads: Ads;
|
||||
hideExtraLetters: boolean;
|
||||
strictSpace: boolean;
|
||||
|
@ -881,4 +881,13 @@ declare namespace MonkeyTypes {
|
|||
}
|
||||
|
||||
type AllRewards = XpReward | BadgeReward;
|
||||
|
||||
type TypingSpeedUnit = "wpm" | "cpm" | "wps" | "cps" | "wph";
|
||||
interface TypingSpeedUnitSettings {
|
||||
convert: (number) => number;
|
||||
convertWithUnitSuffix: (number) => string;
|
||||
fullUnitString: string;
|
||||
histogramDataBucketSize: number;
|
||||
historyStepSize: number;
|
||||
}
|
||||
}
|
||||
|
|
66
frontend/src/ts/utils/typing-speed-units.ts
Normal file
66
frontend/src/ts/utils/typing-speed-units.ts
Normal file
|
@ -0,0 +1,66 @@
|
|||
import { roundTo2 } from "../utils/misc";
|
||||
const typingSpeedUnits: Record<
|
||||
MonkeyTypes.TypingSpeedUnit,
|
||||
MonkeyTypes.TypingSpeedUnitSettings
|
||||
> = {
|
||||
wpm: {
|
||||
convert: (wpm: number) => wpm,
|
||||
convertWithUnitSuffix: (wpm: number) => {
|
||||
return convertTypingSpeedWithUnitSuffix("wpm", wpm);
|
||||
},
|
||||
|
||||
fullUnitString: "Words per Minute",
|
||||
histogramDataBucketSize: 10,
|
||||
historyStepSize: 10,
|
||||
},
|
||||
cpm: {
|
||||
convert: (wpm: number) => wpm * 5,
|
||||
convertWithUnitSuffix: (wpm: number) => {
|
||||
return convertTypingSpeedWithUnitSuffix("cpm", wpm);
|
||||
},
|
||||
fullUnitString: "Characters per Minute",
|
||||
histogramDataBucketSize: 50,
|
||||
historyStepSize: 100,
|
||||
},
|
||||
wps: {
|
||||
convert: (wpm: number) => wpm / 60,
|
||||
convertWithUnitSuffix: (wpm: number) => {
|
||||
return convertTypingSpeedWithUnitSuffix("wps", wpm);
|
||||
},
|
||||
fullUnitString: "Words per Second",
|
||||
histogramDataBucketSize: 0.5,
|
||||
historyStepSize: 2,
|
||||
},
|
||||
cps: {
|
||||
convert: (wpm: number) => (wpm * 5) / 60,
|
||||
convertWithUnitSuffix: (wpm: number) => {
|
||||
return convertTypingSpeedWithUnitSuffix("cps", wpm);
|
||||
},
|
||||
fullUnitString: "Characters per Second",
|
||||
histogramDataBucketSize: 5,
|
||||
historyStepSize: 5,
|
||||
},
|
||||
wph: {
|
||||
convert: (wpm: number) => wpm * 60,
|
||||
convertWithUnitSuffix: (wpm: number) => {
|
||||
return convertTypingSpeedWithUnitSuffix("wph", wpm);
|
||||
},
|
||||
|
||||
fullUnitString: "Words per Hour",
|
||||
histogramDataBucketSize: 60,
|
||||
historyStepSize: 1000,
|
||||
},
|
||||
};
|
||||
|
||||
export function get(
|
||||
unit: MonkeyTypes.TypingSpeedUnit
|
||||
): MonkeyTypes.TypingSpeedUnitSettings {
|
||||
return typingSpeedUnits[unit];
|
||||
}
|
||||
|
||||
function convertTypingSpeedWithUnitSuffix(
|
||||
unit: MonkeyTypes.TypingSpeedUnit,
|
||||
wpm: number
|
||||
): string {
|
||||
return roundTo2(get(unit).convert(wpm)).toFixed(2) + " " + unit;
|
||||
}
|
|
@ -1584,31 +1584,44 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section alwaysShowCPM">
|
||||
<div class="section typingSpeedUnit">
|
||||
<div class="groupTitle">
|
||||
<i class="fas fa-tachometer-alt"></i>
|
||||
<span>always show cpm</span>
|
||||
</div>
|
||||
<div class="text">
|
||||
Always shows characters per minute calculation instead of the default
|
||||
words per minute calculation.
|
||||
<span>typing speed unit</span>
|
||||
</div>
|
||||
<div class="text">Display typing speed in the specified unit.</div>
|
||||
<div class="buttons">
|
||||
<div
|
||||
class="button"
|
||||
alwaysShowCPM="false"
|
||||
typingSpeedUnit="wpm"
|
||||
tabindex="0"
|
||||
onclick="this.blur();"
|
||||
>
|
||||
off
|
||||
wpm
|
||||
</div>
|
||||
<div
|
||||
class="button"
|
||||
alwaysShowCPM="true"
|
||||
typingSpeedUnit="cpm"
|
||||
tabindex="0"
|
||||
onclick="this.blur();"
|
||||
>
|
||||
on
|
||||
cpm
|
||||
</div>
|
||||
<div
|
||||
class="button"
|
||||
typingSpeedUnit="wps"
|
||||
tabindex="0"
|
||||
onclick="this.blur();"
|
||||
>
|
||||
wps
|
||||
</div>
|
||||
<div
|
||||
class="button"
|
||||
typingSpeedUnit="cps"
|
||||
tabindex="0"
|
||||
onclick="this.blur();"
|
||||
>
|
||||
cps
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -2584,11 +2597,11 @@
|
|||
</div>
|
||||
<div
|
||||
class="button"
|
||||
showAverage="wpm"
|
||||
showAverage="speed"
|
||||
tabindex="0"
|
||||
onclick="this.blur();"
|
||||
>
|
||||
wpm
|
||||
speed
|
||||
</div>
|
||||
<div
|
||||
class="button"
|
||||
|
|
Loading…
Add table
Reference in a new issue