Merge branch 'master' into newads

This commit is contained in:
Miodec 2023-03-16 14:36:53 +01:00
commit e97db0cb5a
24 changed files with 764 additions and 181 deletions

View file

@ -388,7 +388,10 @@ export async function addResult(
const { funbox, bailedOut } = result;
const validResultCriteria =
(funbox === "none" || funbox === "plus_one" || funbox === "plus_two") &&
(funbox === "none" ||
funbox === "plus_one" ||
funbox === "plus_two" ||
funbox === "plus_three") &&
!bailedOut &&
user.banned !== true &&
user.lbOptOut !== true &&

View file

@ -87,8 +87,9 @@ const CONFIG_SCHEMA = joi.object({
paceCaretCustomSpeed: joi.number().min(0),
repeatedPace: joi.boolean(),
pageWidth: joi.string().valid("100", "125", "150", "200", "max"),
chartAccuracy: joi.boolean(),
chartStyle: joi.string().valid("line", "scatter"),
accountChart: joi.array().items(joi.string()),
chartAccuracy: joi.boolean().optional(), //remove after a bit
chartStyle: joi.string().valid("line", "scatter").optional(), //remove after a bit
minWpm: joi.string().valid("off", "custom"),
minWpmCustomSpeed: joi.number().min(0),
highlightMode: joi.string().valid("off", "letter", "word"),

View file

@ -75,6 +75,10 @@ const Funboxes: Record<string, MonkeyTypes.FunboxMetadata> = {
canGetPb: true,
difficultyLevel: 0,
},
plus_three: {
canGetPb: true,
difficultyLevel: 0,
},
read_ahead_easy: {
canGetPb: true,
difficultyLevel: 1,

View file

@ -231,28 +231,16 @@
color: var(--sub-color);
margin-top: 1rem;
display: grid;
grid-template-columns: auto 300px;
grid-template-columns: auto 400px;
align-items: center;
.text {
height: min-content;
}
.buttons {
font-size: 0.75rem;
display: grid;
gap: 0.5rem;
.smoothing {
display: grid;
gap: 0.25rem;
grid-template-columns: 1fr auto;
grid-template-rows: auto auto;
justify-items: start;
color: var(--text-color);
.title {
color: var(--text-color);
}
input[type="range"] {
grid-column: 1/3;
}
}
grid-template-columns: 1fr 1fr 1fr;
}
}
.chart {

View file

@ -448,6 +448,13 @@
}
}
.pageAccount {
.group.chart .below {
grid-template-columns: 1fr;
gap: 0.5rem;
}
}
.pageSettings {
.section.themes .tabContent.customTheme {
}
@ -496,10 +503,6 @@
display: none;
}
}
.group.chart .below {
grid-template-columns: 1fr;
gap: 0.5rem;
}
.group.topFilters .buttonsAndTitle .buttons {
display: grid;
justify-content: unset;

View file

@ -299,32 +299,64 @@ export function setBlindMode(blind: boolean, nosave?: boolean): boolean {
return true;
}
export function setChartAccuracy(
chartAccuracy: boolean,
export function setAccountChart(
array: MonkeyTypes.AccountChart,
nosave?: boolean
): boolean {
if (!isConfigValueValid("chart accuracy", chartAccuracy, ["boolean"])) {
if (
!isConfigValueValid("account chart", array, [["on", "off"], "stringArray"])
) {
return false;
}
config.chartAccuracy = chartAccuracy;
saveToLocalStorage("chartAccuracy", nosave);
ConfigEvent.dispatch("chartAccuracy", config.chartAccuracy);
config.accountChart = array;
saveToLocalStorage("accountChart", nosave);
ConfigEvent.dispatch("accountChart", config.accountChart);
return true;
}
export function setChartStyle(
chartStyle: MonkeyTypes.ChartStyle,
export function setAccountChartAccuracy(
value: boolean,
nosave?: boolean
): boolean {
if (!isConfigValueValid("chart style", chartStyle, [["line", "scatter"]])) {
if (!isConfigValueValid("account chart accuracy", value, ["boolean"])) {
return false;
}
config.chartStyle = chartStyle;
saveToLocalStorage("chartStyle", nosave);
ConfigEvent.dispatch("chartStyle", config.chartStyle);
config.accountChart[0] = value ? "on" : "off";
saveToLocalStorage("accountChart", nosave);
ConfigEvent.dispatch("accountChart", config.accountChart);
return true;
}
export function setAccountChartAvg10(
value: boolean,
nosave?: boolean
): boolean {
if (!isConfigValueValid("account chart avg 10", value, ["boolean"])) {
return false;
}
config.accountChart[1] = value ? "on" : "off";
saveToLocalStorage("accountChart", nosave);
ConfigEvent.dispatch("accountChart", config.accountChart);
return true;
}
export function setAccountChartAvg100(
value: boolean,
nosave?: boolean
): boolean {
if (!isConfigValueValid("account chart avg 100", value, ["boolean"])) {
return false;
}
config.accountChart[2] = value ? "on" : "off";
saveToLocalStorage("accountChart", nosave);
ConfigEvent.dispatch("accountChart", config.accountChart);
return true;
}
@ -1824,8 +1856,7 @@ export function apply(
setPaceCaretCustomSpeed(configObj.paceCaretCustomSpeed, true);
setRepeatedPace(configObj.repeatedPace, true);
setPageWidth(configObj.pageWidth, true);
setChartAccuracy(configObj.chartAccuracy, true);
setChartStyle(configObj.chartStyle, true);
setAccountChart(configObj.accountChart, true);
setMinBurst(configObj.minBurst, true);
setMinBurstCustomSpeed(configObj.minBurstCustomSpeed, true);
setMinWpm(configObj.minWpm, true);

View file

@ -69,8 +69,7 @@ export default <MonkeyTypes.Config>{
paceCaretCustomSpeed: 100,
repeatedPace: true,
pageWidth: "125",
chartAccuracy: true,
chartStyle: "line",
accountChart: ["on", "on", "on"],
minWpm: "off",
minWpmCustomSpeed: 100,
highlightMode: "letter",

View file

@ -243,38 +243,76 @@ export let accountHistoryActiveIndex: number;
export const accountHistory: ChartWithUpdateColors<
"line",
MonkeyTypes.HistoryChartData[] | MonkeyTypes.AccChartData[],
| MonkeyTypes.HistoryChartData[]
| MonkeyTypes.AccChartData[]
| MonkeyTypes.OtherChartData[],
string
> = new ChartWithUpdateColors($(".pageAccount #accountHistoryChart"), {
type: "line",
data: {
labels: [],
datasets: [
{
yAxisID: "wpm",
label: "wpm",
fill: false,
data: [],
borderColor: "#f44336",
borderWidth: 2,
trendlineLinear: {
style: "rgba(255,105,180, .8)",
lineStyle: "dotted",
width: 4,
},
fill: false,
borderWidth: 0,
order: 3,
},
{
yAxisID: "pb",
data: [],
fill: false,
stepped: true,
pointRadius: 0,
pointHoverRadius: 0,
order: 4,
},
{
yAxisID: "acc",
label: "acc",
fill: false,
data: [],
borderColor: "#cccccc",
borderWidth: 2,
pointStyle: "triangle",
borderWidth: 0,
pointRadius: 3.5,
order: 3,
},
{
yAxisID: "wpmAvgTen",
data: [],
fill: false,
pointRadius: 0,
pointHoverRadius: 0,
order: 2,
},
{
yAxisID: "accAvgTen",
data: [],
fill: false,
pointRadius: 0,
pointHoverRadius: 0,
order: 2,
},
{
yAxisID: "wpmAvgHundred",
data: [],
fill: false,
pointRadius: 0,
pointHoverRadius: 0,
order: 1,
},
{
yAxisID: "accAvgHundred",
label: "accAvgHundred",
data: [],
fill: false,
pointRadius: 0,
pointHoverRadius: 0,
order: 1,
},
],
},
options: {
responsive: true,
// responsive: true,
maintainAspectRatio: false,
hover: {
mode: "nearest",
@ -283,13 +321,18 @@ export const accountHistory: ChartWithUpdateColors<
scales: {
x: {
axis: "x",
type: "timeseries",
bounds: "ticks",
type: "linear",
min: 0,
ticks: {
stepSize: 10,
},
display: false,
offset: true,
title: {
display: true,
text: "Test Number",
},
grid: {
display: false,
text: "Date",
},
},
wpm: {
@ -306,21 +349,86 @@ export const accountHistory: ChartWithUpdateColors<
},
position: "right",
},
pb: {
axis: "y",
beginAtZero: true,
min: 0,
ticks: {
stepSize: 10,
},
display: false,
},
acc: {
axis: "y",
beginAtZero: true,
min: 0,
max: 100,
reverse: true,
ticks: {
stepSize: 10,
},
display: true,
position: "left",
title: {
display: true,
text: "Error rate (100 - accuracy)",
text: "Accuracy",
},
grid: {
display: false,
},
position: "left",
},
wpmAvgTen: {
axis: "y",
beginAtZero: true,
min: 0,
ticks: {
stepSize: 10,
},
display: false,
grid: {
display: false,
},
},
accAvgTen: {
axis: "y",
beginAtZero: true,
min: 0,
reverse: true,
ticks: {
stepSize: 10,
},
display: false,
grid: {
display: false,
},
},
wpmAvgHundred: {
axis: "y",
beginAtZero: true,
min: 0,
ticks: {
stepSize: 10,
},
display: false,
grid: {
display: false,
},
},
accAvgHundred: {
axis: "y",
beginAtZero: true,
min: 0,
reverse: true,
ticks: {
stepSize: 10,
},
display: false,
grid: {
display: false,
},
},
},
plugins: {
annotation: {
annotations: [],
@ -329,15 +437,26 @@ export const accountHistory: ChartWithUpdateColors<
animation: { duration: 250 },
// Disable the on-canvas tooltip
enabled: true,
intersect: false,
external: function (ctx): void {
if (!ctx) return;
ctx.tooltip.options.displayColors = false;
},
filter: function (tooltipItem): boolean {
return (
tooltipItem.datasetIndex !== 1 &&
tooltipItem.datasetIndex !== 3 &&
tooltipItem.datasetIndex !== 4 &&
tooltipItem.datasetIndex !== 5 &&
tooltipItem.datasetIndex !== 6
);
},
callbacks: {
title: function (): string {
return "";
},
beforeLabel: function (tooltipItem): string {
if (tooltipItem.datasetIndex !== 0) {
const resultData = tooltipItem.dataset.data[
@ -828,21 +947,55 @@ export const miniResult: ChartWithUpdateColors<
});
function updateAccuracy(): void {
accountHistory.data.datasets[1].hidden = !Config.chartAccuracy;
const accOn = Config.accountChart[0] === "on";
const avg10On = Config.accountChart[1] === "on";
const avg100On = Config.accountChart[2] === "on";
if (avg10On && avg100On) {
accountHistory.data.datasets[2].hidden = !accOn;
accountHistory.data.datasets[4].hidden = !accOn;
accountHistory.data.datasets[6].hidden = !accOn;
} else if (avg10On) {
accountHistory.data.datasets[2].hidden = !accOn;
accountHistory.data.datasets[4].hidden = !accOn;
} else if (Config.accountChart[2] === "on") {
accountHistory.data.datasets[2].hidden = !accOn;
accountHistory.data.datasets[6].hidden = !accOn;
} else {
accountHistory.data.datasets[2].hidden = !accOn;
}
(accountHistory.options as ScaleChartOptions<"line">).scales["acc"].display =
Config.chartAccuracy;
accOn;
accountHistory.update();
}
function updateStyle(): void {
if (Config.chartStyle == "scatter") {
accountHistory.data.datasets[0].showLine = false;
accountHistory.data.datasets[1].showLine = false;
function updateAverage10(): void {
const accOn = Config.accountChart[0] === "on";
const avg10On = Config.accountChart[1] === "on";
if (accOn) {
accountHistory.data.datasets[3].hidden = !avg10On;
accountHistory.data.datasets[4].hidden = !avg10On;
} else {
accountHistory.data.datasets[0].showLine = true;
accountHistory.data.datasets[1].showLine = true;
accountHistory.data.datasets[3].hidden = !avg10On;
}
accountHistory.updateColors();
accountHistory.update();
}
function updateAverage100(): void {
const accOn = Config.accountChart[0] === "on";
const avg100On = Config.accountChart[2] === "on";
if (accOn) {
accountHistory.data.datasets[5].hidden = !avg100On;
accountHistory.data.datasets[6].hidden = !avg100On;
} else {
accountHistory.data.datasets[5].hidden = !avg100On;
}
accountHistory.updateColors();
accountHistory.update();
}
export async function updateColors<
@ -866,6 +1019,7 @@ export async function updateColors<
const color = isPb ? textcolor : maincolor;
return color;
};
if (chart.data.datasets[1]) {
chart.data.datasets[1].borderColor = subcolor;
}
@ -910,6 +1064,60 @@ export async function updateColors<
)[1].pointBackgroundColor = subcolor;
}
}
if (chart.data.datasets.length === 2) {
chart.data.datasets[1].borderColor = (): string => {
const color = subcolor;
return color;
};
}
if (chart.data.datasets.length === 7) {
chart.data.datasets[2].borderColor = (): string => {
const color = subcolor;
return color;
};
const avg10On = Config.accountChart[1] === "on";
const avg100On = Config.accountChart[2] === "on";
const text02 = Misc.blendTwoHexColors(bgcolor, textcolor, 0.2);
const main02 = Misc.blendTwoHexColors(bgcolor, maincolor, 0.2);
const main04 = Misc.blendTwoHexColors(bgcolor, maincolor, 0.4);
const sub02 = Misc.blendTwoHexColors(bgcolor, subcolor, 0.2);
const sub04 = Misc.blendTwoHexColors(bgcolor, subcolor, 0.4);
const [
wpmDataset,
pbDataset,
accDataset,
ao10wpmDataset,
ao10accDataset,
ao100wpmDataset,
ao100accDataset,
] = chart.data.datasets as ChartDataset<"line", TData>[];
if (avg10On && avg100On) {
wpmDataset.pointBackgroundColor = main02;
pbDataset.borderColor = text02;
accDataset.pointBackgroundColor = sub02;
ao10wpmDataset.borderColor = main04;
ao10accDataset.borderColor = sub04;
ao100wpmDataset.borderColor = maincolor;
ao100accDataset.borderColor = subcolor;
} else if ((avg10On && !avg100On) || (!avg10On && avg100On)) {
pbDataset.borderColor = text02;
wpmDataset.pointBackgroundColor = main04;
accDataset.pointBackgroundColor = sub04;
ao10wpmDataset.borderColor = maincolor;
ao100wpmDataset.borderColor = maincolor;
ao10accDataset.borderColor = subcolor;
ao100accDataset.borderColor = subcolor;
} else {
pbDataset.borderColor = text02;
wpmDataset.pointBackgroundColor = maincolor;
accDataset.pointBackgroundColor = subcolor;
}
}
const chartScaleOptions = chart.options as ScaleChartOptions<TType>;
Object.keys(chartScaleOptions.scales).forEach((scaleID) => {
@ -923,10 +1131,6 @@ export async function updateColors<
chart.data.datasets[0]
.trendlineLinear as TrendlineLinearPlugin.TrendlineLinearOptions
).style = subcolor;
(
chart.data.datasets[1]
.trendlineLinear as TrendlineLinearPlugin.TrendlineLinearOptions
).style = subcolor;
} catch {}
(
@ -958,7 +1162,10 @@ export function updateAllChartColors(): void {
}
ConfigEvent.subscribe((eventKey, eventValue) => {
if (eventKey === "chartAccuracy") updateAccuracy();
if (eventKey === "chartStyle") updateStyle();
if (eventKey === "accountChart") {
updateAccuracy();
updateAverage10();
updateAverage100();
}
if (eventKey === "fontFamily") setDefaultFontFamily(eventValue as string);
});

View file

@ -196,53 +196,17 @@ export function reset(): void {
ChartController.accountActivity.data.datasets[1].data = [];
ChartController.accountHistory.data.datasets[0].data = [];
ChartController.accountHistory.data.datasets[1].data = [];
ChartController.accountHistory.data.datasets[2].data = [];
ChartController.accountHistory.data.datasets[3].data = [];
ChartController.accountHistory.data.datasets[4].data = [];
ChartController.accountHistory.data.datasets[5].data = [];
ChartController.accountHistory.data.datasets[6].data = [];
}
let totalSecondsFiltered = 0;
let chartData: MonkeyTypes.HistoryChartData[] = [];
let accChartData: MonkeyTypes.AccChartData[] = [];
export function smoothHistory(factor: number): void {
const smoothedWpmData = Misc.smooth(
chartData.map((a) => a.y),
factor
);
const smoothedAccData = Misc.smooth(
accChartData.map((a) => a.y),
factor
);
const chartData2 = chartData.map((a, i) => {
const ret = Object.assign({}, a);
ret.y = smoothedWpmData[i];
return ret;
});
const accChartData2 = accChartData.map((a, i) => {
const ret = Object.assign({}, a);
ret.y = smoothedAccData[i];
return ret;
});
ChartController.accountHistory.data.datasets[0].data = chartData2;
ChartController.accountHistory.data.datasets[1].data = accChartData2;
if (chartData2.length || accChartData2.length) {
ChartController.accountHistory.options.animation = false;
ChartController.accountHistory.update();
delete ChartController.accountHistory.options.animation;
}
}
async function applyHistorySmoothing(): Promise<void> {
const smoothing = $(
".pageAccount .content .below .smoothing input"
).val() as string;
$(".pageAccount .content .below .smoothing .value").text(smoothing);
smoothHistory(parseInt(smoothing));
await Misc.sleep(0);
}
function fillContent(): void {
LoadingPage.updateText("Displaying stats...");
LoadingPage.updateBar(100);
@ -310,6 +274,9 @@ function fillContent(): void {
filteredResults = [];
$(".pageAccount .history table tbody").empty();
let testNum = DB.getSnapshot()?.results?.length || 0;
DB.getSnapshot()?.results?.forEach(
(result: MonkeyTypes.Result<MonkeyTypes.Mode>) => {
// totalSeconds += tt;
@ -634,7 +601,7 @@ function fillContent(): void {
}
chartData.push({
x: result.timestamp,
x: testNum,
y: Config.alwaysShowCPM ? Misc.roundTo2(result.wpm * 5) : result.wpm,
wpm: Config.alwaysShowCPM ? Misc.roundTo2(result.wpm * 5) : result.wpm,
acc: result.acc,
@ -653,11 +620,13 @@ function fillContent(): void {
wpmChartData.push(result.wpm);
accChartData.push({
x: result.timestamp,
y: 100 - result.acc,
x: testNum,
y: result.acc,
errorRate: 100 - result.acc,
});
testNum--;
if (result.wpm > topWpm) {
const puncsctring = result.punctuation ? ",<br>with punctuation" : "";
const numbsctring = result.numbers
@ -759,8 +728,76 @@ function fillContent(): void {
accountHistoryScaleOptions["wpm"].title.text = "Words per Minute";
}
ChartController.accountHistory.data.datasets[0].data = chartData;
ChartController.accountHistory.data.datasets[1].data = accChartData;
if (chartData.length > 0) {
chartData.reverse();
accChartData.reverse();
// get pb points
let currentPb = 0;
const pb: { x: number; y: number }[] = [];
chartData.forEach((a) => {
if (a.y > currentPb) {
currentPb = a.y;
pb.push(a);
}
});
// add last point to pb
const xMax = chartData.length;
pb.push({
x: xMax,
y: pb[pb.length - 1].y,
});
const avgTen = [];
const avgTenAcc = [];
const avgHundred = [];
const avgHundredAcc = [];
for (let i = 0; i < chartData.length; i++) {
// calculate 10 subset averages
const startIdxTen = i < 10 ? 0 : i - 9;
const subsetTen = chartData.slice(startIdxTen, i + 1);
const accSubsetTen = accChartData.slice(startIdxTen, i + 1);
const avgTenValue =
subsetTen.reduce((acc, { y }) => acc + y, 0) / subsetTen.length;
const accAvgTenValue =
accSubsetTen.reduce((acc, { y }) => acc + y, 0) / subsetTen.length;
// add values to arrays
avgTen.push({ x: i + 1, y: avgTenValue });
avgTenAcc.push({ x: i + 1, y: accAvgTenValue });
// calculate 100 subset averages
const startIdxHundred = i < 100 ? 0 : i - 99;
const subsetHundred = chartData.slice(startIdxHundred, i + 1);
const accSubsetHundred = accChartData.slice(startIdxHundred, i + 1);
const avgHundredValue =
subsetHundred.reduce((acc, { y }) => acc + y, 0) / subsetHundred.length;
const accAvgHundredValue =
accSubsetHundred.reduce((acc, { y }) => acc + y, 0) /
subsetHundred.length;
// add values to arrays
avgHundred.push({ x: i + 1, y: avgHundredValue });
avgHundredAcc.push({ x: i + 1, y: accAvgHundredValue });
}
ChartController.accountHistory.data.datasets[0].data = chartData;
ChartController.accountHistory.data.datasets[1].data = pb;
ChartController.accountHistory.data.datasets[2].data = accChartData;
ChartController.accountHistory.data.datasets[3].data = avgTen;
ChartController.accountHistory.data.datasets[4].data = avgTenAcc;
ChartController.accountHistory.data.datasets[5].data = avgHundred;
ChartController.accountHistory.data.datasets[6].data = avgHundredAcc;
accountHistoryScaleOptions["x"].max = xMax + 1;
chartData.reverse();
accChartData.reverse();
}
const wpms = chartData.map((r) => r.y);
const minWpmChartVal = Math.min(...wpms);
@ -769,6 +806,12 @@ function fillContent(): void {
// 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));
if (!Config.startGraphsAtZero) {
accountHistoryScaleOptions["wpm"].min = Math.floor(minWpmChartVal);
@ -800,8 +843,6 @@ function fillContent(): void {
Misc.secondsToString(Math.round(totalSecondsFiltered), true, true)
);
const wpmCpm = Config.alwaysShowCPM ? "cpm" : "wpm";
let highestSpeed: number | string = topWpm;
if (Config.alwaysShowCPM) {
highestSpeed = topWpm * 5;
@ -812,6 +853,8 @@ function fillContent(): void {
highestSpeed = Math.round(highestSpeed);
}
const wpmCpm = Config.alwaysShowCPM ? "cpm" : "wpm";
$(".pageAccount .highestWpm .title").text(`highest ${wpmCpm}`);
$(".pageAccount .highestWpm .val").text(highestSpeed);
@ -981,12 +1024,17 @@ function fillContent(): void {
Misc.roundTo2(
Config.alwaysShowCPM ? wpmChangePerHour * 5 : wpmChangePerHour
)
} ${Config.alwaysShowCPM ? "cpm" : "wpm"}.`
} ${Config.alwaysShowCPM ? "cpm" : "wpm"}`
);
$(".pageAccount .estimatedWordsTyped .val").text(totalEstimatedWords);
applyHistorySmoothing();
if (chartData.length || accChartData.length) {
ChartController.accountHistory.options.animation = false;
ChartController.accountHistory.update();
delete ChartController.accountHistory.options.animation;
}
ChartController.accountActivity.update();
ChartController.accountHistogram.update();
LoadingPage.updateBar(100, true);
@ -1117,15 +1165,15 @@ function sortAndRefreshHistory(
}
$(".pageAccount .toggleAccuracyOnChart").on("click", () => {
UpdateConfig.setChartAccuracy(!Config.chartAccuracy);
UpdateConfig.setAccountChartAccuracy(!(Config.accountChart[0] == "on"));
});
$(".pageAccount .toggleChartStyle").on("click", () => {
if (Config.chartStyle == "line") {
UpdateConfig.setChartStyle("scatter");
} else {
UpdateConfig.setChartStyle("line");
}
$(".pageAccount .toggleAverage10OnChart").on("click", () => {
UpdateConfig.setAccountChartAvg10(!(Config.accountChart[1] == "on"));
});
$(".pageAccount .toggleAverage100OnChart").on("click", () => {
UpdateConfig.setAccountChartAvg100(!(Config.accountChart[2] == "on"));
});
$(".pageAccount .loadMoreButton").on("click", () => {
@ -1232,10 +1280,6 @@ $(".pageAccount .group.presetFilterButtons").on(
}
);
$(".pageAccount .content .below .smoothing input").on("input", () => {
applyHistorySmoothing();
});
$(".pageAccount .content .group.aboveHistory .exportCSV").on("click", () => {
Misc.downloadResultsCSV(filteredResults);
});

View file

@ -118,6 +118,11 @@ const list: MonkeyTypes.FunboxMetadata[] = [
info: "Only two future words are visible.",
properties: ["changesWordsVisibility", "toPush:3", "noInfiniteDuration"],
},
{
name: "plus_three",
info: "Only three future words are visible.",
properties: ["changesWordsVisibility", "toPush:4", "noInfiniteDuration"],
},
{
name: "read_ahead_easy",
info: "Only the current word is invisible.",

View file

@ -901,14 +901,25 @@ export async function init(): Promise<void> {
?.properties?.find((fp) => fp.startsWith("toPush:"));
if (funboxToPush) {
wordsBound = +funboxToPush.split(":")[1];
if (
(Config.mode === "words" && Config.words < wordsBound) ||
(Config.mode === "custom" &&
!CustomText.isTimeRandom &&
CustomText.word < wordsBound)
) {
if (Config.mode === "words" && Config.words < wordsBound) {
wordsBound = Config.words;
}
if (
Config.mode === "custom" &&
!CustomText.isTimeRandom &&
CustomText.isWordRandom &&
CustomText.word < wordsBound
) {
wordsBound = CustomText.word;
}
if (
Config.mode === "custom" &&
!CustomText.isTimeRandom &&
!CustomText.isWordRandom &&
CustomText.text.length < wordsBound
) {
wordsBound = CustomText.text.length;
}
} else if (Config.showAllLines) {
if (Config.mode === "quote") {
wordsBound = 100;
@ -1798,7 +1809,7 @@ async function saveResult(
}
DB.saveLocalResult(completedEvent);
DB.updateLocalStats(
TestStats.restartCount + 1,
completedEvent.incompleteTests.length + 1,
completedEvent.testDuration +
completedEvent.incompleteTestSeconds -
completedEvent.afkDuration

View file

@ -54,6 +54,8 @@ ConfigEvent.subscribe((eventKey, eventValue, nosave) => {
updateWordsInputPosition(true);
}
if (eventKey === "theme") applyBurstHeatmap();
if (eventValue === undefined || typeof eventValue !== "boolean") return;
if (eventKey === "flipTestColors") flipColors(eventValue);
if (eventKey === "colorfulMode") colorful(eventValue);

View file

@ -119,7 +119,7 @@ declare namespace MonkeyTypes {
type PageWidth = "100" | "125" | "150" | "200" | "max";
type ChartStyle = "line" | "scatter";
type AccountChart = ("off" | "on")[];
type MinimumWordsPerMinute = "off" | "custom";
@ -177,6 +177,11 @@ declare namespace MonkeyTypes {
errorRate: number;
}
interface OtherChartData {
x: number;
y: number;
}
interface ActivityChartDataPoint {
x: number;
y: number;
@ -448,8 +453,7 @@ declare namespace MonkeyTypes {
paceCaretCustomSpeed: number;
repeatedPace: boolean;
pageWidth: PageWidth;
chartAccuracy: boolean;
chartStyle: ChartStyle;
accountChart: AccountChart;
minWpm: MinimumWordsPerMinute;
minWpmCustomSpeed: number;
highlightMode: HighlightMode;

View file

@ -288,6 +288,7 @@ function hexToRgb(hex: string):
} else {
return undefined;
}
return { r, g, b };
}

View file

@ -94,6 +94,11 @@
"info": "Only two future words are visible.",
"canGetPb": true
},
{
"name": "plus_three",
"info": "Only three future words are visible.",
"canGetPb": true
},
{
"name": "read_ahead_easy",
"info": "Only the current word is invisible.",

View file

@ -430,21 +430,21 @@
<div class="chart">
<canvas id="accountHistoryChart"></canvas>
</div>
<div class="below">
<div class="text"></div>
<div class="buttons">
<div class="smoothing">
<div class="title">smoothing</div>
<div class="value">0</div>
<input type="range" min="0" max="20" value="0" step="1" />
</div>
<div class="toggleAccuracyOnChart button">
<i class="fas fa-bullseye"></i>
Toggle Accuracy
Accuracy
</div>
<div class="toggleChartStyle button">
<div class="toggleAverage10OnChart button">
<i class="fas fa-chart-line"></i>
Toggle Chart Style
Avg of 10
</div>
<div class="toggleAverage100OnChart button">
<i class="fas fa-chart-line"></i>
Avg of 100
</div>
</div>
</div>

View file

@ -272,7 +272,7 @@
},
{
"name": "swedish",
"languages": ["swedish", "swedish_1k"]
"languages": ["swedish", "swedish_1k", "swedish_diacritics"]
},
{
"name": "serbian",

View file

@ -151,6 +151,7 @@
,"hausa_1k"
,"swedish"
,"swedish_1k"
,"swedish_diacritics"
,"serbian"
,"yoruba_1k"
,"swahili_1k"

View file

@ -185,6 +185,7 @@
["neighbored", "neighboured"],
["neighbor", "neighbour"],
["neighbors", "neighbours"],
["neighboring", "neighbouring"],
["magnetizer", "magnetiser"],
["appetizer", "appetiser"],
["hebraizing", "hebraising"],

View file

@ -2924,7 +2924,7 @@
"negotiation",
"neighbor",
"neighborhood",
"neighbouring",
"neighboring",
"neither",
"nerve",
"nervous",

View file

@ -0,0 +1,268 @@
{
"name": "swedish_diacritics",
"leftToRight": true,
"bcp47": "sv-SE",
"words": [
"blåbär",
"blågrå",
"blåräv",
"blåröd",
"blåträ",
"blåögd",
"bågsåg",
"bärvåg",
"bärår",
"böjträ",
"börsår",
"dådlös",
"därför",
"därhän",
"därpå",
"däråt",
"dödsjö",
"dödsår",
"frångå",
"fåfäng",
"fåmäld",
"fårlår",
"fåtölj",
"fänkål",
"fästmö",
"fäsör",
"förbön",
"föregå",
"förgå",
"förgås",
"förhör",
"förklä",
"förköp",
"förlåt",
"förmå",
"förmån",
"förnäm",
"förnär",
"förråa",
"förråd",
"förrän",
"förslå",
"försmå",
"förstå",
"försåt",
"försök",
"förvår",
"förväg",
"förår",
"förära",
"föräta",
"föröda",
"föröka",
"föröva",
"gråblå",
"grågås",
"gråsäl",
"gråögd",
"gåpåig",
"gåsört",
"gåtåg",
"gökärt",
"håglös",
"hålkäl",
"hålslå",
"hålsöm",
"hålväg",
"hålögd",
"hårfön",
"hårlös",
"hårnål",
"hårnät",
"håröm",
"härför",
"härpå",
"härtåg",
"härväg",
"häråt",
"häxbål",
"häxört",
"höfång",
"högblå",
"högröd",
"höhäck",
"höstså",
"hösäck",
"hövålm",
"jämväl",
"järnår",
"knähög",
"knärör",
"kåsör",
"kåsös",
"köksö",
"kölbåt",
"könlös",
"köpråd",
"köpslå",
"körspö",
"körväg",
"låglön",
"läläge",
"läroår",
"läsår",
"lättöl",
"löksås",
"lönlös",
"löshår",
"lössnö",
"lösöre",
"lövlös",
"lövsåg",
"löväng",
"måbär",
"måfå",
"mållös",
"månår",
"märsrå",
"nådeår",
"nåväl",
"närapå",
"närköp",
"nödlån",
"nödår",
"nöthår",
"påbrå",
"påbröd",
"påföra",
"pågå",
"påhäng",
"påkörd",
"pålägg",
"påläst",
"påsköl",
"påstå",
"påstöt",
"påtår",
"påtänd",
"påväxt",
"påökt",
"rådlös",
"rådslå",
"rågång",
"rårörd",
"råstål",
"råämne",
"rättså",
"rävröd",
"rödblå",
"rödkål",
"rödlök",
"rödräv",
"rödögd",
"röjsåg",
"rötägg",
"rövhål",
"sjöblå",
"sjönöd",
"sjönöt",
"sjörök",
"sjöväg",
"slösäd",
"småbåt",
"småkär",
"smålån",
"småsjö",
"småsår",
"småväg",
"småäta",
"snöbär",
"snöhög",
"snölös",
"snörök",
"sådär",
"såhär",
"sångmö",
"sånär",
"såpört",
"såväl",
"sädgås",
"säldöd",
"sälhål",
"sökväg",
"sömhål",
"sömlös",
"träbåt",
"träkåk",
"trälår",
"träpåk",
"tvåårs",
"tvärdö",
"tågrån",
"tåjärn",
"tålös",
"tårlös",
"tårögd",
"tätört",
"töföre",
"töjmån",
"tösnö",
"vrålåk",
"vågrät",
"vårdkö",
"vårlök",
"vårså",
"vårsäd",
"våtår",
"väglös",
"vägnät",
"vägrån",
"västpå",
"åbrädd",
"åbäka",
"åbäke",
"åbäkig",
"ådöma",
"åfåra",
"åhåga",
"åhöra",
"åkhöjd",
"åkpåse",
"ålgräs",
"ålägga",
"åmålit",
"ångbåt",
"ångrör",
"ångtåg",
"årgång",
"årslön",
"åskblå",
"åskrön",
"åskåda",
"åsätta",
"åtbörd",
"återfå",
"återgå",
"åtgå",
"åtgång",
"åtgärd",
"åtlöje",
"åtnöja",
"åtrå",
"äggört",
"älgört",
"ändlös",
"ändträ",
"ändå",
"ängshö",
"ärekär",
"ärelös",
"ärmhål",
"ärmlös",
"ävenså",
"öbåge",
"ödesår",
"ökänd",
"öltält",
"örtsås",
"östpå",
"övergå",
"övärld"
]
}

View file

@ -494,6 +494,17 @@
"row5": [" "]
}
},
"mongolian": {
"keymapShowTopRow": true,
"type": "ansi",
"keys": {
"row1": ["=+", "№1", "-2", "\"3", "₮4", ":5", ".6", "_7", ",8", "%9", "?0", "еЕ", "щЩ"],
"row2": ["фФ", "цЦ", "уУ", "жЖ", "эЭ", "нН", "гГ", "шШ", "үҮ", "зЗ", "кК", "ъЪ", "\\|"],
"row3": ["йЙ", "ыЫ", "бБ", "өӨ", "аА", "хХ", "рР", "оО", "лЛ", "дД", "пП"],
"row4": ["яЯ", "чЧ", "ёЁ", "сС", "мМ", "иИ", "тТ", "ьЬ", "вВ", "юЮ"],
"row5": [" "]
}
},
"JCUKEN": {
"keymapShowTopRow": true,
"type": "ansi",
@ -740,10 +751,10 @@
"keymapShowTopRow": true,
"type": "ansi",
"keys": {
"row1": ["-%", "ๅ+", "/๑", "_๒", "ภ๓", "ถ๔", "ุู", "ึ฿", "ค๕", "ต๖", "จ๗", "ข๘", "ช๙"],
"row1": ["_%", "ๅ+", "/๑", "-๒", "ภ๓", "ถ๔", "ุู", "ึ฿", "ค๕", "ต๖", "จ๗", "ข๘", "ช๙"],
"row2": ["ๆ๐", "ไ\"", "ำฎ", "พฑ", "ะธ", "ัํ", "ี๊", "รณ", "นฯ", "ยญ", "บฐ", "ล,", "ฃฅ"],
"row3": ["ฟฤ", "หฆ", "กฏ", "ดโ", "เฌ", "้็", "่๋", "าษ", "สศ", "วซ", "ง."],
"row4": ["ฃฅ", "ผ(", "ป)", "แฉ", "อฮ", "ิฺ", "ื์", "ท?", "มฒ", "ใฬ"],
"row4": ["ผ(", "ป)", "แฉ", "อฮ", "ิฺ", "ื์", "ท?", "มฒ", "ใฬ", "ฝฦ"],
"row5": [" "]
}
},

View file

@ -4783,12 +4783,6 @@
"id": 814,
"length": 355
},
{
"text": "There's no more harm in a kiss than shaving a monkey and pretending it's a woman.",
"source": "QI",
"id": 815,
"length": 81
},
{
"text": "The most precious treasures we have in life are the images we store in the memory banks of our brains. The sum of these stored experiences is responsible for our sense of personal identity and our sense of connectedness to those around us.",
"source": "Change Your Brain, Change Your Life",
@ -6745,12 +6739,6 @@
"id": 1148,
"length": 211
},
{
"text": "They say that right after God created man, he took a rib from him and made a chick. That's actually a bit of a creation myth - the truth is, after God saw that man was good, he created another man and saw that it was all good.",
"source": "Brocabulary: The New Man-i-festo of Dude Talk",
"id": 1149,
"length": 226
},
{
"text": "When the snow is on our mind, rows and rows and lines and lines. In the haze of your fixation, we all crave for you, we all crave. And you can kill the wildest thing, but when you murder it's a sin. In the midst of your conviction, we all crave for you, we all crave.",
"source": "Crave",
@ -11577,7 +11565,7 @@
},
{
"text": "The world ain't all sunshine and rainbows. It's a very mean and nasty place and I don't care how tough you are, it will beat you to your knees and keep you there permanently if you let it. You, me, or nobody is gonna hit as hard as life. But it ain't about how hard you hit. It's about how hard you can get hit and keep moving forward. That's how winning is done! Now if you know what you're worth then go out and get what you're worth. But you've gotta be willing to take the hits, and not pointing fingers saying you ain't where you wanna be because of him, or her, or anybody! Cowards do that and that ain't you! You're better than that!",
"source": "Rocky IV",
"source": "Rocky Balboa (2006)",
"id": 1984,
"length": 640
},
@ -15991,12 +15979,6 @@
"id": 2753,
"length": 174
},
{
"text": "I know you're coming in the night like a thief, but I've had some time alone to hone my lying technique. I know you think that I'm someone you can trust, but I'm scared I'll get scared and I swear I'll try to nail you back up.",
"source": "Jesus Christ",
"id": 2754,
"length": 226
},
{
"text": "Keep drinking coffee, stare me down across the table while I look outside. So many things I'd say if only I were able, but I just keep quiet and count the cars that pass by.",
"source": "King of Anything",
@ -28039,12 +28021,6 @@
"id": 4844,
"length": 281
},
{
"text": "The oracles of God foretold the rising of an Antichrist in the Christian Church: and in the Pope of Rome, all the characteristics of that Antichrist are so marvelously answered that if any who read the Scriptures do not see it, there is a marvelous blindness upon them.",
"source": "Prophetic Faith of Our Fathers Vol 3",
"id": 4845,
"length": 269
},
{
"text": "We didn't start the fire. It was always burning since the world's been turning. We didn't start the fire. No, we didn't light it, but we tried to fight it.",
"source": "We Didn't Start the Fire",

View file

@ -762,6 +762,24 @@
"source": "Marshal D. Teach - One Piece",
"length": 150,
"id": 126
},
{
"text": "Ayer tuve un amor que hoy me abandonó porque no me quería. Fue tanta la ilusión por hacerla feliz pero todo fue en vano.",
"source": "Manuel Jiménez. Máximo Fonseca - Ayer y Hoy",
"length": 120,
"id": 127
},
{
"text": "Ódiame por piedad yo te lo pido, ódiame sin medida ni clemencia. Odio quiero más que indiferencia, porque el rencor hiere menos que el olvido.",
"source": "Rafael Otero López - Ódiame",
"length": 142,
"id": 128
},
{
"text": "Dicen que con el tiempo los recuerdos se esfuman. Se ahonda en el olvido lo que fue una pasión, Mentira, cuando mueras y bajas a mi tumba verás que aún por ti arde la llama de mi amor.",
"source": "Luis Aguirre Pinto - Reminiscencias",
"length": 184,
"id": 129
}
]
}