ui changes and better secret use
5
TODO.md
@ -7,6 +7,9 @@
|
||||
-[ ] Make js api asynchronous
|
||||
-[ ] Hash passwords on front end so server plaintext password is never sent over the internet
|
||||
-[ ] Use secret to uniquely identify
|
||||
-[ ] Scrollbars
|
||||
-[x] Scrollbars
|
||||
-[ ] Change Color Scheme
|
||||
-[ ] Be able to view others accounts
|
||||
-[ ] Username gets cut off if too long
|
||||
-[ ] Validate username length
|
||||
-[ ] Test caret doesn't keep up
|
@ -10,8 +10,10 @@
|
||||
//! - create structure for the input of post test
|
||||
|
||||
// Imports for json handling and rusqlite
|
||||
use sqlx::sqlite::{SqlitePool, SqlitePoolOptions};
|
||||
use rocket::serde::Serialize;
|
||||
use sqlx::sqlite::{ SqlitePool, SqlitePoolOptions };
|
||||
use rocket::serde::{ Serialize, json::Json };
|
||||
|
||||
use crate::typing::test::PostTest;
|
||||
|
||||
/// Contains the database connection pool
|
||||
pub struct Database(SqlitePool);
|
||||
@ -60,11 +62,23 @@ impl Database {
|
||||
|
||||
/// takes necessary data about a test and creates
|
||||
/// a database record with the data
|
||||
pub async fn create_test(&self, test_type: &str, test_length: u32, test_time: u32, test_seed: i64, quote_id: i32, wpm: u8, accuracy: u8, user_id: u32) -> Result<(), sqlx::Error> {
|
||||
pub async fn create_test(&self, test: Json<PostTest<'_>>) -> Result<(), sqlx::Error> {
|
||||
// Test to see whether the secret is correct
|
||||
let user = sqlx::query!("
|
||||
Select secret
|
||||
From Users
|
||||
Where user_id=?",
|
||||
test.user_id
|
||||
).fetch_one(&self.0).await?;
|
||||
|
||||
if user.secret != test.secret {
|
||||
return Err(sqlx::Error::RowNotFound)
|
||||
}
|
||||
|
||||
sqlx::query!("
|
||||
INSERT INTO Tests (test_type, test_length, test_time, test_seed, quote_id, wpm, accuracy, user_id)
|
||||
VALUES(?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)",
|
||||
test_type, test_length, test_time, test_seed, quote_id, wpm, accuracy, user_id
|
||||
test.test_type, test.test_length, test.test_time, test.test_seed, test.quote_id, test.wpm, test.accuracy, test.user_id
|
||||
).execute(&self.0).await?;
|
||||
|
||||
Ok(())
|
||||
|
@ -17,23 +17,25 @@ use std::{
|
||||
#[derive(Deserialize)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
pub struct PostTest<'r> {
|
||||
test_type: &'r str,
|
||||
test_length: u32,
|
||||
test_time: u32,
|
||||
test_seed: i64,
|
||||
quote_id: i32,
|
||||
wpm: u8,
|
||||
accuracy: u8,
|
||||
user_id: u32
|
||||
pub test_type: &'r str,
|
||||
pub test_length: u32,
|
||||
pub test_time: u32,
|
||||
pub test_seed: i64,
|
||||
pub quote_id: i32,
|
||||
pub wpm: u8,
|
||||
pub accuracy: u8,
|
||||
pub user_id: u32,
|
||||
pub secret: &'r str
|
||||
}
|
||||
|
||||
/// Api Route that accepts test data and posts it to the database
|
||||
/// Acessible from http://url/api/post_test
|
||||
#[post("/post_test", data = "<test>")]
|
||||
pub async fn create_test(test: Json<PostTest<'_>>, database: &State<Database>) {
|
||||
match database.create_test(test.test_type, test.test_length, test.test_time, test.test_seed, test.quote_id, test.wpm, test.accuracy, test.user_id).await {
|
||||
let user_id = test.user_id;
|
||||
match database.create_test(test).await {
|
||||
Err(why) => { println!("A database error occured creating a test, {why}"); }
|
||||
Ok(()) => { println!("Successfully created test for {}", test.user_id); }
|
||||
Ok(()) => { println!("Successfully created test for {user_id}"); }
|
||||
}
|
||||
}
|
||||
|
||||
|
26
websites/Typing/Admin/Delete User/index.html
Executable file
@ -0,0 +1,26 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Admin Panel</title>
|
||||
|
||||
<link rel="stylesheet" href="../index.css">
|
||||
<script src="index.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<nav>
|
||||
<p>Arlo Filley</p>
|
||||
<ul>
|
||||
<li><a href="../">Home</a></li>
|
||||
<li><a href="./">Delete A User</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
<h1>Password</h1>
|
||||
<input id="password" type="password">
|
||||
<table>
|
||||
<tbody id="table"></tbody>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
51
websites/Typing/Admin/Delete User/index.js
Executable file
@ -0,0 +1,51 @@
|
||||
get();
|
||||
|
||||
function get() {
|
||||
let xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', `https://arlofilley.com/api/typing/leaderboard`);
|
||||
xhr.setRequestHeader('Content-Type', 'application/json');
|
||||
xhr.responseType = 'json';
|
||||
xhr.send();
|
||||
xhr.onload = () => {
|
||||
json = xhr.response
|
||||
createTable(json);
|
||||
};
|
||||
}
|
||||
|
||||
function createTable(pJson) {
|
||||
|
||||
let table = document.getElementById("table");
|
||||
console.log(password)
|
||||
|
||||
pJson.forEach(element => {
|
||||
let tr = document.createElement('tr');
|
||||
let button = document.createElement('button');
|
||||
let username = element.username;
|
||||
button.textContent = "delete"
|
||||
button.addEventListener( "click", () => {
|
||||
let xhr = new XMLHttpRequest();
|
||||
let password = document.getElementById("password").value;
|
||||
xhr.open('GET', `https://arlofilley.com/api/typing/delete_user/${password}/${username}/120932187`);
|
||||
xhr.send();
|
||||
})
|
||||
|
||||
create_table_element(tr, "", [element.username, element.wpm], button)
|
||||
table.appendChild(tr)
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function create_table_element(tr, string, elements, button) {
|
||||
if (elements.length > 0) {
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
let td = document.createElement('td');
|
||||
td.appendChild(document.createTextNode(elements[i]));
|
||||
tr.appendChild(td);
|
||||
}
|
||||
} else {
|
||||
let td = document.createElement('td');
|
||||
td.appendChild(document.createTextNode(string));
|
||||
tr.appendChild(td);
|
||||
}
|
||||
tr.appendChild(button);
|
||||
}
|
125
websites/Typing/Admin/index.css
Executable file
@ -0,0 +1,125 @@
|
||||
:root {
|
||||
--navbar: #333333;
|
||||
|
||||
/* Navbar Button Properties */
|
||||
--navbar-button: #058ED9;
|
||||
--navbar-button-hover: #283F3B;
|
||||
--navbar-button-border: #058ED9;
|
||||
|
||||
/* */
|
||||
--background: #1f1f1f;
|
||||
|
||||
/* Table Properties*/
|
||||
--table: #FFD07B;
|
||||
--table-top:#FDB833;
|
||||
|
||||
/* Title Properties */
|
||||
--title-background: #333333;
|
||||
--title-text: #ffffff;
|
||||
|
||||
/* General Properties */
|
||||
--text: #000000;
|
||||
--border: #000000;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #111;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
nav {
|
||||
background-color: #333;
|
||||
height: 75px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
border-bottom-left-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
|
||||
nav p {
|
||||
margin-right: 700px;
|
||||
text-decoration: none;
|
||||
color: #fff;
|
||||
font-size: 38px;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
font-family: "Montserrat", sans-serif;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
display: inline-block;
|
||||
margin: 0 20px;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #fff;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
font-family: "Montserrat", sans-serif;
|
||||
padding: 10px 20px;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
background-color: #555;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-left: 5%;
|
||||
margin-bottom: 0%;
|
||||
color: #fff;
|
||||
font-family: Verdana, Geneva, Tahoma, sans-serif;
|
||||
}
|
||||
|
||||
input {
|
||||
margin-left: 5%;
|
||||
margin-top: 1.5%;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 90%;
|
||||
border-collapse: collapse;
|
||||
margin: 0px;
|
||||
margin-left: 5%;
|
||||
margin-top: 30px;
|
||||
padding: 0px;
|
||||
border-spacing: 0px;
|
||||
border: 2px var(--border) solid;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
table tr:nth-child(n) {
|
||||
background: var(--table);
|
||||
}
|
||||
|
||||
table tr:nth-child(1) {
|
||||
background: var(--table-top);
|
||||
}
|
||||
|
||||
tr {
|
||||
border: 1px black dotted;
|
||||
}
|
||||
|
||||
td {
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
font-size: larger;
|
||||
}
|
||||
|
||||
button {
|
||||
margin-left: 40%;
|
||||
padding: 8px;
|
||||
padding-left: 12.5%;
|
||||
padding-right: 12.5%;
|
||||
}
|
20
websites/Typing/Admin/index.html
Executable file
@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Admin Panel</title>
|
||||
|
||||
<link rel="stylesheet" href="index.css">
|
||||
</head>
|
||||
<body>
|
||||
<nav>
|
||||
<p>Arlo Filley</p>
|
||||
<ul>
|
||||
<li><a href="./">Home</a></li>
|
||||
<li><a href="./Delete User/">Delete A User</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</body>
|
||||
</html>
|
@ -15,12 +15,7 @@
|
||||
*/
|
||||
class API {
|
||||
|
||||
constructor() {
|
||||
// this.url = "https://arlofilley.com/api/";
|
||||
this.url = "../api";
|
||||
// this is the url of the server
|
||||
// this may have to change later on
|
||||
}
|
||||
constructor() { this.url = "../api"; }
|
||||
|
||||
/**
|
||||
* This takes the validated data and makes a post
|
||||
@ -43,7 +38,8 @@ class API {
|
||||
'quote_id': pQuoteId,
|
||||
'wpm': pWpm,
|
||||
'accuracy': pAccuracy,
|
||||
'user_id': pUserId
|
||||
'user_id': pUserId,
|
||||
'secret': user.secret
|
||||
}
|
||||
|
||||
const xhr = new XMLHttpRequest();
|
||||
@ -210,13 +206,8 @@ class API {
|
||||
* @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) {
|
||||
if (localStorage.getItem("username") === pUsername || (initial && localStorage.length === 3) ) {
|
||||
user.userId = localStorage.getItem("userId");
|
||||
user.secret = localStorage.getItem("secret");
|
||||
user.username = localStorage.getItem("username");
|
||||
@ -224,6 +215,11 @@ class API {
|
||||
return
|
||||
}
|
||||
|
||||
// Variable Validation
|
||||
if (pUsername == undefined || pPassword == undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
let xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', `${this.url}/login/${pUsername}/${pPassword}`);
|
||||
xhr.send();
|
||||
@ -231,7 +227,7 @@ class API {
|
||||
let response = JSON.parse(xhr.response);
|
||||
|
||||
// If there is an error with the login we need
|
||||
if (xhr.response == null) {
|
||||
if (xhr.response === null) {
|
||||
alert("Error Logging in, maybe check your password");
|
||||
return
|
||||
}
|
||||
@ -249,7 +245,7 @@ class API {
|
||||
logout() {
|
||||
user = new User();
|
||||
user.username = "no one";
|
||||
user.password = "none";
|
||||
user.password = "";
|
||||
user.userId = 0;
|
||||
user.tests = [];
|
||||
localStorage.clear();
|
||||
|
8
websites/Typing/api/user.js
Normal file → Executable file
@ -17,7 +17,7 @@ class User {
|
||||
constructor() {
|
||||
this.username = "not logged in";
|
||||
this.userId = 0;
|
||||
this.secret = "";
|
||||
this.secret;
|
||||
|
||||
this.leaderboard;
|
||||
this.time = 15;
|
||||
@ -27,8 +27,8 @@ class User {
|
||||
this.nextTest = `satisfy powerful pleasant bells disastrous mean kited is gusted romantic past taste immolate productive leak close show crabby awake handsails finicky betray long-term incompetent wander show manage toys convey hop constitute number send like off ice aboard well-made vast vacuous tramp seed force divergent flower porter fire untidy soggy fetch`;
|
||||
|
||||
this.colorScheme = {
|
||||
background: "#eeeee4",
|
||||
text: "#000",
|
||||
background: "#160C1C",
|
||||
text: "#FBB",
|
||||
|
||||
timerBar: "#50C5B7",
|
||||
timerText: "#000",
|
||||
@ -40,7 +40,7 @@ class User {
|
||||
buttonText: "#fff",
|
||||
buttonBorder: "#000",
|
||||
|
||||
buttonHoverBG: "#0f0",
|
||||
buttonHoverBG: "#FBB",
|
||||
buttonHoverText: "#000",
|
||||
buttonHoverBorder: "#000"
|
||||
}
|
||||
|
0
websites/Typing/assets/favicon/android-chrome-192x192.png
Normal file → Executable file
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
0
websites/Typing/assets/favicon/android-chrome-512x512.png
Normal file → Executable file
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
0
websites/Typing/assets/favicon/apple-touch-icon.png
Normal file → Executable file
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
0
websites/Typing/assets/favicon/favicon-16x16.png
Normal file → Executable file
Before Width: | Height: | Size: 552 B After Width: | Height: | Size: 552 B |
0
websites/Typing/assets/favicon/favicon-32x32.png
Normal file → Executable file
Before Width: | Height: | Size: 968 B After Width: | Height: | Size: 968 B |
0
websites/Typing/assets/favicon/favicon.ico
Normal file → Executable file
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
0
websites/Typing/assets/favicon/site.webmanifest
Normal file → Executable file
0
websites/Typing/assets/fonts/RobotoMono-Medium.ttf
Normal file → Executable file
0
websites/Typing/index.css
Normal file → Executable file
0
websites/Typing/index.html
Normal file → Executable file
@ -32,7 +32,7 @@ function setup() {
|
||||
user = new User();
|
||||
screenManager.setScreen(new StartScreen());
|
||||
|
||||
api.login();
|
||||
api.login(null, null, true);
|
||||
api.getTest();
|
||||
textFont(roboto);
|
||||
}
|
||||
|
0
websites/Typing/lib/p5.js
Normal file → Executable file
0
websites/Typing/lib/p5.min.js
vendored
Normal file → Executable file
0
websites/Typing/screens/accountScreen.js
Normal file → Executable file
0
websites/Typing/screens/endscreen.js
Normal file → Executable file
51
websites/Typing/screens/leaderboardscreen.js
Normal file → Executable file
@ -17,11 +17,12 @@ class LeaderboardScreen {
|
||||
this.menu = new Menu();
|
||||
api.getLeaderBoard();
|
||||
this.testButtons;
|
||||
this.buttons = [
|
||||
new Button(1150, 270, 240, 120, "up"),
|
||||
new Button(1150, 390, 240, 120, "down"),
|
||||
]
|
||||
// this.buttons = [
|
||||
// new Button(1150, 270, 240, 120, "up"),
|
||||
// new Button(1150, 390, 240, 120, "down"),
|
||||
// ]
|
||||
this.offset = 0;
|
||||
this.scroll_bar_button = new Button(1200, 200, 20, 20, "")
|
||||
}
|
||||
|
||||
draw() {
|
||||
@ -39,29 +40,40 @@ class LeaderboardScreen {
|
||||
}
|
||||
}
|
||||
|
||||
fill(user.colorScheme.testBad);
|
||||
rect(1200, 270, 20, 420);
|
||||
|
||||
fill(user.colorScheme.testGood);
|
||||
rect(1200, 270, 20, 420 / user.leaderboard.length * (this.offset + 1));
|
||||
this.scroll_bar_button.height = (user.leaderboard.length)
|
||||
|
||||
if (this.scroll_bar_button.isPressed()) {
|
||||
this.scroll_bar_button.y = mouseY - this.scroll_bar_button.height / 2;
|
||||
}
|
||||
|
||||
// the furthest up the scrollbar can go is the top
|
||||
if (this.scroll_bar_button.y < 270) {
|
||||
this.scroll_bar_button.y = 270;
|
||||
}
|
||||
|
||||
if (this.scroll_bar_button.y > 690 - this.scroll_bar_button.height) {
|
||||
this.scroll_bar_button.y = 690 - this.scroll_bar_button.height;
|
||||
}
|
||||
this.scroll_bar_button.draw();
|
||||
|
||||
if (this.testButtons !== undefined && this.testButtons.length > 1) {
|
||||
for (let i = 0; i < this.testButtons.length; i++) {
|
||||
this.testButtons[i][0].draw()
|
||||
this.testButtons[i][1].draw()
|
||||
this.testButtons[i][2].draw()
|
||||
}
|
||||
if (this.buttons[0].isPressed()) {
|
||||
if (this.offset > 0) {
|
||||
this.offset--;
|
||||
}
|
||||
|
||||
this.createTestButtons(this.offset);
|
||||
} else if (this.buttons[1].isPressed()) {
|
||||
if (this.offset < user.leaderboard.length - 13) {
|
||||
this.offset++;
|
||||
}
|
||||
this.offset = Number((
|
||||
// number of pixels from top of screen / total range of options, put to a whole integer to produce the correct offset
|
||||
(this.scroll_bar_button.y - 270) / ((420 - this.scroll_bar_button.height) / (user.leaderboard.length - 13))
|
||||
).toFixed(0));
|
||||
|
||||
this.createTestButtons(this.offset);
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.buttons.length; i++) {
|
||||
this.buttons[i].draw();
|
||||
}
|
||||
this.createTestButtons(this.offset)
|
||||
} else {
|
||||
fill(user.colorScheme.text);
|
||||
text("Looks Like There Isn't A Leaderboard", windowWidth / 2, 300);
|
||||
@ -78,7 +90,6 @@ class LeaderboardScreen {
|
||||
]];
|
||||
let j = 300;
|
||||
for (let i = 0 + offset; i < user.leaderboard.length && i <= 12+offset; i++) {
|
||||
console.log(i);
|
||||
this.testButtons.push([
|
||||
new Button(400, j, 100, 30, `${i+1}`, true, true, "#000", "#000", "#fff"), // test # button
|
||||
new Button(500, j, 400, 30, `${user.leaderboard[i].username}`, true, true, "#000", "#000", "#fff"), // accuracy button
|
||||
|
0
websites/Typing/screens/loginscreen.js
Normal file → Executable file
72
websites/Typing/screens/profilescreen.js
Normal file → Executable file
@ -19,11 +19,12 @@ class ProfileScreen {
|
||||
this.menu = new Menu();
|
||||
api.getUserTests();
|
||||
this.testButtons;
|
||||
this.buttons = [
|
||||
new Button(950, 270, 240, 120, "up"),
|
||||
new Button(950, 390, 240, 120, "down"),
|
||||
]
|
||||
// this.buttons = [
|
||||
// new Button(950, 270, 240, 120, "up"),
|
||||
// new Button(950, 390, 240, 120, "down"),
|
||||
// ]
|
||||
this.offset = 0;
|
||||
this.scroll_bar_button = new Button(1200, 200, 20, 20, "")
|
||||
}
|
||||
|
||||
draw() {
|
||||
@ -49,21 +50,6 @@ class ProfileScreen {
|
||||
this.testButtons[i][2].draw()
|
||||
this.testButtons[i][3].draw()
|
||||
}
|
||||
if (this.buttons[0].isPressed()) {
|
||||
if (this.offset > 0) {
|
||||
this.offset--;
|
||||
}
|
||||
this.createTestButtons(this.offset);
|
||||
} else if (this.buttons[1].isPressed()) {
|
||||
if (user.tests.length - 13 - this.offset > 0) {
|
||||
this.offset++;
|
||||
}
|
||||
this.createTestButtons(this.offset);
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.buttons.length; i++) {
|
||||
this.buttons[i].draw();
|
||||
}
|
||||
} else {
|
||||
fill(user.colorScheme.text);
|
||||
text("Looks Like You Don't have any tests :(", windowWidth / 2, 300);
|
||||
@ -71,22 +57,54 @@ class ProfileScreen {
|
||||
|
||||
fill(user.colorScheme.text);
|
||||
text(`Logged in as ${user.username}`, windowWidth-150, 15);
|
||||
|
||||
if (user.tests === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
fill(user.colorScheme.testBad);
|
||||
rect(1200, 270, 20, 420);
|
||||
|
||||
fill(user.colorScheme.testGood);
|
||||
rect(1200, 270, 20, 420 / user.tests.length * (this.offset + 1));
|
||||
this.scroll_bar_button.height = (user.tests.length)
|
||||
|
||||
if (this.scroll_bar_button.isPressed()) {
|
||||
this.scroll_bar_button.y = mouseY - this.scroll_bar_button.height / 2;
|
||||
}
|
||||
|
||||
|
||||
// the furthest up the scrollbar can go is the top
|
||||
if (this.scroll_bar_button.y < 270) {
|
||||
this.scroll_bar_button.y = 270;
|
||||
}
|
||||
|
||||
if (this.scroll_bar_button.y > 690 - this.scroll_bar_button.height) {
|
||||
this.scroll_bar_button.y = 690 - this.scroll_bar_button.height;
|
||||
}
|
||||
this.scroll_bar_button.draw();
|
||||
|
||||
this.offset = Number((
|
||||
// number of pixels from top of screen / total range of options, put to a whole integer to produce the correct offset
|
||||
(this.scroll_bar_button.y - 270) / ((420 - this.scroll_bar_button.height) / (user.tests.length - 13))
|
||||
).toFixed(0));
|
||||
this.createTestButtons(this.offset);
|
||||
}
|
||||
|
||||
createTestButtons(offset = 0) {
|
||||
this.testButtons = [[
|
||||
new Button(400, 270, 100, 30, "test #"), // test # button
|
||||
new Button(500, 270, 100, 30, "wpm"), // wpm button
|
||||
new Button(600, 270, 100, 30, "accuracy"), // accuracy button
|
||||
new Button(700, 270, 240, 30, "characters typed")
|
||||
new Button(600, 270, 100, 30, "test #"), // test # button
|
||||
new Button(700, 270, 100, 30, "wpm"), // wpm button
|
||||
new Button(800, 270, 100, 30, "accuracy"), // accuracy button
|
||||
new Button(900, 270, 240, 30, "characters typed")
|
||||
]];
|
||||
let j = 300;
|
||||
for (let i = user.tests.length-1-offset; i >= user.tests.length-13-offset && i >= 0; i--) {
|
||||
this.testButtons.push([
|
||||
new Button(400, j, 100, 30, `${i+1}`, true, true , "#000", "#000", "#fff"), // test # button
|
||||
new Button(500, j, 100, 30, `${user.tests[i].wpm}`, true, true , "#000", "#000", "#fff"), // accuracy button
|
||||
new Button(600, j, 100, 30, `${user.tests[i].accuracy}`, true, true , "#000", "#000", "#fff"), // wpm button
|
||||
new Button(700, j, 240, 30, `${user.tests[i].test_length}`, true, true , "#000", "#000", "#fff")
|
||||
new Button(600, j, 100, 30, `${i+1}`, true, true , "#000", "#000", "#fff"), // test # button
|
||||
new Button(700, j, 100, 30, `${user.tests[i].wpm}`, true, true , "#000", "#000", "#fff"), // accuracy button
|
||||
new Button(800, j, 100, 30, `${user.tests[i].accuracy}`, true, true , "#000", "#000", "#fff"), // wpm button
|
||||
new Button(900, j, 240, 30, `${user.tests[i].test_length}`, true, true , "#000", "#000", "#fff")
|
||||
])
|
||||
j+=30;
|
||||
}
|
||||
|
0
websites/Typing/screens/screenmanager.js
Normal file → Executable file
5
websites/Typing/screens/settingsScreen.js
Normal file → Executable file
@ -17,9 +17,12 @@ class settingsScreen {
|
||||
|
||||
textSize(100);
|
||||
fill(user.colorScheme.text);
|
||||
text("Test Settings", 450, 100);
|
||||
text("Test Settings", windowWidth / 2, 100);
|
||||
|
||||
this.menu.draw();
|
||||
|
||||
fill(user.colorScheme.text);
|
||||
text("Test Duration", windowWidth / 2 - 250, 265)
|
||||
this.timeMenu.draw();
|
||||
fill("#000");
|
||||
text(`Logged in as ${user.username}`, windowWidth-150, 15);
|
||||
|
0
websites/Typing/screens/signUpScreen.js
Normal file → Executable file
0
websites/Typing/screens/startscreen.js
Normal file → Executable file
10
websites/Typing/screens/testscreen.js
Normal file → Executable file
@ -14,9 +14,13 @@
|
||||
*/
|
||||
class TestScreen {
|
||||
constructor() {
|
||||
this.textbox = new Textbox(100,100,windowWidth - 200,windowHeight,0,true,"#000", false, "#000", "#000", true, true);
|
||||
this.textbox = new Textbox(
|
||||
100, windowHeight / 2 - 100,
|
||||
windowWidth - 500,windowHeight,
|
||||
0, true, "#000", false, "#000", "#000", true, true);
|
||||
this.timer = new Timer(0,0,windowWidth,50,0,true,"#fff", true, "#000", "#666", user.time, true);
|
||||
this.timerStarted = false;
|
||||
this.stopButton = new Button(0,50,200,50, "Stop Test");
|
||||
}
|
||||
|
||||
draw() {
|
||||
@ -25,6 +29,10 @@ class TestScreen {
|
||||
if (this.timerStarted) {
|
||||
this.timer.tick();
|
||||
}
|
||||
this.stopButton.draw();
|
||||
if (this.stopButton.isPressed()) {
|
||||
screenManager.setScreen(new StartScreen())
|
||||
}
|
||||
}
|
||||
|
||||
letterTyped(key) {
|
||||
|
1
websites/Typing/ui_elements/button.js
Normal file → Executable file
@ -179,6 +179,7 @@ class Button {
|
||||
return;
|
||||
}
|
||||
textSize(20);
|
||||
textAlign(CENTER, CENTER);
|
||||
|
||||
if (mouseX > this.x && mouseX < this.x + this.width && mouseY > this.y && mouseY < this.y + this.height) {
|
||||
|
||||
|
0
websites/Typing/ui_elements/canvas.js
Normal file → Executable file
10
websites/Typing/ui_elements/menu.js
Normal file → Executable file
@ -13,11 +13,11 @@
|
||||
class Menu {
|
||||
constructor() {
|
||||
this.buttons = [
|
||||
new Button(0, 0, 100, 30, "Account"),
|
||||
new Button(101, 0, 130, 30, "Test Data"),
|
||||
new Button(232, 0, 140, 30, "Start Test"),
|
||||
new Button(373, 0, 140, 30, "Leaderboard"),
|
||||
new Button(514, 0, 180, 30, "Test Settings")
|
||||
new Button(0, 0, 200, 50, "Account"),
|
||||
new Button(201, 0, 200, 50, "Test Data"),
|
||||
new Button(402, 0, 200, 50, "Start Test"),
|
||||
new Button(603, 0, 200, 50, "Leaderboard"),
|
||||
new Button(804, 0, 200, 50, "Test Settings")
|
||||
]
|
||||
}
|
||||
|
||||
|
18
websites/Typing/ui_elements/textbox.js
Normal file → Executable file
@ -27,6 +27,9 @@ class Textbox {
|
||||
* @param {bool} pBorder
|
||||
* @param {hexcode} pBorderColor
|
||||
* @param {hexcode} pBackgroundColor
|
||||
* @param {bool} pLine
|
||||
* @param {bool} pIsTest
|
||||
* @param {bool} pIsPassword
|
||||
*/
|
||||
constructor(
|
||||
pX, pY,
|
||||
@ -73,6 +76,7 @@ class Textbox {
|
||||
|
||||
this.goodColor = user.colorScheme.testGood;
|
||||
this.badColor = user.colorScheme.testBad;
|
||||
this.textColor = user.colorScheme.text;
|
||||
}
|
||||
|
||||
getX() {
|
||||
@ -271,7 +275,9 @@ class Textbox {
|
||||
} else {
|
||||
fill("#00044");
|
||||
}
|
||||
|
||||
text(this.testContent[this.currentLine-1][x], i, j);
|
||||
|
||||
i += 13;
|
||||
}
|
||||
j+= 30;
|
||||
@ -289,10 +295,18 @@ class Textbox {
|
||||
} else {
|
||||
fill(this.textColor);
|
||||
}
|
||||
|
||||
text(this.testContent[this.currentLine][x], i, j);
|
||||
|
||||
if (this.letters.length > 0 && x == this.letters.length && this.line) {
|
||||
fill(this.textColor)
|
||||
rect(i, j-15, 1, 30)
|
||||
}
|
||||
i += 13;
|
||||
}
|
||||
|
||||
|
||||
|
||||
i = this.x;
|
||||
j += 30;
|
||||
|
||||
@ -343,4 +357,8 @@ class Textbox {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
draw_line(line, y) {
|
||||
|
||||
}
|
||||
}
|
24
websites/Typing/ui_elements/timemenu.js
Normal file → Executable file
@ -15,13 +15,14 @@
|
||||
class TimeMenu {
|
||||
constructor() {
|
||||
this.buttons = [
|
||||
new Button(100, 230, 100, 30, "15s"),
|
||||
new Button(100, 260, 100, 30, "30s"),
|
||||
new Button(100, 290, 100, 30, "45s"),
|
||||
new Button(100, 320, 100, 30, "60s"),
|
||||
new Button(900, 250, 100, 30, "15s"),
|
||||
new Button(900, 280, 100, 30, "30s"),
|
||||
new Button(900, 310, 100, 30, "45s"),
|
||||
new Button(900, 340, 100, 30, "60s"),
|
||||
];
|
||||
|
||||
this.topButton = this.buttons[0];
|
||||
this.dropDownButton = new Button(1000, 250, 30, 30, "v")
|
||||
this.dropdown = false;
|
||||
}
|
||||
|
||||
@ -33,29 +34,28 @@ class TimeMenu {
|
||||
|
||||
if (this.buttons[0].isPressed() && user.time != 15) {
|
||||
user.time = 15;
|
||||
this.topButton = new Button(100, 230, 100, 30, "15s");
|
||||
this.topButton = new Button(900, 250, 100, 30, "15s");
|
||||
this.dropdown = false;
|
||||
} else if (this.buttons[1].isPressed()) {
|
||||
user.time = 30;
|
||||
this.topButton = new Button(100, 230, 100, 30, "30s");
|
||||
this.topButton = new Button(900, 250, 100, 30, "30s");
|
||||
this.dropdown = false;
|
||||
} else if (this.buttons[2].isPressed()) {
|
||||
user.time = 45;
|
||||
this.topButton = new Button(100, 230, 100, 30, "45s");
|
||||
this.topButton = new Button(900, 250, 100, 30, "45s");
|
||||
this.dropdown = false;
|
||||
} else if (this.buttons[3].isPressed()) {
|
||||
user.time = 60;
|
||||
this.topButton = new Button(100, 230, 100, 30, "60s");
|
||||
this.topButton = new Button(900, 250, 100, 30, "60s");
|
||||
this.dropdown = false;
|
||||
}
|
||||
} else {
|
||||
this.topButton.draw();
|
||||
}
|
||||
|
||||
if (this.topButton.isPressed()) {
|
||||
this.dropDownButton.draw();
|
||||
if (this.dropDownButton.isPressed()) {
|
||||
this.dropdown = true;
|
||||
} else if (mouseIsPressed) {
|
||||
this.dropdown = false;
|
||||
}
|
||||
} else if (mouseIsPressed) { this.dropdown = false };
|
||||
}
|
||||
}
|