"sort-keys": "off",
"sort-vars": "off",
"space-before-blocks": "error",
- "space-before-function-paren": "off",
+ "space-before-function-paren": ["error", {
+ "anonymous": "always",
+ "named": "never",
+ "asyncArrow": "always"
+ }],
"space-in-parens": [
"error",
"never"
],
- "space-infix-ops": "off",
+ "space-infix-ops": ["error", { "int32Hint": false }],
"space-unary-ops": "error",
"spaced-comment": "off",
"strict": [
# -*- Makefile -*-
-PORT = 3000
+ESLINT?=eslint
+
+ifeq (, $(shell which $(firstword ${ESLINT})))
+ ESLINT?=npx eslint
+endif
help:
@echo make lang: build translation files
cd l10n; make
eslint:
- -npx eslint server/trivabble-server.js
- -npx eslint public/alert.js
- -npx eslint public/config.js
- -npx eslint public/l10n.js
- -npx eslint public/trivabble.js
+ -${ESLINT} **/*.js
start-dev-server:
cd server && make start-dev-server
#!/usr/bin/env node
+/*eslint strict: [2, "global"]*/
+/*eslint no-sync: ["error", { allowAtRootLevel: true }]*/
-var ROOT = "../public/l10n/";
+"use strict";
-/* Builds translation files.*/
+const ROOT = "../public/l10n/";
-var fs = require('fs');
+/* Builds translation files. */
-var langs = fs.readdirSync("po");
-var po, i, len;
+const fs = require("fs");
+
+let po;
+let i;
+let len;
function skipLine() {
- while (i < len && po[i] !== '\n') {
- ++i;
- }
- ++i;
+ while (i < len && po[i] !== "\n") {
+ ++i;
+ }
+ ++i;
}
function skipSpaces() {
- while (i < len && !po[i].trim()) {
- ++i;
- }
+ while (i < len && !po[i].trim()) {
+ ++i;
+ }
}
function parseString() {
- skipSpaces();
- if (po[i] !== '"') {
- return "";
- }
- ++i;
- var deb = i, end;
- while (i < len) {
- if (po[i] === "\\") {
- ++i;
- } else if (po[i] === '"') {
- var str1 = po.substring(deb, i++);
- var end = i;
- skipSpaces();
- var ndeb = i;
- var str2 = parseString();
- if (i === ndeb) { // we did not parse anything
- i = end;
- return str1;
- }
-
- return str1 + str2;
- }
- ++i;
- }
- throw new Error("not ended string at character " + deb);
-}
+ skipSpaces();
+ if (po[i] !== '"') {
+ return "";
+ }
+ ++i;
+ const deb = i;
+
+ while (i < len) {
+ if (po[i] === "\\") {
+ ++i;
+ } else if (po[i] === '"') {
+ const str1 = po.substring(deb, i++);
+ const end = i;
+ skipSpaces();
+ const ndeb = i;
+ const str2 = parseString();
-for (var l in langs) {
- var lang = langs[l];
- var jsFile = fs.openSync(ROOT + "js/" + lang + ".js", "w");
- fs.writeSync(jsFile, "(function(){var ");
- var poFiles = fs.readdirSync("po/" + lang);
- for (var p in poFiles) {
- var poFile = poFiles[p];
+ if (i === ndeb) { // we did not parse anything
+ i = end;
+ return str1;
+ }
- var translationFunction = fs.readFileSync("pot/" + poFile + 't', {encoding:'utf-8'})
- .match(/\#TranslationFunction[\s]+([\S]+)/)[1];
+ return str1 + str2;
+ }
+ ++i;
+ }
+ throw new Error("not ended string at character " + deb);
+}
- fs.writeSync(jsFile, "_=" + translationFunction + ".l10n;");
+let msgid;
+let msgstr;
- po = fs.readFileSync("po/" + lang + '/' + poFile, {encoding:'utf-8'});
+for (const lang of fs.readdirSync("po")) {
+ const jsFile = fs.openSync(ROOT + "js/" + lang + ".js", "w");
+ fs.writeSync(jsFile, "(function(){var ");
+ let translationFunction = "translationFunction";
- i = 0; len = po.length;
- while (i < len) {
- skipSpaces();
+ for (const poFile of fs.readdirSync("po/" + lang)) {
+ translationFunction = fs.readFileSync("pot/" + poFile + "t", {encoding: "utf-8"})
+ .match(/#TranslationFunction[\s]+(?<functionName>[\S]+)/u).groups.functionName;
- if (po[i] === '#') {
- skipLine();
- continue;
- }
+ fs.writeSync(jsFile, "_=" + translationFunction + ".l10n;");
- if (po.substr(i, 5) === "msgid") {
- if (po[i+5].trim() && po[i+5] !== '"') {
- skipLine(); // don't understand this line
- continue;
- }
- i+=5;
+ po = fs.readFileSync("po/" + lang + "/" + poFile, {encoding: "utf-8"});
+
+ i = 0;
+ len = po.length;
+ while (i < len) {
skipSpaces();
- msgid = parseString();
- } else if (po.substr(i, 6) === "msgstr") {
- if (po[i+6].trim() && po[i+6] !== '"') {
- skipLine(); // don't understand this line
- continue;
+
+ if (po.substr(i, 5) === "msgid") {
+ if (po[i + 5].trim() && po[i + 5] !== '"') {
+ skipLine(); // don't understand this line
+ } else {
+ i += 5;
+ skipSpaces();
+ msgid = parseString();
+ }
+ } else if (po.substr(i, 6) === "msgstr") {
+ if (po[i + 6].trim() && po[i + 6] !== '"') {
+ skipLine(); // don't understand this line
+ } else {
+ i += 6;
+ msgstr = parseString();
+ fs.writeSync(
+ jsFile,
+ '_("' + lang + '","' + msgid.replace(/\n/gu, "") + '","' + msgstr.replace(/\n/gu, "") + '");'
+ );
+ }
}
- i+=6;
- msgstr = parseString();
- fs.writeSync(jsFile, '_("' + lang + '","' + msgid.replace(/\n/g,"") + '","' + msgstr.replace(/\n/g,"") + '");');
- }
- skipLine();
- }
- }
-
- fs.writeSync(jsFile, "if(" + translationFunction + ".applyL10n){" + translationFunction + ".applyL10n();}})();");
- fs.close(jsFile, function (e) {
- if (e) {
- console.error(e);
- }
- });
+
+ // if po[i] === "#", ignore
+ skipLine();
+ }
+ }
+
+ fs.writeSync(
+ jsFile,
+ "if(" + translationFunction + ".applyL10n){" + translationFunction + ".applyL10n();}})();"
+ );
+
+ fs.close(jsFile, function (e) {
+ if (e) {
+ console.error(e);
+ }
+ });
}
let alertInput;
let divAlertContent;
- const _ = (window.libD && libD.l10n) ? libD.l10n() : function (s) {return s;};
+ const _ = (window.libD && libD.l10n) ? libD.l10n() : function (s) {
+ return s;
+ };
function promptOK() {
divAlert.style.display = "none";
</select>
</label>
</div>
- <div id="board-lang-selection">
+ <div id="board-lang-selection" style="display:none">
<label>
<span data-l10n="text-content">Board language: </span>
<select id="board-lang">
const t = [];
- function f (lang, orig, translated) {
+ function f(lang, orig, translated) {
if (!orig) {
return (
(libD.lang && t[libD.lang] && t[libD.lang][lang])
}
t[lang][orig] = translated;
- };
+ }
return f;
},
//thx http://stackoverflow.com/questions/1517924/javascript-mapping-touch-events-to-mouse-events#1781750
+/*global jQuery*/
function touchHandler(event) {
- var nn = event.target.nodeName.toLowerCase();
+ "use strict";
+
+ const nn = event.target.nodeName.toLowerCase();
if (nn === "input" || nn === "select" || nn === "button" || nn === "textarea") {
return;
}
- var touches = event.changedTouches,
- first = touches ? touches[0] : event,
- type = "";
+ const touches = event.changedTouches;
+ const first = touches ? touches[0] : event;
+ let type = "";
switch (event.type) {
- case "touchstart": type = "mousedown"; break;
- case "touchmove": type = "mousemove"; break;
- case "touchend": type = "mouseup"; break;
- case "tap": type = "click"; break;
- case "dbltap": type = "dblclick"; break;
- default: return;
+ case "touchstart":
+ type = "mousedown";
+ break;
+ case "touchmove":
+ type = "mousemove";
+ break;
+ case "touchend":
+ type = "mouseup";
+ break;
+ case "tap":
+ type = "click";
+ break;
+ case "dbltap":
+ type = "dblclick";
+ break;
+ default:
+ return;
}
-//initMouseEvent(type, canBubble, cancelable, view, clickCount,
-// screenX, screenY, clientX, clientY, ctrlKey,
-// altKey, shiftKey, metaKey, button, relatedTarget);
-
- var simulatedEvent = document.createEvent("MouseEvent");
- simulatedEvent.initMouseEvent(type, true, true, window, 1,
- first.screenX, first.screenY,
- first.clientX, first.clientY, false,
- false, false, false, 0/*left*/, null);
- first.target.dispatchEvent(simulatedEvent);
- event.preventDefault();
+ //initMouseEvent(type, canBubble, cancelable, view, clickCount,
+ // screenX, screenY, clientX, clientY, ctrlKey,
+ // altKey, shiftKey, metaKey, button, relatedTarget);
+
+ const simulatedEvent = document.createEvent("MouseEvent");
+ simulatedEvent.initMouseEvent(type, true, true, window, 1,
+ first.screenX, first.screenY,
+ first.clientX, first.clientY, false,
+ false, false, false, 0/*left*/, null);
+ first.target.dispatchEvent(simulatedEvent);
+ event.preventDefault();
}
-document.addEventListener('touchend', (function(speed, distance) {
-/*
- * Copyright (c)2012 Stephen M. McKamey.
- * Licensed under The MIT License.
- * src: https://raw.github.com/mckamey/doubleTap.js/master/doubleTap.js
- */
+document.addEventListener("touchend", (function (speed, distance) {
+ // Copyright (c)2012 Stephen M. McKamey.
+ // Licensed under The MIT License.
+ // src: https://raw.github.com/mckamey/doubleTap.js/master/doubleTap.js
"use strict";
// default dblclick speed to half sec (default for Windows & Mac OS X)
- speed = Math.abs(+speed) || 500;//ms
+ speed = Math.abs(Number(speed)) || 500;//ms
// default dblclick distance to within 40x40 pixel area
- distance = Math.abs(+distance) || 40;//px
+ distance = Math.abs(Number(distance)) || 40;//px
// Date.now() polyfill
- var now = Date.now || function() {
- return +new Date();
+ const now = Date.now || function () {
+ return Number(new Date());
};
- var cancelEvent = function(e) {
+ function cancelEvent(e) {
e = (e || window.event);
if (e) {
}
}
return false;
- };
+ }
- var taps = 0,
- last = 0,
- // NaN will always test false
- x = NaN,
- y = NaN;
+ let taps = 0;
+ let last = 0;
+ // NaN will always test false
+ let x = NaN;
+ let y = NaN;
return function (e) {
e = (e || window.event);
- var time = now(),
- touch = e.changedTouches ? e.changedTouches[0] : e,
- nextX = +touch.clientX,
- nextY = +touch.clientY,
- target = e.target || e.srcElement,
- e2,
- parent;
+ const time = now();
+ const touch = e.changedTouches ? e.changedTouches[0] : e;
+ const nextX = Number(touch.clientX);
+ const nextY = Number(touch.clientY);
+ const target = e.target || e.srcElement;
+ let e2;
+ let parent;
if ((last + speed) > time &&
Math.abs(nextX - x) < distance &&
// fire tap event
if (document.createEvent) {
- e2 = document.createEvent('MouseEvents');
+ e2 = document.createEvent("MouseEvents");
e2.initMouseEvent(
- 'tap',
+ "tap",
true, // click bubbles
true, // click cancelable
e.view, // copy view
// DOM Level 0, IE
parent.ontap(e);
- } else if (typeof jQuery !== 'undefined') {
+ } else if (typeof jQuery !== "undefined") {
// cop out and patch IE6-8 with jQuery
- jQuery(this).trigger('tap', e);
+ jQuery(this).trigger("tap", e); /* eslint-disable-line no-invalid-this */
}
}
if (taps === 2) {
// fire dbltap event only for 2nd click
if (document.createEvent) {
- e2 = document.createEvent('MouseEvents');
+ e2 = document.createEvent("MouseEvents");
e2.initMouseEvent(
- 'dbltap',
+ "dbltap",
true, // dblclick bubbles
true, // dblclick cancelable
e.view, // copy view
// DOM Level 0, IE
parent.ondbltap(e);
- } else if (typeof jQuery !== 'undefined') {
+ } else if (typeof jQuery !== "undefined") {
// cop out and patch IE6-8 with jQuery
- jQuery(this).trigger('dbltap', e);
+ jQuery(this).trigger("dbltap", e); /* eslint-disable-line no-invalid-this */
}
}
}
let board;
let rack;
const boardCells = [];
- let langName;
let scoreOf;
let bag;
+ let boardLangSelect;
const playerLetters = [];
let currentPlayer = "";
refreshCurrentPlayer();
}
+ function setPlayers(players) {
+ if (participantPlaceholder) {
+ participantPlaceholder.parentNode.removeChild(participantPlaceholder);
+ participantPlaceholder = null;
+ }
+
+ for (let i = 0; i < players.length; i++) {
+ const player = players[i];
+ const playerName = players[i].player;
+
+ if (!tablePlayers[playerName]) {
+ let before = null;
+
+ for (let j = 1; j < participants.rows.length; j++) {
+ if (playerName < participants.rows[j].cells[0].textContent) {
+ before = participants.rows[j];
+ break;
+ }
+ }
+
+ const row = document.createElement("tr");
+ participants.insertBefore(row, before);
+ row.appendChild(document.createElement("td"));
+ row.lastChild.textContent = playerName;
+
+ row.appendChild(document.createElement("td"));
+
+ row.appendChild(document.createElement("td"));
+ row.lastChild.className = "score-cell";
+
+ row.lastChild.appendChild(document.createElement("span"));
+ row.lastChild.lastChild.onclick = (
+ function (row) {
+ return function () {
+ if (!pollingReady) {
+ showConnectionLost();
+ return;
+ }
+
+ myPrompt(
+ format(
+ _("Enter the new score of {0}:"),
+ row.firstChild.textContent
+ ),
+ function (res) {
+ res = parseInt(res);
+ if (!isNaN(res)) {
+ sendCmds([{
+ cmd: "score",
+ player: row.firstChild.textContent,
+ score: res
+ }]);
+ }
+ },
+ row.lastChild.firstChild.textContent
+ );
+ };
+ }
+ )(row);
+ row.lastChild.appendChild(document.createTextNode(" "));
+ row.lastChild.appendChild(document.createElement("button"));
+ row.lastChild.lastChild.textContent = "+";
+ row.lastChild.lastChild.onclick = (
+ function (row) {
+ return function () {
+ if (!pollingReady) {
+ showConnectionLost();
+ return;
+ }
+
+ myPrompt(
+ format(
+ _("Enter the score to add to {0}:"),
+ row.firstChild.textContent
+ ),
+ function (res) {
+ res = parseInt(res);
+ if (!isNaN(res)) {
+ sendCmds([{
+ cmd: "score",
+ player: row.firstChild.textContent,
+ score: parseInt(row.childNodes[2].childNodes[0].textContent) + res
+ }]);
+ }
+ },
+ row.lastChild.firstChild.textContent
+ );
+ };
+ }
+ )(row);
+
+
+ row.appendChild(document.createElement("td"));
+ row.lastChild.className = "turn-cell";
+ row.lastChild.appendChild(document.createElement("button"));
+ const img = new Image();
+ img.src = "baton.svg";
+ row.lastChild.lastChild.appendChild(img);
+ row.lastChild.lastChild.onclick = (function (playerName) {
+ sendCmds([{
+ cmd: "currentPlayer",
+ player: playerName === currentPlayer ? "" : playerName
+ }]);
+ }).bind(null, playerName);
+
+ tablePlayers[playerName] = row;
+ }
+
+ if (Object.prototype.hasOwnProperty.call(player, "score")) {
+ const scoreCell = tablePlayers[playerName].childNodes[2].childNodes[0];
+ scoreCell.textContent = player.score;
+ blink(scoreCell);
+ }
+
+ if (Object.prototype.hasOwnProperty.call(player, "rackCount")) {
+ const countCell = tablePlayers[playerName].childNodes[1];
+ countCell.textContent = player.rackCount;
+ blink(countCell);
+ }
+ }
+
+ refreshCurrentPlayer();
+ }
+
+ function applyAction(data) {
+ switch (data.action) {
+ case "pushBag": //TODO
+ break;
+
+ case "popBag": //TODO
+ break;
+
+ case "reset": //TODO
+ tablePlayers = {};
+ while (participants.rows[1]) {
+ participants.removeChild(participants.rows[1]);
+ }
+ sendCmds([{cmd: "hello"}]);
+ break;
+
+ case "moveLetter":
+ if (data.from === "board") {
+ setCell(data.indexFrom, "");
+ } else if (data.from === "rack") {
+ setRackCell(data.indexFrom, "");
+ }
+
+ if (data.to === "board") {
+ setCell(data.indexTo, data.letter, Object.prototype.hasOwnProperty.call(data, "player") && data.player !== localStorage.trivabblePlayerName);
+ } else if (data.to === "rack") {
+ setRackCell(data.indexTo, data.letter);
+ }
+ break;
+ case "setCell":
+ setCell(data.indexTo, data.letter, Object.prototype.hasOwnProperty.call(data, "player") && data.player !== localStorage.trivabblePlayerName);
+ break;
+ case "setRackCell":
+ setRackCell(data.indexTo, data.letter);
+ }
+ }
+
function handleReceivedData(data) {
if (Array.isArray(data)) {
data.forEach(handleReceivedData);
}
if (data.players) {
- if (participantPlaceholder) {
- participantPlaceholder.parentNode.removeChild(participantPlaceholder);
- participantPlaceholder = null;
- }
-
- for (let i = 0; i < data.players.length; i++) {
- const player = data.players[i];
- const playerName = data.players[i].player;
-
- if (!tablePlayers[playerName]) {
- let before = null;
-
- for (let j = 1; j < participants.rows.length; j++) {
- if (playerName < participants.rows[j].cells[0].textContent) {
- before = participants.rows[j];
- break;
- }
- }
-
- const row = document.createElement("tr");
- participants.insertBefore(row, before);
- row.appendChild(document.createElement("td"));
- row.lastChild.textContent = playerName;
-
- row.appendChild(document.createElement("td"));
-
- row.appendChild(document.createElement("td"));
- row.lastChild.className = "score-cell";
-
- row.lastChild.appendChild(document.createElement("span"));
- row.lastChild.lastChild.onclick = (
- function (row) {
- return function () {
- if (!pollingReady) {
- showConnectionLost();
- return;
- }
-
- myPrompt(
- format(
- _("Enter the new score of {0}:"),
- row.firstChild.textContent
- ),
- function (res) {
- res = parseInt(res);
- if (!isNaN(res)) {
- sendCmds([{
- cmd: "score",
- player: row.firstChild.textContent,
- score: res
- }]);
- }
- },
- row.lastChild.firstChild.textContent
- );
- };
- }
- )(row);
- row.lastChild.appendChild(document.createTextNode(" "));
- row.lastChild.appendChild(document.createElement("button"));
- row.lastChild.lastChild.textContent = "+";
- row.lastChild.lastChild.onclick = (
- function (row) {
- return function() {
- if (!pollingReady) {
- showConnectionLost();
- return;
- }
-
- myPrompt(
- format(
- _("Enter the score to add to {0}:"),
- row.firstChild.textContent
- ),
- function (res) {
- res = parseInt(res);
- if (!isNaN(res)) {
- sendCmds([{
- cmd: "score",
- player: row.firstChild.textContent,
- score: parseInt(row.childNodes[2].childNodes[0].textContent) + res
- }]);
- }
- },
- row.lastChild.firstChild.textContent
- );
- };
- }
- )(row);
-
-
- row.appendChild(document.createElement("td"));
- row.lastChild.className = "turn-cell";
- row.lastChild.appendChild(document.createElement("button"));
- const img = new Image();
- img.src = "baton.svg";
- row.lastChild.lastChild.appendChild(img);
- row.lastChild.lastChild.onclick = (function (playerName) {
- sendCmds([{
- cmd: "currentPlayer",
- player: playerName === currentPlayer ? "" : playerName
- }]);
- }).bind(null, playerName);
-
- tablePlayers[playerName] = row;
- }
-
- if (Object.prototype.hasOwnProperty.call(player, "score")) {
- const scoreCell = tablePlayers[playerName].childNodes[2].childNodes[0];
- scoreCell.textContent = player.score;
- blink(scoreCell);
- }
-
- if (Object.prototype.hasOwnProperty.call(player, "rackCount")) {
- const countCell = tablePlayers[playerName].childNodes[1];
- countCell.textContent = player.rackCount;
- blink(countCell);
- }
- }
-
- refreshCurrentPlayer();
+ setPlayers(data.players);
}
if (data.playerName) {
set("gameNumber", data.gameNumber);
}
- if (data.boardLang) {
- set("boardLang", data.boardLang);
+ if (data.availableBoardLangs) {
+ setAvailableBoardLangs(data.availableBoardLangs);
}
- if (data.langName) {
- langName = data.langName;
- createLangBoardSelect(langName);
+ if (data.boardLang) {
+ set("boardLang", data.boardLang);
+ (boardLangSelect || {}).value = data.boardLang;
}
if (data.letterValues) {
setRack(data.rack);
}
- if (!data.action) {
- return;
- }
-
- switch (data.action) {
- case "pushBag": //TODO
- break;
-
- case "popBag": //TODO
- break;
-
- case "reset": //TODO
- tablePlayers = {};
- while (participants.rows[1]) {
- participants.removeChild(participants.rows[1]);
- }
- sendCmds([{cmd: "hello"}]);
- break;
-
- case "moveLetter":
- if (data.from === "board") {
- setCell(data.indexFrom, "");
- } else if (data.from === "rack") {
- setRackCell(data.indexFrom, "");
- }
-
- if (data.to === "board") {
- setCell(data.indexTo, data.letter, Object.prototype.hasOwnProperty.call(data, "player") && data.player !== localStorage.trivabblePlayerName);
- } else if (data.to === "rack") {
- setRackCell(data.indexTo, data.letter);
- }
- break;
- case "setCell":
- setCell(data.indexTo, data.letter, Object.prototype.hasOwnProperty.call(data, "player") && data.player !== localStorage.trivabblePlayerName);
- break;
- case "setRackCell":
- setRackCell(data.indexTo, data.letter);
+ if (data.action) {
+ applyAction(data);
}
}
);
}
- function changeBoardLang(lang) {
+ function onChangeBoardLang() {
+ const code = document.getElementById("board-lang").value;
+ const lang = document.getElementById("board-lang").textContent;
+
myConfirm(
- format(_("Are you sure you want to change board to '{0}'? This will put all the tiles back in the bag and start another game."), _(langName[lang])),
+ format(_("Are you sure you want to change board to '{0}'? This will put all the tiles back in the bag and start another game."), _(lang)),
function () {
- sendCmds([{cmd: "changeBoard", lang: lang}]);
+ sendCmds([{cmd: "changeBoard", lang: code}]);
},
function () {
document.getElementById("board-lang").value = localStorage.trivabbleBoardLang;
}
);
}
- function onChangeBoardLang() {
- changeBoardLang(document.getElementById("board-lang").value);
- }
function clearRack() {
myConfirm(
trivabble.run();
};
- function createLangBoardSelect(langName) {
-
- const select = document.getElementById("board-lang");
-
- for (let i = select.options.length - 1; i >=0; i--) {
- select.remove(i);
+ function setAvailableBoardLangs(availableBoardLangs) {
+ if (!boardLangSelect) {
+ const boardLangSelection = document.getElementById("board-lang-selection");
+ boardLangSelect = boardLangSelection.querySelector("select");
+ boardLangSelection.style.display = "";
}
- for (const key in langName) {/* eslint-disable-line guard-for-in */
- select.add(new Option(_(langName[key]), key));
+ boardLangSelect.textContent = "";
+
+ for (const key of Object.keys(availableBoardLangs)) {
+ boardLangSelect.add(new Option(_(availableBoardLangs[key]), key));
}
- Array.prototype.sort.call(select.options,
- function(a, b) {return (a.textContent > b.textContent) ? 1 : ((b.textContent > a.textContent) ? -1 : 0);});
+ Array.prototype.sort.call(boardLangSelect.options, function (a, b) {
+ return (
+ (a.textContent > b.textContent)
+ ? 1
+ : (
+ (b.textContent > a.textContent)
+ ? -1
+ : 0
+ )
+ );
+ });
- select.value = localStorage.trivabbleBoardLang;
+ boardLangSelect.value = localStorage.trivabbleBoardLang;
}
function langSelectionChange(e) {
specialCell("doubleWord", board.lastChild.lastChild);
} else if ((i % 4 === 1) && (j % 4 === 1)) {
specialCell("tripleLetter", board.lastChild.lastChild);
- } else if ((i < 8 && doubleLetter[i + "," + j]) || (i > 7 && doubleLetter[(14-i) + "," + j]) || (i === 7 && (j === 3 || j === 11))) {
+ } else if ((i < 8 && doubleLetter[i + "," + j]) || (i > 7 && doubleLetter[(14 - i) + "," + j]) || (i === 7 && (j === 3 || j === 11))) {
specialCell("doubleLetter", board.lastChild.lastChild);
}
}
const SAVE_TIMEOUT = 5000;
const KEEP_ALIVE = 30000;
const GAMES_BACKUP = process.env.TRIVABBLE_GAMES_BACKUP || "games.backup.json";
+const DEFAULT_BOARD_LANG = process.env.TRIVABBLE_DEFAULT_BOARD_LANG || "fr";
const VERSION = 202005070100;
const debuglog = DEBUG_LOG ? console.log.bind(console) : () => null;
-const http = require("http");
-const fs = require("fs");
+const http = require("http");
+const path = require("path");
+const fs = require("fs");
const crypto = require("crypto");
-const path = require("path");
const REQUEST_TYPE_LONG_POLLING = 1;
const REQUEST_TYPE_SSE = 2;
/* eslint no-sync: ["error", { allowAtRootLevel: true }] */
/* Manage multi language board */
-const boardPieces = {};
-const langName = {};
-const langFiles = fs.readdirSync(path.join(__dirname, "lang"));
-for (let i = 0; i < langFiles.length; i++) {
- console.log(path.join(__dirname, "lang", langFiles[i]));
- const data = require(path.join(__dirname, "lang", langFiles[i])); /* eslint-disable-line global-require */
- boardPieces[data.code] = data;
- langName[data.code] = data.name;
+const boardTilesPerLang = {};
+const availableBoardLangs = {};
+
+for (const lang of fs.readdirSync(path.join(__dirname, "lang"))) {
+ const data = require(path.join(__dirname, "lang", lang)); // eslint-disable-line global-require
+ boardTilesPerLang[data.code] = data;
+ availableBoardLangs[data.code] = data.name;
}
-const defaultLang = "fr";
const games = {};
Game.prototype.init = function (lang) {
this.board = newBoard();
- this.lang = lang || defaultLang;
- this.langName = langName;
- this.bag = boardPieces[this.lang].bag.slice();
- this.letterValues = boardPieces[this.lang].letterValues;
+ this.lang = lang || DEFAULT_BOARD_LANG;
+ this.bag = boardTilesPerLang[this.lang].bag.slice();
+ this.letterValues = boardTilesPerLang[this.lang].letterValues;
this.racks = {};
this.scores = {};
this.lastUpdated = new Date();
return {
board: this.board,
lang: this.lang,
- langName: this.langName,
bag: this.bag,
letterValues: this.letterValues,
racks: this.racks,
Game.fromJSON = function (obj) {
const game = new Game();
game.board = obj.board || newBoard();
- game.lang = obj.lang || defaultLang;
- game.langName = obj.langName;
- game.bag = boardPieces[game.lang].bag.slice();
- game.letterValues = boardPieces[game.lang].letterValues;
+ game.lang = obj.lang || DEFAULT_BOARD_LANG;
+ game.bag = boardTilesPerLang[game.lang].bag.slice();
+ game.letterValues = boardTilesPerLang[game.lang].letterValues;
game.racks = obj.racks || {};
game.scores = obj.scores || {};
game.lastUpdated = obj.lastUpdated ? new Date(obj.lastUpdated) : new Date();
const players = [];
- for (let player in this.racks) {
- if (Object.prototype.hasOwnProperty.call(this.racks, player)) {
- player = player.slice(1); // '#'
- players.push({
- player: player,
- score: this.getPlayerScore(player),
- rackCount: countTiles(this.getPlayerRack(player))
- });
- }
+ for (let player of Object.keys(this.racks)) {
+ player = player.slice(1); // '#'
+ players.push({
+ player: player,
+ score: this.getPlayerScore(player),
+ rackCount: countTiles(this.getPlayerRack(player))
+ });
}
this.pendingEvents.push({players: players});
let closed = false;
- function close () {
+ function close() {
if (closed) {
return;
}
gameNumber: gameNumber,
playerName: playerName,
boardLang: game.lang,
- langName: langName,
+ availableBoardLangs: availableBoardLangs,
currentPlayer: game.currentPlayer,
rack: game.getPlayerRack(playerName),
board: game.board,
}
case "changeBoard": {
- game.lang = cmd.lang || defaultLang;
+ game.lang = cmd.lang || DEFAULT_BOARD_LANG;
game.reset();
reply(message, response, cmdNumber, {error: 0, boardLang: game.lang, letterValues: game.letterValues});
break;
writeMessage(responseAndType,
JSON.stringify({
- playerName: message.playerName,
- currentPlayer: game.currentPlayer,
- gameNumber: gameNumber,
- boardLang: game.lang,
- langName: langName,
- letterValues: game.letterValues,
- rack: game.getPlayerRack(message.playerName),
- board: game.board,
- remainingLetters: game.bag.length,
- version: VERSION
+ playerName: message.playerName,
+ currentPlayer: game.currentPlayer,
+ gameNumber: gameNumber,
+ boardLang: game.lang,
+ availableBoardLangs: availableBoardLangs,
+ letterValues: game.letterValues,
+ rack: game.getPlayerRack(message.playerName),
+ board: game.board,
+ remainingLetters: game.bag.length,
+ version: VERSION
})
);
debuglog("Serving " + request.url);
- fs.exists("../public/" + request.url, function (exists) {
+ const requestedPath = path.join(__dirname, "..", "public", request.url);
+ fs.exists(requestedPath, function (exists) {
if (exists) {
- fs.readFile("../public/" + request.url, function(err, contents) {
+ fs.readFile(requestedPath, function (err, contents) {
if (err) {
response.statusCode = 500;
response.setHeader("Content-Type", "text/plain; charset=utf-8");
} else {
const backup = JSON.parse(data);
- for (const gameNumber in backup) {
- if (Object.prototype.hasOwnProperty.call(backup, gameNumber)) {
- games[gameNumber] = Game.fromJSON(backup[gameNumber]);
- }
+ for (const gameNumber of Object.keys(backup)) {
+ games[gameNumber] = Game.fromJSON(backup[gameNumber]);
}
}
} catch (e) {
console.error("An error happened in the HTTP server", error);
});
- server.listen(port, function() {
+ server.listen(port, function () {
console.log("Server listening on: http://localhost:%s", port);
});
});