--- /dev/null
+{
+ "env": {
+ "browser": true,
+ "commonjs": true,
+ "es6": true,
+ "node": true
+ },
+ "extends": "eslint:recommended",
+ "globals": {
+ "Atomics": "readonly",
+ "SharedArrayBuffer": "readonly"
+ },
+ "parserOptions": {
+ "ecmaVersion": 2018
+ },
+ "rules": {
+ "accessor-pairs": "error",
+ "array-bracket-newline": "off",
+ "array-bracket-spacing": [
+ "error",
+ "never"
+ ],
+ "array-callback-return": "error",
+ "array-element-newline": "off",
+ "arrow-body-style": "error",
+ "arrow-parens": "error",
+ "arrow-spacing": "error",
+ "block-scoped-var": "off",
+ "block-spacing": [
+ "error",
+ "never"
+ ],
+ "brace-style": [
+ "error",
+ "1tbs",
+ {
+ "allowSingleLine": true
+ }
+ ],
+ "callback-return": "error",
+ "camelcase": "error",
+ "capitalized-comments": [
+ "error",
+ "always"
+ ],
+ "class-methods-use-this": "error",
+ "comma-dangle": "error",
+ "comma-spacing": [
+ "error",
+ {
+ "after": true,
+ "before": false
+ }
+ ],
+ "comma-style": [
+ "error",
+ "last"
+ ],
+ "complexity": "off",
+ "computed-property-spacing": [
+ "error",
+ "never"
+ ],
+ "consistent-return": "off",
+ "consistent-this": "error",
+ "curly": "error",
+ "default-case": "off",
+ "default-param-last": "error",
+ "dot-location": "error",
+ "dot-notation": "error",
+ "eol-last": "error",
+ "eqeqeq": "error",
+ "func-call-spacing": "error",
+ "func-name-matching": "error",
+ "func-names": "off",
+ "func-style": [
+ "error",
+ "declaration"
+ ],
+ "function-call-argument-newline": "off",
+ "function-paren-newline": "off",
+ "generator-star-spacing": "error",
+ "global-require": "error",
+ "grouped-accessor-pairs": "error",
+ "guard-for-in": "error",
+ "handle-callback-err": "error",
+ "id-blacklist": "error",
+ "id-length": "off",
+ "id-match": "error",
+ "implicit-arrow-linebreak": "error",
+ "indent": "off",
+ "indent-legacy": "off",
+ "init-declarations": "off",
+ "jsx-quotes": "error",
+ "key-spacing": "off",
+ "keyword-spacing": [
+ "error", {
+ "before": true,
+ "after": true
+ }
+ ],
+ "line-comment-position": "off",
+ "linebreak-style": [
+ "error",
+ "unix"
+ ],
+ "lines-around-comment": "error",
+ "lines-around-directive": "error",
+ "lines-between-class-members": "error",
+ "max-classes-per-file": "error",
+ "max-depth": "off",
+ "max-len": "off",
+ "max-lines": "off",
+ "max-lines-per-function": "off",
+ "max-nested-callbacks": "error",
+ "max-params": "error",
+ "max-statements": "off",
+ "max-statements-per-line": "off",
+ "multiline-comment-style": "error",
+ "new-cap": "error",
+ "new-parens": "error",
+ "newline-after-var": "off",
+ "newline-before-return": "off",
+ "newline-per-chained-call": "error",
+ "no-alert": "error",
+ "no-array-constructor": "error",
+ "no-await-in-loop": "error",
+ "no-bitwise": "error",
+ "no-buffer-constructor": "error",
+ "no-caller": "error",
+ "no-catch-shadow": "error",
+ "no-confusing-arrow": "error",
+ "no-console": "off",
+ "no-constant-condition": [
+ "error",
+ {
+ "checkLoops": false
+ }
+ ],
+ "no-constructor-return": "error",
+ "no-continue": "error",
+ "no-div-regex": "error",
+ "no-dupe-else-if": "error",
+ "no-duplicate-imports": "error",
+ "no-else-return": "off",
+ "no-empty-function": "error",
+ "no-eq-null": "error",
+ "no-eval": "error",
+ "no-extend-native": "error",
+ "no-extra-bind": "error",
+ "no-extra-label": "error",
+ "no-extra-parens": "off",
+ "no-floating-decimal": "error",
+ "no-implicit-coercion": "error",
+ "no-implicit-globals": "error",
+ "no-implied-eval": "error",
+ "no-import-assign": "error",
+ "no-inline-comments": "off",
+ "no-inner-declarations": [
+ "error",
+ "functions"
+ ],
+ "no-invalid-this": "error",
+ "no-iterator": "error",
+ "no-label-var": "error",
+ "no-labels": "error",
+ "no-lone-blocks": "error",
+ "no-lonely-if": "error",
+ "no-loop-func": "off",
+ "no-magic-numbers": "off",
+ "no-mixed-operators": "error",
+ "no-mixed-requires": "error",
+ "no-multi-assign": "off",
+ "no-multi-spaces": "off",
+ "no-multi-str": "error",
+ "no-multiple-empty-lines": "error",
+ "no-native-reassign": "off",
+ "no-negated-condition": "error",
+ "no-negated-in-lhs": "error",
+ "no-nested-ternary": "error",
+ "no-new": "error",
+ "no-new-func": "error",
+ "no-new-object": "error",
+ "no-new-require": "error",
+ "no-new-wrappers": "error",
+ "no-octal-escape": "error",
+ "no-param-reassign": "off",
+ "no-path-concat": "error",
+ "no-plusplus": "off",
+ "no-process-env": "error",
+ "no-process-exit": "error",
+ "no-proto": "error",
+ "no-restricted-globals": "error",
+ "no-restricted-imports": "error",
+ "no-restricted-modules": "error",
+ "no-restricted-properties": "error",
+ "no-restricted-syntax": "error",
+ "no-return-assign": "error",
+ "no-return-await": "error",
+ "no-script-url": "error",
+ "no-self-compare": "error",
+ "no-sequences": "error",
+ "no-setter-return": "error",
+ "no-shadow": "off",
+ "no-spaced-func": "error",
+ "no-sync": "error",
+ "no-tabs": "error",
+ "no-template-curly-in-string": "error",
+ "no-ternary": "off",
+ "no-throw-literal": "error",
+ "no-undef-init": "error",
+ "no-undefined": "error",
+ "no-underscore-dangle": "off",
+ "no-unmodified-loop-condition": "error",
+ "no-unneeded-ternary": "error",
+ "no-unused-expressions": "error",
+ "no-use-before-define": "off",
+ "no-useless-call": "error",
+ "no-useless-computed-key": "error",
+ "no-useless-concat": "error",
+ "no-useless-constructor": "error",
+ "no-useless-rename": "error",
+ "no-useless-return": "error",
+ "no-var": "off",
+ "no-void": "error",
+ "no-warning-comments": "off",
+ "no-whitespace-before-property": "error",
+ "nonblock-statement-body-position": "error",
+ "object-curly-newline": "error",
+ "object-curly-spacing": [
+ "error",
+ "never"
+ ],
+ "object-shorthand": "off",
+ "one-var": "off",
+ "one-var-declaration-per-line": "off",
+ "operator-assignment": [
+ "error",
+ "always"
+ ],
+ "operator-linebreak": "error",
+ "padded-blocks": "off",
+ "padding-line-between-statements": "error",
+ "prefer-arrow-callback": "off",
+ "prefer-const": "error",
+ "prefer-destructuring": "off",
+ "prefer-exponentiation-operator": "error",
+ "prefer-named-capture-group": "error",
+ "prefer-numeric-literals": "error",
+ "prefer-object-spread": "error",
+ "prefer-promise-reject-errors": "error",
+ "prefer-reflect": "off",
+ "prefer-regex-literals": "error",
+ "prefer-rest-params": "error",
+ "prefer-spread": "error",
+ "prefer-template": "off",
+ "quote-props": "off",
+ "quotes": "off",
+ "radix": [
+ "error",
+ "as-needed"
+ ],
+ "require-atomic-updates": "error",
+ "require-await": "error",
+ "require-jsdoc": "off",
+ "require-unicode-regexp": "error",
+ "rest-spread-spacing": "error",
+ "semi": "off",
+ "semi-spacing": [
+ "error",
+ {
+ "after": true,
+ "before": false
+ }
+ ],
+ "semi-style": [
+ "error",
+ "last"
+ ],
+ "sort-imports": "error",
+ "sort-keys": "off",
+ "sort-vars": "off",
+ "space-before-blocks": "error",
+ "space-before-function-paren": "off",
+ "space-in-parens": [
+ "error",
+ "never"
+ ],
+ "space-infix-ops": "off",
+ "space-unary-ops": "error",
+ "spaced-comment": "off",
+ "strict": [
+ "error",
+ "function"
+ ],
+ "switch-colon-spacing": "error",
+ "symbol-description": "error",
+ "template-curly-spacing": "error",
+ "template-tag-spacing": "error",
+ "unicode-bom": [
+ "error",
+ "never"
+ ],
+ "valid-jsdoc": "error",
+ "vars-on-top": "off",
+ "wrap-iife": [
+ "error",
+ "any"
+ ],
+ "wrap-regex": "error",
+ "yield-star-spacing": "error",
+ "yoda": [
+ "error",
+ "never"
+ ]
+ }
+}
-(function (that) {
+/*global libD*/
+
+(function (global) {
+ "use strict";
+
var divAlert, divAlertInput, divAlertConfirm, divAlertButton, alertButtonOK,
- divAlertCallback, divAlertCallbackYes, divAlertCallbackNo, alertInput;
+ divAlertCallback, divAlertCallbackYes, divAlertCallbackNo, alertInput,
+ divAlertContent;
- var _ = (window.libD && libD.l10n) ? libD.l10n() : function (s) { return s; };
+ var _ = (window.libD && libD.l10n) ? libD.l10n() : function (s) {return s;};
function promptOK() {
divAlert.style.display = "none";
- divAlertCallback && divAlertCallback(alertInput.value);
+ if (divAlertCallback) {
+ divAlertCallback(alertInput.value);
+ }
}
function promptCancel() {
divAlert.style.display = "none";
- divAlertCallback && divAlertCallback(null);
+ if (divAlertCallback) {
+ divAlertCallback(null);
+ }
}
function confirmYes() {
divAlert.style.display = "none";
- divAlertCallbackYes && divAlertCallbackYes();
+ if (divAlertCallbackYes) {
+ divAlertCallbackYes();
+ }
}
-
function confirmNo() {
divAlert.style.display = "none";
- divAlertCallbackNo && divAlertCallbackNo();
+ if (divAlertCallbackNo) {
+ divAlertCallbackNo();
+ }
}
function alertOK() {
divAlert.style.display = "none";
- divAlertCallback && divAlertCallback();
+ if (divAlertCallback) {
+ divAlertCallback();
+ }
}
function prepare() {
document.body.appendChild(divAlert);
}
- that.myAlert = function (msg, callback) {
+ global.myAlert = function (msg, callback) {
if (!divAlert) {
prepare();
}
divAlertButton.getElementsByTagName("button")[0].focus();
};
- that.myAlert.l10n = _;
+ global.myAlert.l10n = _;
- that.myPrompt = function (msg, callback, defaultText) {
+ global.myPrompt = function (msg, callback, defaultText) {
if (!divAlert) {
prepare();
}
divAlertCallback = callback;
divAlert.style.display = "";
- if (!/iPad|iPhone|iPod/g.test(navigator.userAgent)) {
+ if (!(/iPad|iPhone|iPod/gu).test(navigator.userAgent)) {
alertInput.focus();
alertInput.setSelectionRange(0, alertInput.value.length);
alertInput.select();
}
};
- that.myConfirm = function (msg, callbackYes, callbackNo) {
+ global.myConfirm = function (msg, callbackYes, callbackNo) {
if (!divAlert) {
prepare();
}
+/* eslint-disable no-process-env */
/**
* Copyright (C) 2016-2020 Raphaël Jakse <raphael.trivabble@jakse.fr>
*
* @source: https://gitlab.com/raphj/trivabble/
*/
-const port = 3000;
+ /*eslint strict: [2, "global"]*/
+
+"use strict";
+
+const port = parseInt(process.env.TRIVABBLE_PORT || "3000");
const SAVE_TIMEOUT = 5000;
const KEEP_ALIVE = 30000;
+const DEV_ENABLE_SERVING_FILES = (process.env.DEV_ENABLE_SERVING_FILES || "").toLowerCase() === "true";
+
+if (DEV_ENABLE_SERVING_FILES) {
+ console.log("DEV_ENABLE_SERVING_FILES: Serving files in the current directory. Please never do this on a production server, this is for development purposes only.");
+}
+
var http = require("http");
var fs = require("fs");
-
var frBag = [
- " ", " ", // jokers
- "E","E","E","E","E","E","E","E","E","E","E","E","E","E","E",
- "A","A","A","A","A","A","A","A","A",
- "I","I","I","I","I","I","I","I",
- "N","N","N","N","N","N",
- "O","O","O","O","O","O",
- "R","R","R","R","R","R",
- "S","S","S","S","S","S",
- "T","T","T","T","T","T",
- "U","U","U","U","U","U",
- "L","L","L","L","L",
- "D","D","D",
- "M","M","M",
- "G","G",
- "B","B",
- "C","C",
- "P","P",
- "F","F",
- "H","H",
- "V","V",
+ " ", " ", // Jokers
+ "E", "E", "E", "E", "E", "E", "E", "E", "E", "E", "E", "E", "E", "E", "E",
+ "A", "A", "A", "A", "A", "A", "A", "A", "A",
+ "I", "I", "I", "I", "I", "I", "I", "I",
+ "N", "N", "N", "N", "N", "N",
+ "O", "O", "O", "O", "O", "O",
+ "R", "R", "R", "R", "R", "R",
+ "S", "S", "S", "S", "S", "S",
+ "T", "T", "T", "T", "T", "T",
+ "U", "U", "U", "U", "U", "U",
+ "L", "L", "L", "L", "L",
+ "D", "D", "D",
+ "M", "M", "M",
+ "G", "G",
+ "B", "B",
+ "C", "C",
+ "P", "P",
+ "F", "F",
+ "H", "H",
+ "V", "V",
"J",
"Q",
"K",
"X": 10,
"Y": 10,
"Z": 10
-}
+};
var games = {};
var saveTo = null;
function saveGames() {
- fs.writeFile('games.backup.json', JSON.stringify(games), function (err) {
+ fs.writeFile("games.backup.json", JSON.stringify(games), function (err) {
if (err) {
- console.error('ERROR: Cannot save games!');
+ console.error("ERROR: Cannot save games!");
}
});
this.remainingLetters = this.bag.length;
this.racks = {};
this.scores = {};
-}
+};
Game.prototype.toJSON = function () {
return {
game.bag = obj.bag || frBag.slice();
game.remainingLetters = obj.remainingLetters || game.bag.length;
game.racks = obj.racks || {};
- game.scores = obj.scores || {}
+ game.scores = obj.scores || {};
return game;
};
Game.prototype.setPlayerScore = function (player, score) {
var playerID = "#" + player;
- if (!this.racks.hasOwnProperty(playerID) || typeof score !== "number") {
+ if (!Object.prototype.hasOwnProperty.call(this.racks, playerID) || typeof score !== "number") {
return;
}
Game.prototype.playerJoined = function (playerName) {
if (playerName) {
- this.getPlayerRack(playerName); // create the player's rack
+ this.getPlayerRack(playerName); // Create the player's rack
}
var players = [];
for (var player in this.racks) {
- if (this.racks.hasOwnProperty(player)) {
+ if (Object.prototype.hasOwnProperty.call(this.racks, player)) {
player = player.slice(1); // '#'
players.push(
{
Game.prototype.commit = function () {
var pendingEvents = this.pendingEvents;
this.pendingEvents = [];
- msg = JSON.stringify(pendingEvents);
+ var msg = JSON.stringify(pendingEvents);
for (var i = 0; i < this.listeningPlayers.length; i++) {
while (i < this.listeningPlayers.length && !this.listeningPlayers[i]) {
this.listeningPlayers[i] = this.listeningPlayers[this.listeningPlayers.length - 1];
- that.listeningPlayers.pop();
+ this.listeningPlayers.pop();
}
if (this.listeningPlayers[i]) {
Game.prototype.bagPopLetter = function (player) {
if (this.remainingLetters) {
var letter = "";
- var index
+ var index;
+
while (letter === "") {
index = Math.floor(Math.random() * this.bag.length);
letter = this.bag[index];
this.bag[index] = "";
this.remainingLetters--;
- this.pendingEvents.push(
- {
- player: player,
- action: "popBag",
- remainingLetters: this.remainingLetters,
- date: dateNow
- }
- );
+ this.pendingEvents.push({
+ player: player,
+ action: "popBag",
+ remainingLetters: this.remainingLetters,
+ date: dateNow
+ });
+
return letter;
}
Game.prototype.getCell = function (index) {
return this.board[index];
-}
+};
Game.prototype.setCell = function (index, letter, player) {
this.board[index] = letter;
Game.prototype.reset = function (player) {
this.init();
- this.pendingEvents.push(
- {
- player: player,
- action: "reset",
- board: this.board,
- remainingLetters: this.remainingLetters,
- rack: [],
- date: dateNow
- }
- );
+ this.pendingEvents.push({
+ player: player,
+ action: "reset",
+ board: this.board,
+ remainingLetters: this.remainingLetters,
+ rack: [],
+ date: dateNow
+ });
this.playerJoined();
};
return number.toString();
}
-destToAction = {
- "rack": "setRackCell",
- "board": "setCell",
- "bag": "pushBag"
-}
-
function countTiles(rack) {
var count = 0;
var game = games[gameNumber];
if (!game && (cmd.cmd !== "joinGame")) {
- response.write('{"error":1,"reason":"Missing or bad game number"}');
+ response.write("{\"error\":1,\"reason\":\"Missing or bad game number\"}");
return;
}
var rack = null;
switch (cmd.cmd) {
- case "joinGame":
- if (!gameNumber) {
- gameNumber = newGameId();
- }
-
- if (!game) {
- game = games[gameNumber] = new Game();
- }
+ case "joinGame":
+ if (!gameNumber) {
+ gameNumber = newGameId();
+ }
- game.playerJoined(playerName);
+ if (!game) {
+ game = games[gameNumber] = new Game();
+ }
- response.write(
- JSON.stringify(
- {
- "error":0,
- "gameNumber": gameNumber,
- "playerName": playerName,
- "rack": game.getPlayerRack(playerName),
- "board": game.board,
- "remainingLetters": game.remainingLetters,
- "letterValues": frValues,
- "date": dateNow,
- "sync": false
- }
- )
+ game.playerJoined(playerName);
+
+ response.write(
+ JSON.stringify({
+ "error":0,
+ "gameNumber": gameNumber,
+ "playerName": playerName,
+ "rack": game.getPlayerRack(playerName),
+ "board": game.board,
+ "remainingLetters": game.remainingLetters,
+ "letterValues": frValues,
+ "date": dateNow,
+ "sync": false
+ })
);
- break;
+ break;
+
+ case "hello":
+ game.playerJoined(playerName);
+ break;
+
+ case "sync":
+ response.write(
+ JSON.stringify({
+ "error":0,
+ "gameNumber": gameNumber,
+ "playerName": playerName,
+ "rack": game.getPlayerRack(playerName),
+ "board": game.board,
+ "remainingLetters": game.remainingLetters,
+ "letterValues": frValues,
+ "date": dateNow,
+ "sync": true
+ })
+ );
+ break;
- case "hello":
- game.playerJoined(playerName);
- break;
+ case "score":
+ game.setPlayerScore(cmd.player, cmd.score);
+ response.write("{\"error\":0}");
+ break;
- case "sync":
- response.write(
- JSON.stringify(
- {
- "error":0,
- "gameNumber": gameNumber,
- "playerName": playerName,
- "rack": game.getPlayerRack(playerName),
- "board": game.board,
- "remainingLetters": game.remainingLetters,
- "letterValues": frValues,
- "date": dateNow,
- "sync": true
- }
- )
- );
- break;
+ case "moveLetter":
+ var letter = "";
- case "score":
- game.setPlayerScore(cmd.player, cmd.score);
- response.write('{"error":0}');
- break;
+ switch (cmd.from) {
+ case "rack":
+ rack = game.getPlayerRack(playerName);
- case "moveLetter":
- var letter = "";
+ letter = rack[cmd.indexFrom];
+ rack[cmd.indexFrom] = "";
+ break;
- switch (cmd.from) {
- case "rack":
- rack = game.getPlayerRack(playerName);
- letter = rack[cmd.indexFrom];
- rack[cmd.indexFrom] = "";
- updateRack = true;
- break;
+ case "board":
+ if (cmd.indexFrom < 15 * 15) {
+ letter = game.getCell(cmd.indexFrom);
+ game.setCell(cmd.indexFrom, "", playerName);
+ }
+ break;
- case "board":
- if (cmd.indexFrom < 15 * 15) {
- letter = game.getCell(cmd.indexFrom);
- game.setCell(cmd.indexFrom, "", playerName);
- }
- break;
+ case "bag":
+ letter = game.bagPopLetter(playerName);
+ break;
- case "bag":
- letter = game.bagPopLetter(playerName);
- break;
+ default:
+ response.write("{\"error\":1, \"reason\":\"Moving letter from an unknown place\"}");
+ return;
+ }
- default:
- response.write('{"error":1, "reason":"Moving letter from an unknown place"}');
+ switch (cmd.to) {
+ case "rack":
+ if (cmd.indexTo > 7) {
+ response.write("{\"error\":1, \"reason\":\"Moving letter to a index which is too high\"}");
return;
- }
-
- switch (cmd.to) {
- case "rack":
- if (cmd.indexTo > 7) {
- response.write('{"error":1, "reason":"Moving letter to a index which is too high"}');
- return;
- }
-
- rack = rack || game.getPlayerRack(playerName);
- rack[cmd.indexTo] = letter;
- break;
-
- case "board":
- if (cmd.indexTo < 15 * 15) {
- game.setCell(cmd.indexTo, letter, playerName);
- }
- break;
-
- case "bag":
- game.bagPushLetter(letter, playerName);
- break;
-
- default:
- switch (cmd.from) {
- case "rack":
- rack[cmd.indexFrom] = letter;
- break;
-
- case "board":
- game.setCell(cmd.indexFrom, letter, playerName);
- break;
-
- case "bag":
- game.bagPushLetter(letter, playerName);
- break;
+ }
- default:
- console.error("BUG: code should not have been reached.");
- }
+ rack = rack || game.getPlayerRack(playerName);
+ rack[cmd.indexTo] = letter;
+ break;
- response.write('{"error":1, "reason":"Moving letter to an unknown place"}');
- return;
- }
+ case "board":
+ if (cmd.indexTo < 15 * 15) {
+ game.setCell(cmd.indexTo, letter, playerName);
+ }
+ break;
- response.write(
- JSON.stringify(
- {
- error:0,
- letter: letter,
- action: "moveLetter",
- indexTo: typeof cmd.indexTo === "number" ? cmd.indexTo : -1,
- from: cmd.from,
- to: cmd.to,
- indexFrom: typeof cmd.indexFrom === "number" ? cmd.indexFrom : -1,
- remainingLetters: game.remainingLetters,
- date: dateNow
- }
- )
- );
+ case "bag":
+ game.bagPushLetter(letter, playerName);
+ break;
- if (rack) {
- game.pendingEvents.push(
- {
- players: [{
- player: playerName,
- rackCount: countTiles(rack)
- }],
- date: dateNow
- }
- );
- }
- break;
+ default:
+ switch (cmd.from) {
+ case "rack":
+ rack[cmd.indexFrom] = letter;
+ break;
- case "setRack":
- cmd.rack.length = 8;
+ case "board":
+ game.setCell(cmd.indexFrom, letter, playerName);
+ break;
- var rack = game.getPlayerRack(playerName);
+ case "bag":
+ game.bagPushLetter(letter, playerName);
+ break;
- var initialRackCount = countTiles(rack);
- var rackCount = countTiles(cmd.rack);
+ default:
+ console.error("BUG: code should not have been reached.");
+ }
- console.log(rack, cmd.rack);
+ response.write("{\"error\":1, \"reason\":\"Moving letter to an unknown place\"}");
+ return;
+ }
- if (initialRackCount !== rackCount) {
- response.write(
- JSON.stringify({
- error: 1,
- rack: rack,
- reason: "the new rack doesn't contain the same number of letters",
- date: dateNow
- })
- );
- break;
- }
+ response.write(
+ JSON.stringify({
+ error:0,
+ letter: letter,
+ action: "moveLetter",
+ indexTo: typeof cmd.indexTo === "number" ? cmd.indexTo : -1,
+ from: cmd.from,
+ to: cmd.to,
+ indexFrom: typeof cmd.indexFrom === "number" ? cmd.indexFrom : -1,
+ remainingLetters: game.remainingLetters,
+ date: dateNow
+ })
+ );
- var oldRack = rack.slice();
+ if (rack) {
+ game.pendingEvents.push({
+ players: [{
+ player: playerName,
+ rackCount: countTiles(rack)
+ }],
+ date: dateNow
+ });
+ }
+ break;
+ case "setRack":
+ cmd.rack.length = 8;
- for (var i = 0; i < 8; i++) {
- cmd.rack[i] = (cmd.rack[i] || "")[0] || "";
- if (cmd.rack[i]) {
- var indexLetter = oldRack.indexOf(cmd.rack[i]);
+ rack = game.getPlayerRack(playerName);
- if (indexLetter === -1) {
- response.write(
- JSON.stringify({
- error: 1,
- rack: rack,
- reason: "the new rack is not a permutation of the old rack",
- date: dateNow
- })
- );
- break;
- }
- oldRack[indexLetter] = false;
- }
- }
+ var initialRackCount = countTiles(rack);
+ var rackCount = countTiles(cmd.rack);
- for (var i = 0; i < 8; i++) {
- rack[i] = cmd.rack[i];
- }
+ console.log(rack, cmd.rack);
+ if (initialRackCount !== rackCount) {
response.write(
JSON.stringify({
- error: 0,
+ error: 1,
rack: rack,
+ reason: "the new rack doesn't contain the same number of letters",
date: dateNow
})
);
-
- break;
- case "resetGame":
- game.reset();
- response.write('{"error":0}');
break;
+ }
- case "msg":
- game.pendingEvents.push(
- {
- msg: {
- sender: playerName,
- content: cmd.msg
- },
- date: dateNow
+ var oldRack = rack.slice();
+
+
+ for (let i = 0; i < 8; i++) {
+ cmd.rack[i] = (cmd.rack[i] || "")[0] || "";
+ if (cmd.rack[i]) {
+ var indexLetter = oldRack.indexOf(cmd.rack[i]);
+
+ if (indexLetter === -1) {
+ response.write(
+ JSON.stringify({
+ error: 1,
+ rack: rack,
+ reason: "the new rack is not a permutation of the old rack",
+ date: dateNow
+ })
+ );
+ break;
}
- );
- response.write('{"error":0}');
- break;
+ oldRack[indexLetter] = false;
+ }
+ }
- default:
- response.write('{"error":1,"reason":"Unknown command"}');
- break;
+ for (let i = 0; i < 8; i++) {
+ rack[i] = cmd.rack[i];
+ }
+
+ response.write(
+ JSON.stringify({
+ error: 0,
+ rack: rack,
+ date: dateNow
+ })
+ );
+
+ break;
+ case "resetGame":
+ game.reset();
+ response.write("{\"error\":0}");
+ break;
+
+ case "msg":
+ game.pendingEvents.push({
+ msg: {
+ sender: playerName,
+ content: cmd.msg
+ },
+ date: dateNow
+ });
+ response.write("{\"error\":0}");
+ break;
+
+ default:
+ response.write("{\"error\":1,\"reason\":\"Unknown command\"}");
+ break;
}
}
}
writeResponse(responseAndIsES,
- "playerName": cmds.playerName,
- "gameNumber": cmds.gameNumber,
- "letterValues": game.letterValues,
- "rack": game.getPlayerRack(cmds.playerName),
- "board": game.board,
- "remainingLetters": game.remainingLetters,
- "date": dateNow
- }
- )
+ JSON.stringify({
+ "playerName": cmds.playerName,
+ "gameNumber": cmds.gameNumber,
+ "letterValues": game.letterValues,
+ "rack": game.getPlayerRack(cmds.playerName),
+ "board": game.board,
+ "remainingLetters": game.remainingLetters,
+ "date": dateNow
+ })
);
game.addListeningPlayer(cmds.playerName, responseAndIsES);
}
function handleRequest(request, response) {
- var post = '';
+ var post = "";
- // thx http://stackoverflow.com/questions/4295782/how-do-you-extract-post-data-in-node-js
+ // Thx http://stackoverflow.com/questions/4295782/how-do-you-extract-post-data-in-node-js
request.on("data", function (data) {
post += data;
});
request.on("end", function () {
+ if (DEV_ENABLE_SERVING_FILES && !request.url.startsWith("/:")) {
+ if (request.url === "/") {
+ request.url = "/index.html";
+ }
+
+ console.log("Serving " + request.url);
+
+ fs.exists("." + request.url, function (exists) {
+ if (exists) {
+ fs.readFile("." + request.url, function(err, contents) {
+ if (err) {
+ response.statusCode = 500;
+ response.setHeader("Content-Type", "text/plain; charset=utf-8");
+ response.end(err);
+ }
+
+ var mime = "application/xhtml+xml; charset=utf-8";
+ var mimes = {
+ ".mp3": "audio/mpeg",
+ ".ogg": "audio/ogg",
+ ".js": "application/javascript; charset=utf-8",
+ ".css": "text/css; charset=utf-8",
+ ".svg": "image/svg+xml"
+ };
+
+ for (var i in mimes) {
+ if (request.url.endsWith(i)) {
+ mime = mimes[i];
+ break;
+ }
+ }
+
+ response.setHeader("Content-Type", mime);
+ response.end(contents);
+ });
+ } else {
+ response.statusCode = 404;
+ response.end("404");
+ }
+ });
+
+ return;
+ }
+
var isEventSource = false;
var eventSourceMatch = request.url.match(/\/:[^/]+\/sse\/(?<data>[\s\S]+)$/u);
});
}
-fs.readFile('games.backup.json', function (err, data) {
+fs.readFile("games.backup.json", function (err, data) {
try {
if (err) {
console.error("WARNING: Could not restore previous backup of the games");
var backup = JSON.parse(data);
for (var gameNumber in backup) {
- games[gameNumber] = Game.fromJSON(backup[gameNumber]);
+ if (Object.prototype.hasOwnProperty.call(backup, gameNumber)) {
+ games[gameNumber] = Game.fromJSON(backup[gameNumber]);
+ }
}
}
} catch (e) {
console.log("Server listening on: http://localhost:%s", port);
});
});
-
* @source: https://gitlab.com/raphj/trivabble/
*/
+/*global libD, myConfirm, myAlert, myPrompt*/
+
(function () {
"use strict";
var _ = (window.libD && libD.l10n) ? libD.l10n() : function (s) { return s; };
- window.trivabble = {l10n: _};
+ var trivabble = window.trivabble = {l10n: _};
function format(s, v) {
return s.replace("{0}", v);
}
- var newGameBtn, board, rack, boardCells = [], scoreOf, bag;
+ var board, rack, boardCells = [], scoreOf, bag;
var playerLetters = [];
var chatMessages, helpBag, helpClear;
var tablePlayers = {};
- var participantPlaceholder, participants;
+ var participantPlaceholder, participants, name;
var lastDate = 0;
var blockMove = 0;
var gameInProgress = false;
- function onclick(ele, fun) {
- ele.addEventListener("click", fun, false);
- ele.addEventListener("touch", fun, false);
- }
-
function mouseDown(ele, fun, stop) {
var meth = stop ? "removeEventListener" : "addEventListener";
ele[meth]("mousedown", fun, false);
return false;
}
- function dragTileEnd(e) {
+ function dragTileEnd() {
movingTile.style.left = "";
movingTile.style.top = "";
movingTile.style.width = "";
moveCMD = null;
}
- var DBG = 0;
function dragTileMove(e) {
var newLeft = (tileInitCoords.left + (e.clientX - tileInitMouseCoords.clientX));
var newTop = (tileInitCoords.top + (e.clientY - tileInitMouseCoords.clientY));
function set(key, value) {
switch (key) {
case "playerName":
- document.getElementById("name").textContent = localStorage.trivabblePlayerName = value;
+ name.textContent = localStorage.trivabblePlayerName = value;
break;
case "gameNumber":
document.getElementById("number").textContent = localStorage.trivabbleGameNumber = value;
tablePlayers[playerName] = row;
}
- if (player.hasOwnProperty("score")) {
+ if (Object.prototype.hasOwnProperty.call(player, "score")) {
var scoreCell = tablePlayers[playerName].childNodes[2];
scoreCell.textContent = player.score;
blink(scoreCell);
}
- if (player.hasOwnProperty("rackCount")) {
+ if (Object.prototype.hasOwnProperty.call(player, "rackCount")) {
var countCell = tablePlayers[playerName].childNodes[1];
countCell.textContent = player.rackCount;
blink(countCell);
setRack(data.rack);
}
+ if (!data.action) {
+ return;
+ }
- switch(data.action) {
+ switch (data.action) {
case "pushBag": //TODO
break;
}
if (data.to === "board") {
- setCell(data.indexTo, data.letter, data.hasOwnProperty("player") && data.player !== localStorage.trivabblePlayerName);
+ 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, data.hasOwnProperty("player") && data.player !== localStorage.trivabblePlayerName);
+ setCell(data.indexTo, data.letter, Object.prototype.hasOwnProperty.call(data, "player") && data.player !== localStorage.trivabblePlayerName);
break;
case "setRackCell":
setRackCell(data.indexTo, data.letter);
function (newName) {
if (newName && newName.trim()) {
localStorage.trivabblePlayerName = newName.trim();
- document.getElementById("name").textContent = localStorage.trivabblePlayerName;
+ name.textContent = localStorage.trivabblePlayerName;
}
},
localStorage.trivabblePlayerName
audioSourceOGG = document.createElement("source");
audioSourceOGG.src = "receive.ogg";
- var audioSourceMP3 = document.createElement("source");
+ audioSourceMP3 = document.createElement("source");
audioSourceMP3.src = "receive.mp3";
audioChat.appendChild(audioSourceOGG);
audioChat.appendChild(audioSourceMP3);
localStorage.trivabbleMsgSound = msgSound.checked;
};
- if (localStorage.hasOwnProperty("trivabbleMsgSound")) {
+ if (Object.prototype.hasOwnProperty.call(localStorage, "trivabbleMsgSound")) {
msgSound.checked = localStorage.trivabbleMsgSound === "true";
} else {
localStorage.trivabbleMsgSound = msgSound.checked;
}
- if (localStorage.hasOwnProperty("trivabbleTileSound")) {
+ if (Object.prototype.hasOwnProperty.call(localStorage, "trivabbleTileSound")) {
tilesSound.checked = localStorage.trivabbleTileSound === "true";
} else {
localStorage.trivabbleTilesSound = tilesSound.checked;
return;
}
- document.getElementById("name").textContent = localStorage.trivabblePlayerName;
+ name.textContent = localStorage.trivabblePlayerName;
var letters = "ABCDEFGHIJKLMNO";
if (i === j && i === 7) {
specialCell("doubleWord", board.lastChild.lastChild);
- var cell = board.lastChild.lastChild.getElementsByClassName("special-cell-label")[0];
+ cell = board.lastChild.lastChild.getElementsByClassName("special-cell-label")[0];
cell.textContent = "★";
board.lastChild.lastChild.id = "center-cell";
} else if (i % 7 === 0 && j % 7 === 0) {