summaryrefslogtreecommitdiff
path: root/public
diff options
context:
space:
mode:
Diffstat (limited to 'public')
-rw-r--r--public/common/play.js836
-rw-r--r--public/sort.js101
2 files changed, 476 insertions, 461 deletions
diff --git a/public/common/play.js b/public/common/play.js
index 80ead70..1df4708 100644
--- a/public/common/play.js
+++ b/public/common/play.js
@@ -1,18 +1,18 @@
-"use strict";
+"use strict"
// TODO: Remove when CSS Images Module Level 4 is widely supported,
-(function () {
- let avif = new Image();
- avif.src = "data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUEAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAABkAAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAEAAAABAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgSAAAAAAABNjb2xybmNseAABAA0ABoAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAACFtZGF0EgAKBzgABhAQ0GkyDBZABhhhhAAAeUzRyg==";
- avif.onload = () => document.documentElement.className = "avif";
- avif.onerror = () => document.documentElement.className = "jpeg";
-})();
+;(function () {
+ let avif = new Image()
+ avif.src = "data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUEAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAABkAAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAEAAAABAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgSAAAAAAABNjb2xybmNseAABAA0ABoAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAACFtZGF0EgAKBzgABhAQ0GkyDBZABhhhhAAAeUzRyg=="
+ avif.onload = () => document.documentElement.className = "avif"
+ avif.onerror = () => document.documentElement.className = "jpeg"
+})()
/* URL: /$title_id/(re)play:$game_id:$role */
if (!/\/[\w-]+\/(re)?play:\d+(:[\w-]+)?/.test(window.location.pathname)) {
- document.getElementById("prompt").textContent = "Invalid game ID.";
- throw Error("Invalid game ID.");
+ document.getElementById("prompt").textContent = "Invalid game ID."
+ throw Error("Invalid game ID.")
}
let params = {
@@ -22,129 +22,129 @@ let params = {
role: decodeURIComponent(window.location.pathname.split("/")[2]).split(":")[2] || "Observer",
}
-let roles = Array.from(document.querySelectorAll(".role")).map(x=>({id:x.id,role:x.id.replace(/^role_/,"").replace(/_/g," ")}));
+let roles = Array.from(document.querySelectorAll(".role")).map(x=>({id:x.id,role:x.id.replace(/^role_/,"").replace(/_/g," ")}))
-let view = null;
-let player = "Observer";
-let socket = null;
-let chat = null;
+let view = null
+let player = "Observer"
+let socket = null
+let chat = null
function scroll_with_middle_mouse(panel_sel, multiplier) {
- let panel = document.querySelector(panel_sel);
- let down_x, down_y, scroll_x, scroll_y;
+ let panel = document.querySelector(panel_sel)
+ let down_x, down_y, scroll_x, scroll_y
if (!multiplier)
- multiplier = 1;
+ multiplier = 1
function md(e) {
if (e.button === 1) {
- down_x = e.clientX;
- down_y = e.clientY;
- scroll_x = panel.scrollLeft;
- scroll_y = panel.scrollTop;
- window.addEventListener("mousemove", mm);
- window.addEventListener("mouseup", mu);
- e.preventDefault();
+ down_x = e.clientX
+ down_y = e.clientY
+ scroll_x = panel.scrollLeft
+ scroll_y = panel.scrollTop
+ window.addEventListener("mousemove", mm)
+ window.addEventListener("mouseup", mu)
+ e.preventDefault()
}
}
function mm(e) {
- let dx = down_x - e.clientX;
- let dy = down_y - e.clientY;
- panel.scrollLeft = scroll_x + dx * multiplier;
- panel.scrollTop = scroll_y + dy * multiplier;
- e.preventDefault();
+ let dx = down_x - e.clientX
+ let dy = down_y - e.clientY
+ panel.scrollLeft = scroll_x + dx * multiplier
+ panel.scrollTop = scroll_y + dy * multiplier
+ e.preventDefault()
}
function mu(e) {
if (e.button === 1) {
- window.removeEventListener("mousemove", mm);
- window.removeEventListener("mouseup", mu);
- e.preventDefault();
+ window.removeEventListener("mousemove", mm)
+ window.removeEventListener("mouseup", mu)
+ e.preventDefault()
}
}
- panel.addEventListener("mousedown", md);
+ panel.addEventListener("mousedown", md)
}
function drag_element_with_mouse(element_sel, grabber_sel) {
- let element = document.querySelector(element_sel);
- let grabber = document.querySelector(grabber_sel) || element;
- let save_x, save_y;
+ let element = document.querySelector(element_sel)
+ let grabber = document.querySelector(grabber_sel) || element
+ let save_x, save_y
function md(e) {
if (e.button === 0) {
- save_x = e.clientX;
- save_y = e.clientY;
- window.addEventListener("mousemove", mm);
- window.addEventListener("mouseup", mu);
- e.preventDefault();
+ save_x = e.clientX
+ save_y = e.clientY
+ window.addEventListener("mousemove", mm)
+ window.addEventListener("mouseup", mu)
+ e.preventDefault()
}
}
function mm(e) {
- let dx = save_x - e.clientX;
- let dy = save_y - e.clientY;
- save_x = e.clientX;
- save_y = e.clientY;
- element.style.left = (element.offsetLeft - dx) + "px";
- element.style.top = (element.offsetTop - dy) + "px";
- e.preventDefault();
+ let dx = save_x - e.clientX
+ let dy = save_y - e.clientY
+ save_x = e.clientX
+ save_y = e.clientY
+ element.style.left = (element.offsetLeft - dx) + "px"
+ element.style.top = (element.offsetTop - dy) + "px"
+ e.preventDefault()
}
function mu(e) {
if (e.button === 0) {
- window.removeEventListener("mousemove", mm);
- window.removeEventListener("mouseup", mu);
- e.preventDefault();
+ window.removeEventListener("mousemove", mm)
+ window.removeEventListener("mouseup", mu)
+ e.preventDefault()
}
}
- grabber.addEventListener("mousedown", md);
+ grabber.addEventListener("mousedown", md)
}
/* TITLE BLINKER */
-let blink_title = document.title;
-let blink_timer = 0;
+let blink_title = document.title
+let blink_timer = 0
function start_blinker(message) {
- let tick = false;
+ let tick = false
if (blink_timer)
- stop_blinker();
+ stop_blinker()
if (!document.hasFocus()) {
- document.title = message;
+ document.title = message
blink_timer = setInterval(function () {
- document.title = tick ? message : blink_title;
- tick = !tick;
- }, 1000);
+ document.title = tick ? message : blink_title
+ tick = !tick
+ }, 1000)
}
}
function stop_blinker() {
- document.title = blink_title;
- clearInterval(blink_timer);
- blink_timer = 0;
+ document.title = blink_title
+ clearInterval(blink_timer)
+ blink_timer = 0
}
-window.addEventListener("focus", stop_blinker);
+window.addEventListener("focus", stop_blinker)
/* CHAT */
function init_chat() {
// only fetch new messages when we reconnect!
if (chat !== null) {
- send_message("getchat", chat.log);
- return;
+ send_message("getchat", chat.log)
+ return
}
- let chat_window = document.createElement("div");
- chat_window.id = "chat_window";
+ let chat_window = document.createElement("div")
+ chat_window.id = "chat_window"
chat_window.innerHTML = `
<div id="chat_x" onclick="toggle_chat()">\u274c</div>
<div id="chat_header">Chat</div>
<div id="chat_text"></div>
<form id="chat_form" action=""><input id="chat_input" autocomplete="off"></form>
- `;
- document.querySelector("body").appendChild(chat_window);
+ `
+ document.querySelector("body").appendChild(chat_window)
- let chat_button = document.createElement("div");
- chat_button.id = "chat_button";
- chat_button.className = "icon_button";
- chat_button.innerHTML = '<img src="/images/chat-bubble.svg">';
- chat_button.addEventListener("click", toggle_chat);
- document.querySelector("#toolbar").appendChild(chat_button);
+ let chat_button = document.createElement("div")
+ chat_button.id = "chat_button"
+ chat_button.className = "icon_button"
+ chat_button.innerHTML = '<img src="/images/chat-bubble.svg">'
+ chat_button.addEventListener("click", toggle_chat)
+ document.querySelector("#toolbar").appendChild(chat_button)
chat = {
is_visible: false,
@@ -154,737 +154,737 @@ function init_chat() {
log: 0
}
- chat.seen = window.localStorage.getItem(chat.key) | 0;
+ chat.seen = window.localStorage.getItem(chat.key) | 0
- drag_element_with_mouse("#chat_window", "#chat_header");
+ drag_element_with_mouse("#chat_window", "#chat_header")
document.getElementById("chat_form").addEventListener("submit", e => {
- let input = document.getElementById("chat_input");
- e.preventDefault();
+ let input = document.getElementById("chat_input")
+ e.preventDefault()
if (input.value) {
- send_message("chat", input.value);
- input.value = "";
+ send_message("chat", input.value)
+ input.value = ""
} else {
- hide_chat();
+ hide_chat()
}
- });
+ })
document.querySelector("body").addEventListener("keydown", e => {
if (e.key === "Escape") {
if (chat.is_visible) {
- e.preventDefault();
- hide_chat();
+ e.preventDefault()
+ hide_chat()
}
}
if (e.key === "Enter") {
- let input = document.getElementById("chat_input");
+ let input = document.getElementById("chat_input")
if (document.activeElement !== input) {
- e.preventDefault();
- show_chat();
+ e.preventDefault()
+ show_chat()
}
}
- });
+ })
- send_message("getchat", 0);
+ send_message("getchat", 0)
}
function save_chat() {
- window.localStorage.setItem(chat.key, chat.log);
+ window.localStorage.setItem(chat.key, chat.log)
}
function update_chat(chat_id, utc_date, user, message) {
function format_time(date) {
- let mm = date.getMinutes();
- let hh = date.getHours();
- if (mm < 10) mm = "0" + mm;
- if (hh < 10) hh = "0" + hh;
- return hh + ":" + mm;
+ let mm = date.getMinutes()
+ let hh = date.getHours()
+ if (mm < 10) mm = "0" + mm
+ if (hh < 10) hh = "0" + hh
+ return hh + ":" + mm
}
function add_date_line(date) {
- let line = document.createElement("div");
- line.className = "date";
- line.textContent = "~ " + date + " ~";
- chat.text_element.appendChild(line);
+ let line = document.createElement("div")
+ line.className = "date"
+ line.textContent = "~ " + date + " ~"
+ chat.text_element.appendChild(line)
}
function add_chat_line(time, user, message) {
- let line = document.createElement("div");
- line.textContent = "[" + time + "] " + user + " \xbb " + message;
- chat.text_element.appendChild(line);
- chat.text_element.scrollTop = chat.text_element.scrollHeight;
+ let line = document.createElement("div")
+ line.textContent = "[" + time + "] " + user + " \xbb " + message
+ chat.text_element.appendChild(line)
+ chat.text_element.scrollTop = chat.text_element.scrollHeight
}
if (chat_id > chat.log) {
- chat.log = chat_id;
- let date = new Date(utc_date + "Z");
- let day = date.toDateString();
+ chat.log = chat_id
+ let date = new Date(utc_date + "Z")
+ let day = date.toDateString()
if (day !== chat.last_day) {
- add_date_line(day);
- chat.last_day = day;
+ add_date_line(day)
+ chat.last_day = day
}
- add_chat_line(format_time(date), user, message);
+ add_chat_line(format_time(date), user, message)
}
if (chat_id > chat.seen) {
- let button = document.getElementById("chat_button");
- start_blinker("NEW MESSAGE");
+ let button = document.getElementById("chat_button")
+ start_blinker("NEW MESSAGE")
if (!chat.is_visible)
- button.classList.add("new");
+ button.classList.add("new")
else
- save_chat();
+ save_chat()
}
}
function show_chat() {
if (!chat.is_visible) {
- document.getElementById("chat_button").classList.remove("new");
- document.getElementById("chat_window").classList.add("show");
- document.getElementById("chat_input").focus();
- chat.is_visible = true;
- save_chat();
+ document.getElementById("chat_button").classList.remove("new")
+ document.getElementById("chat_window").classList.add("show")
+ document.getElementById("chat_input").focus()
+ chat.is_visible = true
+ save_chat()
}
}
function hide_chat() {
if (chat.is_visible) {
- document.getElementById("chat_window").classList.remove("show");
- document.getElementById("chat_input").blur();
- chat.is_visible = false;
+ document.getElementById("chat_window").classList.remove("show")
+ document.getElementById("chat_input").blur()
+ chat.is_visible = false
}
}
function toggle_chat() {
if (chat.is_visible)
- hide_chat();
+ hide_chat()
else
- show_chat();
+ show_chat()
}
/* REMATCH BUTTON */
function remove_resign_menu() {
- document.querySelectorAll(".resign").forEach(x => x.remove());
+ document.querySelectorAll(".resign").forEach(x => x.remove())
}
function goto_rematch() {
- window.location = "/rematch/" + params.game_id + "/" + params.role;
+ window.location = "/rematch/" + params.game_id + "/" + params.role
}
function goto_replay() {
- window.location = "/" + params.title_id + "/replay:" + params.game_id;
+ window.location = "/" + params.title_id + "/replay:" + params.game_id
}
function on_game_over() {
function icon_button(id, img, title, fn) {
if (!document.getElementById(id)) {
- let button = document.createElement("div");
- button.id = id;
- button.title = title;
- button.className = "icon_button";
- button.innerHTML = '<img src="/images/' + img + '.svg">';
- button.addEventListener("click", fn);
- document.querySelector("header").appendChild(button);
+ let button = document.createElement("div")
+ button.id = id
+ button.title = title
+ button.className = "icon_button"
+ button.innerHTML = '<img src="/images/' + img + '.svg">'
+ button.addEventListener("click", fn)
+ document.querySelector("header").appendChild(button)
}
}
- icon_button("replay_button", "sherlock-holmes-mirror", "Watch replay", goto_replay);
+ icon_button("replay_button", "sherlock-holmes-mirror", "Watch replay", goto_replay)
if (player !== "Observer")
- icon_button("rematch_button", "cycle", "Propose a rematch!", goto_rematch);
- remove_resign_menu();
+ icon_button("rematch_button", "cycle", "Propose a rematch!", goto_rematch)
+ remove_resign_menu()
}
/* CONNECT TO GAME SERVER */
function init_player_names(players) {
for (let i = 0; i < roles.length; ++i) {
- let sel = "#" + roles[i].id + " .role_user";
- let p = players.find(p => p.role === roles[i].role);
- document.querySelector(sel).textContent = p ? p.name : "NONE";
+ let sel = "#" + roles[i].id + " .role_user"
+ let p = players.find(p => p.role === roles[i].role)
+ document.querySelector(sel).textContent = p ? p.name : "NONE"
}
}
function send_message(cmd, arg) {
- let data = JSON.stringify([cmd, arg]);
- console.log("SEND %s %s", cmd, arg);
- socket.send(data);
+ let data = JSON.stringify([cmd, arg])
+ console.log("SEND %s %s", cmd, arg)
+ socket.send(data)
}
-let reconnect_count = 0;
-let reconnect_max = 10;
+let reconnect_count = 0
+let reconnect_max = 10
function connect_play() {
if (reconnect_count >= reconnect_max) {
- document.title = "DISCONNECTED";
- document.getElementById("prompt").textContent = "Disconnected.";
- return;
+ document.title = "DISCONNECTED"
+ document.getElementById("prompt").textContent = "Disconnected."
+ return
}
- let protocol = (window.location.protocol === "http:") ? "ws" : "wss";
- let seen = document.getElementById("log").children.length;
- let url = `${protocol}://${window.location.host}/play-socket?title=${params.title_id}&game=${params.game_id}&role=${params.role}&seen=${seen}`;
+ let protocol = (window.location.protocol === "http:") ? "ws" : "wss"
+ let seen = document.getElementById("log").children.length
+ let url = `${protocol}://${window.location.host}/play-socket?title=${params.title_id}&game=${params.game_id}&role=${params.role}&seen=${seen}`
- console.log("CONNECTING", url);
- document.getElementById("prompt").textContent = "Connecting... ";
+ console.log("CONNECTING", url)
+ document.getElementById("prompt").textContent = "Connecting... "
- socket = new WebSocket(url);
+ socket = new WebSocket(url)
window.addEventListener('beforeunload', function () {
- socket.close(1000);
- });
+ socket.close(1000)
+ })
socket.onopen = function (evt) {
- console.log("OPEN");
- document.querySelector("header").classList.remove("disconnected");
- reconnect_count = 0;
+ console.log("OPEN")
+ document.querySelector("header").classList.remove("disconnected")
+ reconnect_count = 0
}
socket.onclose = function (evt) {
- console.log("CLOSE %d", evt.code);
+ console.log("CLOSE %d", evt.code)
if (evt.code === 1000 && evt.reason !== "") {
- document.getElementById("prompt").textContent = "Disconnected: " + evt.reason;
- document.title = "DISCONNECTED";
+ document.getElementById("prompt").textContent = "Disconnected: " + evt.reason
+ document.title = "DISCONNECTED"
}
if (evt.code !== 1000) {
- document.querySelector("header").classList.add("disconnected");
- document.getElementById("prompt").textContent = `Reconnecting soon... (${reconnect_count+1}/${reconnect_max})`;
- let wait = 1000 * (Math.random() + 0.5) * Math.pow(2, reconnect_count++);
- console.log("WAITING %.1f TO RECONNECT", wait/1000);
- setTimeout(connect_play, wait);
+ document.querySelector("header").classList.add("disconnected")
+ document.getElementById("prompt").textContent = `Reconnecting soon... (${reconnect_count+1}/${reconnect_max})`
+ let wait = 1000 * (Math.random() + 0.5) * Math.pow(2, reconnect_count++)
+ console.log("WAITING %.1f TO RECONNECT", wait/1000)
+ setTimeout(connect_play, wait)
}
}
socket.onmessage = function (evt) {
- let [ cmd, arg ] = JSON.parse(evt.data);
- console.log("MESSAGE %s", cmd);
+ let [ cmd, arg ] = JSON.parse(evt.data)
+ console.log("MESSAGE %s", cmd)
switch (cmd) {
case 'error':
- document.getElementById("prompt").textContent = arg;
- break;
+ document.getElementById("prompt").textContent = arg
+ break
case 'chat':
- update_chat(arg[0], arg[1], arg[2], arg[3]);
- break;
+ update_chat(arg[0], arg[1], arg[2], arg[3])
+ break
case 'players':
- player = arg[0];
- document.querySelector("body").classList.add(player.replace(/ /g, "_"));
+ player = arg[0]
+ document.querySelector("body").classList.add(player.replace(/ /g, "_"))
if (player !== "Observer")
- init_chat();
+ init_chat()
else
- remove_resign_menu();
- init_player_names(arg[1]);
- break;
+ remove_resign_menu()
+ init_player_names(arg[1])
+ break
case 'presence':
for (let i = 0; i < roles.length; ++i) {
- let elt = document.getElementById(roles[i].id);
+ let elt = document.getElementById(roles[i].id)
if (roles[i].role in arg)
- elt.classList.add("present");
+ elt.classList.add("present")
else
- elt.classList.remove("present");
+ elt.classList.remove("present")
}
- break;
+ break
case 'state':
- view = arg;
- on_update_header();
+ view = arg
+ on_update_header()
if (typeof on_update === 'function')
- on_update();
- on_update_log();
+ on_update()
+ on_update_log()
if (view.game_over)
- on_game_over();
- break;
+ on_game_over()
+ break
case 'reply':
if (typeof on_reply === 'function')
- on_reply(arg[0], arg[1]);
- break;
+ on_reply(arg[0], arg[1])
+ break
case 'save':
- window.localStorage[params.title_id + "/save"] = arg;
- break;
+ window.localStorage[params.title_id + "/save"] = arg
+ break
}
}
}
/* HEADER */
-let is_your_turn = false;
-let old_active = null;
+let is_your_turn = false
+let old_active = null
function on_update_header() {
- document.getElementById("prompt").textContent = view.prompt;
+ document.getElementById("prompt").textContent = view.prompt
if (params.mode === "replay")
- return;
+ return
if (view.actions) {
- document.querySelector("header").classList.add("your_turn");
+ document.querySelector("header").classList.add("your_turn")
if (!is_your_turn || old_active !== view.active)
- start_blinker("YOUR TURN");
- is_your_turn = true;
+ start_blinker("YOUR TURN")
+ is_your_turn = true
} else {
- document.querySelector("header").classList.remove("your_turn");
- is_your_turn = false;
+ document.querySelector("header").classList.remove("your_turn")
+ is_your_turn = false
}
- old_active = view.active;
+ old_active = view.active
}
/* LOG */
function on_update_log() {
- let div = document.getElementById("log");
- let to_delete = div.children.length - view.log_start;
+ let div = document.getElementById("log")
+ let to_delete = div.children.length - view.log_start
while (to_delete-- > 0)
- div.removeChild(div.lastChild);
+ div.removeChild(div.lastChild)
for (let text of view.log) {
if (typeof on_log === 'function') {
- div.appendChild(on_log(text));
+ div.appendChild(on_log(text))
} else {
- let entry = document.createElement("div");
- entry.textContent = text;
- div.appendChild(entry);
+ let entry = document.createElement("div")
+ entry.textContent = text
+ div.appendChild(entry)
}
}
- scroll_log_to_end();
+ scroll_log_to_end()
}
function scroll_log_to_end() {
- let div = document.getElementById("log");
- div.scrollTop = div.scrollHeight;
+ let div = document.getElementById("log")
+ div.scrollTop = div.scrollHeight
}
try {
- new ResizeObserver(scroll_log_to_end).observe(document.getElementById("log"));
+ new ResizeObserver(scroll_log_to_end).observe(document.getElementById("log"))
} catch (err) {
- window.addEventListener("resize", scroll_log_to_end);
+ window.addEventListener("resize", scroll_log_to_end)
}
/* MAP ZOOM */
function toggle_log() {
- document.querySelector("aside").classList.toggle("hide");
- zoom_map();
+ document.querySelector("aside").classList.toggle("hide")
+ zoom_map()
}
function toggle_zoom() {
- let mapwrap = document.getElementById("mapwrap");
+ let mapwrap = document.getElementById("mapwrap")
if (mapwrap) {
- mapwrap.classList.toggle("fit");
- zoom_map();
+ mapwrap.classList.toggle("fit")
+ zoom_map()
}
}
function zoom_map() {
- let mapwrap = document.getElementById("mapwrap");
+ let mapwrap = document.getElementById("mapwrap")
if (mapwrap) {
- let main = document.querySelector("main");
- let map = document.getElementById("map");
- map.style.transform = null;
- mapwrap.style.width = null;
- mapwrap.style.height = null;
+ let main = document.querySelector("main")
+ let map = document.getElementById("map")
+ map.style.transform = null
+ mapwrap.style.width = null
+ mapwrap.style.height = null
if (mapwrap.classList.contains("fit")) {
- let { width: gw, height: gh } = main.getBoundingClientRect();
- let { width: ww, height: wh } = mapwrap.getBoundingClientRect();
- let { width: cw, height: ch } = map.getBoundingClientRect();
- let scale = Math.min(ww / cw, gh / ch);
+ let { width: gw, height: gh } = main.getBoundingClientRect()
+ let { width: ww, height: wh } = mapwrap.getBoundingClientRect()
+ let { width: cw, height: ch } = map.getBoundingClientRect()
+ let scale = Math.min(ww / cw, gh / ch)
if (scale < 1) {
- map.style.transform = "scale(" + scale + ")";
- mapwrap.style.width = (cw * scale) + "px";
- mapwrap.style.height = (ch * scale) + "px";
+ map.style.transform = "scale(" + scale + ")"
+ mapwrap.style.width = (cw * scale) + "px"
+ mapwrap.style.height = (ch * scale) + "px"
}
}
}
}
-window.addEventListener("resize", zoom_map);
+window.addEventListener("resize", zoom_map)
window.addEventListener("keydown", (evt) => {
if (evt.key === "Shift")
- document.querySelector("body").classList.add("shift");
-});
+ document.querySelector("body").classList.add("shift")
+})
window.addEventListener("keyup", (evt) => {
if (evt.key === "Shift")
- document.querySelector("body").classList.remove("shift");
-});
+ document.querySelector("body").classList.remove("shift")
+})
/* ACTIONS */
function action_button_imp(action, label, callback) {
if (params.mode === "replay")
- return;
- let id = action + "_button";
- let button = document.getElementById(id);
+ return
+ let id = action + "_button"
+ let button = document.getElementById(id)
if (!button) {
- button = document.createElement("button");
- button.id = id;
- button.textContent = label;
- button.addEventListener("click", callback);
- document.getElementById("actions").appendChild(button);
+ button = document.createElement("button")
+ button.id = id
+ button.textContent = label
+ button.addEventListener("click", callback)
+ document.getElementById("actions").appendChild(button)
}
if (view.actions && action in view.actions) {
- button.classList.remove("hide");
+ button.classList.remove("hide")
if (view.actions[action]) {
if (label === undefined)
- button.textContent = view.actions[action];
- button.disabled = false;
+ button.textContent = view.actions[action]
+ button.disabled = false
} else {
- button.disabled = true;
+ button.disabled = true
}
} else {
- button.classList.add("hide");
+ button.classList.add("hide")
}
}
function action_button(action, label) {
- action_button_imp(action, label, evt => send_action(action));
+ action_button_imp(action, label, evt => send_action(action))
}
function confirm_action_button(action, label, message) {
- action_button_imp(action, label, evt => confirm_action(message, action));
+ action_button_imp(action, label, evt => confirm_action(message, action))
}
function send_action(verb, noun) {
if (params.mode === "replay")
- return;
+ return
// Reset action list here so we don't send more than one action per server prompt!
if (noun !== undefined) {
- let realnoun = Array.isArray(noun) ? noun[0] : noun;
+ let realnoun = Array.isArray(noun) ? noun[0] : noun
if (view.actions && view.actions[verb] && view.actions[verb].includes(realnoun)) {
- view.actions = null;
- send_message("action", [verb, noun]);
- return true;
+ view.actions = null
+ send_message("action", [verb, noun])
+ return true
}
} else {
if (view.actions && view.actions[verb]) {
- view.actions = null;
- send_message("action", [verb]);
- return true;
+ view.actions = null
+ send_message("action", [verb])
+ return true
}
}
- return false;
+ return false
}
function confirm_action(message, verb, noun) {
if (window.confirm(message))
- send_action(verb, noun);
+ send_action(verb, noun)
}
-let replay_query = null;
+let replay_query = null
function send_query(q, param) {
- if (param !== undefined)
+ if (param !== undefined) {
if (replay_query)
- replay_query(q, param);
+ replay_query(q, param)
else
- send_message("query", [q, param]);
- else
+ send_message("query", [q, param])
+ } else {
if (replay_query)
- replay_query(q, undefined);
+ replay_query(q, undefined)
else
- send_message("query", q);
+ send_message("query", q)
+ }
}
function confirm_resign() {
if (window.confirm("Are you sure that you want to resign?"))
- send_message("resign");
+ send_message("resign")
}
/* MOBILE PHONE LAYOUT */
-let mobile_scroll_header = document.querySelector("header");
-let mobile_scroll_last_y = 0;
+let mobile_scroll_header = document.querySelector("header")
+let mobile_scroll_last_y = 0
window.addEventListener("scroll", function scroll_mobile_fix (evt) {
if (mobile_scroll_header.clientWidth <= 640) {
if (window.scrollY > 40) {
if (mobile_scroll_last_y <= 40)
- mobile_scroll_header.classList.add("mobilefix");
+ mobile_scroll_header.classList.add("mobilefix")
} else {
if (mobile_scroll_last_y > 40)
- mobile_scroll_header.classList.remove("mobilefix");
+ mobile_scroll_header.classList.remove("mobilefix")
}
- mobile_scroll_last_y = window.scrollY;
+ mobile_scroll_last_y = window.scrollY
}
-});
+})
/* DEBUGGING */
function send_save() {
- send_message("save");
+ send_message("save")
}
function send_restore() {
- send_message("restore", window.localStorage[params.title_id + "/save"]);
+ send_message("restore", window.localStorage[params.title_id + "/save"])
}
function send_restart(scenario) {
- send_message("restart", scenario);
+ send_message("restart", scenario)
}
/* REPLAY */
function adler32(data) {
- let a = 1, b = 0;
+ let a = 1, b = 0
for (let i = 0, n = data.length; i < n; ++i) {
- a = (a + data.charCodeAt(i)) % 65521;
- b = (b + a) % 65521;
+ a = (a + data.charCodeAt(i)) % 65521
+ b = (b + a) % 65521
}
- return (b << 16) | a;
+ return (b << 16) | a
}
async function require(path) {
- let cache = {};
+ let cache = {}
if (!path.endsWith(".js"))
- path = path + ".js";
+ path = path + ".js"
if (path.startsWith("./"))
- path = path.substring(2);
+ path = path.substring(2)
- console.log("REQUIRE", path);
+ console.log("REQUIRE", path)
- let response = await fetch(path);
- let source = await response.text();
+ let response = await fetch(path)
+ let source = await response.text()
for (let [_, subpath] of source.matchAll(/require\(['"]([^)]*)['"]\)/g))
if (cache[subpath] === undefined)
- cache[subpath] = await require(subpath);
+ cache[subpath] = await require(subpath)
- let module = { exports: {} };
- Function("module", "exports", "require", source)
- (module, module.exports, path => cache[path]);
- return module.exports;
+ let module = { exports: {} }
+ Function("module", "exports", "require", source)(module, module.exports, path => cache[path])
+ return module.exports
}
-let replay = null;
+let replay = null
async function init_replay() {
- remove_resign_menu();
+ remove_resign_menu()
- document.getElementById("prompt").textContent = "Loading replay...";
+ document.getElementById("prompt").textContent = "Loading replay..."
- console.log("LOADING RULES");
- let rules = await require("rules.js");
+ console.log("LOADING RULES")
+ let rules = await require("rules.js")
- console.log("LOADING REPLAY");
- let response = await fetch("/replay/" + params.game_id);
- let body = await response.json();
- replay = body.replay;
+ console.log("LOADING REPLAY")
+ let response = await fetch("/replay/" + params.game_id)
+ let body = await response.json()
+ replay = body.replay
- init_player_names(body.players);
+ init_player_names(body.players)
- let viewpoint = "Observer";
- let log_length = 0;
- let p = 0;
- let s = {};
+ let viewpoint = "Observer"
+ let log_length = 0
+ let p = 0
+ let s = {}
function eval_action(item) {
switch (item.action) {
case "setup":
- s = rules.setup(item.arguments[0], item.arguments[1], item.arguments[2]);
- break;
+ s = rules.setup(item.arguments[0], item.arguments[1], item.arguments[2])
+ break
case "resign":
- s = rules.resign(s, item.role);
- break;
+ s = rules.resign(s, item.role)
+ break
default:
- s = rules.action(s, item.role, item.action, item.arguments);
- break;
+ s = rules.action(s, item.role, item.action, item.arguments)
+ break
}
}
replay_query = function (query, params) {
- let reply = rules.query(s, player, query, params);
- on_reply(query, reply);
+ let reply = rules.query(s, player, query, params)
+ on_reply(query, reply)
}
- let ss;
+ let ss
for (p = 0; p < replay.length; ++p) {
- replay[p].arguments = JSON.parse(replay[p].arguments);
+ replay[p].arguments = JSON.parse(replay[p].arguments)
if (rules.is_checkpoint) {
- replay[p].is_checkpoint = (p > 0 && rules.is_checkpoint(ss, s));
- ss = Object.assign({}, s);
+ replay[p].is_checkpoint = (p > 0 && rules.is_checkpoint(ss, s))
+ ss = Object.assign({}, s)
}
try {
- eval_action(replay[p]);
+ eval_action(replay[p])
} catch (err) {
- console.log("ERROR IN REPLAY %d %s %s/%s/%s", p, s.state, replay[p].role, replay[p].action, replay[p].arguments);
- console.log(err);
- replay.length = 0;
- break;
+ console.log("ERROR IN REPLAY %d %s %s/%s/%s", p, s.state, replay[p].role, replay[p].action, replay[p].arguments)
+ console.log(err)
+ replay.length = 0
+ break
}
- replay[p].digest = adler32(JSON.stringify(s));
+ replay[p].digest = adler32(JSON.stringify(s))
for (let k = p-1; k > 0; --k) {
if (replay[k].digest === replay[p].digest && !replay[k].is_undone) {
for (let a = k+1; a <= p; ++a)
if (!replay[a].is_undone)
- replay[a].is_undone = true;
- break;
+ replay[a].is_undone = true
+ break
}
}
}
- replay = replay.filter(x => !x.is_undone);
+ replay = replay.filter(x => !x.is_undone)
function set_hash(n) {
- history.replaceState(null, "", window.location.pathname + "#" + n);
+ history.replaceState(null, "", window.location.pathname + "#" + n)
}
- let timer = 0;
+ let timer = 0
function play_pause_replay(evt) {
if (timer === 0) {
- evt.target.textContent = "Stop";
+ evt.target.textContent = "Stop"
timer = setInterval(() => {
if (p < replay.length)
- goto_replay(p+1);
+ goto_replay(p+1)
else
- play_pause_replay(evt);
- }, 1000);
+ play_pause_replay(evt)
+ }, 1000)
} else {
- evt.target.textContent = "Run";
- clearInterval(timer);
- timer = 0;
+ evt.target.textContent = "Run"
+ clearInterval(timer)
+ timer = 0
}
}
function prev() {
for (let i = p - 1; i > 1; --i)
if (replay[i].is_checkpoint)
- return i;
- return 1;
+ return i
+ return 1
}
function next() {
for (let i = p + 1; i < replay.length; ++i)
if (replay[i].is_checkpoint)
- return i;
- return replay.length;
+ return i
+ return replay.length
}
function on_hash_change() {
- goto_replay(parseInt(window.location.hash.slice(1)) || 1);
+ goto_replay(parseInt(window.location.hash.slice(1)) || 1)
}
function goto_replay(np) {
if (np < 1)
- np = 1;
+ np = 1
if (np > replay.length)
- np = replay.length;
- set_hash(np);
+ np = replay.length
+ set_hash(np)
if (p > np)
- p = 0, s = {};
+ p = 0, s = {}
while (p < np)
- eval_action(replay[p++]);
- update_replay_view();
+ eval_action(replay[p++])
+ update_replay_view()
}
function update_replay_view() {
- player = viewpoint;
+ player = viewpoint
if (viewpoint === "Active") {
- player = s.active;
+ player = s.active
if (player === "All" || player === "Both" || player === "None" || !player)
- player = "Observer";
+ player = "Observer"
}
- let body = document.querySelector("body");
- body.classList.remove("Observer");
+ let body = document.querySelector("body")
+ body.classList.remove("Observer")
for (let i = 0; i < roles.length; ++i)
- body.classList.remove(roles[i].role.replace(/ /g, "_"));
- body.classList.add(player.replace(/ /g, "_"));
+ body.classList.remove(roles[i].role.replace(/ /g, "_"))
+ body.classList.add(player.replace(/ /g, "_"))
- view = rules.view(s, player);
- view.actions = null;
+ view = rules.view(s, player)
+ view.actions = null
if (viewpoint === "Observer")
- view.game_over = 1;
+ view.game_over = 1
if (s.state === "game_over")
- view.game_over = 1;
+ view.game_over = 1
if (replay.length > 0) {
if (document.querySelector("body").classList.contains("shift")) {
- view.prompt = `[${p}/${replay.length}] ${s.active} / ${s.state}`;
+ view.prompt = `[${p}/${replay.length}] ${s.active} / ${s.state}`
if (p < replay.length)
- view.prompt += ` / ${replay[p].action} ${replay[p].arguments}`;
+ view.prompt += ` / ${replay[p].action} ${replay[p].arguments}`
} else {
- view.prompt = "[" + p + "/" + replay.length + "] " + view.prompt;
+ view.prompt = "[" + p + "/" + replay.length + "] " + view.prompt
}
}
if (log_length < view.log.length)
- view.log_start = log_length;
+ view.log_start = log_length
else
- view.log_start = view.log.length;
- log_length = view.log.length;
- view.log = view.log.slice(view.log_start);
+ view.log_start = view.log.length
+ log_length = view.log.length
+ view.log = view.log.slice(view.log_start)
- on_update_header();
- on_update();
- on_update_log();
+ on_update_header()
+ on_update()
+ on_update_log()
}
function text_button(div, txt, fn) {
- let button = document.createElement("button");
- button.addEventListener("click", fn);
- button.textContent = txt;
- div.appendChild(button);
- return button;
+ let button = document.createElement("button")
+ button.addEventListener("click", fn)
+ button.textContent = txt
+ div.appendChild(button)
+ return button
}
function set_viewpoint(vp) {
- viewpoint = vp;
- update_replay_view();
+ viewpoint = vp
+ update_replay_view()
}
- let div = document.createElement("div");
- div.className = "replay";
+ let div = document.createElement("div")
+ div.className = "replay"
if (replay.length > 0)
- text_button(div, "Active", () => set_viewpoint("Active"));
+ text_button(div, "Active", () => set_viewpoint("Active"))
for (let r of roles)
- text_button(div, r.role, () => set_viewpoint(r.role));
- text_button(div, "Observer", () => set_viewpoint("Observer"));
- document.querySelector("header").appendChild(div);
+ text_button(div, r.role, () => set_viewpoint(r.role))
+ text_button(div, "Observer", () => set_viewpoint("Observer"))
+ document.querySelector("header").appendChild(div)
if (replay.length > 0) {
- console.log("REPLAY READY");
-
- div = document.createElement("div");
- div.className = "replay";
- text_button(div, "<<<", () => goto_replay(1));
- text_button(div, "<<", () => goto_replay(prev()));
- text_button(div, "<\xa0", () => goto_replay(p-1));
- text_button(div, "\xa0>", () => goto_replay(p+1));
- text_button(div, ">>", () => goto_replay(next()));
- text_button(div, "Run", play_pause_replay).style.width = "65px";
- document.querySelector("header").appendChild(div);
+ console.log("REPLAY READY")
+
+ div = document.createElement("div")
+ div.className = "replay"
+ text_button(div, "<<<", () => goto_replay(1))
+ text_button(div, "<<", () => goto_replay(prev()))
+ text_button(div, "<\xa0", () => goto_replay(p-1))
+ text_button(div, "\xa0>", () => goto_replay(p+1))
+ text_button(div, ">>", () => goto_replay(next()))
+ text_button(div, "Run", play_pause_replay).style.width = "65px"
+ document.querySelector("header").appendChild(div)
if (window.location.hash === "")
- set_hash(replay.length);
+ set_hash(replay.length)
- on_hash_change();
+ on_hash_change()
- window.addEventListener("hashchange", on_hash_change);
+ window.addEventListener("hashchange", on_hash_change)
} else {
- console.log("REPLAY NOT AVAILABLE");
- s = JSON.parse(body.state);
- update_replay_view();
+ console.log("REPLAY NOT AVAILABLE")
+ s = JSON.parse(body.state)
+ update_replay_view()
}
}
window.addEventListener("load", function () {
- zoom_map();
+ zoom_map()
if (params.mode === "replay")
- init_replay();
+ init_replay()
if (params.mode === "play")
- connect_play();
-});
+ connect_play()
+})
function init_home_menu(link, text) {
- let popup = document.querySelector(".menu_popup");
- let sep = document.createElement("div");
- sep.className = "menu_separator";
- popup.insertBefore(sep, popup.firstChild);
- let item = document.createElement("div");
- item.className = "menu_item";
- item.onclick = () => window.open(link, "_self");
- item.textContent = text;
- popup.insertBefore(item, popup.firstChild);
-}
-
-init_home_menu("/games/active", "Go Home");
+ let popup = document.querySelector(".menu_popup")
+ let sep = document.createElement("div")
+ sep.className = "menu_separator"
+ popup.insertBefore(sep, popup.firstChild)
+ let item = document.createElement("div")
+ item.className = "menu_item"
+ item.onclick = () => window.open(link, "_self")
+ item.textContent = text
+ popup.insertBefore(item, popup.firstChild)
+}
+
+init_home_menu("/games/active", "Go Home")
diff --git a/public/sort.js b/public/sort.js
index b98334d..ed243c2 100644
--- a/public/sort.js
+++ b/public/sort.js
@@ -1,67 +1,82 @@
-"use strict";
+"use strict"
function sort_table_column(table, column) {
- const minute = 60000;
- const hour = 60 * minute;
- const day = 24 * hour;
- const week = 7 * day;
+ const minute = 60000
+ const hour = 60 * minute
+ const day = 24 * hour
+ const week = 7 * day
function is_date(s) {
if (s.match(/^\d{4}-\d{2}-\d{2}$/))
- return true;
+ return true
if (s.match(/^\d+ (minutes?|hours?|days|weeks) ago$/))
- return true;
+ return true
if (s.match(/^(Yesterday|now)$/))
- return true;
- return false;
+ return true
+ return false
}
function parse_date(s) {
- if (s.match(/^\d{4}-\d{2}-\d{2}$/)) return new Date(s).valueOf();
- if (s === 'now') return Date.now();
- if (s === 'Yesterday') return Date.now() - day;
- let [ _, value, unit ] = s.match(/^(\d+) (minutes?|hours?|days|weeks) ago$/);
+ if (s.match(/^\d{4}-\d{2}-\d{2}$/))
+ return new Date(s).valueOf()
+ if (s === "now")
+ return Date.now()
+ if (s === "Yesterday")
+ return Date.now() - day
+ let [ _, value, unit ] = s.match(/^(\d+) (minutes?|hours?|days|weeks) ago$/)
switch (unit) {
- default: unit = 0; break;
- case 'minute': case 'minutes': unit = minute; break;
- case 'hours': case 'hours': unit = hour; break;
- case 'days': unit = day; break;
- case 'weeks': unit = week; break;
+ default:
+ unit = 0
+ break
+ case "minute":
+ case "minutes":
+ unit = minute
+ break
+ case "hours":
+ case "hours":
+ unit = hour
+ break
+ case "days":
+ unit = day
+ break
+ case "weeks":
+ unit = week
+ break
}
- return Date.now() - Number(value) * unit;
+ return Date.now() - Number(value) * unit
}
- let tbody = table.querySelector("tbody");
- let rows = Array.from(tbody.querySelectorAll("tr"));
+ let tbody = table.querySelector("tbody")
+ let rows = Array.from(tbody.querySelectorAll("tr"))
rows.sort((row_a, row_b) => {
- let cell_a = row_a.querySelectorAll("td")[column].textContent;
- let cell_b = row_b.querySelectorAll("td")[column].textContent;
+ let cell_a = row_a.querySelectorAll("td")[column].textContent
+ let cell_b = row_b.querySelectorAll("td")[column].textContent
if (is_date(cell_a) && is_date(cell_b)) {
- let age_a = parse_date(cell_a);
- let age_b = parse_date(cell_b);
- if (age_a > age_b) return -1;
- if (age_a < age_b) return 1;
- return 0;
+ let age_a = parse_date(cell_a)
+ let age_b = parse_date(cell_b)
+ if (age_a > age_b) return -1
+ if (age_a < age_b) return 1
+ return 0
} else if (cell_a.match(/^\d+$/) && cell_b.match(/^\d+$/)) {
- cell_a = Number(cell_a);
- cell_b = Number(cell_b);
- if (cell_a > cell_b) return -1;
- if (cell_a < cell_b) return 1;
- return 0;
+ cell_a = Number(cell_a)
+ cell_b = Number(cell_b)
+ if (cell_a > cell_b) return -1
+ if (cell_a < cell_b) return 1
+ return 0
} else {
- if (cell_a > cell_b) return 1;
- if (cell_a < cell_b) return -1;
- return 0;
+ if (cell_a > cell_b) return 1
+ if (cell_a < cell_b) return -1
+ return 0
}
- });
- rows.forEach(row => tbody.appendChild(row));
+ })
+ rows.forEach((row) => tbody.appendChild(row))
}
-document.querySelectorAll("table.sort").forEach(table => {
+document.querySelectorAll("table.sort").forEach((table) => {
table.querySelectorAll("th").forEach((th, column) => {
if (th.textContent !== "") {
- th.addEventListener("click", evt => sort_table_column(table, column));
- th.style.cursor = "pointer";
+ th.addEventListener("click", (evt) => sort_table_column(table, column))
+ th.style.cursor = "pointer"
}
- });
-});
+ })
+})