mirror of
https://github.com/monkeytypegame/monkeytype.git
synced 2025-11-18 14:55:34 +08:00
Merge branch 'master' into tribe
This commit is contained in:
commit
adcb2b4d09
22 changed files with 1039 additions and 620 deletions
|
|
@ -9,14 +9,14 @@
|
|||
|
||||
1. [Create a new Firebase project.](https://console.firebase.google.com/u/0/)
|
||||
|
||||
- The project name doesn't really matter, but just name it `monkey-type`.
|
||||
- The project name doesn't really matter, but just name it `monkeytype`.
|
||||
- Google Analytics is not necessary.
|
||||
|
||||
2. [Install the Firebase CLI](https://firebase.google.com/docs/cli)
|
||||
3. Run `firebase login` on your terminal to log in to the same google account as you just used to create the project.
|
||||
4. Git clone this project.
|
||||
5. Run `npm install` in the `functions/` directory
|
||||
6. Compile `public/css/style.scss` to `public/css/style.min.css` as [described below](https://github.com/Miodec/monkey-type/blob/master/CONTRIBUTING.md#standards--conventions)
|
||||
6. Compile `public/css/style.scss` to `public/css/style.min.css` as [described below](https://github.com/Miodec/monkeytype/blob/master/CONTRIBUTING.md#standards--conventions)
|
||||
7. Rename `.firebaserc_example` to `.firebaserc` and change the project name of default to the firebase project id you just created.
|
||||
|
||||
- If `.firebaserc_example` does not exist after cloning, create your own with:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# about
|
||||
|
||||
Monkey-type is a minimalistic, customisable typing test, featuring many test modes, an account system to save your typing speed history and user configurable features like themes, a smooth caret and more.
|
||||
Monkeytype is a minimalistic, customisable typing test, featuring many test modes, an account system to save your typing speed history and user configurable features like themes, a smooth caret and more.
|
||||
|
||||
# features
|
||||
|
||||
|
|
@ -36,4 +36,4 @@ If you wish to support further development and feeling extra awesome, you can do
|
|||
|
||||
# how to contribute
|
||||
|
||||
Refer to [CONTRIBUTING.md](https://github.com/Miodec/monkey-type/blob/master/CONTRIBUTING.md)
|
||||
Refer to [CONTRIBUTING.md](https://github.com/Miodec/monkeytype/blob/master/CONTRIBUTING.md)
|
||||
|
|
|
|||
|
|
@ -2,13 +2,6 @@
|
|||
"hosting": {
|
||||
"public": "public",
|
||||
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
|
||||
"redirects": [
|
||||
{
|
||||
"source": "/soon",
|
||||
"destination": "/",
|
||||
"type": 301
|
||||
}
|
||||
],
|
||||
"rewrites": [
|
||||
{
|
||||
"source": "**",
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ admin.initializeApp({
|
|||
});
|
||||
|
||||
const db = admin.firestore();
|
||||
const fetch = require("node-fetch");
|
||||
|
||||
async function getAllNames() {
|
||||
// return admin
|
||||
|
|
@ -261,6 +262,8 @@ function checkIfPB(uid, obj, userdata) {
|
|||
wpm: obj.wpm,
|
||||
acc: obj.acc,
|
||||
raw: obj.rawWpm,
|
||||
timestamp: Date.now(),
|
||||
consistency: obj.consistency
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
@ -284,6 +287,8 @@ function checkIfPB(uid, obj, userdata) {
|
|||
wpm: obj.wpm,
|
||||
acc: obj.acc,
|
||||
raw: obj.rawWpm,
|
||||
timestamp: Date.now(),
|
||||
consistency: obj.consistency
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
@ -315,6 +320,8 @@ function checkIfPB(uid, obj, userdata) {
|
|||
pb.wpm = obj.wpm;
|
||||
pb.acc = obj.acc;
|
||||
pb.raw = obj.rawWpm;
|
||||
pb.timestamp = Date.now();
|
||||
pb.consistency = obj.consistency;
|
||||
toUpdate = true;
|
||||
} else {
|
||||
//no pb
|
||||
|
|
@ -331,6 +338,8 @@ function checkIfPB(uid, obj, userdata) {
|
|||
wpm: obj.wpm,
|
||||
acc: obj.acc,
|
||||
raw: obj.rawWpm,
|
||||
timestamp: Date.now(),
|
||||
consistency: obj.consistency
|
||||
});
|
||||
toUpdate = true;
|
||||
}
|
||||
|
|
@ -345,6 +354,8 @@ function checkIfPB(uid, obj, userdata) {
|
|||
wpm: obj.wpm,
|
||||
acc: obj.acc,
|
||||
raw: obj.rawWpm,
|
||||
timestamp: Date.now(),
|
||||
consistency: obj.consistency
|
||||
},
|
||||
];
|
||||
toUpdate = true;
|
||||
|
|
@ -474,6 +485,55 @@ exports.getPatreons = functions.https.onRequest(async (request, response) => {
|
|||
}
|
||||
});
|
||||
|
||||
exports.verifyUser = functions.https.onRequest(async (request, response) => {
|
||||
response.set("Access-Control-Allow-Origin", "*");
|
||||
response.set("Access-Control-Allow-Headers", "*");
|
||||
response.set("Access-Control-Allow-Credentials", "true");
|
||||
if (request.method === "OPTIONS") {
|
||||
// Send response to OPTIONS requests
|
||||
response.set("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
|
||||
response.set("Access-Control-Allow-Headers", "Authorization,Content-Type");
|
||||
response.set("Access-Control-Max-Age", "3600");
|
||||
response.status(204).send("");
|
||||
return;
|
||||
}
|
||||
request = request.body.data;
|
||||
if (request.uid == undefined) {
|
||||
response.status(200).send({ data: { status: -1, message: "Need to provide uid" } });
|
||||
return;
|
||||
}
|
||||
try {
|
||||
return fetch("https://discord.com/api/users/@me", {
|
||||
headers: {
|
||||
authorization: `${request.tokenType} ${request.accessToken}`,
|
||||
},
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then(async (res2) => {
|
||||
let did = res2.id;
|
||||
await db.collection('users').doc(request.uid).update({
|
||||
discordId: did
|
||||
})
|
||||
await db.collection("bot-commands").add({
|
||||
command: "verify",
|
||||
arguments: [did,request.uid],
|
||||
executed: false,
|
||||
requestTimestamp: Date.now(),
|
||||
});
|
||||
response.status(200).send({ data: { status: 1, message: "Verified", did: did } });
|
||||
return;
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error('Something went wrong when trying to verify user ' + e.message);
|
||||
response.status(200).send({ data: { status: -1, message: e.message } });
|
||||
return;
|
||||
});
|
||||
} catch (e) {
|
||||
response.status(200).send({ data: { status: -1, message: e } });
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
async function incrementTestCounter(uid, userData) {
|
||||
try {
|
||||
if (userData.completedTests === undefined) {
|
||||
|
|
@ -651,9 +711,13 @@ exports.testCompleted = functions
|
|||
return;
|
||||
}
|
||||
request = request.body.data;
|
||||
if (request === undefined) {
|
||||
response.status(200).send({ data: { resultCode: -999 } });
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (request.uid === undefined || request.obj === undefined) {
|
||||
console.error(`error saving result for ${request.uid} - missing input`);
|
||||
console.error(`error saving result for - missing input`);
|
||||
response.status(200).send({ data: { resultCode: -999 } });
|
||||
return;
|
||||
}
|
||||
|
|
@ -771,7 +835,7 @@ exports.testCompleted = functions
|
|||
keySpacing.sd <= 15 ||
|
||||
keyDuration.sd <= 10 ||
|
||||
keyDuration.average < 15 ||
|
||||
(obj.wpm > 200 && obj.consistency < 60)
|
||||
(obj.wpm > 200 && obj.consistency < 70)
|
||||
) {
|
||||
console.error(
|
||||
`possible bot detected by user (${obj.wpm} ${obj.rawWpm} ${
|
||||
|
|
@ -789,7 +853,7 @@ exports.testCompleted = functions
|
|||
(keyDuration.average > 15 && keyDuration.average <= 20)
|
||||
) {
|
||||
console.error(
|
||||
`very close to bot threshold by user (${obj.wpm} ${
|
||||
`very close to bot detected threshold by user (${obj.wpm} ${
|
||||
obj.rawWpm
|
||||
} ${obj.acc}) ${
|
||||
request.uid
|
||||
|
|
@ -1434,143 +1498,138 @@ class Leaderboard {
|
|||
}
|
||||
}
|
||||
|
||||
exports.generatePairingCode = functions
|
||||
.runWith({
|
||||
timeoutSeconds: 100,
|
||||
memory: "2GB",
|
||||
})
|
||||
.https.onRequest((request, response) => {
|
||||
response.set("Access-Control-Allow-Origin", "*");
|
||||
if (request.method === "OPTIONS") {
|
||||
// Send response to OPTIONS requests
|
||||
response.set("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
|
||||
response.set(
|
||||
"Access-Control-Allow-Headers",
|
||||
"Authorization,Content-Type"
|
||||
);
|
||||
response.set("Access-Control-Max-Age", "3600");
|
||||
response.status(204).send("");
|
||||
return;
|
||||
}
|
||||
request = request.body.data;
|
||||
try {
|
||||
if (request === null) {
|
||||
console.error(
|
||||
`error while trying to generate discord pairing code - no input`
|
||||
);
|
||||
response.status(200).send({ data: { status: -999 } });
|
||||
return;
|
||||
}
|
||||
// exports.generatePairingCode = functions
|
||||
// .runWith({
|
||||
// timeoutSeconds: 100,
|
||||
// memory: "2GB",
|
||||
// })
|
||||
// .https.onRequest((request, response) => {
|
||||
// response.set("Access-Control-Allow-Origin", "*");
|
||||
// if (request.method === "OPTIONS") {
|
||||
// // Send response to OPTIONS requests
|
||||
// response.set("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
|
||||
// response.set(
|
||||
// "Access-Control-Allow-Headers",
|
||||
// "Authorization,Content-Type"
|
||||
// );
|
||||
// response.set("Access-Control-Max-Age", "3600");
|
||||
// response.status(204).send("");
|
||||
// return;
|
||||
// }
|
||||
// request = request.body.data;
|
||||
// try {
|
||||
// if (request === null) {
|
||||
// console.error(
|
||||
// `error while trying to generate discord pairing code - no input`
|
||||
// );
|
||||
// response.status(200).send({ data: { status: -999 } });
|
||||
// return;
|
||||
// }
|
||||
|
||||
return db
|
||||
.collection("users")
|
||||
.doc(request.uid)
|
||||
.get()
|
||||
.then(async (userDoc) => {
|
||||
userDocData = userDoc.data();
|
||||
if (
|
||||
userDocData.discordPairingCode !== undefined &&
|
||||
userDocData.discordPairingCode !== null
|
||||
) {
|
||||
console.log(
|
||||
`user ${request.uid} already has code ${userDocData.discordPairingCode}`
|
||||
);
|
||||
response.status(200).send({
|
||||
data: {
|
||||
status: -999,
|
||||
pairingCode: userDocData.discordPairingCode,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
let stepSize = 1000;
|
||||
let existingCodes = [];
|
||||
let query = await db
|
||||
.collection(`users`)
|
||||
.where("discordPairingCode", ">", "")
|
||||
.limit(stepSize)
|
||||
.get();
|
||||
let lastDoc;
|
||||
while (query.docs.length > 0) {
|
||||
lastDoc = query.docs[query.docs.length - 1];
|
||||
query.docs.forEach((doc) => {
|
||||
let docData = doc.data();
|
||||
if (
|
||||
docData.discordPairingCode !== undefined &&
|
||||
docData.discordPairingCode !== null
|
||||
) {
|
||||
existingCodes.push(docData.discordPairingCode);
|
||||
}
|
||||
});
|
||||
query = await db
|
||||
.collection(`users`)
|
||||
.where("discordPairingCode", ">", "")
|
||||
.limit(stepSize)
|
||||
.startAfter(lastDoc)
|
||||
.get();
|
||||
}
|
||||
// return db
|
||||
// .collection("users")
|
||||
// .doc(request.uid)
|
||||
// .get()
|
||||
// .then(async (userDoc) => {
|
||||
// userDocData = userDoc.data();
|
||||
// if (
|
||||
// userDocData.discordPairingCode !== undefined &&
|
||||
// userDocData.discordPairingCode !== null
|
||||
// ) {
|
||||
// console.log(
|
||||
// `user ${request.uid} already has code ${userDocData.discordPairingCode}`
|
||||
// );
|
||||
// response.status(200).send({
|
||||
// data: {
|
||||
// status: -999,
|
||||
// pairingCode: userDocData.discordPairingCode,
|
||||
// },
|
||||
// });
|
||||
// } else {
|
||||
// let stepSize = 1000;
|
||||
// let existingCodes = [];
|
||||
// let query = await db
|
||||
// .collection(`users`)
|
||||
// .where("discordPairingCode", ">", "")
|
||||
// .limit(stepSize)
|
||||
// .get();
|
||||
// let lastDoc;
|
||||
// while (query.docs.length > 0) {
|
||||
// lastDoc = query.docs[query.docs.length - 1];
|
||||
// query.docs.forEach((doc) => {
|
||||
// let docData = doc.data();
|
||||
// if (
|
||||
// docData.discordPairingCode !== undefined &&
|
||||
// docData.discordPairingCode !== null
|
||||
// ) {
|
||||
// existingCodes.push(docData.discordPairingCode);
|
||||
// }
|
||||
// });
|
||||
// query = await db
|
||||
// .collection(`users`)
|
||||
// .where("discordPairingCode", ">", "")
|
||||
// .limit(stepSize)
|
||||
// .startAfter(lastDoc)
|
||||
// .get();
|
||||
// }
|
||||
|
||||
let randomCode = generate(9);
|
||||
// let randomCode = generate(9);
|
||||
|
||||
while (existingCodes.includes(randomCode)) {
|
||||
randomCode = generate(9);
|
||||
}
|
||||
// while (existingCodes.includes(randomCode)) {
|
||||
// randomCode = generate(9);
|
||||
// }
|
||||
|
||||
return db
|
||||
.collection("users")
|
||||
.doc(request.uid)
|
||||
.update(
|
||||
{
|
||||
discordPairingCode: randomCode,
|
||||
},
|
||||
{ merge: true }
|
||||
)
|
||||
.then((res) => {
|
||||
console.log(`generated ${randomCode} for user ${request.uid}`);
|
||||
response.status(200).send({
|
||||
data: {
|
||||
status: 1,
|
||||
pairingCode: randomCode,
|
||||
},
|
||||
});
|
||||
return;
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(
|
||||
`error while trying to set discord pairing code ${randomCode} for user ${request.uid} - ${e}`
|
||||
);
|
||||
response.status(200).send({
|
||||
data: {
|
||||
status: -999,
|
||||
},
|
||||
});
|
||||
return;
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(
|
||||
`error while trying to generate discord pairing code for user ${request.uid} - ${e}`
|
||||
);
|
||||
response.status(200).send({
|
||||
data: {
|
||||
status: -999,
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
});
|
||||
// return db
|
||||
// .collection("users")
|
||||
// .doc(request.uid)
|
||||
// .update(
|
||||
// {
|
||||
// discordPairingCode: randomCode,
|
||||
// },
|
||||
// { merge: true }
|
||||
// )
|
||||
// .then((res) => {
|
||||
// console.log(`generated ${randomCode} for user ${request.uid}`);
|
||||
// response.status(200).send({
|
||||
// data: {
|
||||
// status: 1,
|
||||
// pairingCode: randomCode,
|
||||
// },
|
||||
// });
|
||||
// return;
|
||||
// })
|
||||
// .catch((e) => {
|
||||
// console.error(
|
||||
// `error while trying to set discord pairing code ${randomCode} for user ${request.uid} - ${e}`
|
||||
// );
|
||||
// response.status(200).send({
|
||||
// data: {
|
||||
// status: -999,
|
||||
// },
|
||||
// });
|
||||
// return;
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
// } catch (e) {
|
||||
// console.error(
|
||||
// `error while trying to generate discord pairing code for user ${request.uid} - ${e}`
|
||||
// );
|
||||
// response.status(200).send({
|
||||
// data: {
|
||||
// status: -999,
|
||||
// },
|
||||
// });
|
||||
// return;
|
||||
// }
|
||||
// });
|
||||
|
||||
|
||||
|
||||
exports.unlinkDiscord = functions.https.onRequest((request, response) => {
|
||||
response.set("Access-Control-Allow-Origin", "*");
|
||||
if (request.method === "OPTIONS") {
|
||||
// Send response to OPTIONS requests
|
||||
response.set("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
|
||||
response.set(
|
||||
"Access-Control-Allow-Headers",
|
||||
"Authorization,Content-Type"
|
||||
);
|
||||
response.set("Access-Control-Allow-Headers", "Authorization,Content-Type");
|
||||
response.set("Access-Control-Max-Age", "3600");
|
||||
response.status(204).send("");
|
||||
return;
|
||||
|
|
@ -1578,40 +1637,46 @@ exports.unlinkDiscord = functions.https.onRequest((request, response) => {
|
|||
request = request.body.data;
|
||||
try {
|
||||
if (request === null || request.uid === undefined) {
|
||||
response.status(200).send({ data: { status: -999, message: "Empty request" } });
|
||||
response
|
||||
.status(200)
|
||||
.send({ data: { status: -999, message: "Empty request" } });
|
||||
return;
|
||||
}
|
||||
return db.collection(`users`).doc(request.uid).update({
|
||||
discordId: null
|
||||
}).then(f => {
|
||||
response.status(200).send({
|
||||
data: {
|
||||
status: 1,
|
||||
message: "Unlinked"
|
||||
},
|
||||
return db
|
||||
.collection(`users`)
|
||||
.doc(request.uid)
|
||||
.update({
|
||||
discordId: null,
|
||||
})
|
||||
.then((f) => {
|
||||
response.status(200).send({
|
||||
data: {
|
||||
status: 1,
|
||||
message: "Unlinked",
|
||||
},
|
||||
});
|
||||
return;
|
||||
})
|
||||
.catch((e) => {
|
||||
response.status(200).send({
|
||||
data: {
|
||||
status: -999,
|
||||
message: e.message,
|
||||
},
|
||||
});
|
||||
return;
|
||||
});
|
||||
return;
|
||||
}).catch(e => {
|
||||
response.status(200).send({
|
||||
data: {
|
||||
status: -999,
|
||||
message: e.message
|
||||
},
|
||||
});
|
||||
return;
|
||||
})
|
||||
} catch (e) {
|
||||
response.status(200).send({
|
||||
data: {
|
||||
status: -999,
|
||||
message: e
|
||||
message: e,
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
async function checkLeaderboards(
|
||||
resultObj,
|
||||
type,
|
||||
|
|
|
|||
35
package-lock.json
generated
35
package-lock.json
generated
|
|
@ -611,7 +611,7 @@
|
|||
"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
|
||||
"integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
|
||||
"requires": {
|
||||
"lodash": ">=4.17.19"
|
||||
"lodash": "^4.17.14"
|
||||
}
|
||||
},
|
||||
"async-each": {
|
||||
|
|
@ -1773,7 +1773,7 @@
|
|||
"jsonschema": "^1.0.2",
|
||||
"jsonwebtoken": "^8.2.1",
|
||||
"leven": "^3.1.0",
|
||||
"lodash": ">=4.17.19",
|
||||
"lodash": "^4.17.14",
|
||||
"marked": "^0.7.0",
|
||||
"marked-terminal": "^3.3.0",
|
||||
"minimatch": "^3.0.4",
|
||||
|
|
@ -2272,7 +2272,7 @@
|
|||
"cli-width": "^2.0.0",
|
||||
"external-editor": "^3.0.3",
|
||||
"figures": "^2.0.0",
|
||||
"lodash": ">=4.17.19",
|
||||
"lodash": "^4.17.11",
|
||||
"mute-stream": "0.0.7",
|
||||
"run-async": "^2.2.0",
|
||||
"rxjs": "^6.4.0",
|
||||
|
|
@ -2429,6 +2429,17 @@
|
|||
"requires": {
|
||||
"node-fetch": "^1.0.1",
|
||||
"whatwg-fetch": ">=0.10.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"node-fetch": {
|
||||
"version": "1.7.3",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
|
||||
"integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==",
|
||||
"requires": {
|
||||
"encoding": "^0.1.11",
|
||||
"is-stream": "^1.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"isstream": {
|
||||
|
|
@ -2979,7 +2990,7 @@
|
|||
"requires": {
|
||||
"async": "^1.3.0",
|
||||
"flat-arguments": "^1.0.0",
|
||||
"lodash": ">=4.17.19",
|
||||
"lodash": "^4.17.5",
|
||||
"minimist": "^1.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
|
|
@ -3014,13 +3025,9 @@
|
|||
}
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "1.7.3",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
|
||||
"integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==",
|
||||
"requires": {
|
||||
"encoding": "^0.1.11",
|
||||
"is-stream": "^1.0.1"
|
||||
}
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
|
||||
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
|
||||
},
|
||||
"node-forge": {
|
||||
"version": "0.9.1",
|
||||
|
|
@ -3712,7 +3719,7 @@
|
|||
"home-dir": "^1.0.0",
|
||||
"is-url": "^1.2.2",
|
||||
"join-path": "^1.1.1",
|
||||
"lodash": ">=4.17.19",
|
||||
"lodash": "^4.17.4",
|
||||
"mime-types": "^2.1.16",
|
||||
"minimatch": "^3.0.4",
|
||||
"morgan": "^1.8.2",
|
||||
|
|
@ -3958,7 +3965,7 @@
|
|||
"resolved": "https://registry.npmjs.org/toxic/-/toxic-1.0.1.tgz",
|
||||
"integrity": "sha512-WI3rIGdcaKULYg7KVoB0zcjikqvcYYvcuT6D89bFPz2rVR0Rl0PK6x8/X62rtdLtBKIE985NzVf/auTtGegIIg==",
|
||||
"requires": {
|
||||
"lodash": ">=4.17.19"
|
||||
"lodash": "^4.17.10"
|
||||
}
|
||||
},
|
||||
"traverse": {
|
||||
|
|
@ -4233,7 +4240,7 @@
|
|||
"requires": {
|
||||
"http-parser-js": ">=0.4.0 <0.4.11",
|
||||
"safe-buffer": ">=5.1.0",
|
||||
"websocket-extensions": ">=0.1.4"
|
||||
"websocket-extensions": ">=0.1.1"
|
||||
}
|
||||
},
|
||||
"websocket-extensions": {
|
||||
|
|
|
|||
|
|
@ -1470,6 +1470,7 @@ key {
|
|||
width: 100%;
|
||||
align-content: flex-start;
|
||||
user-select: none;
|
||||
padding-bottom: 1em;
|
||||
|
||||
/* a little hack for right-to-left languages */
|
||||
&.rightToLeftTest {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Monkey Type</title>
|
||||
<title>Monkeytype</title>
|
||||
<!-- <link rel="stylesheet" href="css/fa.css" /> -->
|
||||
<link rel="stylesheet" href="css/balloon.css" />
|
||||
<link rel="stylesheet" href="css/style.min.css?v=58" />
|
||||
|
|
@ -11,34 +11,28 @@
|
|||
<link rel="stylesheet" href="" id="funBoxTheme" />
|
||||
<link id="favicon" rel="shortcut icon" href="fav.png" />
|
||||
<link rel="shortcut icon" href="fav.png" />
|
||||
<meta name="name" content="Monkey Type" />
|
||||
<meta name="image" content="https://monkey-type.com/mtsocial_large.png" />
|
||||
<meta name="name" content="Monkeytype" />
|
||||
<meta name="image" content="https://monkeytype.com/mtsocial.png" />
|
||||
<meta
|
||||
name="description"
|
||||
content="A super customisable, minimalistic typing test."
|
||||
content="A minimalistic, customisable typing website. Test yourself in various modes, track your progress and improve your typing speed."
|
||||
/>
|
||||
<meta
|
||||
name="keywords"
|
||||
content="typing, test, typing-test, typing test, monkey-type, monkeytype, monkey type, monkey-types, monkey types, types, monkey, type, miodec, wpm, words per minute, typing website, minimalistic, custom typing test, customizable, customisable, themes, random words, smooth caret, smooth, new, new typing site, new typing website, minimalist typing website, minimalistic typing website, minimalist typing test"
|
||||
content="typing, test, typing-test, typing test, monkey-type, monkeytype, monkey type, monkey-types, monkeytypes, monkey types, types, monkey, type, miodec, wpm, words per minute, typing website, minimalistic, custom typing test, customizable, customisable, themes, random words, smooth caret, smooth, new, new typing site, new typing website, minimalist typing website, minimalistic typing website, minimalist typing test"
|
||||
/>
|
||||
<meta name="author" content="Miodec" />
|
||||
<meta property="og:title" content="Monkey Type" />
|
||||
<meta property="og:url" content="https://monkey-type.com/" />
|
||||
<meta property="og:title" content="Monkeytype" />
|
||||
<meta property="og:url" content="https://monkeytype.com/" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta
|
||||
property="og:description"
|
||||
content="A super customisable, minimalistic typing test."
|
||||
/>
|
||||
<meta
|
||||
property="og:image"
|
||||
content="https://monkey-type.com/mtsocial_large.png"
|
||||
content="A minimalistic, customisable typing website. Test yourself in various modes, track your progress and improve your typing speed."
|
||||
/>
|
||||
<meta property="og:image" content="https://monkeytype.com/mtsocial.png" />
|
||||
<meta name="theme-color" content="#e2b714" id="metaThemeColor" />
|
||||
<meta name="twitter:title" content="Monkey Type" />
|
||||
<meta
|
||||
name="twitter:image"
|
||||
content="https://monkey-type.com/mtsocial_large.png"
|
||||
/>
|
||||
<meta name="twitter:title" content="Monkeytype" />
|
||||
<meta name="twitter:image" content="https://monkeytype.com/mtsocial.png" />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
</head>
|
||||
|
||||
|
|
@ -749,7 +743,7 @@
|
|||
<div class="suggestions"></div>
|
||||
</div>
|
||||
<div id="commandInput" class="hidden">
|
||||
<input type="text" class="input" placeholder="input"></input>
|
||||
<input type="text" class="input" placeholder="input" />
|
||||
</div>
|
||||
</div>
|
||||
<div id="timerWrapper">
|
||||
|
|
@ -758,17 +752,22 @@
|
|||
<div id="centerContent" class="hidden">
|
||||
<div id="top">
|
||||
<div class="logo">
|
||||
<div class="top">monkey-see</div>
|
||||
<div class="bottom">monkey-type</div>
|
||||
<div class="top">monkey see</div>
|
||||
<div class="bottom">monkeytype</div>
|
||||
</div>
|
||||
<div id="menu">
|
||||
<div class="icon-button" tabindex="2" href="/" onclick="this.blur();">
|
||||
<div
|
||||
class="icon-button view-start"
|
||||
tabindex="2"
|
||||
href="/"
|
||||
onclick="this.blur();"
|
||||
>
|
||||
<div class="icon">
|
||||
<i class="fas fa-fw fa-keyboard"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="icon-button leaderboards"
|
||||
class="icon-button leaderboards view-leaderboards"
|
||||
tabindex="2"
|
||||
onclick="this.blur();"
|
||||
>
|
||||
|
|
@ -777,7 +776,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="icon-button"
|
||||
class="icon-button view-about"
|
||||
tabindex="2"
|
||||
href="/about"
|
||||
onclick="this.blur();"
|
||||
|
|
@ -799,7 +798,7 @@
|
|||
</div>
|
||||
</a> -->
|
||||
<div
|
||||
class="icon-button"
|
||||
class="icon-button view-settings"
|
||||
tabindex="2"
|
||||
href="/settings"
|
||||
onclick="this.blur();"
|
||||
|
|
@ -809,7 +808,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="icon-button hidden account"
|
||||
class="icon-button hidden account view-account"
|
||||
tabindex="2"
|
||||
href="/account"
|
||||
onclick="this.blur();"
|
||||
|
|
@ -820,7 +819,7 @@
|
|||
<div class="text"></div>
|
||||
</div>
|
||||
<div
|
||||
class="icon-button login"
|
||||
class="icon-button login view-login"
|
||||
tabindex="2"
|
||||
href="/login"
|
||||
onclick="this.blur();"
|
||||
|
|
@ -832,12 +831,7 @@
|
|||
</div>
|
||||
|
||||
<div class="config">
|
||||
<div
|
||||
style="
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
"
|
||||
>
|
||||
<div style="display: grid; grid-auto-flow: column;">
|
||||
<div class="group punctuationMode">
|
||||
<!-- <div class="title">time</div> -->
|
||||
<div class="buttons">
|
||||
|
|
@ -928,7 +922,6 @@
|
|||
</div>
|
||||
<div id="middle">
|
||||
<div class="page pageTest active">
|
||||
|
||||
<div id="typingTest">
|
||||
<div id="capsWarning" class="hidden">
|
||||
<i class="fas fa-lock"></i>
|
||||
|
|
@ -936,14 +929,17 @@
|
|||
</div>
|
||||
<div id="testModesNotice"></div>
|
||||
<div id="caret" class="default size15"></div>
|
||||
<div id="paceCaret"class="default size15 hidden"></div>
|
||||
<div id="paceCaret" class="default size15 hidden"></div>
|
||||
<input id="wordsInput" class="" tabindex="0" autocomplete="off" />
|
||||
<div id="timerNumber"><div>60</div></div>
|
||||
<div id="miniTimerAndLiveWpm">
|
||||
<div class="time hidden">1:00</div>
|
||||
<div class="wpm">60</div>
|
||||
</div>
|
||||
<div class="outOfFocusWarning"><i class="fas fa-mouse-pointer"></i> Click the words to focus</div>
|
||||
<div class="outOfFocusWarning">
|
||||
<i class="fas fa-mouse-pointer"></i>
|
||||
Click the words to focus
|
||||
</div>
|
||||
<div id="wordsWrapper">
|
||||
<div id="words" class="size15"></div>
|
||||
</div>
|
||||
|
|
@ -1157,10 +1153,10 @@
|
|||
<div class="group wpm">
|
||||
<div class="top">
|
||||
wpm
|
||||
<div class="crown hidden" aria-label="" data-balloon-pos="up">
|
||||
<i class="fas fa-crown"></i>
|
||||
</div>
|
||||
<div class="crown hidden" aria-label="" data-balloon-pos="up">
|
||||
<i class="fas fa-crown"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bottom" aria-label="" data-balloon-pos="up">-</div>
|
||||
</div>
|
||||
<div class="group acc">
|
||||
|
|
@ -1190,8 +1186,15 @@
|
|||
<div class="bottom" aria-label="" data-balloon-pos="up">-</div>
|
||||
</div>
|
||||
<div class="group key">
|
||||
<div class="top">char</div>
|
||||
<div class="bottom" aria-label="" data-balloon-pos="up">-</div>
|
||||
<div class="top">characters</div>
|
||||
<div
|
||||
class="bottom"
|
||||
aria-label="Correct, incorrect, extra and missed"
|
||||
data-balloon-break=""
|
||||
data-balloon-pos="up"
|
||||
>
|
||||
-
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- </div> -->
|
||||
|
|
@ -1237,7 +1240,7 @@
|
|||
<div class="words"></div>
|
||||
</div>
|
||||
<div class="loginTip">Sign in to save your results</div>
|
||||
<div class="ssWatermark hidden">monkey-type.com</div>
|
||||
<div class="ssWatermark hidden">monkeytype.com</div>
|
||||
<div class="buttons">
|
||||
<div
|
||||
id="nextTestButton"
|
||||
|
|
@ -1291,7 +1294,7 @@
|
|||
<div class="section">
|
||||
<h1>about</h1>
|
||||
<p>
|
||||
Monkey-type is a minimalistic typing test, featuring many test
|
||||
Monkeytype is a minimalistic typing test, featuring many test
|
||||
modes, an account system to save your typing speed history and
|
||||
user configurable features like themes, a smooth caret and more.
|
||||
</p>
|
||||
|
|
@ -1362,9 +1365,10 @@
|
|||
Discord server
|
||||
</a>
|
||||
, send me a message on Reddit or create an issue on
|
||||
<a href="https://github.com/Miodec/monkey-type" target="_blank">
|
||||
GitHub.
|
||||
<a href="https://github.com/Miodec/monkeytype" target="_blank">
|
||||
GitHub
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
</div>
|
||||
<div class="section">
|
||||
|
|
@ -1397,7 +1401,7 @@
|
|||
</p>
|
||||
<p>
|
||||
<a
|
||||
href="https://github.com/Miodec/monkey-type/graphs/contributors"
|
||||
href="https://github.com/Miodec/monkeytype/graphs/contributors"
|
||||
>
|
||||
Contributors
|
||||
</a>
|
||||
|
|
@ -1451,36 +1455,33 @@
|
|||
<div class="section discordIntegration">
|
||||
<h1>discord integration</h1>
|
||||
<div class="text">
|
||||
When you connect your monkey-type account to your Discord
|
||||
When you connect your monkeytype account to your Discord
|
||||
account, you will be automatically assigned a new role every
|
||||
time you achieve a new personal best in a 60 second test.
|
||||
<div class="howto hidden">
|
||||
To pair your accounts, direct message 'George' (our bot) on
|
||||
Discord with the message '!verify
|
||||
<span class="howtocode">-</span>
|
||||
'.
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<div class="button generateCodeButton">
|
||||
Generate pairing code
|
||||
</div>
|
||||
<a
|
||||
class="button"
|
||||
href="https://discord.com/api/oauth2/authorize?client_id=757704816532258856&redirect_uri=https%3A%2F%2Fmonkeytype.com%2Fverify&response_type=token&scope=identify"
|
||||
style="text-decoration: none;"
|
||||
>
|
||||
Verify with Discord
|
||||
</a>
|
||||
</div>
|
||||
<div class="info hidden">
|
||||
<div>
|
||||
<i class="fas fa-check"></i>
|
||||
Your accounts are paired!
|
||||
</div>
|
||||
<div id="unlinkDiscordButton" class="text-button" aria-label="Unlink" data-balloon-pos="up">
|
||||
<div
|
||||
id="unlinkDiscordButton"
|
||||
class="text-button"
|
||||
aria-label="Unlink"
|
||||
data-balloon-pos="up"
|
||||
>
|
||||
<i class="fas fa-unlink" aria-hidden="true"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="code hidden">
|
||||
<div style="width: min-content; white-space: nowrap;">
|
||||
<div class="top">code</div>
|
||||
<div class="bottom">-</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sectionSpacer"></div>
|
||||
</div>
|
||||
|
|
@ -1543,8 +1544,6 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="section blindMode">
|
||||
<h1>blind mode</h1>
|
||||
<div class="text">
|
||||
|
|
@ -1607,6 +1606,34 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section singleListCommandLine">
|
||||
<h1>single list command line</h1>
|
||||
<div class="text">
|
||||
When enabled, it will show the command line with all commands in
|
||||
a single list instead of submenu arrangements. Selecting
|
||||
'manual' will expose all commands only after typing
|
||||
<key>></key>
|
||||
.
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<div
|
||||
class="button"
|
||||
singleListCommandLine="manual"
|
||||
tabindex="0"
|
||||
onclick="this.blur();"
|
||||
>
|
||||
manual
|
||||
</div>
|
||||
<div
|
||||
class="button"
|
||||
singleListCommandLine="on"
|
||||
tabindex="0"
|
||||
onclick="this.blur();"
|
||||
>
|
||||
on
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section language">
|
||||
<h1>language</h1>
|
||||
<div class="buttons"></div>
|
||||
|
|
@ -1923,7 +1950,14 @@
|
|||
>
|
||||
custom
|
||||
</div>
|
||||
<input type="number" step="1" class="customPaceCaretSpeed" placeholder="wpm" min="0" value="">
|
||||
<input
|
||||
type="number"
|
||||
step="1"
|
||||
class="customPaceCaretSpeed"
|
||||
placeholder="wpm"
|
||||
min="0"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section paceCaretStyle" section="">
|
||||
|
|
@ -2137,7 +2171,8 @@
|
|||
<div class="section startGraphsAtZero">
|
||||
<h1>start graphs at zero</h1>
|
||||
<div class="text">
|
||||
Force graph axis to always start at zero, no matter what the data is. Turning this off may exaggerate the value changes.
|
||||
Force graph axis to always start at zero, no matter what the
|
||||
data is. Turning this off may exaggerate the value changes.
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<div class="button off" tabindex="0" onclick="this.blur();">
|
||||
|
|
@ -2560,7 +2595,8 @@
|
|||
<div class="section showOutOfFocusWarning">
|
||||
<h1>out of focus warning</h1>
|
||||
<div class="text">
|
||||
Shows an out of focus reminder after 1 second of being 'out of focus' (not being able to type).
|
||||
Shows an out of focus reminder after 1 second of being 'out of
|
||||
focus' (not being able to type).
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<div class="button off" tabindex="0" onclick="this.blur();">
|
||||
|
|
@ -2605,12 +2641,9 @@
|
|||
<div class="register side">
|
||||
<div class="title">register</div>
|
||||
<form action="" autocomplete="off">
|
||||
<input type="text" placeholder="username" />
|
||||
<input type="text" placeholder="username" />
|
||||
<input type="text" placeholder="email" />
|
||||
<input
|
||||
type="password"
|
||||
placeholder="password"
|
||||
/>
|
||||
<input type="password" placeholder="password" />
|
||||
<input type="password" placeholder="verify password" />
|
||||
<div class="button">
|
||||
<i class="fas fa-user-plus"></i>
|
||||
|
|
@ -2622,8 +2655,12 @@
|
|||
<div class="title">login</div>
|
||||
<div id="forgotPasswordButton">Forgot password?</div>
|
||||
<form action="">
|
||||
<input type="text" placeholder="email" autocomplete="email"/>
|
||||
<input type="password" placeholder="password" autocomplete="password"/>
|
||||
<input type="text" placeholder="email" autocomplete="email" />
|
||||
<input
|
||||
type="password"
|
||||
placeholder="password"
|
||||
autocomplete="password"
|
||||
/>
|
||||
<div>
|
||||
<label id="rememberMe">
|
||||
<input type="checkbox" checked />
|
||||
|
|
@ -2667,9 +2704,7 @@
|
|||
</div>
|
||||
<div class="group">
|
||||
<div class="title">personal bests</div>
|
||||
<div style="display: grid;
|
||||
grid-auto-flow: column;
|
||||
gap: 1rem;">
|
||||
<div style="display: grid; grid-auto-flow: column; gap: 1rem;">
|
||||
<div class="titleAndTable timePbTable">
|
||||
<table width="100%">
|
||||
<thead>
|
||||
|
|
@ -2678,6 +2713,7 @@
|
|||
<td>wpm</td>
|
||||
<td>raw</td>
|
||||
<td>accuracy</td>
|
||||
<td>consistency</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
@ -2716,6 +2752,7 @@
|
|||
<td>wpm</td>
|
||||
<td>raw</td>
|
||||
<td>accuracy</td>
|
||||
<td>consistency</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
@ -2768,7 +2805,10 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttonsAndTitle testDate" style="grid-column: 1/3;margin-top: 1rem">
|
||||
<div
|
||||
class="buttonsAndTitle testDate"
|
||||
style="grid-column: 1/3; margin-top: 1rem;"
|
||||
>
|
||||
<!-- <div class="title">date</div> -->
|
||||
<div class="buttons filterGroup" group="date">
|
||||
<div class="button" filter="last_day">last day</div>
|
||||
|
|
@ -2778,7 +2818,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="group filterButtons" style="display:none">
|
||||
<div class="group filterButtons" style="display: none;">
|
||||
<div class="buttonsAndTitle" style="grid-column: 1/3;">
|
||||
<div class="title">advanced filters</div>
|
||||
<div class="buttons">
|
||||
|
|
@ -2861,7 +2901,10 @@
|
|||
<canvas id="resultHistoryChart"></canvas>
|
||||
</div>
|
||||
<div class="below"></div>
|
||||
<div class="toggleAccuracyOnChart button"><i class="fas fa-bullseye"></i>Toggle Accuracy</div>
|
||||
<div class="toggleAccuracyOnChart button">
|
||||
<i class="fas fa-bullseye"></i>
|
||||
Toggle Accuracy
|
||||
</div>
|
||||
</div>
|
||||
<div class="group dailyActivityChart">
|
||||
<div class="chart" style="height: 200px;">
|
||||
|
|
@ -2914,7 +2957,13 @@
|
|||
<div class="group testsCompleted">
|
||||
<div class="title">
|
||||
tests completed
|
||||
<span data-balloon-length="xlarge" aria-label="Due to the increasing number of results in the database, you can now only see your last 1000 results in detail. Total time spent typing, started and completed tests stats will still be up to date at the top of the page, above the filters." data-balloon-pos="up"><i class="fas fa-question-circle"></i></span>
|
||||
<span
|
||||
data-balloon-length="xlarge"
|
||||
aria-label="Due to the increasing number of results in the database, you can now only see your last 1000 results in detail. Total time spent typing, started and completed tests stats will still be up to date at the top of the page, above the filters."
|
||||
data-balloon-pos="up"
|
||||
>
|
||||
<i class="fas fa-question-circle"></i>
|
||||
</span>
|
||||
</div>
|
||||
<div class="val">-</div>
|
||||
</div>
|
||||
|
|
@ -3016,9 +3065,9 @@
|
|||
<div>
|
||||
<i class="fas fa-fw fa-code"></i>
|
||||
Contribute on
|
||||
<a
|
||||
href="https://github.com/Miodec/monkey-type"
|
||||
target="_blank">GitHub</a>
|
||||
<a href="https://github.com/Miodec/monkeytype" target="_blank">
|
||||
GitHub
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<i class="fas fa-fw fa-donate"></i>
|
||||
|
|
@ -3037,7 +3086,9 @@
|
|||
</div>
|
||||
<div>
|
||||
<i class="fab fa-discord"></i>
|
||||
<a href="https://www.discord.gg/monkeytype" class="discordLink">discord.gg/monkeytype</a>
|
||||
<a href="https://www.discord.gg/monkeytype" class="discordLink">
|
||||
discord.gg/monkeytype
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<i class="fas fa-code-branch"></i>
|
||||
|
|
@ -3060,7 +3111,10 @@
|
|||
|
||||
<!-- Initialize Firebase -->
|
||||
<script src="/__/firebase/init.js"></script>
|
||||
<script src="https://kit.fontawesome.com/f3f87d89b4.js" crossorigin="anonymous"></script>
|
||||
<script
|
||||
src="https://kit.fontawesome.com/f3f87d89b4.js"
|
||||
crossorigin="anonymous"
|
||||
></script>
|
||||
<script src="js/jquery-3.5.1.min.js"></script>
|
||||
<script src="js/jquery.color.min.js"></script>
|
||||
<script src="js/easing.js"></script>
|
||||
|
|
|
|||
|
|
@ -261,6 +261,17 @@ firebase.auth().onAuthStateChanged(function (user) {
|
|||
// showNotification('Signed in', 1000);
|
||||
$(".pageLogin .preloader").addClass("hidden");
|
||||
$("#menu .icon-button.account .text").text(displayName);
|
||||
if (verifyUserWhenLoggedIn !== null) {
|
||||
showNotification('Verifying', 1000);
|
||||
verifyUserWhenLoggedIn.uid = user.uid;
|
||||
verifyUser(verifyUserWhenLoggedIn).then(data => {
|
||||
showNotification(data.data.message, 3000);
|
||||
if (data.data.status === 1) {
|
||||
dbSnapshot.discordId = data.data.did;
|
||||
updateDiscordSettingsSection()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -536,6 +547,16 @@ let activityChart = new Chart($(".pageAccount #activityChart"), {
|
|||
lineStyle: "dotted",
|
||||
width: 2,
|
||||
},
|
||||
order: 3
|
||||
},
|
||||
{
|
||||
yAxisID: "avgWpm",
|
||||
label: "Average Wpm",
|
||||
data: [],
|
||||
type: "line",
|
||||
order: 2,
|
||||
lineTension: 0,
|
||||
fill: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
@ -612,6 +633,28 @@ let activityChart = new Chart($(".pageAccount #activityChart"), {
|
|||
fontFamily: "Roboto Mono",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "avgWpm",
|
||||
ticks: {
|
||||
fontFamily: "Roboto Mono",
|
||||
beginAtZero: true,
|
||||
min: 0,
|
||||
autoSkip: true,
|
||||
stepSize: 1,
|
||||
autoSkipPadding: 40,
|
||||
stepSize: 10,
|
||||
},
|
||||
display: true,
|
||||
position: "right",
|
||||
scaleLabel: {
|
||||
display: true,
|
||||
labelString: "Average Wpm",
|
||||
fontFamily: "Roboto Mono",
|
||||
},
|
||||
gridLines: {
|
||||
display: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
|
|
@ -849,6 +892,9 @@ function updateHoverChart(filteredId) {
|
|||
if (!config.startGraphsAtZero) {
|
||||
hoverChart.options.scales.yAxes[0].ticks.min = Math.round(minChartVal);
|
||||
hoverChart.options.scales.yAxes[1].ticks.min = Math.round(minChartVal);
|
||||
} else {
|
||||
hoverChart.options.scales.yAxes[0].ticks.min = 0;
|
||||
hoverChart.options.scales.yAxes[1].ticks.min = 0;
|
||||
}
|
||||
|
||||
hoverChart.update({ duration: 0 });
|
||||
|
|
@ -1452,24 +1498,28 @@ function fillPbTables() {
|
|||
<td>-</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>30</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>60</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>120</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
</tr>
|
||||
`);
|
||||
$(".pageAccount .wordsPbTable tbody").html(`
|
||||
|
|
@ -1478,24 +1528,28 @@ function fillPbTables() {
|
|||
<td>-</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>25</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>50</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>100</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
</tr>
|
||||
`);
|
||||
|
||||
|
|
@ -1511,6 +1565,9 @@ function fillPbTables() {
|
|||
<td>${pbData.wpm}</td>
|
||||
<td>${pbData.raw === undefined ? "-" : pbData.raw}</td>
|
||||
<td>${pbData.acc === undefined ? "-" : pbData.acc + "%"}</td>
|
||||
<td>
|
||||
${pbData.consistency === undefined ? "-" : pbData.consistency + "%"}
|
||||
</td>
|
||||
</tr>`;
|
||||
} catch (e) {
|
||||
text += `<tr>
|
||||
|
|
@ -1518,6 +1575,7 @@ function fillPbTables() {
|
|||
<td>-</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
</tr>`;
|
||||
}
|
||||
try {
|
||||
|
|
@ -1527,6 +1585,9 @@ function fillPbTables() {
|
|||
<td>${pbData.wpm}</td>
|
||||
<td>${pbData.raw === undefined ? "-" : pbData.raw}</td>
|
||||
<td>${pbData.acc === undefined ? "-" : pbData.acc + "%"}</td>
|
||||
<td>
|
||||
${pbData.consistency === undefined ? "-" : pbData.consistency + "%"}
|
||||
</td>
|
||||
</tr>`;
|
||||
} catch (e) {
|
||||
text += `<tr>
|
||||
|
|
@ -1534,6 +1595,7 @@ function fillPbTables() {
|
|||
<td>-</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
</tr>`;
|
||||
}
|
||||
try {
|
||||
|
|
@ -1543,6 +1605,9 @@ function fillPbTables() {
|
|||
<td>${pbData.wpm}</td>
|
||||
<td>${pbData.raw === undefined ? "-" : pbData.raw}</td>
|
||||
<td>${pbData.acc === undefined ? "-" : pbData.acc + "%"}</td>
|
||||
<td>
|
||||
${pbData.consistency === undefined ? "-" : pbData.consistency + "%"}
|
||||
</td>
|
||||
</tr>`;
|
||||
} catch (e) {
|
||||
text += `<tr>
|
||||
|
|
@ -1550,6 +1615,7 @@ function fillPbTables() {
|
|||
<td>-</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
</tr>`;
|
||||
}
|
||||
try {
|
||||
|
|
@ -1559,6 +1625,9 @@ function fillPbTables() {
|
|||
<td>${pbData.wpm}</td>
|
||||
<td>${pbData.raw === undefined ? "-" : pbData.raw}</td>
|
||||
<td>${pbData.acc === undefined ? "-" : pbData.acc + "%"}</td>
|
||||
<td>
|
||||
${pbData.consistency === undefined ? "-" : pbData.consistency + "%"}
|
||||
</td>
|
||||
</tr>`;
|
||||
} catch (e) {
|
||||
text += `<tr>
|
||||
|
|
@ -1566,6 +1635,7 @@ function fillPbTables() {
|
|||
<td>-</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
</tr>`;
|
||||
}
|
||||
$(".pageAccount .timePbTable tbody").html(text);
|
||||
|
|
@ -1578,6 +1648,9 @@ function fillPbTables() {
|
|||
<td>${pbData.wpm}</td>
|
||||
<td>${pbData.raw === undefined ? "-" : pbData.raw}</td>
|
||||
<td>${pbData.acc === undefined ? "-" : pbData.acc + "%"}</td>
|
||||
<td>
|
||||
${pbData.consistency === undefined ? "-" : pbData.consistency + "%"}
|
||||
</td>
|
||||
</tr>`;
|
||||
} catch (e) {
|
||||
text += `<tr>
|
||||
|
|
@ -1585,6 +1658,7 @@ function fillPbTables() {
|
|||
<td>-</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
</tr>`;
|
||||
}
|
||||
try {
|
||||
|
|
@ -1594,6 +1668,9 @@ function fillPbTables() {
|
|||
<td>${pbData.wpm}</td>
|
||||
<td>${pbData.raw === undefined ? "-" : pbData.raw}</td>
|
||||
<td>${pbData.acc === undefined ? "-" : pbData.acc + "%"}</td>
|
||||
<td>
|
||||
${pbData.consistency === undefined ? "-" : pbData.consistency + "%"}
|
||||
</td>
|
||||
</tr>`;
|
||||
} catch (e) {
|
||||
text += `<tr>
|
||||
|
|
@ -1601,6 +1678,7 @@ function fillPbTables() {
|
|||
<td>-</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
</tr>`;
|
||||
}
|
||||
try {
|
||||
|
|
@ -1610,6 +1688,9 @@ function fillPbTables() {
|
|||
<td>${pbData.wpm}</td>
|
||||
<td>${pbData.raw === undefined ? "-" : pbData.raw}</td>
|
||||
<td>${pbData.acc === undefined ? "-" : pbData.acc + "%"}</td>
|
||||
<td>
|
||||
${pbData.consistency === undefined ? "-" : pbData.consistency + "%"}
|
||||
</td>
|
||||
</tr>`;
|
||||
} catch (e) {
|
||||
text += `<tr>
|
||||
|
|
@ -1617,6 +1698,7 @@ function fillPbTables() {
|
|||
<td>-</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
</tr>`;
|
||||
}
|
||||
try {
|
||||
|
|
@ -1626,6 +1708,9 @@ function fillPbTables() {
|
|||
<td>${pbData.wpm}</td>
|
||||
<td>${pbData.raw === undefined ? "-" : pbData.raw}</td>
|
||||
<td>${pbData.acc === undefined ? "-" : pbData.acc + "%"}</td>
|
||||
<td>
|
||||
${pbData.consistency === undefined ? "-" : pbData.consistency + "%"}
|
||||
</td>
|
||||
</tr>`;
|
||||
} catch (e) {
|
||||
text += `<tr>
|
||||
|
|
@ -1633,6 +1718,7 @@ function fillPbTables() {
|
|||
<td>-</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
<td>-</td>
|
||||
</tr>`;
|
||||
}
|
||||
$(".pageAccount .wordsPbTable tbody").html(text);
|
||||
|
|
@ -1854,7 +1940,7 @@ function refreshAccountPage() {
|
|||
let consCount = 0;
|
||||
|
||||
let dailyActivityDays = [];
|
||||
let activityChartData = [];
|
||||
let activityChartData = {};
|
||||
|
||||
filteredResults = [];
|
||||
$(".pageAccount .history table tbody").empty();
|
||||
|
|
@ -2004,9 +2090,13 @@ function refreshAccountPage() {
|
|||
resultDate = resultDate.getTime();
|
||||
|
||||
if (Object.keys(activityChartData).includes(String(resultDate))) {
|
||||
activityChartData[resultDate] = activityChartData[resultDate] + 1;
|
||||
activityChartData[resultDate].amount++;
|
||||
activityChartData[resultDate].totalWpm += result.wpm;
|
||||
} else {
|
||||
activityChartData[resultDate] = 1;
|
||||
activityChartData[resultDate] = {
|
||||
amount: 1,
|
||||
totalWpm: result.wpm,
|
||||
}
|
||||
}
|
||||
|
||||
tt = 0;
|
||||
|
|
@ -2101,7 +2191,8 @@ function refreshAccountPage() {
|
|||
thisDate.setMilliseconds(0);
|
||||
thisDate = thisDate.getTime();
|
||||
|
||||
let tempChartData = [];
|
||||
let activityChartData_amount = [];
|
||||
let activityChartData_avgWpm = [];
|
||||
let lastTimestamp = 0;
|
||||
Object.keys(activityChartData).forEach((date) => {
|
||||
let datecheck;
|
||||
|
|
@ -2115,23 +2206,27 @@ function refreshAccountPage() {
|
|||
|
||||
if (numDaysBetweenTheDays > 1) {
|
||||
if (datecheck === thisDate) {
|
||||
tempChartData.push({
|
||||
activityChartData_amount.push({
|
||||
x: parseInt(thisDate),
|
||||
y: 0,
|
||||
});
|
||||
}
|
||||
|
||||
for (let i = 0; i < numDaysBetweenTheDays - 1; i++) {
|
||||
tempChartData.push({
|
||||
activityChartData_amount.push({
|
||||
x: parseInt(datecheck) - 86400000 * (i + 1),
|
||||
y: 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
tempChartData.push({
|
||||
activityChartData_amount.push({
|
||||
x: parseInt(date),
|
||||
y: activityChartData[date],
|
||||
y: activityChartData[date].amount,
|
||||
});
|
||||
activityChartData_avgWpm.push({
|
||||
x: parseInt(date),
|
||||
y: roundTo2(activityChartData[date].totalWpm / activityChartData[date].amount),
|
||||
});
|
||||
lastTimestamp = date;
|
||||
});
|
||||
|
|
@ -2150,7 +2245,18 @@ function refreshAccountPage() {
|
|||
activityChart.options.legend.labels.fontColor = themeColors.sub;
|
||||
activityChart.data.datasets[0].trendlineLinear.style = themeColors.sub;
|
||||
|
||||
activityChart.data.datasets[0].data = tempChartData;
|
||||
activityChart.data.datasets[0].data = activityChartData_amount;
|
||||
|
||||
activityChart.options.scales.yAxes[1].ticks.minor.fontColor =
|
||||
themeColors.sub;
|
||||
activityChart.options.scales.yAxes[1].scaleLabel.fontColor =
|
||||
themeColors.sub;
|
||||
activityChart.data.datasets[1].borderColor = themeColors.sub;
|
||||
// activityChart.data.datasets[1].backgroundColor = themeColors.main;
|
||||
activityChart.data.datasets[1].data = activityChartData_avgWpm;
|
||||
|
||||
|
||||
activityChart.options.legend.labels.fontColor = themeColors.sub;
|
||||
|
||||
resultHistoryChart.options.scales.xAxes[0].ticks.minor.fontColor =
|
||||
themeColors.sub;
|
||||
|
|
@ -2192,6 +2298,9 @@ function refreshAccountPage() {
|
|||
resultHistoryChart.options.scales.yAxes[1].ticks.min = Math.floor(
|
||||
minAccuracyChartVal
|
||||
);
|
||||
} else {
|
||||
resultHistoryChart.options.scales.yAxes[0].ticks.min = 0;
|
||||
resultHistoryChart.options.scales.yAxes[1].ticks.min = 0;
|
||||
}
|
||||
|
||||
if (chartData == [] || chartData.length == 0) {
|
||||
|
|
@ -2361,6 +2470,7 @@ function refreshAccountPage() {
|
|||
try {
|
||||
cont();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
showNotification(`Something went wrong: ${e}`, 5000);
|
||||
}
|
||||
}
|
||||
|
|
@ -2393,21 +2503,6 @@ function hideResultEditTagsPanel() {
|
|||
}
|
||||
}
|
||||
|
||||
let chartAccuracyVisible = true;
|
||||
|
||||
function toggleChartAccuracy() {
|
||||
if (chartAccuracyVisible) {
|
||||
resultHistoryChart.data.datasets[1].hidden = true;
|
||||
resultHistoryChart.options.scales.yAxes[1].display = false;
|
||||
chartAccuracyVisible = false;
|
||||
} else {
|
||||
resultHistoryChart.data.datasets[1].hidden = false;
|
||||
resultHistoryChart.options.scales.yAxes[1].display = true;
|
||||
chartAccuracyVisible = true;
|
||||
}
|
||||
resultHistoryChart.update();
|
||||
}
|
||||
|
||||
$(".pageAccount .toggleAccuracyOnChart").click((params) => {
|
||||
toggleChartAccuracy();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,6 +2,57 @@ function capitalizeFirstLetter(str) {
|
|||
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||
}
|
||||
|
||||
function addChildCommands(unifiedCommands, commandItem, parentCommandDisplay = '') {
|
||||
let commandItemDisplay = commandItem.display.replace(/\s?\.\.\.$/g,'');
|
||||
if (parentCommandDisplay) commandItemDisplay = parentCommandDisplay + " > " + commandItemDisplay;
|
||||
if (commandItem.subgroup) {
|
||||
try {
|
||||
commandItem.exec();
|
||||
currentCommandsIndex = currentCommands.length-1;
|
||||
currentCommands[currentCommandsIndex].list.forEach( cmd => addChildCommands(unifiedCommands, cmd, commandItemDisplay));
|
||||
currentCommands.pop();
|
||||
} catch(e) {}
|
||||
} else {
|
||||
let tempCommandItem = {...commandItem};
|
||||
if (parentCommandDisplay)
|
||||
tempCommandItem.display = commandItemDisplay;
|
||||
unifiedCommands.push(tempCommandItem);
|
||||
}
|
||||
}
|
||||
|
||||
function generateSingleListOfCommands() {
|
||||
allCommands = [];
|
||||
oldShowCommandLine = showCommandLine;
|
||||
showCommandLine = () => {};
|
||||
commands.list.forEach(c => addChildCommands(allCommands, c));
|
||||
showCommandLine = oldShowCommandLine;
|
||||
return {
|
||||
title: "All Commands",
|
||||
list: allCommands};
|
||||
}
|
||||
|
||||
function isSingleListCommandLineActive() {
|
||||
return $("#commandLine").hasClass("allCommands");
|
||||
}
|
||||
|
||||
function useSingleListCommandLine(show = true) {
|
||||
let allCommands = generateSingleListOfCommands();
|
||||
if (config.singleListCommandLine == "manual") currentCommands.push(allCommands);
|
||||
else if (config.singleListCommandLine == "on") currentCommands = [allCommands];
|
||||
|
||||
if (config.singleListCommandLine != "off") $("#commandLine").addClass("allCommands");
|
||||
if (show) showCommandLine();
|
||||
}
|
||||
|
||||
function restoreOldCommandLine(show = true) {
|
||||
if (isSingleListCommandLineActive()) {
|
||||
$("#commandLine").removeClass("allCommands");
|
||||
currentCommands = currentCommands.filter( l => l.title != "All Commands");
|
||||
if (currentCommands.length < 1) currentCommands = [commands];
|
||||
}
|
||||
if (show) showCommandLine();
|
||||
}
|
||||
|
||||
let commands = {
|
||||
title: "",
|
||||
list: [
|
||||
|
|
@ -167,6 +218,15 @@ let commands = {
|
|||
toggleQuickEnd();
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "singleListCommandLine",
|
||||
display: "Single list command line...",
|
||||
subgroup: true,
|
||||
exec: () => {
|
||||
currentCommands.push(commandsSingleListCommandLine);
|
||||
showCommandLine();
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "togglePlaySoundOnError",
|
||||
display: "Toggle play sound on error",
|
||||
|
|
@ -404,6 +464,34 @@ let commands = {
|
|||
display: "Next random theme",
|
||||
exec: () => randomiseTheme()
|
||||
},
|
||||
{
|
||||
id: "viewTypingPage",
|
||||
display: "View Typing Page",
|
||||
exec: () => $('#top #menu .icon-button.view-start').click()
|
||||
},
|
||||
{
|
||||
id: "viewLeaderboards",
|
||||
display: "View Leaderboards Page",
|
||||
exec: () => $('#top #menu .icon-button.view-leaderboards').click()
|
||||
},
|
||||
{
|
||||
id: "viewAbout",
|
||||
display: "View About Page",
|
||||
exec: () => $('#top #menu .icon-button.view-about').click()
|
||||
},
|
||||
{
|
||||
id: "viewSettings",
|
||||
display: "View Settings Page",
|
||||
exec: () => $('#top #menu .icon-button.view-settings').click()
|
||||
},
|
||||
{
|
||||
id: "viewAccount",
|
||||
display: "View Account Page",
|
||||
exec: () =>
|
||||
$('#top #menu .icon-button.view-account').hasClass('hidden') ?
|
||||
$('#top #menu .icon-button.view-login').click() :
|
||||
$('#top #menu .icon-button.view-account').click()
|
||||
},
|
||||
{
|
||||
id: "bailOut",
|
||||
display: "Bail out...",
|
||||
|
|
@ -841,6 +929,26 @@ let commandsTimerColor = {
|
|||
],
|
||||
};
|
||||
|
||||
let commandsSingleListCommandLine = {
|
||||
title: "Single list command line...",
|
||||
list: [
|
||||
{
|
||||
id: "singleListCommandLineManual",
|
||||
display: "manual",
|
||||
exec: () => {
|
||||
setSingleListCommandLine("manual");
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "singleListCommandLineOn",
|
||||
display: "on",
|
||||
exec: () => {
|
||||
setSingleListCommandLine("on");
|
||||
},
|
||||
}
|
||||
],
|
||||
};
|
||||
|
||||
let commandsTimerOpacity = {
|
||||
title: "Change timer opacity...",
|
||||
list: [
|
||||
|
|
@ -1351,12 +1459,20 @@ $(document).ready((e) => {
|
|||
//escape
|
||||
if ((event.keyCode == 27 && !config.swapEscAndTab) || (event["keyCode"] == 9 && config.swapEscAndTab)) {
|
||||
event.preventDefault();
|
||||
if ($("#commandLineWrapper").hasClass("hidden")) {
|
||||
currentCommands = [commands];
|
||||
if (!$("#leaderboardsWrapper").hasClass("hidden")) { //maybe add more condition for closing other dialogs in the future as well
|
||||
event.preventDefault();
|
||||
hideLeaderboards();
|
||||
return;
|
||||
} else if ($("#commandLineWrapper").hasClass("hidden")) {
|
||||
if (config.singleListCommandLine == "on")
|
||||
useSingleListCommandLine(false);
|
||||
else
|
||||
currentCommands = [commands];
|
||||
showCommandLine();
|
||||
} else {
|
||||
if (currentCommands.length > 1) {
|
||||
currentCommands.pop();
|
||||
$("#commandLine").removeClass("allCommands");
|
||||
showCommandLine();
|
||||
} else {
|
||||
hideCommandLine();
|
||||
|
|
@ -1414,18 +1530,34 @@ $("#commandLineWrapper #commandLine .suggestions").on("mouseover", (e) => {
|
|||
});
|
||||
|
||||
$("#commandLineWrapper #commandLine .suggestions").click((e) => {
|
||||
$(".suggestions .entry").removeClass('activeKeyboard');
|
||||
triggerCommand($(e.target).attr("command"));
|
||||
});
|
||||
|
||||
$("#commandLineWrapper").click((e) => {
|
||||
if ($(e.target).attr("id") === "commandLineWrapper") {
|
||||
hideCommandLine();
|
||||
setTheme(config.theme, true);
|
||||
}
|
||||
});
|
||||
|
||||
$(document).keydown((e) => {
|
||||
if (isPreviewingTheme) {
|
||||
previewTheme(config.theme, false);
|
||||
}
|
||||
if (!$("#commandLineWrapper").hasClass("hidden")) {
|
||||
$("#commandLine input").focus();
|
||||
if (e.key == ">" && config.singleListCommandLine == "manual") {
|
||||
if (!isSingleListCommandLineActive()) {
|
||||
useSingleListCommandLine();
|
||||
return;
|
||||
} else if ($("#commandLine input").val() == ">") { //so that it will ignore succeeding ">" when input is already ">"
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (e.keyCode == 8 && $("#commandLine input").val().length == 1 && config.singleListCommandLine == "manual" && isSingleListCommandLineActive())
|
||||
restoreOldCommandLine();
|
||||
if (e.keyCode == 13) {
|
||||
//enter
|
||||
e.preventDefault();
|
||||
|
|
@ -1533,6 +1665,7 @@ function hideCommandLine() {
|
|||
100,
|
||||
() => {
|
||||
$("#commandLineWrapper").addClass("hidden");
|
||||
$("#commandLine").removeClass("allCommands");
|
||||
focusWords();
|
||||
}
|
||||
);
|
||||
|
|
@ -1572,9 +1705,11 @@ function showCommandInput(command, placeholder) {
|
|||
}
|
||||
|
||||
function updateSuggestedCommands() {
|
||||
let inputVal = $("#commandLine input").val().toLowerCase().split(" ");
|
||||
let inputVal = $("#commandLine input").val().toLowerCase().split(" ").filter((s,i) => s||i==0); //remove empty entries after first
|
||||
let list = currentCommands[currentCommands.length - 1];
|
||||
if (inputVal[0] == "") {
|
||||
//ignore the preceeding ">"s in the command line input
|
||||
if (inputVal[0] && inputVal[0][0] == ">") inputVal[0] = inputVal[0].replace(/^>+/,'');
|
||||
if (inputVal[0] == "" && inputVal.length == 1) {
|
||||
$.each(list.list, (index, obj) => {
|
||||
if (obj.visible !== false) obj.found = true;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ async function db_getUserSnapshot() {
|
|||
.then((res) => {
|
||||
// console.log('getting data from db!');
|
||||
let data = res.data();
|
||||
if (data === undefined) return;
|
||||
try {
|
||||
if (data.personalBests !== undefined) {
|
||||
snap.personalBests = data.personalBests;
|
||||
|
|
@ -207,7 +208,8 @@ async function db_saveLocalPB(
|
|||
difficulty,
|
||||
wpm,
|
||||
acc,
|
||||
raw
|
||||
raw,
|
||||
consistency
|
||||
) {
|
||||
function cont() {
|
||||
try {
|
||||
|
|
@ -225,6 +227,8 @@ async function db_saveLocalPB(
|
|||
pb.wpm = wpm;
|
||||
pb.acc = acc;
|
||||
pb.raw = raw;
|
||||
pb.timestamp = Date.now();
|
||||
pb.consistency = consistency;
|
||||
}
|
||||
});
|
||||
if (!found) {
|
||||
|
|
@ -236,6 +240,8 @@ async function db_saveLocalPB(
|
|||
wpm: wpm,
|
||||
acc: acc,
|
||||
raw: raw,
|
||||
timestamp: Date.now(),
|
||||
consistency: consistency
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
|
|
@ -249,6 +255,8 @@ async function db_saveLocalPB(
|
|||
wpm: wpm,
|
||||
acc: acc,
|
||||
raw: raw,
|
||||
timestamp: Date.now(),
|
||||
consistency: consistency
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27168,12 +27168,6 @@
|
|||
"id": 4573,
|
||||
"length": 262
|
||||
},
|
||||
{
|
||||
"text": "I've flown seven million miles. And I've been waiting on people almost 20 years. The best job I could get after my bust was Cabo Air, which is the worst job you can get in this industry. And now with this arrest hanging over my head, I'm scared. If I lose my job I gotta start all over again, but I got nothing to start over with.",
|
||||
"source": "Jackie Brown",
|
||||
"id": 4574,
|
||||
"length": 330
|
||||
},
|
||||
{
|
||||
"text": "Raindrops on roses and whiskers on kittens, bright copper kettles and warm woolen mittens, brown paper packages tied up with strings, these are a few of my favorite things. Cream-colored ponies and crisp apple strudels, doorbells and sleigh bells and schnitzel with noodles, wild geese that fly with the moon on their wings, these are a few of my favorite things.",
|
||||
"source": "My Favorite Things",
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ function showNotification(text, time) {
|
|||
|
||||
function getReleasesFromGitHub() {
|
||||
$.getJSON(
|
||||
"https://api.github.com/repos/Miodec/monkey-type/releases",
|
||||
"https://api.github.com/repos/Miodec/monkeytype/releases",
|
||||
(data) => {
|
||||
$("#bottom .version").text(data[0].name).css("opacity", 1);
|
||||
$("#versionHistory .releases").empty();
|
||||
|
|
|
|||
|
|
@ -41,6 +41,8 @@ let caretAnimating = true;
|
|||
let lastSecondNotRound = false;
|
||||
let paceCaret = null;
|
||||
let missedWords = [];
|
||||
let verifyUserWhenLoggedIn = null;
|
||||
let modeBeforePractise = null;
|
||||
|
||||
let themeColors = {
|
||||
bg: "#323437",
|
||||
|
|
@ -73,6 +75,8 @@ let keypressStats = {
|
|||
let errorSound = new Audio("../sound/error.wav");
|
||||
let clickSounds = null;
|
||||
|
||||
let isPreviewingTheme = false;
|
||||
|
||||
function initClickSounds() {
|
||||
clickSounds = {
|
||||
"1": [
|
||||
|
|
@ -207,6 +211,8 @@ const generatePairingCode = firebase
|
|||
.httpsCallable("generatePairingCode");
|
||||
const saveLbMemory = firebase.functions().httpsCallable("saveLbMemory");
|
||||
const unlinkDiscord = firebase.functions().httpsCallable("unlinkDiscord");
|
||||
const verifyUser = firebase.functions().httpsCallable("verifyUser");
|
||||
|
||||
|
||||
|
||||
function refreshThemeColorObject() {
|
||||
|
|
@ -431,7 +437,7 @@ function initWords() {
|
|||
config.mode == "custom"
|
||||
) {
|
||||
// let wordsBound = config.mode == "time" ? 60 : config.words;
|
||||
let wordsBound = 60;
|
||||
let wordsBound = 100;
|
||||
if (config.showAllLines) {
|
||||
if (config.mode === "custom") {
|
||||
if (customTextIsRandom) {
|
||||
|
|
@ -710,10 +716,9 @@ function punctuateWord(previousWord, currentWord, index, maxindex) {
|
|||
}
|
||||
|
||||
function addWord() {
|
||||
let bound = 60;
|
||||
let bound = 100;
|
||||
if (activeFunBox === "plus_one") bound = 1;
|
||||
if (
|
||||
!config.showAllLines &&
|
||||
(wordsList.length - inputHistory.length > bound ||
|
||||
(config.mode === "words" && wordsList.length >= config.words) ||
|
||||
(config.mode === "custom" &&
|
||||
|
|
@ -1510,19 +1515,32 @@ function countChars() {
|
|||
}
|
||||
} else {
|
||||
//not enough chars
|
||||
let toAdd = {
|
||||
correct: 0,
|
||||
incorrect: 0,
|
||||
missed: 0
|
||||
}
|
||||
for (let c = 0; c < wordsList[i].length; c++) {
|
||||
if (c < inputHistory[i].length) {
|
||||
//on char that still has a word list pair
|
||||
if (inputHistory[i][c] == wordsList[i][c]) {
|
||||
correctChars++;
|
||||
toAdd.correct++;
|
||||
} else {
|
||||
incorrectChars++;
|
||||
toAdd.incorrect++;
|
||||
}
|
||||
} else {
|
||||
//on char that is extra
|
||||
missedChars++;
|
||||
toAdd.missed++;
|
||||
}
|
||||
}
|
||||
correctChars += toAdd.correct;
|
||||
incorrectChars += toAdd.incorrect;
|
||||
if (i === inputHistory.length - 1 && config.mode == "time") {
|
||||
//last word - check if it was all correct - add to correct word chars
|
||||
if(toAdd.incorrect === 0) correctWordChars += toAdd.correct;
|
||||
} else {
|
||||
missedChars += toAdd.missed;
|
||||
}
|
||||
}
|
||||
if (i < inputHistory.length - 1) {
|
||||
spaces++;
|
||||
|
|
@ -1573,7 +1591,9 @@ function calculateStats() {
|
|||
wpmRaw: isNaN(wpmraw) ? 0 : wpmraw,
|
||||
acc: acc,
|
||||
correctChars: chars.correctWordChars,
|
||||
incorrectChars: chars.incorrectChars + chars.extraChars + chars.missedChars,
|
||||
incorrectChars: chars.incorrectChars,
|
||||
missedChars: chars.missedChars,
|
||||
extraChars: chars.extraChars,
|
||||
allChars:
|
||||
chars.allCorrectChars +
|
||||
chars.spaces +
|
||||
|
|
@ -1621,6 +1641,8 @@ function showResult(difficultyFailed = false) {
|
|||
acc: 0,
|
||||
correctChars: 0,
|
||||
incorrectChars: 0,
|
||||
missedChars: 0,
|
||||
extraChars: 0,
|
||||
time: 0,
|
||||
spaces: 0,
|
||||
correctSpaces: 0,
|
||||
|
|
@ -1667,15 +1689,18 @@ function showResult(difficultyFailed = false) {
|
|||
|
||||
let correctcharpercent = roundTo2(
|
||||
((stats.correctChars + stats.correctSpaces) /
|
||||
(stats.correctChars + stats.correctSpaces + stats.incorrectChars)) *
|
||||
(stats.correctChars + stats.correctSpaces + stats.incorrectChars + stats.extraChars)) *
|
||||
100
|
||||
);
|
||||
$("#result .stats .key .bottom").text(testtime + "s");
|
||||
$("#result .stats .key .bottom").attr("aria-label", `${correctcharpercent}%`);
|
||||
// $("#result .stats .key .bottom").attr("aria-label", `Correct, incorrect, missed and extra \n ${correctcharpercent}%`);
|
||||
$("#words").removeClass("blurred");
|
||||
$(".outOfFocusWarning").addClass("hidden");
|
||||
$("#result .stats .key .bottom").text(
|
||||
stats.correctChars + stats.correctSpaces + "/" + stats.incorrectChars
|
||||
stats.correctChars + stats.correctSpaces +
|
||||
"/" + stats.incorrectChars +
|
||||
"/" + stats.extraChars +
|
||||
"/" + stats.missedChars
|
||||
);
|
||||
|
||||
setTimeout(function () {
|
||||
|
|
@ -1808,6 +1833,9 @@ function showResult(difficultyFailed = false) {
|
|||
if (!config.startGraphsAtZero) {
|
||||
wpmOverTimeChart.options.scales.yAxes[0].ticks.min = minChartVal;
|
||||
wpmOverTimeChart.options.scales.yAxes[1].ticks.min = minChartVal;
|
||||
} else {
|
||||
wpmOverTimeChart.options.scales.yAxes[0].ticks.min = 0;
|
||||
wpmOverTimeChart.options.scales.yAxes[1].ticks.min = 0;
|
||||
}
|
||||
|
||||
// wpmOverTimeChart.options.scales.yAxes[0].ticks.min = Math.round(minChartVal);
|
||||
|
|
@ -2011,9 +2039,9 @@ function showResult(difficultyFailed = false) {
|
|||
if (dbSnapshot !== null && dbSnapshot.results !== undefined) {
|
||||
dbSnapshot.results.unshift(completedEvent);
|
||||
if (dbSnapshot.globalStats.time == undefined) {
|
||||
dbSnapshot.globalStats.time = testtime;
|
||||
dbSnapshot.globalStats.time = testtime + completedEvent.incompleteTestSeconds;
|
||||
} else {
|
||||
dbSnapshot.globalStats.time += testtime;
|
||||
dbSnapshot.globalStats.time += testtime + completedEvent.incompleteTestSeconds;
|
||||
}
|
||||
if (dbSnapshot.globalStats.started == undefined) {
|
||||
dbSnapshot.globalStats.started = restartCount + 1;
|
||||
|
|
@ -2211,6 +2239,7 @@ function showResult(difficultyFailed = false) {
|
|||
stats.wpm,
|
||||
stats.acc,
|
||||
stats.wpmRaw,
|
||||
consistency
|
||||
);
|
||||
} else if (e.data.resultCode === 1) {
|
||||
if (localPb) {
|
||||
|
|
@ -2563,6 +2592,12 @@ function restartTest(withSameWordset = false, nosave = false) {
|
|||
}
|
||||
}
|
||||
|
||||
if (modeBeforePractise !== null) {
|
||||
showNotification("Reverting to previous settings.", 1500);
|
||||
changeMode(modeBeforePractise);
|
||||
modeBeforePractise = null;
|
||||
}
|
||||
|
||||
manualRestart = false;
|
||||
clearTimeout(timer);
|
||||
time = 0;
|
||||
|
|
@ -3101,7 +3136,7 @@ async function loadWordsHistory() {
|
|||
let wordEl = "";
|
||||
try {
|
||||
if (input === "") throw Exception;
|
||||
if (correctedHistory[i] !== "") {
|
||||
if (correctedHistory[i] !== undefined && correctedHistory[i] !== "") {
|
||||
wordEl = `<div class='word' input="${correctedHistory[i].replace(
|
||||
/"/g,
|
||||
"""
|
||||
|
|
@ -3109,11 +3144,41 @@ async function loadWordsHistory() {
|
|||
} else {
|
||||
wordEl = `<div class='word' input="${input.replace(/"/g, """)}">`;
|
||||
}
|
||||
if (input !== wordsList[i]) {
|
||||
wordEl = `<div class='word error' input="${input.replace(
|
||||
/"/g,
|
||||
"""
|
||||
)}">`;
|
||||
if (i === inputHistory.length - 1) {
|
||||
//last word
|
||||
let word = {
|
||||
correct: 0,
|
||||
incorrect: 0,
|
||||
missed: 0
|
||||
}
|
||||
for (let c = 0; c < wordsList[i].length; c++) {
|
||||
if (c < inputHistory[i].length) {
|
||||
//on char that still has a word list pair
|
||||
if (inputHistory[i][c] == wordsList[i][c]) {
|
||||
word.correct++;
|
||||
} else {
|
||||
word.incorrect++;
|
||||
}
|
||||
} else {
|
||||
//on char that is extra
|
||||
word.missed++;
|
||||
}
|
||||
}
|
||||
if (word.incorrect !== 0 || config.mode !== "time") {
|
||||
if (input !== wordsList[i]) {
|
||||
wordEl = `<div class='word error' input="${input.replace(
|
||||
/"/g,
|
||||
"""
|
||||
)}">`;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (input !== wordsList[i]) {
|
||||
wordEl = `<div class='word error' input="${input.replace(
|
||||
/"/g,
|
||||
"""
|
||||
)}">`;
|
||||
}
|
||||
}
|
||||
|
||||
let loop;
|
||||
|
|
@ -3134,7 +3199,7 @@ async function loadWordsHistory() {
|
|||
correctedChar = undefined;
|
||||
}
|
||||
let extraCorrected = "";
|
||||
if (c + 1 === loop && correctedHistory[i].length > input.length) {
|
||||
if (c + 1 === loop && correctedHistory[i] !== undefined &&correctedHistory[i].length > input.length) {
|
||||
extraCorrected = "extraCorrected";
|
||||
}
|
||||
if (wordsList[i][c] !== undefined) {
|
||||
|
|
@ -4141,11 +4206,13 @@ $(document.body).on("click", "#restartTestButton", (event) => {
|
|||
$(document).on("keypress", "#practiseMissedWordsButton", (event) => {
|
||||
if (event.keyCode == 13) {
|
||||
if (missedWords.length > 0) {
|
||||
let currentMode = config.mode;
|
||||
changeMode("custom");
|
||||
customText = missedWords;
|
||||
customTextIsRandom = true;
|
||||
customTextWordCount = 50;
|
||||
restartTest();
|
||||
modeBeforePractise = currentMode;
|
||||
} else {
|
||||
showNotification("You haven't missed any words.", 2000);
|
||||
}
|
||||
|
|
@ -4154,11 +4221,13 @@ $(document).on("keypress", "#practiseMissedWordsButton", (event) => {
|
|||
|
||||
$(document.body).on("click", "#practiseMissedWordsButton", (event) => {
|
||||
if (missedWords.length > 0) {
|
||||
let currentMode = config.mode;
|
||||
changeMode("custom");
|
||||
customText = missedWords;
|
||||
customTextIsRandom = true;
|
||||
customTextWordCount = 50;
|
||||
restartTest();
|
||||
modeBeforePractise = currentMode;
|
||||
} else {
|
||||
showNotification("You haven't missed any words.", 2000);
|
||||
}
|
||||
|
|
@ -4774,11 +4843,11 @@ $(document).keydown((event) => {
|
|||
) {
|
||||
updateTimer();
|
||||
}
|
||||
if (config.showAllLines) {
|
||||
if (config.mode == "time") {
|
||||
addWord();
|
||||
}
|
||||
} else {
|
||||
// if (config.showAllLines) {
|
||||
// if (config.mode == "time") {
|
||||
// addWord();
|
||||
// }
|
||||
// } else {
|
||||
if (
|
||||
config.mode == "time" ||
|
||||
config.mode == "words" ||
|
||||
|
|
@ -4786,7 +4855,7 @@ $(document).keydown((event) => {
|
|||
) {
|
||||
addWord();
|
||||
}
|
||||
}
|
||||
// }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -4824,7 +4893,10 @@ $(document).on("mouseenter", "#resultWordsHistory .words .word", (e) => {
|
|||
});
|
||||
|
||||
$(document).on("click", "#bottom .leftright .right .current-theme", (e) => {
|
||||
currentCommands.push(commandsThemes);
|
||||
if (config.customTheme) {
|
||||
togglePresetCustomTheme();
|
||||
}
|
||||
currentCommands = [commandsThemes];
|
||||
showCommandLine();
|
||||
});
|
||||
|
||||
|
|
@ -4866,7 +4938,18 @@ $(document).ready(() => {
|
|||
setCustomThemeInputs();
|
||||
applyCustomThemeColors();
|
||||
}
|
||||
if (window.location.pathname === "/account") {
|
||||
if (window.location.pathname === "/verify") {
|
||||
const fragment = new URLSearchParams(window.location.hash.slice(1));
|
||||
if (fragment.has("access_token")) {
|
||||
const accessToken = fragment.get("access_token");
|
||||
const tokenType = fragment.get("token_type");
|
||||
verifyUserWhenLoggedIn = {
|
||||
accessToken: accessToken,
|
||||
tokenType: tokenType
|
||||
}
|
||||
history.replaceState("/", null, "/");
|
||||
}
|
||||
}else if (window.location.pathname === "/account") {
|
||||
history.replaceState("/", null, "/");
|
||||
} else if (window.location.pathname !== "/") {
|
||||
let page = window.location.pathname.replace("/", "");
|
||||
|
|
|
|||
|
|
@ -147,6 +147,10 @@ settingsGroups.alwaysShowWordsHistory = new SettingsGroup(
|
|||
"alwaysShowWordsHistory",
|
||||
setAlwaysShowWordsHistory
|
||||
);
|
||||
settingsGroups.singleListCommandLine = new SettingsGroup(
|
||||
"singleListCommandLine",
|
||||
setSingleListCommandLine
|
||||
);
|
||||
settingsGroups.flipTestColors = new SettingsGroup(
|
||||
"flipTestColors",
|
||||
setFlipTestColors
|
||||
|
|
@ -467,7 +471,7 @@ $("#shareCustomThemeButton").click((e) => {
|
|||
}
|
||||
);
|
||||
|
||||
let url = "https://monkey-type.com?" + objectToQueryString({ customTheme: share });
|
||||
let url = "https://monkeytype.com?" + objectToQueryString({ customTheme: share });
|
||||
navigator.clipboard.writeText(url).then(function () {
|
||||
showNotification("URL Copied to clipboard", 2000);
|
||||
}, function (err) {
|
||||
|
|
@ -611,48 +615,20 @@ function updateDiscordSettingsSection() {
|
|||
$(".pageSettings .section.discordIntegration").removeClass("hidden");
|
||||
|
||||
if (
|
||||
dbSnapshot.pairingCode == undefined &&
|
||||
dbSnapshot.discordId == undefined
|
||||
) {
|
||||
//show button
|
||||
$(".pageSettings .section.discordIntegration .howto").addClass("hidden");
|
||||
$(".pageSettings .section.discordIntegration .buttons").removeClass(
|
||||
"hidden"
|
||||
);
|
||||
$(".pageSettings .section.discordIntegration .info").addClass("hidden");
|
||||
$(".pageSettings .section.discordIntegration .code").addClass("hidden");
|
||||
} else if (
|
||||
dbSnapshot.pairingCode != undefined &&
|
||||
dbSnapshot.discordId == undefined
|
||||
) {
|
||||
//show code
|
||||
$(".pageSettings .section.discordIntegration .code .bottom").text(
|
||||
dbSnapshot.pairingCode
|
||||
);
|
||||
$(".pageSettings .section.discordIntegration .howtocode").text(
|
||||
dbSnapshot.pairingCode
|
||||
);
|
||||
$(".pageSettings .section.discordIntegration .howto").removeClass(
|
||||
"hidden"
|
||||
);
|
||||
$(".pageSettings .section.discordIntegration .buttons").addClass(
|
||||
"hidden"
|
||||
);
|
||||
$(".pageSettings .section.discordIntegration .info").addClass("hidden");
|
||||
$(".pageSettings .section.discordIntegration .code").removeClass(
|
||||
"hidden"
|
||||
);
|
||||
} else if (
|
||||
dbSnapshot.discordId != undefined
|
||||
) {
|
||||
$(".pageSettings .section.discordIntegration .howto").addClass("hidden");
|
||||
} else{
|
||||
$(".pageSettings .section.discordIntegration .buttons").addClass(
|
||||
"hidden"
|
||||
);
|
||||
$(".pageSettings .section.discordIntegration .info").removeClass(
|
||||
"hidden"
|
||||
);
|
||||
$(".pageSettings .section.discordIntegration .code").addClass("hidden");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ let defaultConfig = {
|
|||
smoothLineScroll: false,
|
||||
alwaysShowDecimalPlaces: false,
|
||||
alwaysShowWordsHistory: false,
|
||||
singleListCommandLine: "manual",
|
||||
playSoundOnError: false,
|
||||
playSoundOnClick: "off",
|
||||
startGraphsAtZero: true,
|
||||
|
|
@ -60,7 +61,8 @@ let defaultConfig = {
|
|||
showOutOfFocusWarning: true,
|
||||
paceCaret: "off",
|
||||
paceCaretCustomSpeed: 100,
|
||||
pageWidth: "100"
|
||||
pageWidth: "100",
|
||||
chartAccuracy: true
|
||||
};
|
||||
|
||||
let cookieConfig = null;
|
||||
|
|
@ -190,6 +192,7 @@ function applyConfig(configObj) {
|
|||
setShowTimerProgress(configObj.showTimerProgress, true);
|
||||
setAlwaysShowDecimalPlaces(configObj.alwaysShowDecimalPlaces, true);
|
||||
setAlwaysShowWordsHistory(configObj.alwaysShowWordsHistory, true);
|
||||
setSingleListCommandLine(configObj.singleListCommandLine, true);
|
||||
setPlaySoundOnError(configObj.playSoundOnError, true);
|
||||
setPlaySoundOnClick(configObj.playSoundOnClick, true);
|
||||
setStopOnError(configObj.stopOnError, true);
|
||||
|
|
@ -201,6 +204,7 @@ function applyConfig(configObj) {
|
|||
setPaceCaret(configObj.paceCaret, true);
|
||||
setPaceCaretCustomSpeed(configObj.paceCaretCustomSpeed, true);
|
||||
setPageWidth(configObj.pageWidth, true);
|
||||
setChartAccuracy(configObj.chartAccuracy, true);
|
||||
|
||||
config.startGraphsAtZero = configObj.startGraphsAtZero;
|
||||
// if (
|
||||
|
|
@ -306,6 +310,31 @@ function setBlindMode(blind, nosave) {
|
|||
if (!nosave) saveConfigToCookie();
|
||||
}
|
||||
|
||||
function updateChartAccuracy() {
|
||||
resultHistoryChart.data.datasets[1].hidden = !config.chartAccuracy;
|
||||
resultHistoryChart.options.scales.yAxes[1].display = config.chartAccuracy;
|
||||
resultHistoryChart.update();
|
||||
}
|
||||
|
||||
function toggleChartAccuracy() {
|
||||
if (config.chartAccuracy) {
|
||||
config.chartAccuracy = false;
|
||||
} else {
|
||||
config.chartAccuracy = true;
|
||||
}
|
||||
updateChartAccuracy();
|
||||
saveConfigToCookie();
|
||||
}
|
||||
|
||||
function setChartAccuracy(chartAccuracy, nosave) {
|
||||
if (chartAccuracy == undefined) {
|
||||
chartAccuracy = true;
|
||||
}
|
||||
config.chartAccuracy = chartAccuracy;
|
||||
updateChartAccuracy();
|
||||
if (!nosave) saveConfigToCookie();
|
||||
}
|
||||
|
||||
//read ahead mode
|
||||
// function toggleReadAheadMode() {
|
||||
// config.readAheadMode = !config.readAheadMode;
|
||||
|
|
@ -429,6 +458,13 @@ function setAlwaysShowWordsHistory(val, nosave) {
|
|||
if (!nosave) saveConfigToCookie();
|
||||
}
|
||||
|
||||
//single list command line
|
||||
function setSingleListCommandLine(option, nosave) {
|
||||
if (!option) option = "manual";
|
||||
config.singleListCommandLine = option;
|
||||
if (!nosave) saveConfigToCookie();
|
||||
}
|
||||
|
||||
//show all lines
|
||||
function toggleShowAllLines() {
|
||||
sal = !config.showAllLines;
|
||||
|
|
@ -873,7 +909,7 @@ function setIndicateTypos(it, nosave) {
|
|||
}
|
||||
|
||||
|
||||
function previewTheme(name) {
|
||||
function previewTheme(name, setIsPreviewingVar = true) {
|
||||
if (
|
||||
(testActive || resultVisible) &&
|
||||
(config.theme === "nausea" || config.theme === "round_round_baby")
|
||||
|
|
@ -881,6 +917,7 @@ function previewTheme(name) {
|
|||
return;
|
||||
if (resultVisible && (name === "nausea" || name === "round_round_baby"))
|
||||
return;
|
||||
isPreviewingTheme = setIsPreviewingVar;
|
||||
$("#currentTheme").attr("href", `themes/${name}.css`);
|
||||
setTimeout(() => {
|
||||
refreshThemeColorObject();
|
||||
|
|
@ -969,6 +1006,7 @@ function applyCustomThemeColors() {
|
|||
document.documentElement.style.setProperty(e, array[index]);
|
||||
});
|
||||
} else {
|
||||
$(".current-theme").text(config.theme.replace('_',' '));
|
||||
previewTheme(config.theme);
|
||||
colorVars.forEach((e) => {
|
||||
document.documentElement.style.setProperty(e, "");
|
||||
|
|
|
|||
|
|
@ -4158,206 +4158,206 @@ const words = {
|
|||
italian: {
|
||||
leftToRight: true,
|
||||
words: [
|
||||
"cosa",
|
||||
"modo",
|
||||
"giorno",
|
||||
"morire",
|
||||
"venire",
|
||||
"chiamare",
|
||||
"chiudere",
|
||||
"prendere",
|
||||
"perché",
|
||||
"città",
|
||||
"signore",
|
||||
"ora",
|
||||
"quello",
|
||||
"grande",
|
||||
"potere",
|
||||
"fenomeno",
|
||||
"amore",
|
||||
"momento",
|
||||
"anno",
|
||||
"macchina",
|
||||
"fratello",
|
||||
"amico",
|
||||
"nemico",
|
||||
"bisogno",
|
||||
"salire",
|
||||
"uno",
|
||||
"capacità",
|
||||
"capire",
|
||||
"italiano",
|
||||
"francese",
|
||||
"spagnolo",
|
||||
"cane",
|
||||
"oca",
|
||||
"panda",
|
||||
"koala",
|
||||
"nero",
|
||||
"ciano",
|
||||
"rosa",
|
||||
"colore",
|
||||
"superiore",
|
||||
"fisica",
|
||||
"latino",
|
||||
"volare",
|
||||
"spendere",
|
||||
"veloce",
|
||||
"uscire",
|
||||
"invitare",
|
||||
"vento",
|
||||
"dormire",
|
||||
"rettangolo",
|
||||
"chiesa",
|
||||
"tutti",
|
||||
"calcio",
|
||||
"benvenuto",
|
||||
"video",
|
||||
"cinema",
|
||||
"nemmeno",
|
||||
"come",
|
||||
"ho",
|
||||
"ascoltare",
|
||||
"guidare",
|
||||
"treno",
|
||||
"rispondere",
|
||||
"niente",
|
||||
"utente",
|
||||
"disegno",
|
||||
"sole",
|
||||
"cuore",
|
||||
"sicuramente",
|
||||
"egocentrico",
|
||||
"basso",
|
||||
"pasta",
|
||||
"libro",
|
||||
"baciare",
|
||||
"cavallo",
|
||||
"merenda",
|
||||
"quaderno",
|
||||
"vero",
|
||||
"facile",
|
||||
"recipiente",
|
||||
"dove",
|
||||
"quando",
|
||||
"comode",
|
||||
"albero",
|
||||
"poco",
|
||||
"bambino",
|
||||
"notare",
|
||||
"moda",
|
||||
"naturale",
|
||||
"chiaro",
|
||||
"scuro",
|
||||
"chiaramente",
|
||||
"lentamente",
|
||||
"pavimento",
|
||||
"parete",
|
||||
"semplice",
|
||||
"probabilità",
|
||||
"domani",
|
||||
"oggi",
|
||||
"ieri",
|
||||
"tantissimo",
|
||||
"bello",
|
||||
"brevemente",
|
||||
"righello",
|
||||
"conoscere",
|
||||
"anello",
|
||||
"no",
|
||||
"sì",
|
||||
"si",
|
||||
"pensare",
|
||||
"fino",
|
||||
"montagna",
|
||||
"mare",
|
||||
"legno",
|
||||
"opera",
|
||||
"oceano",
|
||||
"salice",
|
||||
"cipresso",
|
||||
"musica",
|
||||
"Sanremo",
|
||||
"mela",
|
||||
"pera",
|
||||
"grano",
|
||||
"arancia",
|
||||
"commentare",
|
||||
"coltivare",
|
||||
"seminare",
|
||||
"nuvole",
|
||||
"temporale",
|
||||
"tempo",
|
||||
"mancano",
|
||||
"onda",
|
||||
"energia",
|
||||
"spirito",
|
||||
"anima",
|
||||
"corrente",
|
||||
"respirazione",
|
||||
"concentrazione",
|
||||
"universo",
|
||||
"mondo",
|
||||
"salto",
|
||||
"dolore",
|
||||
"cento",
|
||||
"camicia",
|
||||
"motore",
|
||||
"meccanismo",
|
||||
"Milano",
|
||||
"Roma",
|
||||
"Palermo",
|
||||
"cucinare",
|
||||
"camera",
|
||||
"delfino",
|
||||
"pesce",
|
||||
"canzone",
|
||||
"comprare",
|
||||
"vendere",
|
||||
"chiedere",
|
||||
"camminare",
|
||||
"io",
|
||||
"nostro",
|
||||
"per",
|
||||
"sono",
|
||||
"con",
|
||||
"uno",
|
||||
"è",
|
||||
"avevamo",
|
||||
"questo",
|
||||
"quello",
|
||||
"da",
|
||||
"caldo",
|
||||
"freddo",
|
||||
"corsa",
|
||||
"forse",
|
||||
"nove",
|
||||
"nuovo",
|
||||
"sorridere",
|
||||
"permettere",
|
||||
"già",
|
||||
"più",
|
||||
"parola",
|
||||
"però",
|
||||
"cosa",
|
||||
"alcuni",
|
||||
"vostro",
|
||||
"posto",
|
||||
"altro",
|
||||
"era",
|
||||
"no",
|
||||
"fare",
|
||||
"sì",
|
||||
"preso",
|
||||
"chiesto",
|
||||
"fatto",
|
||||
"tempo",
|
||||
"ogni",
|
||||
"dire",
|
||||
"tre",
|
||||
"quattro",
|
||||
"chiedere",
|
||||
"bene",
|
||||
"anche",
|
||||
"diversità",
|
||||
"sognare",
|
||||
"occhio",
|
||||
"giocare",
|
||||
"piccolo",
|
||||
"male",
|
||||
"morto",
|
||||
"mettere",
|
||||
"finire",
|
||||
"leggere",
|
||||
"mano",
|
||||
"casa",
|
||||
"grande",
|
||||
"più",
|
||||
"terra",
|
||||
"giovane",
|
||||
"vecchio",
|
||||
"giardino",
|
||||
"essere",
|
||||
"piccolo",
|
||||
"medio",
|
||||
"memoria",
|
||||
"mentre",
|
||||
"tipo",
|
||||
"bisogno",
|
||||
"provare",
|
||||
"mamma",
|
||||
"papà",
|
||||
"vicino",
|
||||
"nuovo",
|
||||
"indietro",
|
||||
"anno",
|
||||
"treno",
|
||||
"pensiero",
|
||||
"turno",
|
||||
"molte",
|
||||
"troppo",
|
||||
"italiano",
|
||||
"quando",
|
||||
"sarebbe",
|
||||
"lungo",
|
||||
"guardare",
|
||||
"andare",
|
||||
"potuto",
|
||||
"numero",
|
||||
"compagno",
|
||||
"governo",
|
||||
"grazie",
|
||||
"prego",
|
||||
"che",
|
||||
"mano",
|
||||
"piede",
|
||||
"gamba",
|
||||
"sembrare",
|
||||
"qualcuno",
|
||||
"qualcosa",
|
||||
"presto",
|
||||
"moneta",
|
||||
"giorno",
|
||||
"mio",
|
||||
"sapere",
|
||||
"acqua",
|
||||
"aranciata",
|
||||
"limone",
|
||||
"cestino",
|
||||
"problema",
|
||||
"preoccuparsi"
|
||||
"trovare",
|
||||
"fondare",
|
||||
"risposta",
|
||||
"scuola",
|
||||
"ancora",
|
||||
"qualche",
|
||||
"chiamare",
|
||||
"chiudere",
|
||||
"portare",
|
||||
"idea",
|
||||
"colore",
|
||||
"legno",
|
||||
"bianco",
|
||||
"nero",
|
||||
"camminare",
|
||||
"alleviare",
|
||||
"secondo",
|
||||
"abbastanza",
|
||||
"primo",
|
||||
"misurare",
|
||||
"metà",
|
||||
"capire",
|
||||
"comprendere",
|
||||
"prendere",
|
||||
"studiare",
|
||||
"mancare",
|
||||
"lontano",
|
||||
"sicuramente",
|
||||
"veloce",
|
||||
"velocemente",
|
||||
"giustamente",
|
||||
"intelligente",
|
||||
"oceano",
|
||||
"matematica",
|
||||
"mente",
|
||||
"ricordo",
|
||||
"interessante",
|
||||
"parole",
|
||||
"nuvole",
|
||||
"cielo",
|
||||
"pace",
|
||||
"amore",
|
||||
"fratello",
|
||||
"sorella",
|
||||
"famiglia",
|
||||
"massa",
|
||||
"palla",
|
||||
"cuore",
|
||||
"venire",
|
||||
"inverno",
|
||||
"strano",
|
||||
"viaggio",
|
||||
"musica",
|
||||
"melodia",
|
||||
"pausa",
|
||||
"salire",
|
||||
"giardino",
|
||||
"giusto",
|
||||
"bambino",
|
||||
"latte",
|
||||
"ordinare",
|
||||
"povero",
|
||||
"metallo",
|
||||
"legge",
|
||||
"per",
|
||||
"a",
|
||||
"anello",
|
||||
"raggio",
|
||||
"atomo",
|
||||
"umano",
|
||||
"pasta",
|
||||
"pizza",
|
||||
"pianoforte",
|
||||
"maggiore",
|
||||
"minore",
|
||||
"scale",
|
||||
"corrente",
|
||||
"pranzo",
|
||||
"mangiare",
|
||||
"uscire",
|
||||
"cane",
|
||||
"gatto",
|
||||
"elefante",
|
||||
"mucca",
|
||||
"albero",
|
||||
"fiore",
|
||||
"vincere",
|
||||
"perdere",
|
||||
"baciare",
|
||||
"vediamo",
|
||||
"chiacchierare",
|
||||
"video",
|
||||
"canzone",
|
||||
"vento",
|
||||
"contro",
|
||||
"serpente",
|
||||
"presto",
|
||||
"mentre",
|
||||
"durante",
|
||||
"dentro",
|
||||
"completamente",
|
||||
"chimica",
|
||||
"fisica",
|
||||
"latino",
|
||||
"viaggiare",
|
||||
"loro",
|
||||
"bicicletta",
|
||||
"automobile",
|
||||
"pensare",
|
||||
"curare",
|
||||
"lago",
|
||||
"aperto",
|
||||
"sentire",
|
||||
"cavallo",
|
||||
"notte",
|
||||
"bello",
|
||||
"brutto",
|
||||
"oggi",
|
||||
"domani",
|
||||
"ieri",
|
||||
"dormire",
|
||||
"sognare",
|
||||
"divano",
|
||||
"arrogante",
|
||||
"ballare"
|
||||
]
|
||||
},
|
||||
norwegian: {
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 15 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 39 KiB |
|
|
@ -1,70 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Monkey Type - Coming Soon!</title>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css?family=Roboto+Mono&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<style>
|
||||
* {
|
||||
font-family: "Roboto Mono";
|
||||
color: var(--main-color);
|
||||
}
|
||||
:root {
|
||||
--main-color: #eee;
|
||||
--sub-color: #444;
|
||||
--bg-color: #111;
|
||||
--caret-color: #fff;
|
||||
--active-word-color: #444;
|
||||
--roundness: 0.25rem;
|
||||
}
|
||||
body {
|
||||
background-color: var(--bg-color);
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: grid;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.grid {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
}
|
||||
.logo {
|
||||
margin-bottom: -0.12rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.logo .top {
|
||||
font-size: 0.65rem;
|
||||
line-height: 0.65rem;
|
||||
margin-bottom: -0.4rem;
|
||||
margin-left: -0.1rem;
|
||||
color: var(--sub-color);
|
||||
}
|
||||
|
||||
.logo .bottom {
|
||||
margin-left: -0.15rem;
|
||||
font-size: 2.3rem;
|
||||
line-height: 2.3rem;
|
||||
}
|
||||
.soon {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="grid">
|
||||
<div class="logo">
|
||||
<div class="top">monkey-see</div>
|
||||
<div class="bottom">monkey-type</div>
|
||||
</div>
|
||||
<div class="soon">Coming very soon...</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
19
public/themes/lil_dragon.css
Normal file
19
public/themes/lil_dragon.css
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
:root {
|
||||
--bg-color: #ebe1ef;
|
||||
--main-color: #8a5bd6;
|
||||
--caret-color: #212b43;
|
||||
--sub-color: #ac76e5;
|
||||
--text-color: #212b43;
|
||||
--error-color: #f794ca;
|
||||
--error-extra-color: #f279c2;
|
||||
--colorful-error-color: #f794ca;
|
||||
--colorful-error-extra-color: #f279c2;
|
||||
}
|
||||
|
||||
#menu .icon-button {
|
||||
color: #ba96db;
|
||||
}
|
||||
|
||||
#menu .icon-button:hover {
|
||||
color: #212b43;
|
||||
}
|
||||
|
|
@ -388,5 +388,15 @@
|
|||
"name": "nebula",
|
||||
"bgColor": "#212135",
|
||||
"textColor": "#be3c88"
|
||||
},
|
||||
{
|
||||
"name": "lil_dragon",
|
||||
"bgColor": "#ebe1ef",
|
||||
"textColor": "#8a5bd6"
|
||||
},
|
||||
{
|
||||
"name": "pastel",
|
||||
"bgColor": "#ffd1dc",
|
||||
"textColor": "#b39eb5"
|
||||
}
|
||||
]
|
||||
|
|
|
|||
11
public/themes/pastel.css
Normal file
11
public/themes/pastel.css
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
:root {
|
||||
--bg-color: #e0b2bd;
|
||||
--main-color: #fbf4b6;
|
||||
--caret-color: #fbf4b6;
|
||||
--sub-color: #b4e9ff;
|
||||
--text-color: #6d5c6f;
|
||||
--error-color: #ff6961;
|
||||
--error-extra-color: #c23b22;
|
||||
--colorful-error-color: #ff6961;
|
||||
--colorful-error-extra-color: #c23b22;
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue