mirror of
https://github.com/monkeytypegame/monkeytype.git
synced 2024-09-22 00:06:16 +08:00
commit
ed69343b3b
5
.firebaserc_example
Normal file
5
.firebaserc_example
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"projects": {
|
||||
"default": "project id"
|
||||
}
|
||||
}
|
19
README.md
19
README.md
|
@ -14,23 +14,16 @@ Monkey-type is a minimalistic, customisable typing test, featuring many test mod
|
|||
- command line
|
||||
- and much more
|
||||
|
||||
# keybinds
|
||||
You can use `tab` and `enter` (or just `tab` if you have quick tab mode enabled) to restart the typing test. Open the command line by pressing `esc` - there you can access all the functionality you need without touching your mouse.
|
||||
|
||||
# stats
|
||||
- wpm - total amount of characters in the correctly typed words, divided by 5
|
||||
- acc - percentage of correctly pressed keys
|
||||
- key - correct characters / incorrect characters. Calculated after the test has ended
|
||||
|
||||
# discord bot
|
||||
Recently, a Discord bot was added to autoassign roles. You can find the code for it over at https://github.com/Miodec/monkey-bot
|
||||
|
||||
# bug report or feature request
|
||||
If you encounter a bug, or have a feature request - send me a message on Reddit, create an issue on GitHub or send me a message using the command line `esc`.
|
||||
If you encounter a bug, or have a feature request - send me a message on Reddit, create an issue or join the [Discord server](https://discord.com/invite/yENzqcB).
|
||||
|
||||
# credits
|
||||
montydrei for the name suggestion
|
||||
everyone who provided valuable feedback on the original reddit post for the prototype of this website
|
||||
Montydrei for the name suggestion
|
||||
Everyone who provided valuable feedback on the original reddit post for the prototype of this website
|
||||
Contributors that have helped with implementing various features, adding themes and more.
|
||||
|
||||
# support
|
||||
If you wish to support further development and feeling extra awesome, you can do so [here](https://www.paypal.me/jackbartnik).
|
||||
|
@ -38,9 +31,9 @@ If you wish to support further development and feeling extra awesome, you can do
|
|||
# how to contribute
|
||||
1. Head to [the firebase console](https://console.firebase.google.com/u/0/) and make a new project (the project name doesnt really matter, but just name it `monkey-type`). You dont need to enable analytics for it.
|
||||
2. Install the [Firebase Command Line Interface](https://firebase.google.com/docs/cli), and use `firebase login` to log in to the same google account as you just used to make the project.
|
||||
3. Git clone the project and make sure to rename `.firebaserc_example` to `.firebaserc` and changed the project name inside to your firebase project name.
|
||||
3. Git clone the project and make sure to rename `.firebaserc_example` to `.firebaserc` and change the project name inside to your firebase project name you just created.
|
||||
4. Make sure you use a SCSS compiler. For VSCode I recommend `Easy Sass` or `Live Sass Compiler` extension.
|
||||
5. Run `firebase serve --only hosting` to start a local server on post 5000. Use ctrl+c to stop it.
|
||||
5. Run `firebase serve` to start a local server on port 5000. Use ctrl+c to stop it.
|
||||
6. Make sure to install `Prettier`. Its a code formatter, and it will make sure that we avoid any whitespace or formatting issues when merging code.
|
||||
|
||||
That should be it. If you run into any problems, let me know.
|
||||
|
|
|
@ -423,6 +423,9 @@ exports.testCompleted = functions.https.onCall((request, response) => {
|
|||
);
|
||||
}
|
||||
|
||||
obj.keySpacing = "removed";
|
||||
obj.keyDuration = "removed";
|
||||
|
||||
return db
|
||||
.collection("users")
|
||||
.doc(request.uid)
|
||||
|
@ -468,89 +471,94 @@ exports.testCompleted = functions.https.onCall((request, response) => {
|
|||
checkLeaderboards(request.obj, "global", banned, name),
|
||||
checkLeaderboards(request.obj, "daily", banned, name),
|
||||
checkIfPB(request.uid, request.obj),
|
||||
]).then((values) => {
|
||||
let globallb = values[0].insertedAt;
|
||||
let dailylb = values[1].insertedAt;
|
||||
let ispb = values[2];
|
||||
// console.log(values);
|
||||
])
|
||||
.then((values) => {
|
||||
let globallb = values[0].insertedAt;
|
||||
let dailylb = values[1].insertedAt;
|
||||
let ispb = values[2];
|
||||
// console.log(values);
|
||||
|
||||
let usr =
|
||||
userdata.discordId !== undefined
|
||||
? userdata.discordId
|
||||
: userdata.name;
|
||||
|
||||
if (
|
||||
globallb !== null &&
|
||||
[1, 2, 3].includes(globallb.insertedAt + 1) &&
|
||||
globallb.newBest
|
||||
) {
|
||||
let lbstring = `${obj.mode} ${obj.mode2} global`;
|
||||
console.log(
|
||||
`sending command to the bot to announce lb update ${
|
||||
userdata.discordId
|
||||
} ${globallb + 1} ${lbstring} ${obj.wpm}`
|
||||
);
|
||||
|
||||
announceLbUpdate(
|
||||
usr,
|
||||
globallb.insertedAt + 1,
|
||||
lbstring,
|
||||
obj.wpm
|
||||
);
|
||||
}
|
||||
|
||||
let returnobj = {
|
||||
resultCode: null,
|
||||
globalLeaderboard: globallb,
|
||||
dailyLeaderboard: dailylb,
|
||||
lbBanned: banned,
|
||||
name: name,
|
||||
};
|
||||
request.obj.keySpacing = "removed";
|
||||
request.obj.keyDuration = "removed";
|
||||
if (ispb) {
|
||||
console.log(
|
||||
`saved result for ${request.uid} (new PB) - ${JSON.stringify(
|
||||
request.obj
|
||||
)}`
|
||||
);
|
||||
if (
|
||||
obj.mode === "time" &&
|
||||
String(obj.mode2) === "60" &&
|
||||
userdata.discordId !== null &&
|
||||
let usr =
|
||||
userdata.discordId !== undefined
|
||||
? userdata.discordId
|
||||
: userdata.name;
|
||||
|
||||
if (
|
||||
globallb !== null &&
|
||||
globallb.insertedAt >= 0 &&
|
||||
globallb.insertedAt <= 9 &&
|
||||
globallb.newBest
|
||||
) {
|
||||
let lbstring = `${obj.mode} ${obj.mode2} global`;
|
||||
console.log(
|
||||
`sending command to the bot to update the role for user ${request.uid} with wpm ${obj.wpm}`
|
||||
`sending command to the bot to announce lb update ${
|
||||
userdata.discordId
|
||||
} ${globallb + 1} ${lbstring} ${obj.wpm}`
|
||||
);
|
||||
|
||||
announceLbUpdate(
|
||||
usr,
|
||||
globallb.insertedAt + 1,
|
||||
lbstring,
|
||||
obj.wpm
|
||||
);
|
||||
updateDiscordRole(userdata.discordId, Math.round(obj.wpm));
|
||||
return;
|
||||
}
|
||||
returnobj.resultCode = 2;
|
||||
} else {
|
||||
console.log(
|
||||
`saved result for ${request.uid} - ${JSON.stringify(
|
||||
request.obj
|
||||
)}`
|
||||
|
||||
let returnobj = {
|
||||
resultCode: null,
|
||||
globalLeaderboard: globallb,
|
||||
dailyLeaderboard: dailylb,
|
||||
lbBanned: banned,
|
||||
name: name,
|
||||
};
|
||||
|
||||
if (ispb) {
|
||||
console.log(
|
||||
`saved result for ${
|
||||
request.uid
|
||||
} (new PB) - ${JSON.stringify(request.obj)}`
|
||||
);
|
||||
if (
|
||||
obj.mode === "time" &&
|
||||
String(obj.mode2) === "60" &&
|
||||
userdata.discordId !== null &&
|
||||
userdata.discordId !== undefined
|
||||
) {
|
||||
console.log(
|
||||
`sending command to the bot to update the role for user ${request.uid} with wpm ${obj.wpm}`
|
||||
);
|
||||
updateDiscordRole(userdata.discordId, Math.round(obj.wpm));
|
||||
}
|
||||
returnobj.resultCode = 2;
|
||||
} else {
|
||||
console.log(
|
||||
`saved result for ${request.uid} - ${JSON.stringify(
|
||||
request.obj
|
||||
)}`
|
||||
);
|
||||
returnobj.resultCode = 1;
|
||||
}
|
||||
return returnobj;
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(
|
||||
`error saving result when checking for PB / checking leaderboards for ${request.uid} - ${e.message}`
|
||||
);
|
||||
returnobj.resultCode = 1;
|
||||
}
|
||||
// console.log(returnobj);
|
||||
return returnobj;
|
||||
});
|
||||
return { resultCode: -999, message: e.message };
|
||||
});
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(
|
||||
`error saving result when checking for PB / checking leaderboards for ${request.uid} - ${e.message}`
|
||||
`error saving result when adding result to the db for ${request.uid} - ${e.message}`
|
||||
);
|
||||
return { resultCode: -999 };
|
||||
return { resultCode: -999, message: e.message };
|
||||
});
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(
|
||||
`error saving result when getting user data for ${request.uid} - ${e.message}`
|
||||
);
|
||||
return { resultCode: -999 };
|
||||
return { resultCode: -999, message: e.message };
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(
|
||||
|
@ -558,7 +566,7 @@ exports.testCompleted = functions.https.onCall((request, response) => {
|
|||
request.obj
|
||||
)} - ${e}`
|
||||
);
|
||||
return { resultCode: -999 };
|
||||
return { resultCode: -999, message: e.message };
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -308,6 +308,29 @@ a:hover {
|
|||
}
|
||||
}
|
||||
|
||||
#customThemeShareWrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.75);
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
display: grid;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 5rem 0;
|
||||
|
||||
#customThemeShare {
|
||||
width: 50vw;
|
||||
background: var(--bg-color);
|
||||
border-radius: var(--roundness);
|
||||
padding: 2rem;
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
}
|
||||
|
||||
#resultEditTagsPanelWrapper {
|
||||
width: 100%;
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<title>Monkey Type</title>
|
||||
<link rel="stylesheet" href="css/fa.css">
|
||||
<link rel="stylesheet" href="css/balloon.css">
|
||||
<link rel="stylesheet" href="css/style.css?v=33">
|
||||
<link rel="stylesheet" href="css/style.css?v=36">
|
||||
<link rel="stylesheet" href="themes/serika_dark.css" id="currentTheme">
|
||||
<link id="favicon" rel="shortcut icon" href="fav.png">
|
||||
<link rel="shortcut icon" href="fav.png">
|
||||
|
@ -37,6 +37,12 @@
|
|||
Important information about your account. Please click this message.
|
||||
</div>
|
||||
<div class="notification">Signed in</div>
|
||||
<div id="customThemeShareWrapper" class="hidden">
|
||||
<div id="customThemeShare" action="">
|
||||
<input type="text">
|
||||
<div class="button">ok</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="tagsWrapper" class="hidden">
|
||||
<div id="tagsEdit" action="" tagid="">
|
||||
<div class="title"></div>
|
||||
|
@ -548,7 +554,7 @@
|
|||
</div>
|
||||
<div class="icon-button login" tabindex="2" href="/login" onclick="this.blur();">
|
||||
<div class="icon">
|
||||
<i class="fas fa-fw fa-sign-in-alt"></i>
|
||||
<i class="far fa-fw fa-user"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -868,16 +874,16 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="section timerStyle" section="">
|
||||
<h1>timer style</h1>
|
||||
<div class="text">Change the style of the timer during a timed test.</div>
|
||||
<h1>timer/progress style</h1>
|
||||
<div class="text">Change the style of the timer/progress during a timed test.</div>
|
||||
<div class="buttons">
|
||||
<div class="button" timer="bar" tabindex="0" onclick="this.blur();">bar</div>
|
||||
<div class="button" timer="text" tabindex="0" onclick="this.blur();">text</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section timerColor" section="">
|
||||
<h1>timer color</h1>
|
||||
<div class="text">Change the color of the timer number/bar and live wpm number.</div>
|
||||
<h1>timer/progress color</h1>
|
||||
<div class="text">Change the color of the timer/progress number/bar and live wpm number.</div>
|
||||
<div class="buttons">
|
||||
<div class="button" color="black" tabindex="0" onclick="this.blur();">black</div>
|
||||
<div class="button" color="sub" tabindex="0" onclick="this.blur();">sub</div>
|
||||
|
@ -886,8 +892,8 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="section timerOpacity" section="">
|
||||
<h1>timer opacity</h1>
|
||||
<div class="text">Change the opacity of the timer number/bar and live wpm number.</div>
|
||||
<h1>timer/progress opacity</h1>
|
||||
<div class="text">Change the opacity of the timer/progress number/bar and live wpm number.</div>
|
||||
<div class="buttons">
|
||||
<div class="button" opacity="0.25" tabindex="0" onclick="this.blur();">0.25</div>
|
||||
<div class="button" opacity="0.5" tabindex="0" onclick="this.blur();">0.5</div>
|
||||
|
@ -999,7 +1005,10 @@
|
|||
<label for="--colorful-error-extra-color">#e2b714</label>
|
||||
<input type="color" value="" id="--colorful-error-extra-color">
|
||||
</span>
|
||||
<div class="button saveCustomThemeButton" style="grid-column: 4;">Save</div>
|
||||
<div style="grid-column: 3/5;display:grid;gap:1rem;grid-auto-flow: column;">
|
||||
<div class="button" id="shareCustomThemeButton">share</div>
|
||||
<div class="button saveCustomThemeButton">save</div>
|
||||
</div>
|
||||
</div>
|
||||
<div tabContent="preset" class="tabContent buttons">
|
||||
</div>
|
||||
|
@ -1076,6 +1085,7 @@
|
|||
<div class="page pageAccount hidden">
|
||||
<div class="preloader"><i class="fas fa-fw fa-spin fa-circle-notch"></i></div>
|
||||
<div class="content hidden">
|
||||
<div class="button" id="currentConfigFilter">set filters to current settings</div>
|
||||
<div class="group filterButtons">
|
||||
<div class="buttonsAndTitle">
|
||||
<div class="title">filters</div>
|
||||
|
@ -1097,6 +1107,7 @@
|
|||
<div class="buttons modeFilters">
|
||||
<div class="button" filter="mode_words">words</div>
|
||||
<div class="button" filter="mode_time">time</div>
|
||||
<div class="button" filter="mode_quote">quote</div>
|
||||
<div class="button" filter="mode_custom">custom</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1300,17 +1311,17 @@
|
|||
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment.min.js"></script>
|
||||
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.3/Chart.min.js"></script> -->
|
||||
<script src="js/chart.js"></script>
|
||||
<script src="js/chartjs-plugin-trendline.js?v=33"></script>
|
||||
<script src="js/chartjs-plugin-trendline.js?v=36"></script>
|
||||
<script src="js/chartjs-plugin-annotation.js"></script>
|
||||
<script src="js/html2canvas.js"></script>
|
||||
<script src="js/words.js?v=33"></script>
|
||||
<script src="js/layouts.js?v=33"></script>
|
||||
<script src="js/db.js?v=33"></script>
|
||||
<script src="js/userconfig.js?v=33"></script>
|
||||
<script src="js/commandline.js?v=33"></script>
|
||||
<script src="js/leaderboards.js?v=33"></script>
|
||||
<script src="js/settings.js?v=33"></script>
|
||||
<script src="js/account.js?v=33"></script>
|
||||
<script src="js/script.js?v=33"></script>
|
||||
<script src="js/words.js?v=36"></script>
|
||||
<script src="js/layouts.js?v=36"></script>
|
||||
<script src="js/db.js?v=36"></script>
|
||||
<script src="js/userconfig.js?v=36"></script>
|
||||
<script src="js/commandline.js?v=36"></script>
|
||||
<script src="js/leaderboards.js?v=36"></script>
|
||||
<script src="js/settings.js?v=36"></script>
|
||||
<script src="js/account.js?v=36"></script>
|
||||
<script src="js/script.js?v=36"></script>
|
||||
|
||||
</html>
|
|
@ -398,14 +398,14 @@ var resultHistoryChart = new Chart($(".pageAccount #resultHistoryChart"), {
|
|||
|
||||
Object.keys(words).forEach((language) => {
|
||||
$(".pageAccount .content .filterButtons .buttons.languages").append(
|
||||
`<div class="button" filter="${language}">${language.replace(
|
||||
`<div class="button" filter="lang_${language}">${language.replace(
|
||||
"_",
|
||||
" "
|
||||
)}</div>`
|
||||
);
|
||||
if (language === "english_expanded") {
|
||||
$(".pageAccount .content .filterButtons .buttons.languages").append(
|
||||
`<div class="button" filter="english_10k">english 10k</div>`
|
||||
`<div class="button" filter="lang_english_10k">english 10k</div>`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -580,6 +580,61 @@ $(".pageAccount .filterButtons").click(".button", (e) => {
|
|||
saveConfigToCookie();
|
||||
});
|
||||
|
||||
$(".pageAccount #currentConfigFilter").click((e) => {
|
||||
let disableGroups = [
|
||||
"globalFilters",
|
||||
"difficultyFilters",
|
||||
"modeFilters",
|
||||
"punctuationFilter",
|
||||
"wordsFilter",
|
||||
"timeFilter",
|
||||
"languages",
|
||||
"tags",
|
||||
];
|
||||
disableGroups.forEach((group) => {
|
||||
$.each(
|
||||
$(`.pageAccount .filterButtons .buttons.${group} .button`),
|
||||
(index, button) => {
|
||||
let fl = $(button).attr("filter");
|
||||
disableFilterButton(fl);
|
||||
config.resultFilters = config.resultFilters.filter((f) => f !== fl);
|
||||
}
|
||||
);
|
||||
});
|
||||
updateActiveFilters();
|
||||
|
||||
//rewrite this monstrosity soon pls
|
||||
config.resultFilters.push(`difficulty_${config.difficulty}`);
|
||||
toggleFilterButton(`difficulty_${config.difficulty}`);
|
||||
config.resultFilters.push(`mode_${config.mode}`);
|
||||
toggleFilterButton(`mode_${config.mode}`);
|
||||
config.resultFilters.push(`words_${config.words}`);
|
||||
toggleFilterButton(`words_${config.words}`);
|
||||
config.resultFilters.push(`time_${config.time}`);
|
||||
toggleFilterButton(`time_${config.time}`);
|
||||
let puncfilter = config.punctuation ? "punc_on" : "punc_off";
|
||||
config.resultFilters.push(puncfilter);
|
||||
toggleFilterButton(puncfilter);
|
||||
config.resultFilters.push(`lang_${config.language}`);
|
||||
toggleFilterButton(`lang_${config.language}`);
|
||||
|
||||
let activeTags = [];
|
||||
try {
|
||||
dbSnapshot.tags.forEach((tag) => {
|
||||
if (tag.active === true) {
|
||||
activeTags.push(tag.id);
|
||||
}
|
||||
});
|
||||
} catch (e) {}
|
||||
|
||||
activeTags.forEach((tag) => {
|
||||
config.resultFilters.push(`tag_${tag}`);
|
||||
toggleFilterButton(`tag_${tag}`);
|
||||
});
|
||||
|
||||
saveConfigToCookie();
|
||||
});
|
||||
|
||||
let filteredResults = [];
|
||||
let visibleTableLines = 0;
|
||||
|
||||
|
@ -749,7 +804,7 @@ function refreshAccountPage() {
|
|||
if (!activeFilters.includes(wordfilter)) return;
|
||||
}
|
||||
|
||||
if (!activeFilters.includes(result.language)) return;
|
||||
if (!activeFilters.includes("lang_" + result.language)) return;
|
||||
|
||||
let puncfilter = "punc_off";
|
||||
if (result.punctuation) {
|
||||
|
@ -808,17 +863,19 @@ function refreshAccountPage() {
|
|||
//=======================================
|
||||
|
||||
tt = 0;
|
||||
if (result.timeDuration == null) {
|
||||
//test finished before timeduration field was introduced - estimate
|
||||
if (result.testDuration == undefined) {
|
||||
//test finished before testDuration field was introduced - estimate
|
||||
if (result.mode == "time") {
|
||||
tt = parseFloat(result.mode2);
|
||||
} else if (result.mode == "words") {
|
||||
tt = (parseFloat(result.mode2) / parseFloat(result.wpm)) * 60;
|
||||
}
|
||||
} else {
|
||||
tt = parseFloat(result.timeDuration);
|
||||
tt = parseFloat(result.testDuration);
|
||||
}
|
||||
if (result.restartCount != null) {
|
||||
if (result.incompleteTestSeconds != undefined) {
|
||||
tt += result.incompleteTestSeconds;
|
||||
} else if (result.restartCount != undefined && result.restartCount > 0) {
|
||||
tt += (tt / 4) * result.restartCount;
|
||||
}
|
||||
totalSecondsFiltered += tt;
|
||||
|
|
|
@ -157,7 +157,7 @@ let commands = {
|
|||
},
|
||||
{
|
||||
id: "changeTimerStyle",
|
||||
display: "Change timer style...",
|
||||
display: "Change timer/progress style...",
|
||||
subgroup: true,
|
||||
exec: () => {
|
||||
currentCommands.push(commandsTimerStyle);
|
||||
|
@ -166,7 +166,7 @@ let commands = {
|
|||
},
|
||||
{
|
||||
id: "changeTimerColor",
|
||||
display: "Change timer color...",
|
||||
display: "Change timer/progress color...",
|
||||
subgroup: true,
|
||||
exec: () => {
|
||||
currentCommands.push(commandsTimerColor);
|
||||
|
@ -175,7 +175,7 @@ let commands = {
|
|||
},
|
||||
{
|
||||
id: "changeTimerOpacity",
|
||||
display: "Change timer opacity...",
|
||||
display: "Change timer/progress opacity...",
|
||||
subgroup: true,
|
||||
exec: () => {
|
||||
currentCommands.push(commandsTimerOpacity);
|
||||
|
@ -290,7 +290,7 @@ let commandsCaretStyle = {
|
|||
};
|
||||
|
||||
let commandsTimerStyle = {
|
||||
title: "Change timer...",
|
||||
title: "Change timer/progress style...",
|
||||
list: [
|
||||
{
|
||||
id: "setTimerStyleBar",
|
||||
|
@ -310,7 +310,7 @@ let commandsTimerStyle = {
|
|||
};
|
||||
|
||||
let commandsTimerColor = {
|
||||
title: "Change timer color...",
|
||||
title: "Change timer/progress color...",
|
||||
list: [
|
||||
{
|
||||
id: "setTimerColorBlack",
|
||||
|
@ -350,21 +350,21 @@ let commandsTimerOpacity = {
|
|||
id: "setTimerOpacity.25",
|
||||
display: ".25",
|
||||
exec: () => {
|
||||
setTimerOpacity(.25);
|
||||
setTimerOpacity(0.25);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "setTimerOpacity.5",
|
||||
display: ".5",
|
||||
exec: () => {
|
||||
setTimerOpacity(.5);
|
||||
setTimerOpacity(0.5);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "setTimerOpacity.75",
|
||||
display: ".75",
|
||||
exec: () => {
|
||||
setTimerOpacity(.75);
|
||||
setTimerOpacity(0.75);
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
@ -23,6 +23,7 @@ let activeWordTop = 0;
|
|||
let activeWordJumped = false;
|
||||
let sameWordset = false;
|
||||
let quotes = [];
|
||||
let focusState = false;
|
||||
|
||||
let accuracyStats = {
|
||||
correct: 0,
|
||||
|
@ -229,14 +230,15 @@ function getLastChar(word) {
|
|||
}
|
||||
|
||||
function setFocus(foc) {
|
||||
if (foc) {
|
||||
// focus = true;
|
||||
if (foc && !focusState) {
|
||||
focusState = true;
|
||||
stopCaretAnimation();
|
||||
$("#top").addClass("focus");
|
||||
$("#bottom").addClass("focus");
|
||||
$("body").css("cursor", "none");
|
||||
$("#middle").addClass("focus");
|
||||
} else {
|
||||
} else if (!foc && focusState) {
|
||||
focusState = false;
|
||||
startCaretAnimation();
|
||||
$("#top").removeClass("focus");
|
||||
$("#bottom").removeClass("focus");
|
||||
|
@ -298,7 +300,11 @@ function initWords() {
|
|||
}
|
||||
|
||||
if (config.mode == "time" || config.mode == "words") {
|
||||
let wordsBound = config.mode == "time" ? 60 : config.words;
|
||||
// let wordsBound = config.mode == "time" ? 60 : config.words;
|
||||
let wordsBound = 60;
|
||||
if (config.mode === "words" && config.words < wordsBound) {
|
||||
wordsBound = config.words;
|
||||
}
|
||||
for (let i = 0; i < wordsBound; i++) {
|
||||
randomWord = language[Math.floor(Math.random() * language.length)];
|
||||
previousWord = wordsList[i - 1];
|
||||
|
@ -460,6 +466,11 @@ function punctuateWord(previousWord, currentWord, index, maxindex) {
|
|||
}
|
||||
|
||||
function addWord() {
|
||||
if (
|
||||
wordsList.length - inputHistory.length > 60 ||
|
||||
(config.mode === "words" && wordsList.length >= config.words)
|
||||
)
|
||||
return;
|
||||
let language = words[config.language];
|
||||
let randomWord = language[Math.floor(Math.random() * language.length)];
|
||||
previousWord = wordsList[wordsList.length - 1];
|
||||
|
@ -486,52 +497,76 @@ function addWord() {
|
|||
|
||||
function showWords() {
|
||||
$("#words").empty();
|
||||
if (
|
||||
config.mode == "words" ||
|
||||
config.mode == "custom" ||
|
||||
config.mode == "quote"
|
||||
) {
|
||||
$("#words").css("height", "auto");
|
||||
for (let i = 0; i < wordsList.length; i++) {
|
||||
let w = "<div class='word'>";
|
||||
for (let c = 0; c < wordsList[i].length; c++) {
|
||||
w += "<letter>" + wordsList[i].charAt(c) + "</letter>";
|
||||
}
|
||||
w += "</div>";
|
||||
$("#words").append(w);
|
||||
// if (
|
||||
// config.mode == "words" ||
|
||||
// config.mode == "custom" ||
|
||||
// config.mode == "quote"
|
||||
// ) {
|
||||
// $("#words").css("height", "auto");
|
||||
// for (let i = 0; i < wordsList.length; i++) {
|
||||
// let w = "<div class='word'>";
|
||||
// for (let c = 0; c < wordsList[i].length; c++) {
|
||||
// w += "<letter>" + wordsList[i].charAt(c) + "</letter>";
|
||||
// }
|
||||
// w += "</div>";
|
||||
// $("#words").append(w);
|
||||
// }
|
||||
// } else if (config.mode == "time") {
|
||||
for (let i = 0; i < wordsList.length; i++) {
|
||||
let w = "<div class='word'>";
|
||||
for (let c = 0; c < wordsList[i].length; c++) {
|
||||
w += "<letter>" + wordsList[i].charAt(c) + "</letter>";
|
||||
}
|
||||
} else if (config.mode == "time") {
|
||||
for (let i = 0; i < wordsList.length; i++) {
|
||||
let w = "<div class='word'>";
|
||||
for (let c = 0; c < wordsList[i].length; c++) {
|
||||
w += "<letter>" + wordsList[i].charAt(c) + "</letter>";
|
||||
}
|
||||
w += "</div>";
|
||||
$("#words").append(w);
|
||||
}
|
||||
$("#words").removeClass("hidden");
|
||||
const wordHeight = $($(".word")[0]).outerHeight(true);
|
||||
$("#words")
|
||||
.css("height", wordHeight * 3 + "px")
|
||||
.css("overflow", "hidden");
|
||||
w += "</div>";
|
||||
$("#words").append(w);
|
||||
}
|
||||
$("#words").removeClass("hidden");
|
||||
const wordHeight = $($(".word")[0]).outerHeight(true);
|
||||
$("#words")
|
||||
.css("height", wordHeight * 3 + "px")
|
||||
.css("overflow", "hidden");
|
||||
// }
|
||||
updateActiveElement();
|
||||
updateCaretPosition();
|
||||
}
|
||||
|
||||
function updateActiveElement() {
|
||||
$("#words .word").removeClass("active");
|
||||
$($("#words .word")[currentWordIndex])
|
||||
.addClass("active")
|
||||
.removeClass("error");
|
||||
// activeWordTop = $("#words .word.active").position().top;
|
||||
activeWordTop = document.querySelector("#words .word.active").offsetTop;
|
||||
let active = document.querySelector("#words .active");
|
||||
if (active !== null) {
|
||||
active.classList.remove("active");
|
||||
}
|
||||
// $("#words .word").removeClass("active");
|
||||
// $($("#words .word")[currentWordIndex]).addClass("active").removeClass("error");
|
||||
|
||||
// document.querySelectorAll("#words .word")[currentWordIndex].classList.add("active");
|
||||
try {
|
||||
let activeWord = document.querySelectorAll("#words .word")[
|
||||
currentWordIndex
|
||||
];
|
||||
activeWord.classList.add("active");
|
||||
activeWord.classList.remove("error");
|
||||
|
||||
// activeWordTop = $("#words .word.active").position().top;
|
||||
activeWordTop = document.querySelector("#words .active").offsetTop;
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
function compareInput(wrdIndex, input, showError) {
|
||||
$($("#words .word")[wrdIndex]).empty();
|
||||
// let wrdAtIndex = $("#words .word")[wrdIndex];
|
||||
let wordAtIndex;
|
||||
let currentWord;
|
||||
if (wrdIndex === null) {
|
||||
wordAtIndex = document.querySelector("#words .word.active");
|
||||
currentWord = wordsList[currentWordIndex];
|
||||
} else {
|
||||
wordAtIndex = document.querySelectorAll("#words .word")[wrdIndex];
|
||||
wordsList[wrdIndex];
|
||||
}
|
||||
// while (wordAtIndex.firstChild) {
|
||||
// wordAtIndex.removeChild(wordAtIndex.firstChild);
|
||||
// }
|
||||
let ret = "";
|
||||
let currentWord = wordsList[wrdIndex];
|
||||
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
if (currentWord[i] == input[i]) {
|
||||
ret += '<letter class="correct">' + currentWord[i] + "</letter>";
|
||||
|
@ -567,11 +602,18 @@ function compareInput(wrdIndex, input, showError) {
|
|||
ret += "<letter>" + currentWord[i] + "</letter>";
|
||||
}
|
||||
}
|
||||
$($("#words .word")[wrdIndex]).html(ret);
|
||||
|
||||
wordAtIndex.innerHTML = ret;
|
||||
|
||||
let lastindex = currentWordIndex;
|
||||
if (wrdIndex !== null) {
|
||||
lastindex = wrdIndex;
|
||||
}
|
||||
|
||||
if (
|
||||
(currentWord == input ||
|
||||
(config.quickEnd && currentWord.length == input.length)) &&
|
||||
wrdIndex == wordsList.length - 1
|
||||
lastindex == wordsList.length - 1
|
||||
) {
|
||||
inputHistory.push(input);
|
||||
currentInput = "";
|
||||
|
@ -718,20 +760,30 @@ function updateTimer() {
|
|||
// $("#timerNumber").html(config.time - time);
|
||||
}
|
||||
} else if (
|
||||
(config.mode === "words" ||
|
||||
config.mode === "custom" ||
|
||||
config.mode === "quote") &&
|
||||
config.timerStyle === "bar"
|
||||
config.mode === "words" ||
|
||||
config.mode === "custom" ||
|
||||
config.mode === "quote"
|
||||
) {
|
||||
let percent = Math.floor(((currentWordIndex + 1) / wordsList.length) * 100);
|
||||
$("#timer")
|
||||
.stop(true, true)
|
||||
.animate(
|
||||
{
|
||||
width: percent + "vw",
|
||||
},
|
||||
250
|
||||
if (config.timerStyle === "bar") {
|
||||
let percent = Math.floor(
|
||||
((currentWordIndex + 1) / wordsList.length) * 100
|
||||
);
|
||||
$("#timer")
|
||||
.stop(true, true)
|
||||
.animate(
|
||||
{
|
||||
width: percent + "vw",
|
||||
},
|
||||
250
|
||||
);
|
||||
} else if (config.timerStyle === "text") {
|
||||
let outof = wordsList.length;
|
||||
if (config.mode === "words") {
|
||||
outof = config.words;
|
||||
}
|
||||
$("#timerNumber").html(`${inputHistory.length}/${outof}`);
|
||||
// $("#timerNumber").html(config.time - time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -757,61 +809,68 @@ function startCaretAnimation() {
|
|||
}
|
||||
|
||||
function updateCaretPosition() {
|
||||
// return;
|
||||
if ($("#words").hasClass("hidden")) return;
|
||||
|
||||
let caret = $("#caret");
|
||||
let activeWord = $("#words .word.active");
|
||||
// let activeWord = $("#words .word.active");
|
||||
|
||||
let inputLen = currentInput.length;
|
||||
let currentLetterIndex = inputLen - 1;
|
||||
if (currentLetterIndex == -1) {
|
||||
currentLetterIndex = 0;
|
||||
}
|
||||
let currentLetter = $("#words .word.active letter")[currentLetterIndex];
|
||||
if ($(currentLetter).length == 0) return;
|
||||
// let currentLetterPos = currentLetter.position();
|
||||
let currentLetterPosLeft = currentLetter.offsetLeft;
|
||||
let currentLetterPosTop = currentLetter.offsetTop;
|
||||
let letterHeight = $(currentLetter).height();
|
||||
// let currentLetter = $("#words .word.active letter")[currentLetterIndex];
|
||||
try {
|
||||
let currentLetter = document
|
||||
.querySelector("#words .active")
|
||||
.querySelectorAll("letter")[currentLetterIndex];
|
||||
|
||||
let newTop = 0;
|
||||
let newLeft = 0;
|
||||
if ($(currentLetter).length == 0) return;
|
||||
let currentLetterPosLeft = currentLetter.offsetLeft;
|
||||
let currentLetterPosTop = currentLetter.offsetTop;
|
||||
let letterHeight = $(currentLetter).height();
|
||||
let newTop = 0;
|
||||
let newLeft = 0;
|
||||
|
||||
newTop = currentLetterPosTop - letterHeight / 4;
|
||||
if (inputLen == 0) {
|
||||
newLeft = currentLetterPosLeft - caret.width() / 2;
|
||||
} else {
|
||||
newLeft =
|
||||
currentLetterPosLeft + $(currentLetter).width() - caret.width() / 2;
|
||||
}
|
||||
newTop = currentLetterPosTop - letterHeight / 4;
|
||||
if (inputLen == 0) {
|
||||
newLeft = currentLetterPosLeft - caret.width() / 2;
|
||||
} else {
|
||||
newLeft =
|
||||
currentLetterPosLeft + $(currentLetter).width() - caret.width() / 2;
|
||||
}
|
||||
|
||||
let duration = 0;
|
||||
let duration = 0;
|
||||
|
||||
if (config.smoothCaret) {
|
||||
duration = 100;
|
||||
// if (Math.round(caret[0].offsetTop) != Math.round(newTop)) {
|
||||
// caret.css("top", newTop);
|
||||
// duration = 10;
|
||||
if (config.smoothCaret) {
|
||||
duration = 100;
|
||||
// if (Math.round(caret[0].offsetTop) != Math.round(newTop)) {
|
||||
// caret.css("top", newTop);
|
||||
// duration = 10;
|
||||
// }
|
||||
}
|
||||
|
||||
caret.stop(true, true).animate(
|
||||
{
|
||||
top: newTop,
|
||||
left: newLeft,
|
||||
},
|
||||
duration
|
||||
);
|
||||
|
||||
// let browserHeight = window.innerHeight;
|
||||
// let middlePos = browserHeight / 2 - $("#caret").outerHeight() / 2;
|
||||
// let contentHeight = document.body.scrollHeight;
|
||||
|
||||
// if (newTop >= middlePos && contentHeight > browserHeight) {
|
||||
// window.scrollTo({
|
||||
// left: 0,
|
||||
// top: newTop - middlePos,
|
||||
// behavior: "smooth",
|
||||
// });
|
||||
// }
|
||||
}
|
||||
|
||||
caret.stop(true, true).animate(
|
||||
{
|
||||
top: newTop,
|
||||
left: newLeft,
|
||||
},
|
||||
duration
|
||||
);
|
||||
|
||||
let browserHeight = window.innerHeight;
|
||||
let middlePos = browserHeight / 2 - $("#caret").outerHeight() / 2;
|
||||
let contentHeight = document.body.scrollHeight;
|
||||
|
||||
if (newTop >= middlePos && contentHeight > browserHeight) {
|
||||
window.scrollTo({
|
||||
left: 0,
|
||||
top: newTop - middlePos,
|
||||
behavior: "smooth",
|
||||
});
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
function countChars() {
|
||||
|
@ -1150,6 +1209,10 @@ function showResult(difficultyFailed = false) {
|
|||
}).then((e) => {
|
||||
accountIconLoading(false);
|
||||
// console.log(JSON.stringify(e.data));
|
||||
if (e.data == null) {
|
||||
showNotification("Unexpected response from the server.", 4000);
|
||||
return;
|
||||
}
|
||||
if (e.data.resultCode === -1) {
|
||||
showNotification("Could not save result", 3000);
|
||||
} else if (e.data.resultCode === -2) {
|
||||
|
@ -1163,8 +1226,10 @@ function showResult(difficultyFailed = false) {
|
|||
4000
|
||||
);
|
||||
} else if (e.data.resultCode === -999) {
|
||||
console.error("internal error: " + e.data.message);
|
||||
showNotification(
|
||||
"Internal error. Result not saved. Refresh or contact Miodec on Discord.",
|
||||
"Internal error. Result might not be saved. " +
|
||||
e.data.message,
|
||||
6000
|
||||
);
|
||||
} else if (e.data.resultCode === 1 || e.data.resultCode === 2) {
|
||||
|
@ -1187,22 +1252,22 @@ function showResult(difficultyFailed = false) {
|
|||
if (e.data.globalLeaderboard.newBest) {
|
||||
let pos = e.data.globalLeaderboard.insertedAt + 1;
|
||||
let numend = "th";
|
||||
if (pos === 1) {
|
||||
if (pos % 10 === 1) {
|
||||
numend = "st";
|
||||
} else if (pos === 2) {
|
||||
} else if (pos % 10 === 2) {
|
||||
numend = "nd";
|
||||
} else if (pos === 3) {
|
||||
} else if (pos % 10 === 3) {
|
||||
numend = "rd";
|
||||
}
|
||||
globalLbString = `global: ${pos}${numend} place`;
|
||||
} else {
|
||||
let pos = e.data.globalLeaderboard.foundAt + 1;
|
||||
let numend = "th";
|
||||
if (pos === 1) {
|
||||
if (pos % 10 === 1) {
|
||||
numend = "st";
|
||||
} else if (pos === 2) {
|
||||
} else if (pos % 10 === 2) {
|
||||
numend = "nd";
|
||||
} else if (pos === 3) {
|
||||
} else if (pos % 10 === 3) {
|
||||
numend = "rd";
|
||||
}
|
||||
globalLbString = `global: already ${pos}${numend}`;
|
||||
|
@ -1286,8 +1351,6 @@ function showResult(difficultyFailed = false) {
|
|||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
showNotification("Unexpected response from the server.", 4000);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -1682,7 +1745,34 @@ function changeMode(mode, nosave) {
|
|||
if (!nosave) saveConfigToCookie();
|
||||
}
|
||||
|
||||
function liveWPM() {
|
||||
// function liveWPM() {
|
||||
// let correctWordChars = 0;
|
||||
// for (let i = 0; i < inputHistory.length; i++) {
|
||||
// if (inputHistory[i] == wordsList[i]) {
|
||||
// //the word is correct
|
||||
// //+1 for space
|
||||
// correctWordChars += wordsList[i].length + 1;
|
||||
// }
|
||||
// }
|
||||
// let testNow = Date.now();
|
||||
// let testSeconds = (testNow - testStart) / 1000;
|
||||
// wpm = (correctWordChars * (60 / testSeconds)) / 5;
|
||||
// return Math.round(wpm);
|
||||
// }
|
||||
|
||||
// function liveRaw() {
|
||||
// let chars = 0;
|
||||
// for (let i = 0; i < inputHistory.length; i++) {
|
||||
// chars += inputHistory[i].length + 1;
|
||||
// }
|
||||
// let testNow = Date.now();
|
||||
// let testSeconds = (testNow - testStart) / 1000;
|
||||
// raw = (chars * (60 / testSeconds)) / 5;
|
||||
// return Math.round(raw);
|
||||
// }
|
||||
|
||||
function liveWpmAndRaw() {
|
||||
let chars = 0;
|
||||
let correctWordChars = 0;
|
||||
for (let i = 0; i < inputHistory.length; i++) {
|
||||
if (inputHistory[i] == wordsList[i]) {
|
||||
|
@ -1690,29 +1780,24 @@ function liveWPM() {
|
|||
//+1 for space
|
||||
correctWordChars += wordsList[i].length + 1;
|
||||
}
|
||||
}
|
||||
let testNow = Date.now();
|
||||
let testSeconds = (testNow - testStart) / 1000;
|
||||
wpm = (correctWordChars * (60 / testSeconds)) / 5;
|
||||
return Math.round(wpm);
|
||||
}
|
||||
|
||||
function liveRaw() {
|
||||
let chars = 0;
|
||||
for (let i = 0; i < inputHistory.length; i++) {
|
||||
chars += inputHistory[i].length + 1;
|
||||
}
|
||||
let testNow = Date.now();
|
||||
let testSeconds = (testNow - testStart) / 1000;
|
||||
raw = (chars * (60 / testSeconds)) / 5;
|
||||
return Math.round(raw);
|
||||
let wpm = Math.round((correctWordChars * (60 / testSeconds)) / 5);
|
||||
let raw = Math.round((chars * (60 / testSeconds)) / 5);
|
||||
return {
|
||||
wpm: wpm,
|
||||
raw: raw,
|
||||
};
|
||||
}
|
||||
|
||||
function updateLiveWpm(wpm) {
|
||||
if (!config.showLiveWpm) return;
|
||||
if (wpm == 0 || !testActive) hideLiveWpm();
|
||||
// let wpmstring = wpm < 100 ? ` ${wpm}` : `${wpm}`;
|
||||
$("#liveWpm").html(wpm);
|
||||
document.querySelector("#liveWpm").innerHTML = wpm;
|
||||
// $("#liveWpm").html(wpm);
|
||||
}
|
||||
|
||||
function showLiveWpm() {
|
||||
|
@ -2119,6 +2204,11 @@ $(document).on("click", "#top .config .wordCount .text-button", (e) => {
|
|||
5000
|
||||
);
|
||||
}
|
||||
} else {
|
||||
showNotification(
|
||||
"Custom word amount can only be set between 1 and 10000",
|
||||
3000
|
||||
);
|
||||
}
|
||||
} else {
|
||||
changeWordCount(wrd);
|
||||
|
@ -2138,6 +2228,8 @@ $(document).on("click", "#top .config .time .text-button", (e) => {
|
|||
5000
|
||||
);
|
||||
}
|
||||
} else {
|
||||
showNotification("Custom time can only be set between 1 and 3600", 3000);
|
||||
}
|
||||
} else {
|
||||
changeTimeConfig(time);
|
||||
|
@ -2297,6 +2389,8 @@ $(document).keypress(function (event) {
|
|||
// if (config.mode == "time") {
|
||||
restartTimer();
|
||||
showTimer();
|
||||
$("#liveWpm").text("0");
|
||||
showLiveWpm();
|
||||
// }
|
||||
updateActiveElement();
|
||||
updateTimer();
|
||||
|
@ -2317,11 +2411,18 @@ $(document).keypress(function (event) {
|
|||
if (config.mode === "time") {
|
||||
updateTimer();
|
||||
}
|
||||
let wpm = liveWPM();
|
||||
updateLiveWpm(wpm);
|
||||
showLiveWpm();
|
||||
wpmHistory.push(wpm);
|
||||
rawHistory.push(liveRaw());
|
||||
// console.time("livewpm");
|
||||
// let wpm = liveWPM();
|
||||
// updateLiveWpm(wpm);
|
||||
// showLiveWpm();
|
||||
// wpmHistory.push(wpm);
|
||||
// rawHistory.push(liveRaw());
|
||||
let wpmAndRaw = liveWpmAndRaw();
|
||||
updateLiveWpm(wpmAndRaw.wpm);
|
||||
wpmHistory.push(wpmAndRaw.wpm);
|
||||
rawHistory.push(wpmAndRaw.raw);
|
||||
|
||||
// console.timeEnd("livewpm");
|
||||
keypressPerSecond.push(currentKeypressCount);
|
||||
currentKeypressCount = 0;
|
||||
errorsPerSecond.push(currentErrorCount);
|
||||
|
@ -2362,18 +2463,19 @@ $(document).keypress(function (event) {
|
|||
} else {
|
||||
accuracyStats.correct++;
|
||||
}
|
||||
|
||||
currentKeypressCount++;
|
||||
currentInput += event["key"];
|
||||
$("#words .word.active").attr("input", currentInput);
|
||||
setFocus(true);
|
||||
activeWordTopBeforeJump = activeWordTop;
|
||||
compareInput(currentWordIndex, currentInput, !config.blindMode);
|
||||
compareInput(null, currentInput, !config.blindMode);
|
||||
// let newActiveTop = $("#words .word.active").position().top;
|
||||
|
||||
// console.time("offcheck1");
|
||||
let newActiveTop = document.querySelector("#words .word.active").offsetTop;
|
||||
if (activeWordTopBeforeJump != newActiveTop) {
|
||||
activeWordJumped = true;
|
||||
}
|
||||
// console.timeEnd("offcheck2");
|
||||
updateCaretPosition();
|
||||
});
|
||||
|
||||
|
@ -2441,7 +2543,7 @@ $(document).keydown((event) => {
|
|||
}
|
||||
currentWordIndex--;
|
||||
updateActiveElement();
|
||||
compareInput(currentWordIndex, currentInput, !config.blindMode);
|
||||
compareInput(null, currentInput, !config.blindMode);
|
||||
}
|
||||
} else {
|
||||
// if ($($(".word")[currentWordIndex - 1]).hasClass("hidden")) {
|
||||
|
@ -2452,7 +2554,7 @@ $(document).keydown((event) => {
|
|||
} else {
|
||||
currentInput = currentInput.substring(0, currentInput.length - 1);
|
||||
}
|
||||
compareInput(currentWordIndex, currentInput, !config.blindMode);
|
||||
compareInput(null, currentInput, !config.blindMode);
|
||||
}
|
||||
// currentKeypressCount++;
|
||||
updateCaretPosition();
|
||||
|
@ -2463,42 +2565,45 @@ $(document).keydown((event) => {
|
|||
if (currentInput == "") return;
|
||||
event.preventDefault();
|
||||
let currentWord = wordsList[currentWordIndex];
|
||||
if (config.mode == "time") {
|
||||
// let currentTop = Math.floor($($("#words .word")[currentWordIndex]).position().top);
|
||||
// let nextTop = Math.floor($($("#words .word")[currentWordIndex + 1]).position().top);
|
||||
let currentTop = Math.floor(
|
||||
document.querySelectorAll("#words .word")[currentWordIndex].offsetTop
|
||||
);
|
||||
let nextTop = Math.floor(
|
||||
document.querySelectorAll("#words .word")[currentWordIndex + 1]
|
||||
.offsetTop
|
||||
);
|
||||
if (nextTop > currentTop || activeWordJumped) {
|
||||
//last word of the line
|
||||
if (currentTestLine > 0) {
|
||||
let hideBound = currentTop;
|
||||
if (activeWordJumped) {
|
||||
hideBound = activeWordTopBeforeJump;
|
||||
}
|
||||
activeWordJumped = false;
|
||||
|
||||
let toHide = [];
|
||||
let wordElements = $("#words .word");
|
||||
for (let i = 0; i < currentWordIndex + 1; i++) {
|
||||
if ($(wordElements[i]).hasClass("hidden")) continue;
|
||||
// let forWordTop = Math.floor($(wordElements[i]).position().top);
|
||||
let forWordTop = Math.floor(wordElements[i].offsetTop);
|
||||
if (forWordTop < hideBound) {
|
||||
// $($("#words .word")[i]).addClass("hidden");
|
||||
toHide.push($($("#words .word")[i]));
|
||||
}
|
||||
}
|
||||
toHide.forEach((el) => el.addClass("hidden"));
|
||||
// if (config.mode == "time") {
|
||||
// let currentTop = Math.floor($($("#words .word")[currentWordIndex]).position().top);
|
||||
// let nextTop = Math.floor($($("#words .word")[currentWordIndex + 1]).position().top);
|
||||
let currentTop = Math.floor(
|
||||
document.querySelectorAll("#words .word")[currentWordIndex].offsetTop
|
||||
);
|
||||
let nextTop = Math.floor(
|
||||
document.querySelectorAll("#words .word")[currentWordIndex + 1]
|
||||
.offsetTop
|
||||
);
|
||||
if (nextTop > currentTop || activeWordJumped) {
|
||||
//last word of the line
|
||||
if (currentTestLine > 0) {
|
||||
let hideBound = currentTop;
|
||||
if (activeWordJumped) {
|
||||
hideBound = activeWordTopBeforeJump;
|
||||
}
|
||||
currentTestLine++;
|
||||
activeWordJumped = false;
|
||||
|
||||
let toHide = [];
|
||||
let wordElements = $("#words .word");
|
||||
for (let i = 0; i < currentWordIndex + 1; i++) {
|
||||
if ($(wordElements[i]).hasClass("hidden")) continue;
|
||||
// let forWordTop = Math.floor($(wordElements[i]).position().top);
|
||||
let forWordTop = Math.floor(wordElements[i].offsetTop);
|
||||
if (forWordTop < hideBound) {
|
||||
// $($("#words .word")[i]).addClass("hidden");
|
||||
toHide.push($($("#words .word")[i]));
|
||||
}
|
||||
}
|
||||
toHide.forEach((el) => el.addClass("hidden"));
|
||||
}
|
||||
currentTestLine++;
|
||||
}
|
||||
// }
|
||||
if (config.blindMode) $("#words .word.active letter").addClass("correct");
|
||||
document
|
||||
.querySelector("#words .word.active")
|
||||
.setAttribute("input", currentInput);
|
||||
if (currentWord == currentInput) {
|
||||
inputHistory.push(currentInput);
|
||||
currentInput = "";
|
||||
|
@ -2538,7 +2643,7 @@ $(document).keydown((event) => {
|
|||
) {
|
||||
updateTimer();
|
||||
}
|
||||
if (config.mode == "time") {
|
||||
if (config.mode == "time" || config.mode == "words") {
|
||||
addWord();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,6 +71,73 @@ function updateSettingsPage() {
|
|||
}
|
||||
}
|
||||
|
||||
function showCustomThemeShare() {
|
||||
if ($("#customThemeShareWrapper").hasClass("hidden")) {
|
||||
let save = [];
|
||||
$.each(
|
||||
$(".pageSettings .section.customTheme [type='color']"),
|
||||
(index, element) => {
|
||||
save.push($(element).attr("value"));
|
||||
}
|
||||
);
|
||||
$("#customThemeShareWrapper input").val(JSON.stringify(save));
|
||||
$("#customThemeShareWrapper")
|
||||
.stop(true, true)
|
||||
.css("opacity", 0)
|
||||
.removeClass("hidden")
|
||||
.animate({ opacity: 1 }, 100, (e) => {
|
||||
$("#customThemeShare input").focus();
|
||||
$("#customThemeShare input").select();
|
||||
$("#customThemeShare input").focus();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function hideCustomThemeShare() {
|
||||
if (!$("#customThemeShareWrapper").hasClass("hidden")) {
|
||||
try {
|
||||
config.customThemeColors = JSON.parse(
|
||||
$("#customThemeShareWrapper input").val()
|
||||
);
|
||||
} catch (e) {
|
||||
showNotification(
|
||||
"Something went wrong. Reverting to default custom colors.",
|
||||
3000
|
||||
);
|
||||
config.customThemeColors = defaultConfig.customThemeColors;
|
||||
}
|
||||
setCustomThemeInputs();
|
||||
applyCustomThemeColors();
|
||||
$("#customThemeShareWrapper input").val("");
|
||||
$("#customThemeShareWrapper")
|
||||
.stop(true, true)
|
||||
.css("opacity", 1)
|
||||
.animate(
|
||||
{
|
||||
opacity: 0,
|
||||
},
|
||||
100,
|
||||
(e) => {
|
||||
$("#customThemeShareWrapper").addClass("hidden");
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$("#customThemeShareWrapper").click((e) => {
|
||||
if ($(e.target).attr("id") === "customThemeShareWrapper") {
|
||||
hideCustomThemeShare();
|
||||
}
|
||||
});
|
||||
|
||||
$("#customThemeShare .button").click((e) => {
|
||||
hideCustomThemeShare();
|
||||
});
|
||||
|
||||
$("#shareCustomThemeButton").click((e) => {
|
||||
showCustomThemeShare();
|
||||
});
|
||||
|
||||
function refreshTagsSettingsSection() {
|
||||
if (firebase.auth().currentUser !== null && dbSnapshot !== null) {
|
||||
let tagsEl = $(".pageSettings .section.tags .tagsList").empty();
|
||||
|
@ -192,7 +259,9 @@ function setActiveTimerColorButton() {
|
|||
}
|
||||
|
||||
function setActiveTimerOpacityButton() {
|
||||
$(`.pageSettings .section.timerOpacity .buttons .button`).removeClass("active");
|
||||
$(`.pageSettings .section.timerOpacity .buttons .button`).removeClass(
|
||||
"active"
|
||||
);
|
||||
$(
|
||||
`.pageSettings .section.timerOpacity .buttons .button[opacity="` +
|
||||
config.timerOpacity +
|
||||
|
|
|
@ -42,7 +42,9 @@ let defaultConfig = {
|
|||
|
||||
let cookieConfig = null;
|
||||
|
||||
let config = defaultConfig;
|
||||
let config = {
|
||||
...defaultConfig,
|
||||
};
|
||||
|
||||
//cookies
|
||||
function saveConfigToCookie() {
|
||||
|
|
|
@ -38,6 +38,11 @@
|
|||
"bgColor": "#323437",
|
||||
"textColor": "#e2b714"
|
||||
},
|
||||
{
|
||||
"name": "nausea",
|
||||
"bgColor": "#323437",
|
||||
"textColor": "#e2b714"
|
||||
},
|
||||
{
|
||||
"name": "bushido",
|
||||
"bgColor": "#414755",
|
||||
|
|
63
public/themes/nausea.css
Normal file
63
public/themes/nausea.css
Normal file
|
@ -0,0 +1,63 @@
|
|||
:root {
|
||||
--bg-color: #323437;
|
||||
--main-color: #e2b714;
|
||||
--caret-color: #e2b714;
|
||||
--sub-color: #646669;
|
||||
--text-color: #d1d0c5;
|
||||
--error-color: #ca4754;
|
||||
--error-extra-color: #7e2a33;
|
||||
--colorful-error-color: #ca4754;
|
||||
--colorful-error-extra-color: #7e2a33;
|
||||
}
|
||||
|
||||
|
||||
@keyframes woah {
|
||||
0% {
|
||||
transform: rotateY(-15deg) skewY(10deg) rotateX(-15deg) scaleX(1.2) scaleY(.9);
|
||||
}
|
||||
|
||||
25% {
|
||||
transform: rotateY(15deg) skewY(-10deg) rotateX(15deg) scaleX(1) scaleY(.8);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: rotateY(-15deg) skewY(10deg) rotateX(-15deg) scaleX(0.9) scaleY(.9);
|
||||
}
|
||||
|
||||
75% {
|
||||
transform: rotateY(15deg) skewY(-10deg) rotateX(15deg) scaleX(1.5) scaleY(1.1);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotateY(-15deg) skewY(10deg) rotateX(-15deg) scaleX(1.2) scaleY(.9);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes plsstop {
|
||||
0% {
|
||||
background: #323437;
|
||||
}
|
||||
|
||||
50% {
|
||||
background: #3e4146;
|
||||
}
|
||||
|
||||
|
||||
100% {
|
||||
background: #323437;
|
||||
}
|
||||
}
|
||||
|
||||
#middle {
|
||||
animation: woah 7s infinite cubic-bezier(0.5, 0, 0.5, 1);
|
||||
}
|
||||
|
||||
#centerContent {
|
||||
transform: rotate(5deg);
|
||||
perspective: 500px;
|
||||
}
|
||||
|
||||
body {
|
||||
animation: plsstop 10s infinite cubic-bezier(0.5, 0, 0.5, 1);
|
||||
overflow: hidden;
|
||||
}
|
Loading…
Reference in a new issue