2022-11-28 11:04:49 +00:00
|
|
|
/**
|
|
|
|
* @file This file provides abstracted functions to interact with the api
|
|
|
|
* @author Arlo Filley
|
|
|
|
*
|
|
|
|
* TODO:
|
|
|
|
* - use localstorage for storing test data
|
|
|
|
* - allow for test storage without an account
|
|
|
|
* - validate all inputs that are sent to the server
|
|
|
|
* - give useful errors to the user if there is an api error
|
|
|
|
* - split into multiple files to more easily read code
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This class provides all the useful methods to interact with the api.
|
|
|
|
*/
|
2022-10-05 14:10:32 +01:00
|
|
|
class API {
|
|
|
|
|
|
|
|
constructor() {
|
2023-09-04 21:01:22 +01:00
|
|
|
// this.url = "https://arlofilley.com/api/";
|
|
|
|
this.url = "../api";
|
2022-10-05 14:10:32 +01:00
|
|
|
// this is the url of the server
|
|
|
|
// this may have to change later on
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This takes the validated data and makes a post
|
|
|
|
* request to the rocket server
|
|
|
|
* @param {String} testType
|
|
|
|
* @param {int} testLength
|
|
|
|
* @param {int} testTime
|
|
|
|
* @param {int} testSeed
|
|
|
|
* @param {int} quoteId
|
|
|
|
* @param {int} wpm
|
|
|
|
* @param {int} accuracy
|
|
|
|
* @param {int} userId
|
|
|
|
*/
|
2023-09-04 21:01:22 +01:00
|
|
|
postTest(pTestType, pTestLength, pTestTime, pTestSeed, pQuoteId, pWpm, pAccuracy, pUserId) {
|
2022-10-05 14:10:32 +01:00
|
|
|
const data = {
|
2022-10-05 22:44:06 +01:00
|
|
|
'test_type': pTestType,
|
|
|
|
'test_length': pTestLength,
|
|
|
|
'test_time': pTestTime,
|
|
|
|
'test_seed': pTestSeed,
|
|
|
|
'quote_id': pQuoteId,
|
|
|
|
'wpm': pWpm,
|
|
|
|
'accuracy': pAccuracy,
|
|
|
|
'user_id': pUserId
|
2022-10-05 14:10:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const xhr = new XMLHttpRequest();
|
2022-11-06 23:07:13 +00:00
|
|
|
xhr.open(
|
|
|
|
"POST",
|
2023-09-04 21:01:22 +01:00
|
|
|
`${this.url}/post_test/`
|
2022-11-06 23:07:13 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
xhr.send(
|
|
|
|
JSON.stringify(data)
|
|
|
|
);
|
2022-11-28 11:04:49 +00:00
|
|
|
|
|
|
|
user.lastTest = data;
|
2022-10-05 14:10:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Validates all the parameters used for the postTest function which it then calls
|
|
|
|
*/
|
|
|
|
validateTest() {
|
2022-11-29 12:21:38 +00:00
|
|
|
const test = screenManager.screen.textbox.getWords();
|
2022-10-05 14:10:32 +01:00
|
|
|
const testType = "words";
|
|
|
|
let testLength = test.length;
|
2022-11-28 11:04:49 +00:00
|
|
|
let testTime = screenManager.screen.timer.getTime();
|
2022-10-05 14:10:32 +01:00
|
|
|
const testSeed = 0;
|
|
|
|
const quoteId = 0;
|
|
|
|
let wpm;
|
2022-11-11 19:54:52 +00:00
|
|
|
const userId = Number(user.userId);
|
2022-11-29 12:21:38 +00:00
|
|
|
let test_content = screenManager.screen.textbox.getTestContent();
|
|
|
|
|
2022-11-18 12:27:54 +00:00
|
|
|
let string = "";
|
2022-11-28 11:04:49 +00:00
|
|
|
let inaccurateLetters = 0;
|
2022-11-18 12:27:54 +00:00
|
|
|
for (let letter = 0; letter < test.length; letter++) {
|
|
|
|
if (test[letter] === test_content[letter]) {
|
|
|
|
string += test[letter];
|
2022-11-28 11:04:49 +00:00
|
|
|
} else {
|
|
|
|
inaccurateLetters += 1;
|
2022-11-18 12:27:54 +00:00
|
|
|
}
|
|
|
|
}
|
2022-10-05 14:10:32 +01:00
|
|
|
|
2022-11-29 12:21:38 +00:00
|
|
|
const accuracy = Math.round(((test.length - inaccurateLetters) / test.length) * 100);
|
2022-11-28 11:04:49 +00:00
|
|
|
|
2022-10-05 14:10:32 +01:00
|
|
|
// this is the wpm calculation factoring in the time of test
|
|
|
|
// it assumes that all words are 5 characters long because on average
|
|
|
|
// they are
|
2022-11-18 12:27:54 +00:00
|
|
|
wpm = Math.round((string.length / 5) * (60 / testTime));
|
2022-10-05 14:10:32 +01:00
|
|
|
|
|
|
|
// the following code is a series of if statements that checks the
|
|
|
|
// types of the variables is correct if not it errors it and returns
|
|
|
|
// out of the function
|
|
|
|
|
|
|
|
if ( typeof testType !== "string" ) {
|
|
|
|
console.error(`testType is value ${typeof testType}\nshould be a string`);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ( typeof testLength !== "number") {
|
|
|
|
console.error(`testLength is value ${typeof testLength}\n should be a number`);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ( typeof testTime !== "number") {
|
|
|
|
console.error(`testTime is value ${typeof testTime}\n should be a number`);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ( typeof testSeed !== "number") {
|
|
|
|
console.error(`testSeed is value ${typeof testSeed}\n should be a number`);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ( typeof quoteId !== "number") {
|
|
|
|
console.error(`quoteId is value ${typeof quoteId}\n should be a number`);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ( typeof wpm !== "number") {
|
|
|
|
console.error(`wpm is value ${typeof wpm}\n should be a number`);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ( typeof accuracy !== "number") {
|
|
|
|
console.error(`accuracy is value ${typeof accuracy}\n should be a number`);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ( typeof userId !== "number") {
|
|
|
|
console.error(`userId is value ${typeof userId}\n should be a number`);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// after checking that all variables are of the correct type these if statements check
|
|
|
|
// that they are acceptable values or are in acceptable bounds depending on variable types
|
|
|
|
|
|
|
|
if (testType !== "words") {
|
|
|
|
// currently words is the only acceptable type but
|
|
|
|
// this will change in later iterations
|
|
|
|
|
|
|
|
console.error(`testType is invalid\nacceptable options ['words']`);
|
|
|
|
}
|
|
|
|
// upper bounds for these numbers are less of a concern because the server will automatically
|
|
|
|
// return an error if values are over the limit
|
|
|
|
if (testLength < 0) {
|
|
|
|
console.error(`testLength is too small, min value 0`)
|
|
|
|
}
|
|
|
|
if (testTime < 1) {
|
|
|
|
console.error(`testTime is too small, min value 1`)
|
|
|
|
}
|
|
|
|
if (testSeed < 0) {
|
|
|
|
console.error(`testSeed is too small, min value 0`)
|
|
|
|
}
|
|
|
|
if (quoteId < 0) {
|
|
|
|
console.error(`quoteId is too small, min value 0`)
|
|
|
|
}
|
|
|
|
if (wpm < 0) {
|
|
|
|
console.error(`wpm is too small, min value 0`)
|
|
|
|
}
|
|
|
|
// accuracy needs an upper bound check because users can't have more than 100%
|
|
|
|
// accuracy when completing their tests
|
|
|
|
if (accuracy < 0) {
|
|
|
|
console.error(`accuracy is too small, min value 0`)
|
|
|
|
} else if (accuracy > 100) {
|
|
|
|
console.error(`accuracy is too big, max value 100`)
|
|
|
|
}
|
|
|
|
if (userId < 0) {
|
|
|
|
console.error(`userId is too small, min value 0`)
|
|
|
|
}
|
|
|
|
|
|
|
|
// there will be other tests here in later iterations but for now these tests should suffice
|
|
|
|
|
2023-09-04 21:01:22 +01:00
|
|
|
this.postTest(testType, testLength, testTime, testSeed, quoteId, wpm, accuracy, userId);
|
2022-10-05 14:10:32 +01:00
|
|
|
}
|
2022-11-06 23:07:13 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* takes a validated name and password and sends
|
|
|
|
* a post request to make a user with the given
|
|
|
|
* username and password
|
|
|
|
* @param {String} username
|
|
|
|
* @param {String} password
|
|
|
|
* @returns
|
|
|
|
*/
|
2023-09-04 21:01:22 +01:00
|
|
|
createUser( username, password ) {
|
|
|
|
console.log( username, password );
|
2022-11-06 23:07:13 +00:00
|
|
|
const user = {
|
|
|
|
username: username,
|
|
|
|
password: password
|
|
|
|
};
|
|
|
|
|
|
|
|
const xhr = new XMLHttpRequest();
|
2023-09-04 21:01:22 +01:00
|
|
|
xhr.open( "POST", `${this.url}/create_user/` );
|
2022-11-06 23:07:13 +00:00
|
|
|
|
2023-09-04 21:01:22 +01:00
|
|
|
xhr.send( JSON.stringify(user) );
|
2022-11-11 19:54:52 +00:00
|
|
|
|
2022-11-18 11:33:25 +00:00
|
|
|
xhr.onload = () => {
|
2022-12-01 14:41:14 +00:00
|
|
|
if (xhr.status === 500) {
|
|
|
|
alert("Sorry, looks like your username isn't unique");
|
2023-09-04 21:01:22 +01:00
|
|
|
console.error("Sorry, looks like your username isn't unique")
|
2022-12-01 14:41:14 +00:00
|
|
|
} else {
|
2023-09-04 21:01:22 +01:00
|
|
|
this.login(username, password);
|
2022-12-01 14:41:14 +00:00
|
|
|
}
|
2022-11-18 11:33:25 +00:00
|
|
|
};
|
2022-11-06 23:07:13 +00:00
|
|
|
}
|
2022-11-11 13:04:13 +00:00
|
|
|
|
2023-09-04 21:01:22 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* takes a validated name and password and sends
|
|
|
|
* a post request to make a user with the given
|
|
|
|
* username and password
|
|
|
|
* @param {String} username
|
|
|
|
* @param {String} password
|
|
|
|
* @param {boolean} initial
|
|
|
|
* @returns
|
|
|
|
*/
|
|
|
|
login(pUsername, pPassword, initial = false) {
|
|
|
|
// Variable Validation
|
|
|
|
if (pUsername == undefined || pPassword == undefined) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// If Local Storage has the information we need there is no need to make a request to the server
|
|
|
|
if (localStorage.getItem("username") == pUsername) {
|
|
|
|
user.userId = localStorage.getItem("userId");
|
|
|
|
user.secret = localStorage.getItem("secret");
|
|
|
|
user.username = localStorage.getItem("username");
|
|
|
|
|
|
|
|
return
|
2022-11-11 13:04:13 +00:00
|
|
|
}
|
2023-09-04 21:01:22 +01:00
|
|
|
|
|
|
|
let xhr = new XMLHttpRequest();
|
|
|
|
xhr.open('GET', `${this.url}/login/${pUsername}/${pPassword}`);
|
|
|
|
xhr.send();
|
|
|
|
xhr.onload = () => {
|
|
|
|
let response = JSON.parse(xhr.response);
|
|
|
|
|
|
|
|
// If there is an error with the login we need
|
|
|
|
if (xhr.response == null) {
|
|
|
|
alert("Error Logging in, maybe check your password");
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
user.userId = response.user_id;
|
|
|
|
user.username = pUsername
|
|
|
|
user.secret = response.secret;
|
|
|
|
|
|
|
|
localStorage.setItem("userId", user.userId);
|
|
|
|
localStorage.setItem("username", pUsername);
|
|
|
|
localStorage.setItem("secret", user.secret);
|
|
|
|
};
|
2022-11-11 13:04:13 +00:00
|
|
|
}
|
2022-11-11 19:54:52 +00:00
|
|
|
|
|
|
|
logout() {
|
|
|
|
user = new User();
|
2022-11-17 11:48:12 +00:00
|
|
|
user.username = "no one";
|
|
|
|
user.password = "none";
|
|
|
|
user.userId = 0;
|
|
|
|
user.tests = [];
|
2022-11-11 19:54:52 +00:00
|
|
|
localStorage.clear();
|
2022-12-01 14:41:14 +00:00
|
|
|
this.getTest();
|
2022-11-11 19:54:52 +00:00
|
|
|
}
|
2022-11-17 11:48:12 +00:00
|
|
|
|
|
|
|
getUserTests() {
|
2022-12-01 14:41:14 +00:00
|
|
|
if (user.userId === 0) {
|
|
|
|
user.tests = undefined;
|
|
|
|
return;
|
|
|
|
}
|
2022-11-17 11:48:12 +00:00
|
|
|
let xhr = new XMLHttpRequest();
|
2023-09-04 21:01:22 +01:00
|
|
|
|
|
|
|
xhr.open('GET', `${this.url}/get_tests/${user.userId}/${user.secret}`);
|
2022-11-17 11:48:12 +00:00
|
|
|
xhr.send();
|
|
|
|
xhr.onload = () => {
|
|
|
|
user.tests = JSON.parse(xhr.response);
|
|
|
|
};
|
|
|
|
}
|
2022-11-18 11:33:25 +00:00
|
|
|
|
|
|
|
getLeaderBoard() {
|
|
|
|
let xhr = new XMLHttpRequest();
|
2023-09-04 21:01:22 +01:00
|
|
|
xhr.open('GET', `${this.url}/leaderboard/`);
|
2022-11-18 11:33:25 +00:00
|
|
|
xhr.send();
|
|
|
|
xhr.onload = () => {
|
|
|
|
user.leaderboard = JSON.parse(xhr.response);
|
|
|
|
};
|
|
|
|
}
|
2022-11-18 12:27:54 +00:00
|
|
|
|
|
|
|
getTest() {
|
|
|
|
let xhr = new XMLHttpRequest();
|
2023-09-04 21:01:22 +01:00
|
|
|
xhr.open('GET', `${this.url}/new_test/`);
|
2022-11-18 12:27:54 +00:00
|
|
|
xhr.send();
|
2022-12-16 00:34:58 +00:00
|
|
|
xhr.onload = () =>{
|
2022-11-28 14:17:27 +00:00
|
|
|
const effectiveWidth = (windowWidth - 200) / 13;
|
2022-11-18 12:27:54 +00:00
|
|
|
let textArr = JSON.parse(xhr.response);
|
2022-11-28 14:17:27 +00:00
|
|
|
let finalText = [];
|
2022-11-18 12:27:54 +00:00
|
|
|
let text = "";
|
|
|
|
for (let i = 0; i < textArr.length; i++) {
|
2022-11-28 14:17:27 +00:00
|
|
|
if (text.length + textArr[i].length < effectiveWidth) {
|
|
|
|
text += `${textArr[i]} `
|
|
|
|
} else {
|
2022-11-29 12:21:38 +00:00
|
|
|
finalText.push(text.substring(0,text.length-1));
|
|
|
|
text = `${textArr[i]} `;
|
2022-11-28 14:17:27 +00:00
|
|
|
}
|
2022-11-18 12:27:54 +00:00
|
|
|
}
|
2022-11-28 14:17:27 +00:00
|
|
|
user.nextTest = finalText;
|
2022-11-18 12:27:54 +00:00
|
|
|
};
|
|
|
|
}
|
2022-10-05 14:10:32 +01:00
|
|
|
}
|