This repository has been archived by the owner on Jul 28, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
159 lines (159 loc) · 7.59 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
<!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>PicoScratch Server</title>
<link rel="stylesheet" href="ui.css">
<style>
th, td {
padding: 5px;
text-align: center;
border-radius: 10px;
}
th:nth-of-type(odd),
td:nth-of-type(odd) {
background-color: #2e2e2e;
}
input {
background-color: #1d1d1d;
font-size: 1.5rem;
padding-left: 5px;
color: white;
border-style: solid;
border-color: #2e2e2e;
border-radius: 10px;
}
</style>
</head>
<body style="margin: 0;">
<div id="login" style="display: flex; height: 100vh; flex-direction: column; align-items: center; justify-content: center; gap: 5px;">
<h1>PicoScratch Server</h1>
<input type="password" placeholder="Password" id="pw" disabled>
</div>
<div style="display: flex; margin: 10px; gap: 15px; flex-direction: column; display: none; transform: scale(0) skew(90deg); opacity: 0; transition: transform 500ms, opacity 1s;" id="main">
<div style="padding: 5px; background-color: #1d1d1d; border-color: #2e2e2e; border-width: 2px; border-style: solid; border-radius: 10px; display: flex; align-items: center; justify-content: center; flex-direction: column; gap: 10px;">
<h1 style="margin: 0;">Control</h1>
<button id="start">Start Game</button>
<button id="end">End Game</button>
<div style="display: flex; gap: 10px; align-items: center;">
<p style="font-size: 1.4rem; margin: 0;">Start level: </p>
<input id="startlevel" type="number" style="width: 3ch;" value="1">
<button id="set">Set</button>
</div>
</div>
<div style="padding: 5px; background-color: #1d1d1d; border-color: #2e2e2e; border-width: 2px; border-style: solid; border-radius: 10px; display: flex; align-items: center; justify-content: center; flex-direction: column;">
<h1 style="margin: 0;">Leaderboard</h1>
<table id="leaderboard" style="font-size: 1.5rem;"></table>
</div>
</div>
<script>
let ws = new WebSocket("ws://" + location.host);
let leaderboard = [];
function renderLeaderboard() {
while (document.getElementById("leaderboard").firstChild) {
document.getElementById("leaderboard").removeChild(document.getElementById("leaderboard").lastChild);
}
const tr = document.createElement("tr");
["Platz", "Name", "Level", "Quiz %", "XP", "Actions"].forEach(s => {
const td = document.createElement("th");
td.innerText = s;
tr.appendChild(td);
})
document.getElementById("leaderboard").appendChild(tr);
for(let i = 0; i < leaderboard.length; i++) {
const player = leaderboard[i];
const tr = document.createElement("tr");
const place = document.createElement("td");
place.innerText = i + 1;
tr.appendChild(place);
const name = document.createElement("td");
name.innerText = player.name;
tr.appendChild(name);
const level = document.createElement("td");
level.innerText = player.level;
tr.appendChild(level);
const percentage = document.createElement("td");
percentage.innerText = player.percentage;
tr.appendChild(percentage);
const xp = document.createElement("td");
xp.innerText = player.xp;
tr.appendChild(xp);
const actions = document.createElement("td");
actions.style.display = "flex";
actions.style.gap = "10px";
const kickAction = document.createElement("button");
kickAction.style.padding = "5px";
kickAction.innerHTML = `<svg width="40px" height="40px" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M11 17.5a6.47 6.47 0 0 1 1.022-3.5h-7.77a2.249 2.249 0 0 0-2.249 2.25v.919c0 .572.179 1.13.51 1.596C4.057 20.929 6.58 22 10 22c.931 0 1.796-.08 2.592-.238A6.475 6.475 0 0 1 11 17.5ZM10 2.005a5 5 0 1 1 0 10 5 5 0 0 1 0-10Z" fill="#ffffff"/><path d="M23 17.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0Zm-4.647-2.853a.5.5 0 0 0-.707.707L19.293 17H15a.5.5 0 1 0 0 1h4.293l-1.647 1.647a.5.5 0 0 0 .707.707l2.5-2.5a.497.497 0 0 0 .147-.345V17.5a.498.498 0 0 0-.15-.357l-2.497-2.496Z" fill="#ffffff"/></svg>`;
kickAction.addEventListener("click", () => {
ws.send("kick " + player.name);
})
kickAction.title = "Kick " + player.name;
actions.appendChild(kickAction);
const deleteAction = document.createElement("button");
deleteAction.style.padding = "5px";
deleteAction.innerHTML = `<svg width="40px" height="40px" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M21.5 6a1 1 0 0 1-.883.993L20.5 7h-.845l-1.231 12.52A2.75 2.75 0 0 1 15.687 22H8.313a2.75 2.75 0 0 1-2.737-2.48L4.345 7H3.5a1 1 0 0 1 0-2h5a3.5 3.5 0 1 1 7 0h5a1 1 0 0 1 1 1Zm-7.25 3.25a.75.75 0 0 0-.743.648L13.5 10v7l.007.102a.75.75 0 0 0 1.486 0L15 17v-7l-.007-.102a.75.75 0 0 0-.743-.648Zm-4.5 0a.75.75 0 0 0-.743.648L9 10v7l.007.102a.75.75 0 0 0 1.486 0L10.5 17v-7l-.007-.102a.75.75 0 0 0-.743-.648ZM12 3.5A1.5 1.5 0 0 0 10.5 5h3A1.5 1.5 0 0 0 12 3.5Z" fill="#A03030"/></svg>`;
deleteAction.addEventListener("dblclick", () => {
ws.send("delete " + player.name);
})
deleteAction.title = "Delete " + player.name;
actions.appendChild(deleteAction);
const setLevelAction = document.createElement("button");
setLevelAction.style.padding = "5px";
setLevelAction.innerHTML = `<svg width="40px" height="40px" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M7.975 9.689a1 1 0 1 1-1.45-1.378l4.75-5a1 1 0 0 1 1.45 0l4.75 5a1 1 0 1 1-1.45 1.378L13 6.505v10.99l3.025-3.184a1 1 0 1 1 1.45 1.378l-4.75 5a1 1 0 0 1-1.45 0l-4.75-5a1 1 0 1 1 1.45-1.378L11 17.495V6.505L7.975 9.689Z" fill="#ffffff"/></svg>`;
setLevelAction.addEventListener("click", () => {
const level = prompt("Set level for " + player.name, player.level);
if(level == null) return;
ws.send("setlevel " + level + " " + player.name);
})
setLevelAction.title = "Set level for " + player.name;
actions.appendChild(setLevelAction);
tr.appendChild(actions);
document.getElementById("leaderboard").appendChild(tr);
}
}
document.querySelector("#pw").addEventListener("keypress", (e) => {
if(e.key != "Enter") return;
document.querySelector("#pw").disabled = true;
ws.send("admin " + document.querySelector("#pw").value);
})
document.querySelector("#pw").focus();
ws.addEventListener("open", () => {
document.querySelector("#pw").disabled = false;
})
ws.addEventListener("message", (data) => {
const msg = data.data.toString();
if(msg == "declined") {
document.querySelector("#pw").disabled = false;
document.querySelector("#pw").value = "";
console.log("Declined");
} else if(msg.startsWith("approved")) {
document.querySelector("#login").style.display = "none";
document.querySelector("#main").style.display = "flex";
setTimeout(() => {
document.querySelector("#main").style.transform = "";
document.querySelector("#main").style.opacity = "1";
}, 100);
leaderboard = JSON.parse(msg.split("approved ", 2)[1]);
renderLeaderboard();
ws.send("startlevel");
} else if(msg.startsWith("update")) {
leaderboard = JSON.parse(msg.split("update ", 2)[1]);
renderLeaderboard();
} else if(msg.startsWith("startlevel")) {
document.querySelector("#startlevel").value = parseInt(msg.split("startlevel ", 2)[1]);
}
})
document.querySelector("#start").addEventListener("click", () => {
ws.send("start");
})
document.querySelector("#end").addEventListener("click", () => {
ws.send("end");
})
document.querySelector("#set").addEventListener("click", () => {
ws.send("startlevel " + document.querySelector("#startlevel").value);
})
</script>
</body>
</html>