Finish porting to pure-json RPC

This commit is contained in:
Neale Pickett 2019-02-22 20:46:52 -07:00
parent 8e67abe0c0
commit 5d1886b9e6
5 changed files with 212 additions and 140 deletions

View File

@ -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;
}
}
}

View File

@ -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")

View File

@ -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>

184
theme/puzzle.js Normal file
View File

@ -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()
}

View File

@ -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">