X-Git-Url: https://git.auder.net/variants/Baroque/current/git-favicon.png?a=blobdiff_plain;f=app.js;h=6036d49756f322a8dcbc7f1d33ee676095b2fffd;hb=d262cff4c4e1247c1f9680970846cfc61f21b59f;hp=a46ceb39d5ca33499d6814ef7503844d1b44d28f;hpb=8a9f61cec20509fd4398169d4ce1da73157d32ab;p=xogo.git diff --git a/app.js b/app.js index a46ceb3..6036d49 100644 --- a/app.js +++ b/app.js @@ -4,11 +4,11 @@ let $ = document; //shortcut // Initialisations // https://stackoverflow.com/a/27747377/12660887 -function dec2hex (dec) { return dec.toString(16).padStart(2, "0") } function generateId (len) { - var arr = new Uint8Array((len || 40) / 2) - window.crypto.getRandomValues(arr) - return Array.from(arr, dec2hex).join('') + const dec2hex = (dec) => dec.toString(16).padStart(2, "0"); + let arr = new Uint8Array(len / 2); //len/2 because 2 chars per hex value + window.crypto.getRandomValues(arr); //fill with random integers + return Array.from(arr, dec2hex).join(''); } // Populate variants dropdown list @@ -34,30 +34,34 @@ $.getElementById("myName").value = localStorage.getItem("name"); let inputName = document.getElementById("myName"); let formField = document.getElementById("ng-name"); const setActive = (active) => { - if (active) formField.classList.add("form-field--is-active"); + if (active) + formField.classList.add("form-field--is-active"); else { formField.classList.remove("form-field--is-active"); - inputName.value === "" ? - formField.classList.remove("form-field--is-filled") : - formField.classList.add("form-field--is-filled"); + inputName.value == '' + ? formField.classList.remove("form-field--is-filled") + : formField.classList.add("form-field--is-filled"); } }; +setActive(true); inputName.onblur = () => setActive(false); inputName.onfocus = () => setActive(true); -inputName.focus(); ///////// // Utils function setName() { + // 'onChange' event on name input text field [HTML] localStorage.setItem("name", $.getElementById("myName").value); } // Turn a "tab" on, and "close" all others function toggleVisible(element) { - for (elt of document.querySelectorAll('main > div')) { - if (elt.id != element) elt.style.display = "none"; - else elt.style.display = "block"; + for (elt of document.querySelectorAll("main > div")) { + if (elt.id != element) + elt.style.display = "none"; + else + elt.style.display = "block"; } if (element == "boardContainer") { // Avoid smartphone scrolling effects (TODO?) @@ -67,53 +71,60 @@ function toggleVisible(element) { else { document.querySelector("html").style.overflow = "visible"; document.body.style.overflow = "visible"; - if (element == "newGame") { - // Workaround "superposed texts" effect - inputName.focus(); - inputName.blur(); - } + // Workaround "superposed texts" effect: + if (element == "newGame") + setActive(false); } } let seek_vname; function seekGame() { seek_vname = $.getElementById("selectVariant").value; - send("seekgame", {vname: seek_vname, name: localStorage.getItem("name")}); - toggleVisible("pendingSeek"); + if (send("seekgame", + {vname: seek_vname, name: localStorage.getItem("name")}) + ) { + toggleVisible("pendingSeek"); + } } function cancelSeek() { - send("cancelseek", {vname: seek_vname}); - toggleVisible("newGame"); + if (send("cancelseek", {vname: seek_vname})) + toggleVisible("newGame"); } -function sendRematch() { - send("rematch", { gid: gid }); - toggleVisible("pendingRematch"); +function sendRematch(random) { + if (send("rematch", {gid: gid, random: !!random})) + toggleVisible("pendingRematch"); } function cancelRematch() { - send("norematch", { gid: gid }); - toggleVisible("newGame"); + if (send("norematch", {gid: gid})) + toggleVisible("newGame"); } // Play with a friend (or not ^^) function showNewGameForm() { const vname = $.getElementById("selectVariant").value; - if (vname == "_random") alert("Select a variant first"); + if (vname == "_random") + alert("Select a variant first"); else { $.getElementById("gameLink").innerHTML = ""; $.getElementById("selectColor").selectedIndex = 0; toggleVisible("newGameForm"); import(`/variants/${vname}/class.js`).then(module => { window.V = module.default; + for (const [k, v] of Object.entries(V.Aliases)) + window[k] = v; prepareOptions(); }); } } -function backToNormalSeek() { toggleVisible("newGame"); } +function backToNormalSeek() { + toggleVisible("newGame"); +} -function toggleStyle(e, word) { +function toggleStyle(event, obj) { + const word = obj.innerHTML; options[word] = !options[word]; - e.target.classList.toggle("highlight-word"); + event.target.classList.toggle("highlight-word"); } let options; @@ -155,10 +166,10 @@ function prepareOptions() { while (i < stylesLength) { optHtml += '
WhatsApp / - ToClipboard + ToClipboard
${link}
`; @@ -295,28 +357,20 @@ const messageCenter = (msg) => { break; // Receive opponent's move: case "newmove": - send("gotmove", {fen: obj.fen, gid: gid}); - if (obj.fen == lastFen) break; //got this move already - lastFen = obj.fen; - if (document.hidden) notifyMe("move"); + // Basic check: was it really opponent's turn? + if (vr.turn == playerColor) + break; + if (document.hidden) + notifyMe("move"); vr.playReceivedMove(obj.moves, () => { if (vr.getCurrentScore(obj.moves[obj.moves.length-1]) != "*") { localStorage.removeItem("gid"); setTimeout( () => toggleVisible("gameStopped"), 2000 ); } - else toggleTurnIndicator(true); + else + toggleTurnIndicator(true); }); break; - // The server notifies that it got our move: - case "gotmove": - if (obj.fen == lastFen) { - curMoves = []; - clearTimeout(timeout1); - clearTimeout(timeout2); - clearTimeout(timeout3); - callbackAfterConfirmation(); - } - break; // Opponent stopped game (draw, abort, resign...) case "gameover": toggleVisible("gameStopped"); @@ -330,7 +384,7 @@ const messageCenter = (msg) => { }; const handleError = (err) => { - if (err.code === 'ECONNREFUSED') { + if (err.code === "ECONNREFUSED") { removeAllListeners(); alert("Server refused connection. Please reload page later"); } @@ -344,35 +398,34 @@ const handleClose = () => { }, autoReconnectDelay()); }; -const removeAllListeners = () => { +function removeAllListeners() { socket.removeEventListener("open", tryResumeGame); socket.removeEventListener("message", messageCenter); socket.removeEventListener("error", handleError); socket.removeEventListener("close", handleClose); -}; +} -const connectToWSS = () => { +function connectToWSS() { socket = new WebSocket(`${Params.socket_server}${Params.socket_path}?sid=${sid}`); socket.addEventListener("open", tryResumeGame); socket.addEventListener("message", messageCenter); socket.addEventListener("error", handleError); socket.addEventListener("close", handleClose); - attempt++; -}; + recoAttempt++; +} connectToWSS(); -const send = (code, data) => { - socket.send(JSON.stringify(Object.assign({code: code}, data))); -}; - /////////// // Playing function toggleTurnIndicator(myTurn) { - let indicator = $.getElementById("chessboard"); - if (myTurn) indicator.style.outline = "thick solid green"; - else indicator.style.outline = "thick solid lightgrey"; + let indicator = + $.getElementById("boardContainer").querySelector(".chessboard"); + if (myTurn) + indicator.style.outline = "thick solid green"; + else + indicator.style.outline = "thick solid lightgrey"; } function notifyMe(code) { @@ -381,59 +434,40 @@ function notifyMe(code) { new Notification("New " + code, { vibrate: [200, 100, 200] }); new Audio("/assets/new_" + code + ".mp3").play(); } - if (Notification.permission === 'granted') doNotify(); - else if (Notification.permission !== 'denied') { + if (Notification.permission === "granted") + doNotify(); + else if (Notification.permission !== "denied") { Notification.requestPermission().then(permission => { - if (permission === 'granted') doNotify(); + if (permission === "granted") + doNotify(); }); } } let curMoves = [], - lastFen, lastMove, - timeout1, timeout2, timeout3; -const callbackAfterConfirmation = () => { - const result = vr.getCurrentScore(lastMove); - if (result != "*") { - setTimeout( () => { - toggleVisible("gameStopped"); - send("gameover", { gid: gid }); - }, 2000); - } -}; + lastFen; const afterPlay = (move) => { + const callbackAfterSend = () => { + curMoves = []; + const result = vr.getCurrentScore(move); + if (result != "*") { + setTimeout(() => { + toggleVisible("gameStopped"); + send("gameover", {gid: gid}); + }, 2000); + } + }; // Pack into one moves array, then send - curMoves.push({ - appear: move.appear, - vanish: move.vanish, - start: move.start, - end: move.end - }); - lastMove = move; + curMoves.push(move); if (vr.turn != playerColor) { toggleTurnIndicator(false); - lastFen = vr.getFen(); - const sendMove = - () => send("newmove", {gid: gid, moves: curMoves, fen: lastFen}); - // Send move until we obtain confirmation or timeout, then callback - sendMove(); - timeout1 = setTimeout(sendMove, 500); - timeout2 = setTimeout(sendMove, 1500); - timeout3 = setTimeout( - () => alert("The move may be lost :( Please reload"), - 3000); - } -}; - -// Avoid loading twice the same stylesheet: -const conditionalLoadCSS = (vname) => { - const allIds = [].slice.call($.styleSheets).map(s => s.id); - const newId = vname + "_css"; - if (!allIds.includes(newId)) { - $.getElementsByTagName("head")[0].insertAdjacentHTML( - "beforeend", - ``); + send("newmove", + {gid: gid, moves: curMoves, fen: vr.getFen()}, + { + retry: true, + success: callbackAfterSend, + error: () => alert("Move not sent: reload page") + }); } }; @@ -442,7 +476,17 @@ function initializeGame(obj) { const options = obj.options || {}; import(`/variants/${obj.vname}/class.js`).then(module => { window.V = module.default; - conditionalLoadCSS(obj.vname); + for (const [k, v] of Object.entries(V.Aliases)) + window[k] = v; + // Load CSS. Avoid loading twice the same stylesheet: + const allIds = [].slice.call($.styleSheets).map(s => s.id); + const newId = obj.vname + "_css"; + if (!allIds.includes(newId)) { + $.getElementsByTagName("head")[0].insertAdjacentHTML( + "beforeend", + ``); + } playerColor = (sid == obj.players[0].sid ? "w" : "b"); // Init + remove potential extra DOM elements from a previous game: document.getElementById("boardContainer").innerHTML = ` @@ -464,18 +508,18 @@ function initializeGame(obj) { - `; + `; vr = new V({ seed: obj.seed, //may be null if FEN already exists (running game) fen: obj.fen, - element: "chessboard", + element: "boardContainer", color: playerColor, afterPlay: afterPlay, options: options }); if (!obj.fen) { - // Game creation - if (playerColor == "w") send("setfen", {gid: obj.gid, fen: vr.getFen()}); + // Game creation: both players set FEN, in case of one is offline + send("setfen", {gid: obj.gid, fen: vr.getFen()}); localStorage.setItem("gid", obj.gid); } const select = $.getElementById("selectVariant"); @@ -487,15 +531,16 @@ function initializeGame(obj) { } } fillGameInfos(obj, playerColor == "w" ? 1 : 0); - if (obj.randvar) toggleVisible("gameInfos"); - else toggleVisible("boardContainer"); + if (obj.randvar) + toggleVisible("gameInfos"); + else + toggleVisible("boardContainer"); toggleTurnIndicator(vr.turn == playerColor); }); } function confirmStopGame() { - if (confirm("Stop game?")) { - send("gameover", { gid: gid, relay: true }); + if (confirm("Stop game?") && send("gameover", {gid: gid, relay: true})) { localStorage.removeItem("gid"); toggleVisible("gameStopped"); } @@ -504,16 +549,15 @@ function confirmStopGame() { function toggleGameInfos() { if ($.getElementById("gameInfos").style.display == "none") toggleVisible("gameInfos"); - else { + else toggleVisible("boardContainer"); - // Quickfix for the "vanished piece" bug (move played while on game infos) - vr.setupPieces(); //TODO: understand better - } } $.body.addEventListener("keydown", (e) => { - if (!localStorage.getItem("gid")) return; - if (e.keyCode == 27) confirmStopGame(); + if (!localStorage.getItem("gid")) + return; + if (e.keyCode == 27) + confirmStopGame(); else if (e.keyCode == 32) { e.preventDefault(); toggleGameInfos();