Lint
authorRaphaël Jakse <raphael.jakse@univ-grenoble-alpes.fr>
Sat, 4 Apr 2020 14:27:18 +0000 (16:27 +0200)
committerRaphaël Jakse <raphael.jakse@univ-grenoble-alpes.fr>
Sat, 4 Apr 2020 14:30:44 +0000 (16:30 +0200)
.eslintrc.json [new file with mode: 0644]
alert.js
trivabble-server.js
trivabble.js

diff --git a/.eslintrc.json b/.eslintrc.json
new file mode 100644 (file)
index 0000000..72837bb
--- /dev/null
@@ -0,0 +1,317 @@
+{
+    "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"
+        ]
+    }
+}
index 3e7de4a83762d0c5fe014c445ef7f9f781af3d42..42d3270527ec11651fb1194647bf3fbe25006da0 100644 (file)
--- a/alert.js
+++ b/alert.js
@@ -1,32 +1,46 @@
-(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();
         }
index 85e887f9d61415465c46ecc8b96b1e0ccbfd26a6..21b3140889b15afd5d339f5a9b7f25a98e875863 100644 (file)
@@ -1,3 +1,4 @@
+/* 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",
@@ -88,7 +98,7 @@ var frValues = {
     "X": 10,
     "Y": 10,
     "Z": 10
-}
+};
 
 
 var games = {};
@@ -97,9 +107,9 @@ var dateNow = 0;
 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!");
         }
     });
 
@@ -158,7 +168,7 @@ Game.prototype.init = function () {
     this.remainingLetters = this.bag.length;
     this.racks = {};
     this.scores = {};
-}
+};
 
 Game.prototype.toJSON = function () {
     return {
@@ -176,7 +186,7 @@ Game.fromJSON = function (obj) {
     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;
 };
 
@@ -193,7 +203,7 @@ Game.prototype.getPlayerScore = function (player) {
 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;
     }
 
@@ -212,13 +222,13 @@ Game.prototype.setPlayerScore = function (player, score) {
 
 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(
                 {
@@ -260,12 +270,12 @@ Game.prototype.addListeningPlayer = function (playerName, responseAndIsES) {
 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]) {
@@ -281,7 +291,8 @@ Game.prototype.commit = function () {
 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];
@@ -289,14 +300,13 @@ Game.prototype.bagPopLetter = function (player) {
 
         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;
     }
 
@@ -305,7 +315,7 @@ Game.prototype.bagPopLetter = function (player) {
 
 Game.prototype.getCell = function (index) {
     return this.board[index];
-}
+};
 
 Game.prototype.setCell = function (index, letter, player) {
     this.board[index] = letter;
@@ -345,16 +355,14 @@ Game.prototype.bagPushLetter = function (letter, player) {
 
 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();
 };
@@ -379,12 +387,6 @@ function newGameId() {
     return number.toString();
 }
 
-destToAction = {
-    "rack": "setRackCell",
-    "board": "setCell",
-    "bag": "pushBag"
-}
-
 function countTiles(rack) {
     var count = 0;
 
@@ -401,246 +403,236 @@ function handleCommand(cmd, gameNumber, playerName, response) {
     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;
     }
 }
 
@@ -654,15 +646,15 @@ function handleCommands(cmds, responseAndIsES) {
         }
 
         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);
@@ -689,9 +681,9 @@ function handleCommands(cmds, 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;
 
@@ -702,6 +694,50 @@ function handleRequest(request, response) {
     });
 
     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);
 
@@ -720,7 +756,7 @@ function handleRequest(request, response) {
     });
 }
 
-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");
@@ -728,7 +764,9 @@ fs.readFile('games.backup.json', function (err, data) {
             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) {
@@ -740,4 +778,3 @@ fs.readFile('games.backup.json', function (err, data) {
         console.log("Server listening on: http://localhost:%s", port);
     });
 });
-
index 07748d84c61df4544f13a811296a55fd4d3d6849..e5b0747102f3d02d9f5621d886508adb0afc1022 100644 (file)
@@ -22,6 +22,8 @@
  * @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) {