From: Raphaël Jakse Date: Sat, 4 Apr 2020 14:27:18 +0000 (+0200) Subject: Lint X-Git-Url: https://secure.softndesign.org/git/?a=commitdiff_plain;h=f59e56bd6227aba28e29380ac025fcd46ab35542;p=trivabble.git Lint --- diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..72837bb --- /dev/null +++ b/.eslintrc.json @@ -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" + ] + } +} diff --git a/alert.js b/alert.js index 3e7de4a..42d3270 100644 --- 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() { @@ -86,7 +100,7 @@ document.body.appendChild(divAlert); } - that.myAlert = function (msg, callback) { + global.myAlert = function (msg, callback) { if (!divAlert) { prepare(); } @@ -100,9 +114,9 @@ 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(); } @@ -115,14 +129,14 @@ 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(); } diff --git a/trivabble-server.js b/trivabble-server.js index 85e887f..21b3140 100644 --- a/trivabble-server.js +++ b/trivabble-server.js @@ -1,3 +1,4 @@ +/* eslint-disable no-process-env */ /** * Copyright (C) 2016-2020 Raphaël Jakse * @@ -22,35 +23,44 @@ * @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\/(?[\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); }); }); - diff --git a/trivabble.js b/trivabble.js index 07748d8..e5b0747 100644 --- a/trivabble.js +++ b/trivabble.js @@ -22,6 +22,8 @@ * @source: https://gitlab.com/raphj/trivabble/ */ +/*global libD, myConfirm, myAlert, myPrompt*/ + (function () { "use strict"; @@ -29,13 +31,13 @@ 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 = []; @@ -43,18 +45,13 @@ 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); @@ -116,7 +113,7 @@ return false; } - function dragTileEnd(e) { + function dragTileEnd() { movingTile.style.left = ""; movingTile.style.top = ""; movingTile.style.width = ""; @@ -185,7 +182,6 @@ moveCMD = null; } - var DBG = 0; function dragTileMove(e) { var newLeft = (tileInitCoords.left + (e.clientX - tileInitMouseCoords.clientX)); var newTop = (tileInitCoords.top + (e.clientY - tileInitMouseCoords.clientY)); @@ -427,7 +423,7 @@ 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; @@ -577,13 +573,13 @@ 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); @@ -632,8 +628,11 @@ setRack(data.rack); } + if (!data.action) { + return; + } - switch(data.action) { + switch (data.action) { case "pushBag": //TODO break; @@ -656,13 +655,13 @@ } 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); @@ -849,7 +848,7 @@ function (newName) { if (newName && newName.trim()) { localStorage.trivabblePlayerName = newName.trim(); - document.getElementById("name").textContent = localStorage.trivabblePlayerName; + name.textContent = localStorage.trivabblePlayerName; } }, localStorage.trivabblePlayerName @@ -975,7 +974,7 @@ 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); @@ -992,13 +991,13 @@ 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; @@ -1078,7 +1077,7 @@ return; } - document.getElementById("name").textContent = localStorage.trivabblePlayerName; + name.textContent = localStorage.trivabblePlayerName; var letters = "ABCDEFGHIJKLMNO"; @@ -1121,7 +1120,7 @@ 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) {