1 /* Construction of Game API endpoint paths. Logically all endpoints
2 * are just local resources underneath the current path, but just
3 * referring to them by a local name only works if the current path
4 * has a trailing slash.
6 * That is, if the current path is:
8 * https://lmno.games/empires/WXYZ/
10 * Then a path of "register" goes to:
12 * https://lmno.games/empires/WXYZ/register
14 * Just as we want. But if the current path happens to be:
16 * https://lmno.games/empires/WXYZ
18 * Then a path of "register" goes to:
20 * https://lmno.games/empires/register
22 * Which cannot work since we have lost the game ID in the path.
24 * Of course, we would like to have canonical URLs for the game (with
25 * the trailing slash) but since that depends on macehinery outside
26 * the scope of this file, let's construct API paths that will work
29 function GAME_API(endpoint) {
30 var path = window.location.pathname;
31 if (! path.endsWith('/'));
38 spectator_id: undefined,
43 function undisplay(element) {
44 element.style.display="none";
47 function add_message(severity, message) {
48 message = `<div class="message ${severity}" onclick="undisplay(this)">
50 <span class="hide-button" onclick="undisplay(this.parentElement)">×</span>
52 const message_area = document.getElementById('message-area');
53 message_area.insertAdjacentHTML('beforeend', message);
56 function register(form) {
57 const spectator_req = new XMLHttpRequest();
59 /* Before registering as a player, first remove us as a spectator. */
60 spectator_req.open("DELETE", GAME_API("spectator/") + state.spectator_id);
63 const register_req = new XMLHttpRequest();
65 register_req.open("POST", GAME_API("register"));
66 register_req.setRequestHeader("Content-Type", "application/json; charset=UTF-8");
68 "character": form.character.value
70 register_req.send(JSON.stringify(data));
75 function toggle_host_tools() {
76 const host_tools = document.getElementById("host-tools");
78 console.log("Toggling, host_tools.style.display is '" + host_tools.style.display + "'");
80 if (host_tools.style.display === "block")
81 host_tools.style.display = "none";
83 host_tools.style.display = "block";
86 function post_reveal() {
87 const request = new XMLHttpRequest();
89 request.open("POST", GAME_API("reveal"));
93 function post_start() {
94 const request = new XMLHttpRequest();
96 request.open("POST", GAME_API("start"));
100 function post_reset() {
101 const request = new XMLHttpRequest();
103 request.open("POST", GAME_API("reset"));
107 const events = new EventSource(GAME_API("events"));
109 events.onerror = function(event) {
110 if (event.target.readyState === EventSource.CLOSED) {
111 add_message("danger", "Connection to server lost.");
115 events.addEventListener("spectators", function(event) {
116 const spectators_div = document.getElementById("spectators-div");
117 const spectators_element = document.getElementById("spectators");
118 const spectators = JSON.parse(event.data);
120 spectators_element.innerHTML = '';
121 for (const spectator of spectators) {
122 var li = document.createElement('li');
123 li.id = "spectator-" + spectator.id;
124 li.innerText = spectator.name;
125 spectators_element.appendChild(li);
128 /* Force players list to be visible. */
129 spectators_div.style.display = "block";
132 events.addEventListener("spectator-join", function(event) {
133 const spectators_div = document.getElementById("spectators-div");
134 const spectators = document.getElementById("spectators");
135 const spectator = JSON.parse(event.data);
137 const li = document.createElement('li');
138 li.id = "spectator-" + spectator.id;
139 li.innerText = spectator.name;
140 spectators.appendChild(li);
142 /* Force spectators list to be visible. */
143 spectators_div.style.display = "block";
146 events.addEventListener("spectator-leave", function(event) {
147 const spectators = document.getElementById("spectators");
148 const id = JSON.parse(event.data);
149 const spectator = document.getElementById("spectator-" + id.id);
151 spectators.removeChild(spectator);
154 events.addEventListener("players", function(event) {
155 const players_div = document.getElementById("players-div");
156 const players_element = document.getElementById("players");
157 const players = JSON.parse(event.data);
159 players_element.innerHTML = '';
160 for (const player of players) {
161 var li = document.createElement('li');
162 li.id = "player-" + player.id;
163 li.innerText = player.name;
164 players_element.appendChild(li);
167 /* Force players list to be visible. */
168 players_div.style.display = "block";
171 events.addEventListener("player-join", function(event) {
172 const players_div = document.getElementById("players-div");
173 const players = document.getElementById("players");
174 const player = JSON.parse(event.data);
176 const li = document.createElement('li');
177 li.id = "player-" + player.id;
178 li.innerText = player.name;
179 players.appendChild(li);
181 /* Force players list to be visible. */
182 players_div.style.display = "block";
185 events.addEventListener("player-leave", function(event) {
186 const players = document.getElementById("players");
187 const id = JSON.parse(event.data);
188 const player = document.getElementById("player-" + id.id);
190 add_message("info", player.innerText + " has left the game.");
191 players.removeChild(player);
194 function spectator_on_load() {
195 state.spectator_id = JSON.parse(this.response);
198 events.addEventListener("game-phase", function(event) {
199 const data = JSON.parse(event.data);
200 const old_phase = data.old_phase;
201 const new_phase = data.new_phase;
203 const hide_selector = ".show-phase-" +old_phase+ ",.hide-phase-" +new_phase;
204 const show_selector = ".hide-phase-" +old_phase+ ",.show-phase-" +new_phase;
206 /* Hide all elements based on the phase transition. */
207 var elts = document.querySelectorAll(hide_selector);
208 for (const elt of elts) {
209 elt.style.display = "none";
212 /* And show all elements based on the same phase transition. */
213 elts = document.querySelectorAll(show_selector);
214 for (const elt of elts) {
215 elt.style.display = "block";
218 /* Whenever the game enters the "join" phase, add ourselves as a spectator. */
219 if (new_phase === "join") {
220 const request = new XMLHttpRequest();
222 request.addEventListener("load", spectator_on_load);
223 request.open("POST", GAME_API("spectator"));
228 events.addEventListener("character-reveal", function(event) {
229 const data = JSON.parse(event.data);
230 const character_name = data.character;
232 const character = document.getElementById("character-reveal");
234 character.innerText = character_name;
237 events.addEventListener("capture", function(event_string) {
238 const players = document.getElementById("players");
239 const event = JSON.parse(event_string.data);
240 const player = document.getElementById("player-" + event.captee);
242 players.removeChild(player);