spa.html (13007B)
1 <!DOCTYPE html> 2 <html lang="fr"> 3 <head> 4 <meta charset="utf-8" /> 5 <meta name="viewport" content="width=device-width"> 6 <title>Planification JDR</title> 7 <link rel="stylesheet" type="text/css" href="style.css" /> 8 <script> 9 var val_class=["val0","val1","val2","val3","val4","val5"]; 10 var val_text=["?","−−","−","0","+","++"]; 11 var all_data=null; 12 var radio_map=new Map(); 13 var spinner_map=new Map(); 14 var subject=null; 15 var sent=null; 16 var send_subject=null; 17 var outbox=new Map(); 18 var sender=null; 19 var base_timeout=2000; 20 var retry_timeout=base_timeout; 21 22 window.onerror = function(msg, url, line) { 23 var item = document.createElement("li"); 24 item.appendChild(document.createTextNode(url + "@" + line + ": " + msg)); 25 document.getElementById("error-log").appendChild(item); 26 document.getElementById("error-div").style = "display:block"; 27 item.scrollIntoView(); 28 } 29 30 function pathname_to_topic(str) { 31 var components = str.split("/"); 32 var fileName = components[components.length - 1]; 33 var lastDotIndex = fileName.lastIndexOf('.'); 34 var basename = lastDotIndex !== -1 ? fileName.substring(0, lastDotIndex) : fileName; 35 return basename; 36 } 37 38 var topic=pathname_to_topic(window.location.pathname); 39 40 /************************************/ 41 /* Asynchronous Push of Preferences */ 42 /************************************/ 43 44 async function send(){ 45 var req_body = "topic=" + encodeURIComponent(topic) + "&sub=" + encodeURIComponent(send_subject); 46 sent = outbox.get(send_subject); 47 outbox.delete(send_subject); 48 49 for (const [key, value] of sent) { 50 req_body += "&" + encodeURIComponent(key) + "=" + encodeURIComponent(value); 51 } 52 53 const response = await fetch("do/set-pref", { 54 method: "POST", 55 cache: "no-store", 56 headers: { "Content-Type": "application/x-www-form-urlencoded" }, 57 body: req_body, 58 }); 59 60 var to = base_timeout; 61 62 if (response.ok) { 63 for (const [key, value] of sent) { 64 if (!outbox.has(send_subject) || !outbox.get(send_subject).has(key)) { 65 spinner_map.get(key).style = "display:none"; 66 } 67 } 68 retry_timeout = base_timeout; 69 70 } else { 71 if (!outbox.has(send_subject) && sent.size > 0) { 72 outbox.set(send_subject, new Map()); 73 } 74 75 for (const [key, value] of sent) { 76 if (!outbox.get(send_subject).has(key)) { 77 outbox.get(send_subject).set(key, value); 78 } 79 } 80 to = retry_timeout; 81 retry_timeout *= 2; 82 } 83 84 sent = null; 85 if (outbox.has(send_subject)) { 86 sender = setTimeout(send, to); 87 } else if (outbox.size > 0) { 88 send_subject = outbox.keys().next().value; 89 sender = setTimeout(send, to); 90 } else { 91 send_subject = null; 92 } 93 94 if (!subject) { 95 reload_data(); 96 } 97 } 98 99 function radio_click(name, value){ 100 spinner_map.get(name).style = "display:inline"; 101 102 if (!outbox.has(subject)) { 103 outbox.set(subject, new Map()); 104 } 105 outbox.get(subject).set(name, value); 106 107 if (!sent) { 108 if (!send_subject) { 109 send_subject = subject; 110 } 111 112 if (sender) { 113 clearTimeout(sender); 114 } 115 sender = setTimeout(send, base_timeout); 116 } 117 } 118 119 /********************/ 120 /* Table Generation */ 121 /********************/ 122 123 function append_cell(line, type, cl, text) { 124 var elt = document.createElement(type); 125 elt.className = cl; 126 elt.appendChild(document.createTextNode(text)); 127 line.appendChild(elt); 128 } 129 130 function set_overview_table() { 131 var holder = document.getElementById("overview-table"); 132 while (holder.childNodes.length > 0) { 133 holder.removeChild(holder.childNodes[0]); 134 } 135 var line = document.createElement("tr"); 136 137 append_cell(line, "th", "", "Date"); 138 for (const name in all_data[1]) { 139 var elt = document.createElement("th"); 140 var link = document.createElement("a"); 141 link.href = "#" + encodeURIComponent(name); 142 link.appendChild(document.createTextNode(name)); 143 elt.appendChild(link); 144 line.appendChild(elt); 145 } 146 holder.appendChild(line); 147 148 for (const name of all_data[0]) { 149 line = document.createElement("tr"); 150 append_cell(line, "td", "date", name); 151 for (const [tmp, prefs] of Object.entries(all_data[1])) { 152 var v = prefs[name] || 0; 153 append_cell(line, "td", val_class[v], val_text[v]); 154 } 155 holder.appendChild(line); 156 } 157 } 158 159 function set_pref_table() { 160 var holder = document.getElementById("pref-table-body"); 161 while (holder.childNodes.length > 0) { 162 holder.removeChild(holder.childNodes[0]); 163 } 164 165 radio_map.clear(); 166 spinner_map.clear(); 167 168 for (const name of all_data[0]) { 169 var line = document.createElement("tr"); 170 171 var cell = document.createElement("td"); 172 cell.className = "date"; 173 cell.appendChild(document.createTextNode(name)); 174 line.appendChild(cell); 175 176 var radios = new Array; 177 178 for (const i in val_text) { 179 cell = document.createElement("td"); 180 var elt1 = document.createElement("label"); 181 var elt2 = document.createElement("input"); 182 elt2.type = "radio"; 183 elt2.name = name; 184 elt2.value = i; 185 elt2.onchange = function() { radio_click(name, i) }; 186 radios[i] = elt2; 187 elt1.appendChild(elt2); 188 elt1.appendChild(document.createTextNode(val_text[i])); 189 elt1.className = val_class[i]; 190 cell.appendChild(elt1); 191 if (i > 0) { 192 line.appendChild(cell); 193 } else { 194 line.insertBefore(cell, line.childNodes[0]); 195 } 196 } 197 radio_map.set(name, radios); 198 199 cell = document.createElement("td"); 200 elt1 = document.createElement("img"); 201 elt1.className = "spinner"; 202 elt1.src = "spinner.svg"; 203 elt1.style = "display:none"; 204 spinner_map.set(name, elt1); 205 cell.appendChild(elt1); 206 line.appendChild(cell); 207 holder.appendChild(line); 208 } 209 210 for (const [name, radios] of radio_map) { 211 for (const i in val_text) { 212 radios[i].checked = (i == (all_data[1][subject][name] || 0)); 213 } 214 } 215 } 216 217 /*********************/ 218 /* Page Presentation */ 219 /*********************/ 220 221 function redisplay() { 222 var prev_subject = subject; 223 subject = decodeURIComponent(window.location.hash.substring(1)); 224 document.getElementById("cur-subject").textContent = subject; 225 226 if (!subject) { 227 document.title = "Planification JDR"; 228 229 if (prev_subject) { 230 reload_data(); 231 } 232 233 set_overview_table(); 234 235 document.getElementById("pref-form").style = "display:none"; 236 document.getElementById("change-subject-form").style = "display:none"; 237 document.getElementById("overview-link").style = "display:none"; 238 239 document.getElementById("overview-table").style = ""; 240 document.getElementById("subject-name-label").style = "display:inline"; 241 document.getElementById("create-subject").style = ""; 242 document.getElementById("create-subject-name").type = "text"; 243 document.getElementById("create-subject-name").value = ""; 244 245 open_change_form(); 246 247 } else if (subject in all_data[1]) { 248 set_pref_table(); 249 250 document.title = "Planification JDR - " + subject; 251 252 document.getElementById("create-subject").style = "display:none"; 253 document.getElementById("overview-table").style = "display:none"; 254 255 document.getElementById("pref-form").style = "display:block"; 256 document.getElementById("overview-link").style = "display:inline"; 257 close_change_form(); 258 259 } else { 260 document.title = "Planification JDR - " + subject; 261 262 document.getElementById("pref-form").style = "display:none"; 263 document.getElementById("overview-table").style = "display:none"; 264 265 document.getElementById("create-subject").style = "display:block"; 266 document.getElementById("subject-name-label").style = "display:none"; 267 document.getElementById("create-subject-name").value = subject; 268 269 document.getElementById("overview-link").style = "display:inline"; 270 close_change_form(); 271 } 272 } 273 274 async function reload_data(){ 275 document.getElementById("reload-spinner").style = "display:inline"; 276 const response = await fetch(topic + ".json", { cache: "no-cache" }); 277 all_data = await response.json(); 278 279 redisplay(); 280 281 document.getElementById("reload-spinner").style = "display:none"; 282 } 283 284 async function create_subject(){ 285 const name = document.getElementById("create-subject-name").value; 286 document.getElementById("create-spinner").style = "display:inline"; 287 const response = await fetch("do/new-subject", { 288 method: "POST", 289 cache: "no-store", 290 headers: { "Content-Type": "application/x-www-form-urlencoded" }, 291 body: "topic=" + encodeURIComponent(topic) + "&name=" + encodeURIComponent(name), 292 }) ; 293 document.getElementById("create-subject-name").value = ""; 294 document.getElementById("create-spinner").style = "display:none"; 295 296 if (response.ok) { 297 if (subject !== name) { 298 window.location.hash = "#" + encodeURIComponent(name); 299 } 300 reload_data(); 301 } 302 } 303 304 function set_subject(){ 305 window.location.hash = "#" + encodeURIComponent(document.getElementById("new-subject").value); 306 return false; 307 } 308 309 function close_change_form(){ 310 document.getElementById("change-subject-form").style = "display:block"; 311 document.getElementById("close-subject").style = "display:none"; 312 document.getElementById("cur-subject").style = "display:inline"; 313 document.getElementById("new-subject").style = "display:none"; 314 document.getElementById("open-subject").style = "display:inline"; 315 document.getElementById("set-subject").style = "display:none"; 316 } 317 318 function open_change_form(){ 319 document.getElementById("new-subject").value = ""; 320 document.getElementById("change-subject-form").style = "display:block"; 321 document.getElementById("close-subject").style = subject && "display:inline" || "display:none"; 322 document.getElementById("cur-subject").style = "display:none"; 323 document.getElementById("new-subject").style = "display:inline"; 324 document.getElementById("open-subject").style = "display:none"; 325 document.getElementById("set-subject").style = "display:inline"; 326 } 327 328 window.addEventListener("hashchange", redisplay); 329 </script> 330 </head> 331 <body onload="reload_data()"> 332 <h1>Planification JDR</h1> 333 <p> 334 <a href="#" id="overview-link">Retour à la vue d'ensemble</a> 335 <input name="test" value="Recharger" type="button" onclick="reload_data()"> 336 <img id="reload-spinner" class="spinner" src="spinner.svg" style="display: none"> 337 </p> 338 <table style="margin: 1em"> 339 <tr><td colspan="2" style="text-align: left"><strong>Symboles des préférences :</strong></td></tr> 340 <tr> 341 <td class="val5">++</td> 342 <td style="text-align: left">j'ai très envie de jouer ce soir-là, je vais m'ennuyer autrement</td> 343 </tr> 344 <tr> 345 <td class="val4">+</td> 346 <td style="text-align: left">j'aimerais bien jouer ce soir-là</td> 347 </tr> 348 <tr> 349 <td class="val3">0</td> 350 <td style="text-align: left">je veux bien jouer mais ça ne me dérange pas de ne pas jouer</td> 351 </tr> 352 <tr> 353 <td class="val2">−</td> 354 <td style="text-align: left">je peux jouer ce soir-là mais ça ne m'arrange pas vraiment</td> 355 </tr> 356 <tr> 357 <td class="val1">−−</td> 358 <td style="text-align: left">je ne suis pas du tout disponible ce soir-là</td> 359 </tr> 360 <tr> 361 <td class="val0">?</td> 362 <td style="text-align: left">je ne veux pas me prononcer</td> 363 </tr> 364 </table> 365 <form id="change-subject-form" style="display:none" onsubmit="return set_subject()"> 366 <p> 367 <label> 368 Préferences pour <strong id="cur-subject"></strong><input name="subject" id="new-subject" value="" type="text"> 369 </label> 370 <input id="open-subject" name="change" value="Changer" type="button" onclick="open_change_form()"> 371 <input id="set-subject" name="submit" value="Valider" type="submit"> 372 <input id="close-subject" name="cancel" value="Annuler" type="button" onclick="close_change_form()"> 373 </p> 374 </form> 375 <form id="create-subject" style="display:none" onsubmit="create_subject(); return false;"> 376 <label id="subject-name-label">Nouveau pseudo : 377 <input id="create-subject-name" type="hidden" value=""> 378 </label> 379 <input name="submit" type="submit" value="Créer"> 380 <img id="create-spinner" class="spinner" src="spinner.svg" style="display: none"> 381 </form> 382 <table id="overview-table"> 383 <tr><th>Chargement</th></tr> 384 <tr><td><img src="spinner.svg" style="display: inline; width: 5em; height: 5em"></td></tr> 385 </table> 386 <form id="pref-form" style="display:none"> 387 <table> 388 <thead> 389 <tr id="table-header"> 390 <th></th> 391 <th>Date</th> 392 <th colspan="5">Préférence</th> 393 <th></th> 394 </tr> 395 </thead> 396 <tbody id="pref-table-body"> 397 <tr> 398 <td colspan="8"> 399 <img src="spinner.svg" with="5em" height"5em"> 400 </td> 401 </tr> 402 </tbody> 403 </table> 404 </form> 405 <div id="error-div" style="display:none"> 406 <p>Error log:</p> 407 <ul id="error-log"><ul> 408 </div> 409 </body> 410 </html>