2019-02-22 17:52:41 -07:00
|
|
|
// jshint asi:true
|
|
|
|
|
|
|
|
var teamId
|
|
|
|
var heartbeatInterval = 40000
|
|
|
|
|
2019-02-22 20:46:52 -07:00
|
|
|
function toast(message, timeout=5000) {
|
|
|
|
let p = document.createElement("p")
|
|
|
|
|
|
|
|
p.innerText = message
|
|
|
|
document.getElementById("messages").appendChild(p)
|
|
|
|
setTimeout(
|
|
|
|
e => { p.remove() },
|
|
|
|
timeout
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2019-02-22 17:52:41 -07:00
|
|
|
function renderPuzzles(obj) {
|
|
|
|
let puzzlesElement = document.createElement('div')
|
|
|
|
|
|
|
|
// Create a sorted list of category names
|
|
|
|
let cats = Object.keys(obj)
|
|
|
|
cats.sort()
|
|
|
|
for (let cat of cats) {
|
|
|
|
if (cat.startsWith("__")) {
|
|
|
|
// Skip metadata
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
let puzzles = obj[cat]
|
|
|
|
|
|
|
|
let pdiv = document.createElement('div')
|
|
|
|
pdiv.className = 'category'
|
|
|
|
|
|
|
|
let h = document.createElement('h2')
|
|
|
|
pdiv.appendChild(h)
|
|
|
|
h.textContent = cat
|
|
|
|
|
|
|
|
// Extras if we're running a devel server
|
|
|
|
if (obj.__devel__) {
|
|
|
|
let a = document.createElement('a')
|
|
|
|
h.insertBefore(a, h.firstChild)
|
|
|
|
a.textContent = "⬇️"
|
2019-02-27 16:15:45 -07:00
|
|
|
a.href = "mothballer/" + cat + ".mb"
|
2019-02-22 17:52:41 -07:00
|
|
|
a.classList.add("mothball")
|
|
|
|
a.title = "Download a compiled puzzle for this category"
|
|
|
|
}
|
|
|
|
|
|
|
|
// List out puzzles in this category
|
|
|
|
let l = document.createElement('ul')
|
|
|
|
pdiv.appendChild(l)
|
|
|
|
for (let puzzle of puzzles) {
|
|
|
|
let points = puzzle[0]
|
|
|
|
let id = puzzle[1]
|
|
|
|
|
|
|
|
let i = document.createElement('li')
|
|
|
|
l.appendChild(i)
|
|
|
|
i.textContent = " "
|
|
|
|
|
|
|
|
if (points === 0) {
|
|
|
|
// Sentry: there are no more puzzles in this category
|
|
|
|
i.textContent = "✿"
|
|
|
|
} else {
|
|
|
|
let a = document.createElement('a')
|
|
|
|
i.appendChild(a)
|
|
|
|
a.textContent = points
|
|
|
|
a.href = "puzzle.html?cat=" + cat + "&points=" + points + "&pid=" + id
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
puzzlesElement.appendChild(pdiv)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Drop that thing in
|
|
|
|
let container = document.getElementById("puzzles")
|
|
|
|
while (container.firstChild) {
|
|
|
|
container.firstChild.remove()
|
|
|
|
}
|
|
|
|
container.appendChild(puzzlesElement)
|
|
|
|
}
|
|
|
|
|
|
|
|
function heartbeat(teamId) {
|
2019-10-25 16:53:40 -06:00
|
|
|
fetch("puzzles.json?id=" + teamId)
|
2019-02-22 17:52:41 -07:00
|
|
|
.then(resp => {
|
|
|
|
if (resp.ok) {
|
|
|
|
resp.json()
|
|
|
|
.then(renderPuzzles)
|
|
|
|
.catch(err => {
|
|
|
|
toast("Error fetching recent puzzles. I'll try again in a moment.")
|
|
|
|
console.log(err)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.catch(err => {
|
|
|
|
toast("Error fetching recent puzzles. I'll try again in a moment.")
|
|
|
|
console.log(err)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
function showPuzzles(teamId) {
|
|
|
|
let spinner = document.createElement("span")
|
|
|
|
spinner.classList.add("spinner")
|
|
|
|
|
2019-02-22 19:09:38 -07:00
|
|
|
sessionStorage.setItem("id", teamId)
|
|
|
|
|
2019-02-22 17:52:41 -07:00
|
|
|
document.getElementById("login").style.display = "none"
|
|
|
|
document.getElementById("puzzles").appendChild(spinner)
|
|
|
|
heartbeat(teamId)
|
|
|
|
setInterval(e => { heartbeat(teamId) }, 40000)
|
2019-10-28 11:38:27 -06:00
|
|
|
drawCacheButton(teamId)
|
|
|
|
}
|
2019-10-25 16:53:40 -06:00
|
|
|
|
2019-10-28 11:38:27 -06:00
|
|
|
function drawCacheButton(teamId) {
|
2019-11-04 09:22:44 -07:00
|
|
|
let cacher = document.createElement("li")
|
|
|
|
let cache_button = document.createElement("a")
|
|
|
|
cache_button.innerText = "Cache"
|
|
|
|
cache_button.title = "Cache an offine copy of current content"
|
|
|
|
cache_button.href = "#"
|
2019-10-25 16:53:40 -06:00
|
|
|
cache_button.addEventListener("click", async function() {
|
2019-11-04 09:22:44 -07:00
|
|
|
toast("Caching all currently-open content")
|
|
|
|
await fetchAll(teamId)
|
|
|
|
toast("Done caching content")
|
|
|
|
})
|
|
|
|
cacher.appendChild(cache_button)
|
|
|
|
document.getElementsByTagName("nav")[0].getElementsByTagName("ul")[0].appendChild(cacher)
|
2019-10-28 11:38:27 -06:00
|
|
|
|
|
|
|
function updateCacheButton() {
|
2019-11-04 09:22:44 -07:00
|
|
|
let headers = new Headers()
|
|
|
|
headers.append("pragma", "no-cache")
|
|
|
|
headers.append("cache-control", "no-cache")
|
2019-10-28 11:38:27 -06:00
|
|
|
fetch("current_manifest.json?id=" + teamId, {method: "HEAD", headers: headers})
|
2019-11-07 12:25:48 -07:00
|
|
|
.then(resp => {
|
2019-10-28 11:38:27 -06:00
|
|
|
if (resp.ok) {
|
2019-11-04 09:22:44 -07:00
|
|
|
cacher.style.disply = "initial"
|
2019-10-28 11:38:27 -06:00
|
|
|
} else {
|
2019-11-04 09:22:44 -07:00
|
|
|
cacher.style.display = "none"
|
2019-10-28 11:38:27 -06:00
|
|
|
}
|
2019-11-07 12:25:48 -07:00
|
|
|
})
|
|
|
|
.catch(ex => {
|
2019-11-04 09:22:44 -07:00
|
|
|
cacher.style.display = "none"
|
2019-10-28 12:11:25 -06:00
|
|
|
})
|
2019-10-28 11:38:27 -06:00
|
|
|
}
|
|
|
|
|
2019-11-04 09:22:44 -07:00
|
|
|
setInterval ( updateCacheButton , 30000)
|
|
|
|
updateCacheButton()
|
2019-10-25 16:53:40 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
async function fetchAll(teamId) {
|
2019-11-04 09:22:44 -07:00
|
|
|
let headers = new Headers()
|
|
|
|
headers.append("pragma", "no-cache")
|
|
|
|
headers.append("cache-control", "no-cache")
|
|
|
|
requests = []
|
2019-10-25 16:53:40 -06:00
|
|
|
|
2019-10-28 11:38:27 -06:00
|
|
|
requests.push( fetch("current_manifest.json?id=" + teamId, {headers: headers})
|
2019-11-07 12:25:48 -07:00
|
|
|
.then(resp => {
|
2019-10-25 16:53:40 -06:00
|
|
|
if (resp.ok) {
|
|
|
|
resp.json()
|
2019-11-07 12:25:48 -07:00
|
|
|
.then(contents => {
|
2019-11-04 09:22:44 -07:00
|
|
|
console.log("Processing manifest")
|
2019-10-25 16:53:40 -06:00
|
|
|
for (let resource of contents) {
|
|
|
|
if (resource == "puzzles.json") {
|
2019-11-04 09:22:44 -07:00
|
|
|
continue
|
2019-10-25 16:53:40 -06:00
|
|
|
}
|
2019-10-28 12:11:25 -06:00
|
|
|
fetch(resource)
|
2019-11-07 12:25:48 -07:00
|
|
|
.then(e => {
|
2019-11-04 09:22:44 -07:00
|
|
|
console.log("Fetched " + resource)
|
2019-10-25 16:53:40 -06:00
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2019-11-04 09:22:44 -07:00
|
|
|
}))
|
2019-10-25 16:53:40 -06:00
|
|
|
|
2019-11-04 09:22:44 -07:00
|
|
|
let resp = await fetch("puzzles.json?id=" + teamId, {headers: headers})
|
2019-10-25 16:53:40 -06:00
|
|
|
|
|
|
|
if (resp.ok) {
|
2019-11-04 09:22:44 -07:00
|
|
|
let categories = await resp.json()
|
2019-10-25 16:53:40 -06:00
|
|
|
let cat_names = Object.keys(categories)
|
|
|
|
cat_names.sort()
|
|
|
|
for (let cat_name of cat_names) {
|
|
|
|
if (cat_name.startsWith("__")) {
|
|
|
|
// Skip metadata
|
|
|
|
continue
|
|
|
|
}
|
2019-11-04 09:22:44 -07:00
|
|
|
let puzzles = categories[cat_name]
|
2019-10-25 16:53:40 -06:00
|
|
|
for (let puzzle of puzzles) {
|
2019-11-04 09:22:44 -07:00
|
|
|
let url = "puzzle.html?cat=" + cat_name + "&points=" + puzzle[0] + "&pid=" + puzzle[1]
|
2019-10-28 12:11:25 -06:00
|
|
|
requests.push( fetch(url)
|
2019-11-07 12:25:48 -07:00
|
|
|
.then(e => {
|
2019-11-04 09:22:44 -07:00
|
|
|
console.log("Fetched " + url)
|
|
|
|
}))
|
2019-10-25 16:53:40 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-11-04 09:22:44 -07:00
|
|
|
return Promise.all(requests)
|
2019-02-22 17:52:41 -07:00
|
|
|
}
|
|
|
|
|
2019-02-22 19:09:38 -07:00
|
|
|
function login(e) {
|
2019-02-22 20:46:52 -07:00
|
|
|
e.preventDefault()
|
2019-02-22 17:52:41 -07:00
|
|
|
let name = document.querySelector("[name=name]").value
|
|
|
|
let id = document.querySelector("[name=id]").value
|
|
|
|
|
2019-02-23 12:04:42 -07:00
|
|
|
fetch("register", {
|
|
|
|
method: "POST",
|
|
|
|
body: new FormData(e.target),
|
2019-02-22 17:52:41 -07:00
|
|
|
})
|
|
|
|
.then(resp => {
|
|
|
|
if (resp.ok) {
|
|
|
|
resp.json()
|
|
|
|
.then(obj => {
|
|
|
|
if (obj.status == "success") {
|
|
|
|
toast("Team registered")
|
|
|
|
showPuzzles(id)
|
|
|
|
} else if (obj.data.short == "Already registered") {
|
|
|
|
toast("Logged in with previously-registered team name")
|
|
|
|
showPuzzles(id)
|
|
|
|
} else {
|
|
|
|
toast(obj.data.description)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.catch(err => {
|
|
|
|
toast("Oops, the server has lost its mind. You probably need to tell someone so they can fix it.")
|
|
|
|
console.log(err, resp)
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
toast("Oops, something's wrong with the server. Try again in a few seconds.")
|
|
|
|
console.log(resp)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.catch(err => {
|
|
|
|
toast("Oops, something went wrong. Try again in a few seconds.")
|
|
|
|
console.log(err)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
function init() {
|
2019-02-22 19:09:38 -07:00
|
|
|
// Already signed in?
|
|
|
|
let id = sessionStorage.getItem("id")
|
|
|
|
if (id) {
|
|
|
|
showPuzzles(id)
|
|
|
|
}
|
2019-10-25 16:53:40 -06:00
|
|
|
|
2019-02-22 19:09:38 -07:00
|
|
|
document.getElementById("login").addEventListener("submit", login)
|
2019-02-22 17:52:41 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (document.readyState === "loading") {
|
2019-11-04 09:22:44 -07:00
|
|
|
document.addEventListener("DOMContentLoaded", init)
|
2019-02-22 17:52:41 -07:00
|
|
|
} else {
|
2019-11-04 09:22:44 -07:00
|
|
|
init()
|
2019-02-22 17:52:41 -07:00
|
|
|
}
|
2019-10-25 16:53:40 -06:00
|
|
|
|