mirror of https://github.com/dirtbags/moth.git
Finish porting to pure-json RPC
This commit is contained in:
parent
8e67abe0c0
commit
5d1886b9e6
|
@ -1,47 +0,0 @@
|
||||||
// Devel server addons
|
|
||||||
|
|
||||||
// devel_addin drops a bunch of development extensions into element e.
|
|
||||||
// It will only modify stuff inside e.
|
|
||||||
function devel_addin(obj, e) {
|
|
||||||
let h = document.createElement("h2");
|
|
||||||
e.appendChild(h);
|
|
||||||
h.textContent = "Development Options";
|
|
||||||
|
|
||||||
let g = document.createElement("p");
|
|
||||||
e.appendChild(g);
|
|
||||||
g.innerText = "This section will not appear for participants."
|
|
||||||
|
|
||||||
let keys = Object.keys(obj);
|
|
||||||
keys.sort();
|
|
||||||
for (let key of keys) {
|
|
||||||
switch (key) {
|
|
||||||
case "body":
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let d = document.createElement("div");
|
|
||||||
e.appendChild(d);
|
|
||||||
d.classList.add("kvpair");
|
|
||||||
|
|
||||||
let ktxt = document.createElement("span");
|
|
||||||
d.appendChild(ktxt);
|
|
||||||
ktxt.textContent = key;
|
|
||||||
|
|
||||||
let val = obj[key];
|
|
||||||
if (Array.isArray(val)) {
|
|
||||||
let vi = document.createElement("select");
|
|
||||||
d.appendChild(vi);
|
|
||||||
vi.multiple = true;
|
|
||||||
for (let a of val) {
|
|
||||||
let opt = document.createElement("option");
|
|
||||||
vi.appendChild(opt);
|
|
||||||
opt.innerText = a;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let vi = document.createElement("input");
|
|
||||||
d.appendChild(vi);
|
|
||||||
vi.value = val;
|
|
||||||
vi.disabled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,6 +3,17 @@
|
||||||
var teamId
|
var teamId
|
||||||
var heartbeatInterval = 40000
|
var heartbeatInterval = 40000
|
||||||
|
|
||||||
|
function toast(message, timeout=5000) {
|
||||||
|
let p = document.createElement("p")
|
||||||
|
|
||||||
|
p.innerText = message
|
||||||
|
document.getElementById("messages").appendChild(p)
|
||||||
|
setTimeout(
|
||||||
|
e => { p.remove() },
|
||||||
|
timeout
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function rpc(url, params={}) {
|
function rpc(url, params={}) {
|
||||||
let formData = new FormData()
|
let formData = new FormData()
|
||||||
for (let k in params) {
|
for (let k in params) {
|
||||||
|
@ -110,11 +121,10 @@ function showPuzzles(teamId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function login(e) {
|
function login(e) {
|
||||||
|
e.preventDefault()
|
||||||
let name = document.querySelector("[name=name]").value
|
let name = document.querySelector("[name=name]").value
|
||||||
let id = document.querySelector("[name=id]").value
|
let id = document.querySelector("[name=id]").value
|
||||||
|
|
||||||
e.preventDefault()
|
|
||||||
|
|
||||||
rpc("register", {
|
rpc("register", {
|
||||||
name: name,
|
name: name,
|
||||||
id: id,
|
id: id,
|
||||||
|
@ -148,17 +158,6 @@ function login(e) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function toast(message, timeout=5000) {
|
|
||||||
let p = document.createElement("p")
|
|
||||||
|
|
||||||
p.innerText = message
|
|
||||||
document.getElementById("messages").appendChild(p)
|
|
||||||
setTimeout(
|
|
||||||
e => { p.remove() },
|
|
||||||
timeout
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
// Already signed in?
|
// Already signed in?
|
||||||
let id = sessionStorage.getItem("id")
|
let id = sessionStorage.getItem("id")
|
||||||
|
|
|
@ -5,86 +5,20 @@
|
||||||
<link rel="stylesheet" href="basic.css">
|
<link rel="stylesheet" href="basic.css">
|
||||||
<meta name="viewport" content="width=device-width">
|
<meta name="viewport" content="width=device-width">
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<script src="devel.js"></script>
|
<script src="puzzle.js"></script>
|
||||||
<script>
|
<script>
|
||||||
function init() {
|
|
||||||
let params = new URLSearchParams(window.location.search);
|
|
||||||
let categoryName = params.get("cat");
|
|
||||||
let points = params.get("points");
|
|
||||||
let puzzleId = params.get("pid");
|
|
||||||
|
|
||||||
let puzzle = document.getElementById("puzzle");
|
|
||||||
let base = "content/" + categoryName + "/" + puzzleId + "/";
|
|
||||||
|
|
||||||
fetch(base + "puzzle.json")
|
|
||||||
.then(resp => {
|
|
||||||
return resp.json();
|
|
||||||
})
|
|
||||||
.then(obj => {
|
|
||||||
// Populate authors
|
|
||||||
document.getElementById("authors").textContent = obj.authors.join(", ");
|
|
||||||
|
|
||||||
// If answers are provided, this is the devel server
|
|
||||||
if (obj.answers) {
|
|
||||||
devel_addin(obj, document.getElementById("devel"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load scripts
|
|
||||||
for (let script of obj.scripts) {
|
|
||||||
let st = document.createElement("script");
|
|
||||||
document.head.appendChild(st);
|
|
||||||
st.src = base + script;
|
|
||||||
}
|
|
||||||
|
|
||||||
// List associated files
|
|
||||||
for (let fn of obj.files) {
|
|
||||||
let li = document.createElement("li");
|
|
||||||
let a = document.createElement("a");
|
|
||||||
a.href = base + fn;
|
|
||||||
a.innerText = fn;
|
|
||||||
li.appendChild(a);
|
|
||||||
document.getElementById("files").appendChild(li);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefix `base` to relative URLs in the puzzle body
|
|
||||||
let doc = new DOMParser().parseFromString(obj.body, "text/html");
|
|
||||||
for (let se of doc.querySelectorAll("[src],[href]")) {
|
|
||||||
se.outerHTML = se.outerHTML.replace(/(src|href)="([^/]+)"/i, "$1=\"" + base + "$2\"");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace puzzle children with what's in `doc`
|
|
||||||
Array.from(puzzle.childNodes).map(e => e.remove());
|
|
||||||
Array.from(doc.body.childNodes).map(e => puzzle.appendChild(e));
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
// Show error to the user
|
|
||||||
Array.from(puzzle.childNodes).map(e => e.remove());
|
|
||||||
let p = document.createElement("p");
|
|
||||||
puzzle.appendChild(p);
|
|
||||||
p.classList.add("Error");
|
|
||||||
p.textContent = err;
|
|
||||||
});
|
|
||||||
|
|
||||||
document.title = categoryName + " " + points;
|
|
||||||
document.querySelector("body > h1").innerText = document.title;
|
|
||||||
document.querySelector("input[name=cat]").value = categoryName;
|
|
||||||
document.querySelector("input[name=points]").value = points;
|
|
||||||
}
|
|
||||||
if (document.readyState === "loading") {
|
|
||||||
document.addEventListener("DOMContentLoaded", init);
|
|
||||||
} else {
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Puzzle</h1>
|
<h1>Puzzle</h1>
|
||||||
<section>
|
<section>
|
||||||
<div id="puzzle">Loading...</div>
|
<div id="puzzle"><span class="spinner"></span></div>
|
||||||
<ul id="files"></ul>
|
<ul id="files"></ul>
|
||||||
<p>Puzzle by <span id="authors"></span></p>
|
<p>Puzzle by <span id="authors"></span></p>
|
||||||
</section>
|
</section>
|
||||||
<form action="answer" method="post">
|
<div id="messages"></div>
|
||||||
|
<form>
|
||||||
<input type="hidden" name="cat">
|
<input type="hidden" name="cat">
|
||||||
<input type="hidden" name="points">
|
<input type="hidden" name="points">
|
||||||
Team ID: <input type="text" name="id"> <br>
|
Team ID: <input type="text" name="id"> <br>
|
||||||
|
|
|
@ -0,0 +1,184 @@
|
||||||
|
// devel_addin drops a bunch of development extensions into element e.
|
||||||
|
// It will only modify stuff inside e.
|
||||||
|
function devel_addin(obj, e) {
|
||||||
|
let h = document.createElement("h2")
|
||||||
|
e.appendChild(h)
|
||||||
|
h.textContent = "Development Options"
|
||||||
|
|
||||||
|
let g = document.createElement("p")
|
||||||
|
e.appendChild(g)
|
||||||
|
g.innerText = "This section will not appear for participants."
|
||||||
|
|
||||||
|
let keys = Object.keys(obj)
|
||||||
|
keys.sort()
|
||||||
|
for (let key of keys) {
|
||||||
|
switch (key) {
|
||||||
|
case "body":
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
let d = document.createElement("div")
|
||||||
|
e.appendChild(d)
|
||||||
|
d.classList.add("kvpair")
|
||||||
|
|
||||||
|
let ktxt = document.createElement("span")
|
||||||
|
d.appendChild(ktxt)
|
||||||
|
ktxt.textContent = key
|
||||||
|
|
||||||
|
let val = obj[key]
|
||||||
|
if (Array.isArray(val)) {
|
||||||
|
let vi = document.createElement("select")
|
||||||
|
d.appendChild(vi)
|
||||||
|
vi.multiple = true
|
||||||
|
for (let a of val) {
|
||||||
|
let opt = document.createElement("option")
|
||||||
|
vi.appendChild(opt)
|
||||||
|
opt.innerText = a
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let vi = document.createElement("input")
|
||||||
|
d.appendChild(vi)
|
||||||
|
vi.value = val
|
||||||
|
vi.disabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function rpc(url, params={}) {
|
||||||
|
let formData = new FormData()
|
||||||
|
for (let k in params) {
|
||||||
|
formData.append(k, params[k])
|
||||||
|
}
|
||||||
|
return fetch(url, {
|
||||||
|
method: "POST",
|
||||||
|
body: formData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function toast(message, timeout=5000) {
|
||||||
|
let p = document.createElement("p")
|
||||||
|
|
||||||
|
p.innerText = message
|
||||||
|
document.getElementById("messages").appendChild(p)
|
||||||
|
setTimeout(
|
||||||
|
e => { p.remove() },
|
||||||
|
timeout
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function submit(e) {
|
||||||
|
e.preventDefault()
|
||||||
|
let cat = document.querySelector("input[name=cat]").value
|
||||||
|
let points = document.querySelector("input[name=points]").value
|
||||||
|
let id = document.querySelector("input[name=id]").value
|
||||||
|
let answer = document.querySelector("input[name=answer]").value
|
||||||
|
|
||||||
|
rpc("answer", {
|
||||||
|
cat: cat,
|
||||||
|
points: points,
|
||||||
|
id: id,
|
||||||
|
answer: answer,
|
||||||
|
})
|
||||||
|
.then(resp => {
|
||||||
|
if (resp.ok) {
|
||||||
|
resp.json()
|
||||||
|
.then(obj => {
|
||||||
|
toast(obj.data.description)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
toast("Error submitting your answer. Try again in a few seconds.")
|
||||||
|
console.log(resp)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
toast("Error submitting your answer. Try again in a few seconds.")
|
||||||
|
console.log(err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadPuzzle(categoryName, points, puzzleId) {
|
||||||
|
let puzzle = document.getElementById("puzzle")
|
||||||
|
let base = "content/" + categoryName + "/" + puzzleId + "/"
|
||||||
|
|
||||||
|
fetch(base + "puzzle.json")
|
||||||
|
.then(resp => {
|
||||||
|
return resp.json()
|
||||||
|
})
|
||||||
|
.then(obj => {
|
||||||
|
// Populate authors
|
||||||
|
document.getElementById("authors").textContent = obj.authors.join(", ")
|
||||||
|
|
||||||
|
// If answers are provided, this is the devel server
|
||||||
|
if (obj.answers) {
|
||||||
|
devel_addin(obj, document.getElementById("devel"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load scripts
|
||||||
|
for (let script of obj.scripts) {
|
||||||
|
let st = document.createElement("script")
|
||||||
|
document.head.appendChild(st)
|
||||||
|
st.src = base + script
|
||||||
|
}
|
||||||
|
|
||||||
|
// List associated files
|
||||||
|
for (let fn of obj.files) {
|
||||||
|
let li = document.createElement("li")
|
||||||
|
let a = document.createElement("a")
|
||||||
|
a.href = base + fn
|
||||||
|
a.innerText = fn
|
||||||
|
li.appendChild(a)
|
||||||
|
document.getElementById("files").appendChild(li)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefix `base` to relative URLs in the puzzle body
|
||||||
|
let doc = new DOMParser().parseFromString(obj.body, "text/html")
|
||||||
|
for (let se of doc.querySelectorAll("[src],[href]")) {
|
||||||
|
se.outerHTML = se.outerHTML.replace(/(src|href)="([^/]+)"/i, "$1=\"" + base + "$2\"")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace puzzle children with what's in `doc`
|
||||||
|
Array.from(puzzle.childNodes).map(e => e.remove())
|
||||||
|
Array.from(doc.body.childNodes).map(e => puzzle.appendChild(e))
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
// Show error to the user
|
||||||
|
Array.from(puzzle.childNodes).map(e => e.remove())
|
||||||
|
let p = document.createElement("p")
|
||||||
|
puzzle.appendChild(p)
|
||||||
|
p.classList.add("Error")
|
||||||
|
p.textContent = err
|
||||||
|
})
|
||||||
|
|
||||||
|
document.title = categoryName + " " + points
|
||||||
|
document.querySelector("body > h1").innerText = document.title
|
||||||
|
document.querySelector("input[name=cat]").value = categoryName
|
||||||
|
document.querySelector("input[name=points]").value = points
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
let params = new URLSearchParams(window.location.search)
|
||||||
|
let categoryName = params.get("cat")
|
||||||
|
let points = params.get("points")
|
||||||
|
let puzzleId = params.get("pid")
|
||||||
|
|
||||||
|
if (categoryName && points && puzzleId) {
|
||||||
|
loadPuzzle(categoryName, points, puzzleId)
|
||||||
|
}
|
||||||
|
|
||||||
|
let teamId = sessionStorage.getItem("id")
|
||||||
|
if (teamId) {
|
||||||
|
document.querySelector("input[name=id]").value = teamId
|
||||||
|
}
|
||||||
|
|
||||||
|
document.querySelector("form").addEventListener("submit", submit)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.readyState === "loading") {
|
||||||
|
document.addEventListener("DOMContentLoaded", init)
|
||||||
|
} else {
|
||||||
|
init()
|
||||||
|
}
|
||||||
|
|
|
@ -4,28 +4,30 @@
|
||||||
<title>Redeem Token</title>
|
<title>Redeem Token</title>
|
||||||
<link rel="stylesheet" href="basic.css">
|
<link rel="stylesheet" href="basic.css">
|
||||||
<meta name="viewport" content="width=device-width">
|
<meta name="viewport" content="width=device-width">
|
||||||
|
<script src="puzzle.js"></script>
|
||||||
<script>
|
<script>
|
||||||
function erpdert() {
|
function tokenInput(e) {
|
||||||
let vals = document.querySelector("[name=token]").value.split(":");
|
let vals = e.target.value.split(":")
|
||||||
document.querySelector("[name=cat]").value = vals[0];
|
document.querySelector("input[name=cat]").value = vals[0]
|
||||||
document.querySelector("[name=points]").value = vals[1];
|
document.querySelector("input[name=points]").value = vals[1]
|
||||||
document.querySelector("[name=answer]").value = vals[2];
|
document.querySelector("input[name=answer]").value = vals[2]
|
||||||
}
|
}
|
||||||
|
|
||||||
function init() {
|
function tokenInit() {
|
||||||
document.querySelector("[name=token]").addEventListener("input", erpdert);
|
document.querySelector("input[name=token]").addEventListener("input", tokenInput)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (document.readyState === "loading") {
|
if (document.readyState === "loading") {
|
||||||
document.addEventListener("DOMContentLoaded", init);
|
document.addEventListener("DOMContentLoaded", tokenInit)
|
||||||
} else {
|
} else {
|
||||||
init();
|
tokenInit()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Redeem Token</h1>
|
<h1>Redeem Token</h1>
|
||||||
<form action="token" method="post">
|
<div id="messages"></div>
|
||||||
|
<form id="tokenForm">
|
||||||
<input type="hidden" name="cat">
|
<input type="hidden" name="cat">
|
||||||
<input type="hidden" name="points">
|
<input type="hidden" name="points">
|
||||||
<input type="hidden" name="answer">
|
<input type="hidden" name="answer">
|
||||||
|
|
Loading…
Reference in New Issue