From a0118b0bdfd1638eee23a82cb3836f14114c0881 Mon Sep 17 00:00:00 2001 From: Zutatensuppe Date: Thu, 13 May 2021 22:45:55 +0200 Subject: [PATCH] lots of changes to switch to vue --- common/Time.js | 20 +- package-lock.json | 526 +---------------------- package.json | 1 - public/Communication.js | 17 +- public/WsWrapper.js | 12 +- public/components/GameTeaser.vue.js | 12 +- public/components/HelpOverlay.vue.js | 26 ++ public/components/ImageTeaser.vue.js | 4 +- public/components/PreviewOverlay.vue.js | 27 ++ public/components/PuzzleStatus.vue.js | 36 ++ public/components/Scores.vue.js | 41 ++ public/components/SettingsOverlay.vue.js | 38 ++ public/components/Upload.vue.js | 4 +- public/game.js | 343 ++++----------- public/index.html | 37 +- public/style.css | 7 + public/views/Game.vue.js | 121 +++++- public/views/Replay.vue.js | 145 ++++++- server/Dirs.js | 1 - server/index.js | 32 +- templates/game.html.twig | 23 - templates/replay.html.twig | 29 -- 22 files changed, 582 insertions(+), 920 deletions(-) create mode 100644 public/components/HelpOverlay.vue.js create mode 100644 public/components/PreviewOverlay.vue.js create mode 100644 public/components/PuzzleStatus.vue.js create mode 100644 public/components/Scores.vue.js create mode 100644 public/components/SettingsOverlay.vue.js delete mode 100644 templates/game.html.twig delete mode 100644 templates/replay.html.twig diff --git a/common/Time.js b/common/Time.js index ced1bc8..fc46552 100644 --- a/common/Time.js +++ b/common/Time.js @@ -17,22 +17,23 @@ export const timestamp = () => { ) } -export const timeDiffStr = (from, to) => { - let diff = to - from - const d = Math.floor(diff / DAY) - diff = diff % DAY +export const durationStr = (duration) => { + const d = Math.floor(duration / DAY) + duration = duration % DAY - const h = Math.floor(diff / HOUR) - diff = diff % HOUR + const h = Math.floor(duration / HOUR) + duration = duration % HOUR - const m = Math.floor(diff / MIN) - diff = diff % MIN + const m = Math.floor(duration / MIN) + duration = duration % MIN - const s = Math.floor(diff / SEC) + const s = Math.floor(duration / SEC) return `${d}d ${h}h ${m}m ${s}s` } +export const timeDiffStr = (from, to) => durationStr(to - from) + export default { MS, SEC, @@ -42,4 +43,5 @@ export default { timestamp, timeDiffStr, + durationStr, } diff --git a/package-lock.json b/package-lock.json index f707f8d..7387c60 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,6 @@ "image-size": "^0.9.3", "multer": "^1.4.2", "sharp": "^0.28.1", - "twing": "^5.0.2", "ws": "^7.3.1" }, "devDependencies": { @@ -916,11 +915,6 @@ "@types/istanbul-lib-report": "*" } }, - "node_modules/@types/luxon": { - "version": "1.26.5", - "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-1.26.5.tgz", - "integrity": "sha512-XeQxxRMyJi1znfzHw4CGDLyup/raj84SnjjkI2fDootZPGlB0yqtvlvEIAmzHDa5wiEI5JJevZOWxpcofsaV+A==" - }, "node_modules/@types/node": { "version": "15.0.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.1.tgz", @@ -1628,11 +1622,6 @@ "integrity": "sha512-c0yixVG4v9KBc/tQ2rlbB3A/bgBFRvl8h8M4IeUbqCca4gsiCfvtaheUssbnux/Mb66Vjz7x8yYjDgYcNQOhyQ==", "dev": true }, - "node_modules/capitalize": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/capitalize/-/capitalize-1.0.0.tgz", - "integrity": "sha1-3IAsWAruEBkpAg0soUtMqKCuRL4=" - }, "node_modules/capture-exit": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", @@ -1825,14 +1814,6 @@ "node": ">=8" } }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", - "engines": { - "node": ">=0.8" - } - }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -2063,11 +2044,6 @@ "node": ">= 8" } }, - "node_modules/crypto-js": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.3.0.tgz", - "integrity": "sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q==" - }, "node_modules/cssom": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", @@ -2184,14 +2160,6 @@ "node": ">=0.10.0" } }, - "node_modules/defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", - "dependencies": { - "clone": "^1.0.2" - } - }, "node_modules/define-property": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", @@ -2358,11 +2326,6 @@ "is-arrayish": "^0.2.1" } }, - "node_modules/es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" - }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -2421,14 +2384,6 @@ "node": ">=4" } }, - "node_modules/esrever": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/esrever/-/esrever-0.2.0.tgz", - "integrity": "sha1-lunSj08bGnZ4TNXUkOquAQ50B7g=", - "bin": { - "esrever": "bin/esrever" - } - }, "node_modules/estraverse": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", @@ -2902,16 +2857,6 @@ "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" }, - "node_modules/fs-extra": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz", - "integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==", - "dependencies": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -3069,7 +3014,8 @@ "node_modules/graceful-fs": { "version": "4.2.6", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", + "dev": true }, "node_modules/growly": { "version": "1.3.0", @@ -3214,11 +3160,6 @@ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, - "node_modules/htmlspecialchars": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/htmlspecialchars/-/htmlspecialchars-1.0.5.tgz", - "integrity": "sha1-9DD4wdXzcJvnvrrqaW3ASCQwajA=" - }, "node_modules/http-errors": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", @@ -3457,17 +3398,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-finite": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", - "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-fullwidth-code-point": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", @@ -3488,14 +3418,6 @@ "node": ">=6" } }, - "node_modules/is-integer": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-integer/-/is-integer-1.0.7.tgz", - "integrity": "sha1-a96Bqs3feLZZtmKdYpytxRqIbVw=", - "dependencies": { - "is-finite": "^1.0.0" - } - }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -3509,6 +3431,7 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, "dependencies": { "isobject": "^3.0.1" }, @@ -3574,6 +3497,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -4356,14 +4280,6 @@ "node": ">=6" } }, - "node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, "node_modules/jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -4406,14 +4322,6 @@ "node": ">=6" } }, - "node_modules/levenshtein": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/levenshtein/-/levenshtein-1.0.5.tgz", - "integrity": "sha1-ORFzepy1baNF0Aj1V4LG8TiXm6M=", - "engines": [ - "node >=0.2.0" - ] - }, "node_modules/levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", @@ -4445,26 +4353,11 @@ "node": ">=8" } }, - "node_modules/locutus": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/locutus/-/locutus-2.0.14.tgz", - "integrity": "sha512-0H1o1iHNEp3kJ5rW57bT/CAP5g6Qm0Zd817Wcx2+rOMTYyIJoc482Ja1v9dB6IUjwvWKcBNdYi7x2lRXtlJ3bA==", - "dependencies": { - "es6-promise": "^4.2.5" - }, - "engines": { - "node": ">= 0.12.0" - } - }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lower-case": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", - "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=" + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true }, "node_modules/lru-cache": { "version": "6.0.0", @@ -4477,14 +4370,6 @@ "node": ">=10" } }, - "node_modules/luxon": { - "version": "1.26.0", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.26.0.tgz", - "integrity": "sha512-+V5QIQ5f6CDXQpWNICELwjwuHdqeJM1UenlZWx5ujcRMc9venvluCjFb4t5NYLhb6IhkbMVOxzVuOqkgMxee2A==", - "engines": { - "node": "*" - } - }, "node_modules/make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -4538,11 +4423,6 @@ "node": ">= 0.6" } }, - "node_modules/merge": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.1.tgz", - "integrity": "sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ==" - }, "node_modules/merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -4741,14 +4621,6 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, - "node_modules/no-case": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", - "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", - "dependencies": { - "lower-case": "^1.1.1" - } - }, "node_modules/node-abi": { "version": "2.26.0", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.26.0.tgz", @@ -4996,14 +4868,6 @@ "node": ">=0.10.0" } }, - "node_modules/object-hash": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.3.1.tgz", - "integrity": "sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA==", - "engines": { - "node": ">= 0.10.0" - } - }, "node_modules/object-visit": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", @@ -5079,14 +4943,6 @@ "node": ">= 0.8.0" } }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/p-each-series": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", @@ -5144,17 +5000,6 @@ "node": ">=6" } }, - "node_modules/pad": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pad/-/pad-2.3.0.tgz", - "integrity": "sha512-lxrgnOG5AXmzMRT1O5urWtYFxHnFSE+QntgTHij1nvS4W+ubhQLmQRHmZXDeEvk9I00itAixLqU9Q6fE0gW3sw==", - "dependencies": { - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -5522,11 +5367,6 @@ "node": ">=0.10.0" } }, - "node_modules/regex-parser": { - "version": "2.2.11", - "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", - "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==" - }, "node_modules/remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", @@ -5749,14 +5589,6 @@ "node": "6.* || >= 7.*" } }, - "node_modules/runes": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/runes/-/runes-0.4.3.tgz", - "integrity": "sha512-K6p9y4ZyL9wPzA+PMDloNQPfoDGTiFYDvdlXznyGKgD10BJpcAosvATKrExRKOrNLgD8E7Um7WGW0lxsnOuNLg==", - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -6304,14 +6136,6 @@ "node": ">=8" } }, - "node_modules/snake-case": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-2.1.0.tgz", - "integrity": "sha1-Qb2xtz8w7GagTU4srRt2OH1NbZ8=", - "dependencies": { - "no-case": "^2.2.0" - } - }, "node_modules/snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -6498,6 +6322,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -6969,17 +6794,6 @@ "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", "dev": true }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, "node_modules/tmpl": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", @@ -7097,70 +6911,6 @@ "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", "dev": true }, - "node_modules/twig-lexer": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/twig-lexer/-/twig-lexer-0.7.2.tgz", - "integrity": "sha512-c+SyqPvjH1fDXIdW9E6oWMNGGB0f5Ua64ggEh/3AGUEIImkAGTzVdOY09qLaK1NhLCUSfT/JEjr8VnZbOaZDjg==", - "dependencies": { - "@types/node": "^12.0.8" - } - }, - "node_modules/twig-lexer/node_modules/@types/node": { - "version": "12.20.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.11.tgz", - "integrity": "sha512-gema+apZ6qLQK7k7F0dGkGCWQYsL0qqKORWOQO6tq46q+x+1C0vbOiOqOwRVlh4RAdbQwV/j/ryr3u5NOG1fPQ==" - }, - "node_modules/twing": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/twing/-/twing-5.0.2.tgz", - "integrity": "sha512-uyOnD+KUTH+Ddbs21/KVJJI8DFhlDLNJLEU5UrudGS1W1qLyyCho9HxSus9LUzIjYRYNifXiQnlYwzi2aWI0Pg==", - "dependencies": { - "@types/luxon": "^1.4.0", - "camelcase": "^4.1.0", - "capitalize": "^1.0.0", - "crypto-js": "^3.1.9-1", - "esrever": "^0.2.0", - "fs-extra": "^5.0.0", - "htmlspecialchars": "^1.0.5", - "iconv-lite": "^0.4.19", - "is-integer": "^1.0.7", - "is-number": "^5.0.0", - "is-plain-object": "^2.0.4", - "isobject": "^3.0.1", - "levenshtein": "^1.0.5", - "locutus": "^2.0.11", - "luxon": "^1.19.3", - "merge": "^1.2.1", - "object-hash": "^1.2.0", - "pad": "^2.0.3", - "regex-parser": "^2.2.8", - "runes": "^0.4.3", - "snake-case": "^2.1.0", - "source-map": "^0.6.1", - "tmp": "0.0.33", - "twig-lexer": "^0.7.2", - "utf8-binary-cutter": "^0.9.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/twing/node_modules/camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "engines": { - "node": ">=4" - } - }, - "node_modules/twing/node_modules/is-number": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-5.0.0.tgz", - "integrity": "sha512-LmVHHP5dTJwrwZg2Jjqp7K5jpvcnYvYD1LMpvGadMsMv5+WXoDSLBQ0+zmuBJmuZGh2J2K845ygj/YukxUnr4A==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", @@ -7248,6 +6998,7 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, "engines": { "node": ">= 4.0.0" } @@ -7339,14 +7090,6 @@ "node": ">=0.10.0" } }, - "node_modules/utf8-binary-cutter": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/utf8-binary-cutter/-/utf8-binary-cutter-0.9.2.tgz", - "integrity": "sha512-lS/2TaA9idsyafus4+WaB+C/AfL3JD85C/sgMJBpplZay1G5SwTQcxmd4jiJLI1VxSJr6a3yuNicBxD+iU2MKQ==", - "dependencies": { - "lodash": "^4.17.10" - } - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -7455,14 +7198,6 @@ "makeerror": "1.0.x" } }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", - "dependencies": { - "defaults": "^1.0.3" - } - }, "node_modules/webidl-conversions": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", @@ -8474,11 +8209,6 @@ "@types/istanbul-lib-report": "*" } }, - "@types/luxon": { - "version": "1.26.5", - "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-1.26.5.tgz", - "integrity": "sha512-XeQxxRMyJi1znfzHw4CGDLyup/raj84SnjjkI2fDootZPGlB0yqtvlvEIAmzHDa5wiEI5JJevZOWxpcofsaV+A==" - }, "@types/node": { "version": "15.0.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.1.tgz", @@ -9024,11 +8754,6 @@ "integrity": "sha512-c0yixVG4v9KBc/tQ2rlbB3A/bgBFRvl8h8M4IeUbqCca4gsiCfvtaheUssbnux/Mb66Vjz7x8yYjDgYcNQOhyQ==", "dev": true }, - "capitalize": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/capitalize/-/capitalize-1.0.0.tgz", - "integrity": "sha1-3IAsWAruEBkpAg0soUtMqKCuRL4=" - }, "capture-exit": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", @@ -9187,11 +8912,6 @@ } } }, - "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=" - }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -9392,11 +9112,6 @@ "which": "^2.0.1" } }, - "crypto-js": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.3.0.tgz", - "integrity": "sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q==" - }, "cssom": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", @@ -9491,14 +9206,6 @@ "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", "dev": true }, - "defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", - "requires": { - "clone": "^1.0.2" - } - }, "define-property": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", @@ -9628,11 +9335,6 @@ "is-arrayish": "^0.2.1" } }, - "es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" - }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -9669,11 +9371,6 @@ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, - "esrever": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/esrever/-/esrever-0.2.0.tgz", - "integrity": "sha1-lunSj08bGnZ4TNXUkOquAQ50B7g=" - }, "estraverse": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", @@ -10053,16 +9750,6 @@ "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" }, - "fs-extra": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz", - "integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==", - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -10182,7 +9869,8 @@ "graceful-fs": { "version": "4.2.6", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", + "dev": true }, "growly": { "version": "1.3.0", @@ -10300,11 +9988,6 @@ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, - "htmlspecialchars": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/htmlspecialchars/-/htmlspecialchars-1.0.5.tgz", - "integrity": "sha1-9DD4wdXzcJvnvrrqaW3ASCQwajA=" - }, "http-errors": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", @@ -10471,11 +10154,6 @@ "is-plain-object": "^2.0.4" } }, - "is-finite": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", - "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==" - }, "is-fullwidth-code-point": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", @@ -10490,14 +10168,6 @@ "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", "dev": true }, - "is-integer": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-integer/-/is-integer-1.0.7.tgz", - "integrity": "sha1-a96Bqs3feLZZtmKdYpytxRqIbVw=", - "requires": { - "is-finite": "^1.0.0" - } - }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -10508,6 +10178,7 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, "requires": { "isobject": "^3.0.1" } @@ -10560,7 +10231,8 @@ "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true }, "isstream": { "version": "0.1.2", @@ -11175,14 +10847,6 @@ "minimist": "^1.2.5" } }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "requires": { - "graceful-fs": "^4.1.6" - } - }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -11213,11 +10877,6 @@ "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", "dev": true }, - "levenshtein": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/levenshtein/-/levenshtein-1.0.5.tgz", - "integrity": "sha1-ORFzepy1baNF0Aj1V4LG8TiXm6M=" - }, "levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", @@ -11243,23 +10902,11 @@ "p-locate": "^4.1.0" } }, - "locutus": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/locutus/-/locutus-2.0.14.tgz", - "integrity": "sha512-0H1o1iHNEp3kJ5rW57bT/CAP5g6Qm0Zd817Wcx2+rOMTYyIJoc482Ja1v9dB6IUjwvWKcBNdYi7x2lRXtlJ3bA==", - "requires": { - "es6-promise": "^4.2.5" - } - }, "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "lower-case": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", - "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=" + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true }, "lru-cache": { "version": "6.0.0", @@ -11269,11 +10916,6 @@ "yallist": "^4.0.0" } }, - "luxon": { - "version": "1.26.0", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.26.0.tgz", - "integrity": "sha512-+V5QIQ5f6CDXQpWNICELwjwuHdqeJM1UenlZWx5ujcRMc9venvluCjFb4t5NYLhb6IhkbMVOxzVuOqkgMxee2A==" - }, "make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -11312,11 +10954,6 @@ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, - "merge": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.1.tgz", - "integrity": "sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ==" - }, "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -11470,14 +11107,6 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, - "no-case": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", - "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", - "requires": { - "lower-case": "^1.1.1" - } - }, "node-abi": { "version": "2.26.0", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.26.0.tgz", @@ -11684,11 +11313,6 @@ } } }, - "object-hash": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.3.1.tgz", - "integrity": "sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA==" - }, "object-visit": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", @@ -11746,11 +11370,6 @@ "word-wrap": "~1.2.3" } }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" - }, "p-each-series": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", @@ -11787,14 +11406,6 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, - "pad": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pad/-/pad-2.3.0.tgz", - "integrity": "sha512-lxrgnOG5AXmzMRT1O5urWtYFxHnFSE+QntgTHij1nvS4W+ubhQLmQRHmZXDeEvk9I00itAixLqU9Q6fE0gW3sw==", - "requires": { - "wcwidth": "^1.0.1" - } - }, "parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -12079,11 +11690,6 @@ "safe-regex": "^1.1.0" } }, - "regex-parser": { - "version": "2.2.11", - "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", - "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==" - }, "remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", @@ -12250,11 +11856,6 @@ "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", "dev": true }, - "runes": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/runes/-/runes-0.4.3.tgz", - "integrity": "sha512-K6p9y4ZyL9wPzA+PMDloNQPfoDGTiFYDvdlXznyGKgD10BJpcAosvATKrExRKOrNLgD8E7Um7WGW0lxsnOuNLg==" - }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -12695,14 +12296,6 @@ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, - "snake-case": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-2.1.0.tgz", - "integrity": "sha1-Qb2xtz8w7GagTU4srRt2OH1NbZ8=", - "requires": { - "no-case": "^2.2.0" - } - }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -12853,7 +12446,8 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true }, "source-map-resolve": { "version": "0.5.3", @@ -13223,14 +12817,6 @@ "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", "dev": true }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "requires": { - "os-tmpdir": "~1.0.2" - } - }, "tmpl": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", @@ -13323,65 +12909,6 @@ "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", "dev": true }, - "twig-lexer": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/twig-lexer/-/twig-lexer-0.7.2.tgz", - "integrity": "sha512-c+SyqPvjH1fDXIdW9E6oWMNGGB0f5Ua64ggEh/3AGUEIImkAGTzVdOY09qLaK1NhLCUSfT/JEjr8VnZbOaZDjg==", - "requires": { - "@types/node": "^12.0.8" - }, - "dependencies": { - "@types/node": { - "version": "12.20.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.11.tgz", - "integrity": "sha512-gema+apZ6qLQK7k7F0dGkGCWQYsL0qqKORWOQO6tq46q+x+1C0vbOiOqOwRVlh4RAdbQwV/j/ryr3u5NOG1fPQ==" - } - } - }, - "twing": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/twing/-/twing-5.0.2.tgz", - "integrity": "sha512-uyOnD+KUTH+Ddbs21/KVJJI8DFhlDLNJLEU5UrudGS1W1qLyyCho9HxSus9LUzIjYRYNifXiQnlYwzi2aWI0Pg==", - "requires": { - "@types/luxon": "^1.4.0", - "camelcase": "^4.1.0", - "capitalize": "^1.0.0", - "crypto-js": "^3.1.9-1", - "esrever": "^0.2.0", - "fs-extra": "^5.0.0", - "htmlspecialchars": "^1.0.5", - "iconv-lite": "^0.4.19", - "is-integer": "^1.0.7", - "is-number": "^5.0.0", - "is-plain-object": "^2.0.4", - "isobject": "^3.0.1", - "levenshtein": "^1.0.5", - "locutus": "^2.0.11", - "luxon": "^1.19.3", - "merge": "^1.2.1", - "object-hash": "^1.2.0", - "pad": "^2.0.3", - "regex-parser": "^2.2.8", - "runes": "^0.4.3", - "snake-case": "^2.1.0", - "source-map": "^0.6.1", - "tmp": "0.0.33", - "twig-lexer": "^0.7.2", - "utf8-binary-cutter": "^0.9.2" - }, - "dependencies": { - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" - }, - "is-number": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-5.0.0.tgz", - "integrity": "sha512-LmVHHP5dTJwrwZg2Jjqp7K5jpvcnYvYD1LMpvGadMsMv5+WXoDSLBQ0+zmuBJmuZGh2J2K845ygj/YukxUnr4A==" - } - } - }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", @@ -13449,7 +12976,8 @@ "universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true }, "unpipe": { "version": "1.0.0", @@ -13523,14 +13051,6 @@ "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", "dev": true }, - "utf8-binary-cutter": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/utf8-binary-cutter/-/utf8-binary-cutter-0.9.2.tgz", - "integrity": "sha512-lS/2TaA9idsyafus4+WaB+C/AfL3JD85C/sgMJBpplZay1G5SwTQcxmd4jiJLI1VxSJr6a3yuNicBxD+iU2MKQ==", - "requires": { - "lodash": "^4.17.10" - } - }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -13620,14 +13140,6 @@ "makeerror": "1.0.x" } }, - "wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", - "requires": { - "defaults": "^1.0.3" - } - }, "webidl-conversions": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", diff --git a/package.json b/package.json index 91f2dcf..2a30f87 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,6 @@ "image-size": "^0.9.3", "multer": "^1.4.2", "sharp": "^0.28.1", - "twing": "^5.0.2", "ws": "^7.3.1" }, "devDependencies": { diff --git a/public/Communication.js b/public/Communication.js index 82a27ae..05f46a3 100644 --- a/public/Communication.js +++ b/public/Communication.js @@ -17,10 +17,10 @@ function send(message) { let clientSeq let events -function connect(gameId, clientId) { +function connect(address, gameId, clientId) { clientSeq = 0 events = {} - conn = new WsClient(WS_ADDRESS, clientId + '|' + gameId) + conn = new WsClient(address, clientId + '|' + gameId) return new Promise(resolve => { conn.connect() conn.onSocket('message', async ({ data }) => { @@ -46,10 +46,10 @@ function connect(gameId, clientId) { }) } -function connectReplay(gameId, clientId) { +function connectReplay(address, gameId, clientId) { clientSeq = 0 events = {} - conn = new WsClient(WS_ADDRESS, clientId + '|' + gameId) + conn = new WsClient(address, clientId + '|' + gameId) return new Promise(resolve => { conn.connect() conn.onSocket('message', async ({ data }) => { @@ -67,6 +67,14 @@ function connectReplay(gameId, clientId) { }) } +function disconnect() { + if (conn) { + conn.disconnect() + } + clientSeq = 0 + events = {} +} + function sendClientEvent(evt) { // when sending event, increase number of sent events // and add the event locally @@ -78,6 +86,7 @@ function sendClientEvent(evt) { export default { connect, connectReplay, + disconnect, onServerChange, sendClientEvent, } diff --git a/public/WsWrapper.js b/public/WsWrapper.js index f85ddb4..8616ed2 100644 --- a/public/WsWrapper.js +++ b/public/WsWrapper.js @@ -2,6 +2,8 @@ import Time from '../common/Time.js' +const CODE_CUSTOM_DISCONNECT = 4000 + /** * Wrapper around ws that * - buffers 'send' until a connection is available @@ -57,8 +59,16 @@ export default class WsWrapper { } ws.onclose = (e) => { this.handle = null - this.reconnectTimeout = setTimeout(() => { this.connect() }, 1 * Time.SEC) + if (e.code !== CODE_CUSTOM_DISCONNECT) { + this.reconnectTimeout = setTimeout(() => { this.connect() }, 1 * Time.SEC) + } this.onclose(e) } } + + disconnect() { + if (this.handle) { + this.handle.close(CODE_CUSTOM_DISCONNECT) + } + } } diff --git a/public/components/GameTeaser.vue.js b/public/components/GameTeaser.vue.js index 41b198f..4882625 100644 --- a/public/components/GameTeaser.vue.js +++ b/public/components/GameTeaser.vue.js @@ -2,23 +2,23 @@ import Time from './../../common/Time.js' -const GameTeaser = { +export default { name: 'game-teaser', props: { game: Object, }, template: `
- + 🧩 {{game.tilesFinished}}/{{game.tilesTotal}}
πŸ‘₯ {{game.players}}
{{time(game.started, game.finished)}}
-
- + + β†ͺ️ Watch replay - +
`, computed: { style() { @@ -38,5 +38,3 @@ const GameTeaser = { }, }, } - -export default GameTeaser diff --git a/public/components/HelpOverlay.vue.js b/public/components/HelpOverlay.vue.js new file mode 100644 index 0000000..79f77f2 --- /dev/null +++ b/public/components/HelpOverlay.vue.js @@ -0,0 +1,26 @@ +"use strict" + +// ingame component +// shows the help (key bindings) + +export default { + name: 'help-overlay', + template: `
+ + + + + + + + + + + + +
⬆️ Move up:
W/↑/πŸ–±οΈ
⬇️ Move down:
S/↓/πŸ–±οΈ
⬅️ Move left:
A/←/πŸ–±οΈ
➑️ Move right:
D/β†’/πŸ–±οΈ
Move faster by holding Shift
πŸ”+ Zoom in:
E/πŸ–±οΈ-Wheel
πŸ”- Zoom out:
Q/πŸ–±οΈ-Wheel
πŸ–ΌοΈ Toggle preview:
Space
πŸ§©βœ”οΈ Toggle fixed pieces:
F
πŸ§©β“ Toggle loose pieces:
G
+
`, + emits: { + bgclick: null, + }, +} diff --git a/public/components/ImageTeaser.vue.js b/public/components/ImageTeaser.vue.js index 729c223..54b113a 100644 --- a/public/components/ImageTeaser.vue.js +++ b/public/components/ImageTeaser.vue.js @@ -1,6 +1,6 @@ "use strict" -const ImageTeaser = { +export default { name: 'image-teaser', props: { image: Object @@ -20,5 +20,3 @@ const ImageTeaser = { }, }, } - -export default ImageTeaser diff --git a/public/components/PreviewOverlay.vue.js b/public/components/PreviewOverlay.vue.js new file mode 100644 index 0000000..4b6f430 --- /dev/null +++ b/public/components/PreviewOverlay.vue.js @@ -0,0 +1,27 @@ +"use strict" + +// ingame component +// shows the preview image + +export default { + name: 'preview-overlay', + template: ` +
+
+
+
+
`, + props: { + img: String, + }, + emits: { + bgclick: null, + }, + computed: { + previewStyle () { + return { + backgroundImage: `url('${this.img}')`, + } + }, + }, +} diff --git a/public/components/PuzzleStatus.vue.js b/public/components/PuzzleStatus.vue.js new file mode 100644 index 0000000..98fc93e --- /dev/null +++ b/public/components/PuzzleStatus.vue.js @@ -0,0 +1,36 @@ +"use strict" + +import Time from './../../common/Time.js' + +// ingame component +// shows timer, tiles left, etc.. +// maybe split it up later + +export default { + name: 'puzzle-status', + template: ` +
+
+ 🧩 {{piecesDone}}/{{piecesTotal}} +
+
+ {{icon}} {{durationStr}} +
+ +
+ `, + props: { + finished: Boolean, + duration: Number, + piecesDone: Number, + piecesTotal: Number, + }, + computed: { + icon () { + return this.finished ? '🏁' : '⏳' + }, + durationStr () { + return Time.durationStr(this.duration) + }, + } +} diff --git a/public/components/Scores.vue.js b/public/components/Scores.vue.js new file mode 100644 index 0000000..acba211 --- /dev/null +++ b/public/components/Scores.vue.js @@ -0,0 +1,41 @@ +"use strict" + +// ingame component +// shows player scores + +export default { + name: "scores", + template: ` +
+
Scores
+ + + + + + + + + + + +
⚑{{p.name}}{{p.points}}
πŸ’€{{p.name}}{{p.points}}
+
+ `, + computed: { + actives () { + // TODO: dont sort in place + this.activePlayers.sort((a, b) => b.points - a.points) + return this.activePlayers + }, + idles () { + // TODO: dont sort in place + this.idlePlayers.sort((a, b) => b.points - a.points) + return this.idlePlayers + }, + }, + props: { + activePlayers: Array, + idlePlayers: Array, + }, +} diff --git a/public/components/SettingsOverlay.vue.js b/public/components/SettingsOverlay.vue.js new file mode 100644 index 0000000..1cf8962 --- /dev/null +++ b/public/components/SettingsOverlay.vue.js @@ -0,0 +1,38 @@ +"use strict" + +// ingame component +// allows to change (player) settings + +export default { + name: 'settings-overlay', + template: ` +
+ + + + + + + + + + + + + +
+
+ `, + emits: { + bgclick: null, + 'update:modelValue': null, + }, + props: { + modelValue: Object, + }, + created () { + this.$watch('modelValue', val => { + this.$emit('update:modelValue', val) + }, { deep: true }) + }, +} diff --git a/public/components/Upload.vue.js b/public/components/Upload.vue.js index 83acea0..6a37fd6 100644 --- a/public/components/Upload.vue.js +++ b/public/components/Upload.vue.js @@ -1,6 +1,6 @@ "use strict" -const Upload = { +export default { name: 'upload', props: { accept: String, @@ -27,5 +27,3 @@ const Upload = { }, } } - -export default Upload diff --git a/public/game.js b/public/game.js index d9f564f..68f7d87 100644 --- a/public/game.js +++ b/public/game.js @@ -12,8 +12,8 @@ import fireworksController from './Fireworks.js' import Protocol from '../common/Protocol.js' import Time from '../common/Time.js' -const MODE_PLAY = 'play' -const MODE_REPLAY = 'replay' +export const MODE_PLAY = 'play' +export const MODE_REPLAY = 'replay' let PIECE_VIEW_FIXED = true let PIECE_VIEW_LOOSE = true @@ -39,190 +39,6 @@ function addCanvasToDom(TARGET_EL, canvas) { return canvas } -function addMenuToDom(TARGET_EL, previewImageUrl, setHotkeys) { - const ELEMENTS = {} - const h = (type, props, children) => { - if (typeof props === 'string' || typeof props === 'number') { - children = [props] - props = {} - } - if (props instanceof Array) { - children = props - props = {} - } - if (children && !(children instanceof Array)) { - children = [children] - } - if (!ELEMENTS[type]) { - ELEMENTS[type] = document.createElement(type) - } - const el = ELEMENTS[type].cloneNode(true) - if (children) { - for (const child of children) { - if (typeof child === 'string' || typeof child === 'number') { - el.appendChild(document.createTextNode(child)) - } else { - el.appendChild(child) - } - } - } - if (props) { - for (const k in props) { - if (k === 'click') { - el.addEventListener('click', props[k]) - } else if (k === 'cls') { - if (typeof props[k] === 'string') { - el.classList.add(props[k]) - } else { - for (const cls of props[k]) { - el.classList.add(cls) - } - } - } else if (k === 'style') { - for (const s in props[k]) { - el.style[s] = props[k][s] - } - } else { - el[k] = props[k] - } - } - } - return el - } - - const row = (props, children) => { - if (props instanceof Array) { - children = props - props = {} - } - return h('tr', props, children.map(el => h('td', {}, el))) - } - const bgColorPickerEl = h('input', { type: 'color' }) - const playerColorPickerEl = h('input', { type: 'color' }) - const nameChangeEl = h('input', { type: 'text', maxLength: 16 }) - - const stopProp = (e) => e.stopPropagation() - const mkToggler = (el, hotkeys = true) => () => toggle(el, hotkeys) - const mkSelfToggler = (hotkeys = true) => (ev) => toggle(ev.target, hotkeys) - const toggle = (el, hotkeys = true) => { - el.classList.toggle('closed') - if (hotkeys) { - setHotkeys(el.classList.contains('closed')) - } - } - - const helpOverlay = h( - 'div', - { cls: ['overlay', 'transparent', 'closed'], click: mkSelfToggler() }, - [ - h('table', { cls: 'help', click: stopProp }, [ - row(['⬆️ Move up:', h('div', [h('kbd', 'W'), '/', h('kbd', '↑'), '/πŸ–±οΈ'])]), - row(['⬇️ Move down:', h('div', [h('kbd', 'S'), '/', h('kbd', '↓'), '/πŸ–±οΈ'])]), - row(['⬅️ Move left:', h('div', [h('kbd', 'A'), '/', h('kbd', '←'), '/πŸ–±οΈ'])]), - row(['➑️ Move right:', h('div', [h('kbd', 'D'), '/', h('kbd', 'β†’'), '/πŸ–±οΈ'])]), - row(['', h('div', ['Move faster by holding ', h('kbd', 'Shift')])]), - row(['πŸ”+ Zoom in:', h('div', [h('kbd', 'E'), '/πŸ–±οΈ-Wheel'])]), - row(['πŸ”- Zoom out:', h('div', [h('kbd', 'Q'), '/πŸ–±οΈ-Wheel'])]), - row(['πŸ–ΌοΈ Toggle preview:', h('div', [h('kbd', 'Space')])]), - row(['πŸ§©βœ”οΈ Toggle fixed pieces:', h('div', [h('kbd', 'F')])]), - row(['πŸ§©β“ Toggle loose pieces:', h('div', [h('kbd', 'G')])]), - ]), - ] - ) - - const settingsOverlay = h( - 'div', - { cls: ['overlay', 'transparent', 'closed'], click: mkSelfToggler() }, - [ - h('table', { cls: 'settings', click: stopProp }, [ - row([h('label', 'Background: '), bgColorPickerEl]), - row([h('label', 'Color: '), playerColorPickerEl]), - row([h('label', 'Name: '), nameChangeEl]), - ]), - ] - ) - - const previewOverlay = h( - 'div', - { cls: ['overlay', 'closed'], click: mkSelfToggler() }, - [ - h('div', { cls: 'preview' }, [ - h('div', { cls: 'img', style: { backgroundImage: `url('${previewImageUrl}')` } }) - ]) - ] - ) - - const scoresListEl = h('table') - const updateScoreBoard = (players, ts) => { - const minTs = ts - 30 * Time.SEC - const actives = players.filter(player => player.ts >= minTs) - const nonActives = players.filter(player => player.ts < minTs) - - actives.sort((a, b) => b.points - a.points) - nonActives.sort((a, b) => b.points - a.points) - - const _row = (icon, player) => row( - { style: { color: player.color} }, - [icon, player.name, player.points] - ) - - scoresListEl.innerHTML = '' - actives.forEach(player => scoresListEl.appendChild(_row('⚑', player))) - nonActives.forEach(player => scoresListEl.appendChild(_row('πŸ’€', player))) - } - - const timerCountdownEl = h('div') - const updateTimer = (from, ended, ts) => { - const icon = ended ? '🏁' : '⏳' - const timeDiffStr = Time.timeDiffStr(from, ended || ts) - timerCountdownEl.innerText = `${icon} ${timeDiffStr}` - } - const tilesDoneEl = h('div') - const udateTilesDone = (finished, total) => { - tilesDoneEl.innerText = `🧩 ${finished}/${total}` - } - - const timerEl = h('div', { cls: 'timer' }, [tilesDoneEl, timerCountdownEl]) - - let replayControl = null - if (MODE === MODE_REPLAY) { - const speedUp = h('button', { cls: 'btn' }, ['⏫']) - const speedDown = h('button', { cls: 'btn' }, ['⏬']) - const pause = h('button', { cls: 'btn' }, ['⏸️']) - const speed = h('div') - timerEl.appendChild(h('div', [ speed, speedUp, speedDown, pause ])) - replayControl = { speedUp, speedDown, pause, speed } - } - - TARGET_EL.appendChild(settingsOverlay) - TARGET_EL.appendChild(previewOverlay) - TARGET_EL.appendChild(helpOverlay) - TARGET_EL.appendChild(timerEl) - TARGET_EL.appendChild(h('div', { cls: 'menu' }, [ - h('div', { cls: 'tabs' }, [ - h('a', { cls: 'opener', target: '_blank', href: '/' }, '🧩 Puzzles'), - h('div', { cls: 'opener', click: mkToggler(previewOverlay, false) }, 'πŸ–ΌοΈ Preview'), - h('div', { cls: 'opener', click: mkToggler(settingsOverlay) }, 'πŸ› οΈ Settings'), - h('div', { cls: 'opener', click: mkToggler(helpOverlay) }, 'ℹ️ Help'), - ]) - ])) - TARGET_EL.appendChild(h('div', { cls: 'scores' }, [ - h('div', 'Scores'), - scoresListEl, - ])) - - return { - bgColorPickerEl, - playerColorPickerEl, - nameChangeEl, - updateScoreBoard, - updateTimer, - udateTilesDone, - togglePreview: () => toggle(previewOverlay, false), - replayControl, - } -} - function initme() { let ID = localStorage.getItem('ID') if (!ID) { @@ -359,14 +175,13 @@ function EventAdapter (canvas, window, viewport) { } } -async function main(TARGET_EL) { - if (typeof GAME_ID === 'undefined') throw '[ GAME_ID not set ]' - if (typeof WS_ADDRESS === 'undefined') throw '[ WS_ADDRESS not set ]' +export async function main(gameId, wsAddress, MODE, TARGET_EL, HUD) { + if (typeof gameId === 'undefined') throw '[ gameId not set ]' + if (typeof wsAddress === 'undefined') throw '[ wsAddress not set ]' if (typeof MODE === 'undefined') throw '[ MODE not set ]' if (typeof DEBUG === 'undefined') window.DEBUG = false - const gameId = GAME_ID const CLIENT_ID = initme() const shouldDrawPlayerText = (player) => { return MODE === MODE_REPLAY || player.id !== CLIENT_ID @@ -412,12 +227,12 @@ async function main(TARGET_EL) { let TIME if (MODE === MODE_PLAY) { - const game = await Communication.connect(gameId, CLIENT_ID) + const game = await Communication.connect(wsAddress, gameId, CLIENT_ID) const gameObject = Util.decodeGame(game) Game.setGame(gameObject.id, gameObject) TIME = () => Time.timestamp() } else if (MODE === MODE_REPLAY) { - const {game, log} = await Communication.connectReplay(gameId, CLIENT_ID) + const {game, log} = await Communication.connectReplay(wsAddress, gameId, CLIENT_ID) const gameObject = Util.decodeGame(game) Game.setGame(gameObject.id, gameObject) REPLAY.log = log @@ -468,25 +283,33 @@ async function main(TARGET_EL) { const evts = new EventAdapter(canvas, window, viewport) - const { - bgColorPickerEl, - playerColorPickerEl, - nameChangeEl, - updateScoreBoard, - updateTimer, - udateTilesDone, - togglePreview, - replayControl, - } = addMenuToDom(TARGET_EL, Game.getImageUrl(gameId), evts.setHotkeys) + const previewImageUrl = Game.getImageUrl(gameId) + const activePlayers = (gameId, ts) => { + const minTs = ts - 30 * Time.SEC + const players = Game.getRelevantPlayers(gameId, ts) + return players.filter(player => player.ts >= minTs) + } + const idlePlayers = (gameId, ts) => { + const minTs = ts - 30 * Time.SEC + const players = Game.getRelevantPlayers(gameId, ts) + return players.filter(player => player.ts < minTs) + } const updateTimerElements = () => { - updateTimer(Game.getStartTs(gameId), Game.getFinishTs(gameId), TIME()) + const startTs = Game.getStartTs(gameId) + const finishTs = Game.getFinishTs(gameId) + const ts = TIME() + + HUD.setFinished(!!(finishTs)) + HUD.setDuration((finishTs || ts) - startTs) } updateTimerElements() - udateTilesDone(Game.getFinishedTileCount(gameId), Game.getTileCount(gameId)) + HUD.setPiecesDone(Game.getFinishedTileCount(gameId)) + HUD.setPiecesTotal(Game.getTileCount(gameId)) const ts = TIME() - updateScoreBoard(Game.getRelevantPlayers(gameId, ts), ts) + HUD.setActivePlayers(activePlayers(gameId, ts)) + HUD.setIdlePlayers(idlePlayers(gameId, ts)) const longFinished = !! Game.getFinishTs(gameId) let finished = longFinished @@ -508,49 +331,45 @@ async function main(TARGET_EL) { || 'anon') } + const onBgChange = (value) => { + localStorage.setItem('bg_color', value) + evts.addEvent([Protocol.INPUT_EV_BG_COLOR, value]) + } + const onColorChange = (value) => { + localStorage.setItem('player_color', value) + evts.addEvent([Protocol.INPUT_EV_PLAYER_COLOR, value]) + } + const onNameChange = (value) => { + localStorage.setItem('player_name', value) + evts.addEvent([Protocol.INPUT_EV_PLAYER_NAME, value]) + } + + const doSetSpeedStatus = () => { + HUD.setReplaySpeed(REPLAY.speeds[REPLAY.speedIdx]) + HUD.setReplayPaused(REPLAY.paused) + } + + const replayOnSpeedUp = () => { + if (REPLAY.speedIdx + 1 < REPLAY.speeds.length) { + REPLAY.speedIdx++ + doSetSpeedStatus() + } + } + const replayOnSpeedDown = () => { + if (REPLAY.speedIdx >= 1) { + REPLAY.speedIdx-- + doSetSpeedStatus() + } + } + const replayOnPauseToggle = () => { + REPLAY.paused = !REPLAY.paused + doSetSpeedStatus() + } + if (MODE === MODE_PLAY) { - bgColorPickerEl.value = playerBgColor() - evts.addEvent([Protocol.INPUT_EV_BG_COLOR, bgColorPickerEl.value]) - bgColorPickerEl.addEventListener('change', () => { - localStorage.setItem('bg_color', bgColorPickerEl.value) - evts.addEvent([Protocol.INPUT_EV_BG_COLOR, bgColorPickerEl.value]) - }) - playerColorPickerEl.value = playerColor() - evts.addEvent([Protocol.INPUT_EV_PLAYER_COLOR, playerColorPickerEl.value]) - playerColorPickerEl.addEventListener('change', () => { - localStorage.setItem('player_color', playerColorPickerEl.value) - evts.addEvent([Protocol.INPUT_EV_PLAYER_COLOR, playerColorPickerEl.value]) - }) - nameChangeEl.value = playerName() - evts.addEvent([Protocol.INPUT_EV_PLAYER_NAME, nameChangeEl.value]) - nameChangeEl.addEventListener('change', () => { - localStorage.setItem('player_name', nameChangeEl.value) - evts.addEvent([Protocol.INPUT_EV_PLAYER_NAME, nameChangeEl.value]) - }) setInterval(updateTimerElements, 1000) } else if (MODE === MODE_REPLAY) { - const setSpeedStatus = () => { - replayControl.speed.innerText = 'Replay-Speed: ' + - (REPLAY.speeds[REPLAY.speedIdx] + 'x') + - (REPLAY.paused ? ' Paused' : '') - } - setSpeedStatus() - replayControl.speedUp.addEventListener('click', () => { - if (REPLAY.speedIdx + 1 < REPLAY.speeds.length) { - REPLAY.speedIdx++ - setSpeedStatus() - } - }) - replayControl.speedDown.addEventListener('click', () => { - if (REPLAY.speedIdx >= 1) { - REPLAY.speedIdx-- - setSpeedStatus() - } - }) - replayControl.pause.addEventListener('click', () => { - REPLAY.paused = !REPLAY.paused - setSpeedStatus() - }) + doSetSpeedStatus() } if (MODE === MODE_PLAY) { @@ -678,7 +497,7 @@ async function main(TARGET_EL) { Game.changePlayer(gameId, CLIENT_ID, pos) } } else if (type === Protocol.INPUT_EV_TOGGLE_PREVIEW) { - togglePreview() + HUD.togglePreview() } // LOCAL + SERVER CHANGES @@ -726,7 +545,7 @@ async function main(TARGET_EL) { RERENDER = true } } else if (type === Protocol.INPUT_EV_TOGGLE_PREVIEW) { - togglePreview() + HUD.togglePreview() } } } @@ -818,11 +637,12 @@ async function main(TARGET_EL) { if (DEBUG) Debug.checkpoint('players done') - // DRAW PLAYERS + // propagate HUD changes // --------------------------------------------------------------- - updateScoreBoard(Game.getRelevantPlayers(gameId, ts), ts) - udateTilesDone(Game.getFinishedTileCount(gameId), Game.getTileCount(gameId)) - if (DEBUG) Debug.checkpoint('scores done') + HUD.setActivePlayers(activePlayers(gameId, ts)) + HUD.setIdlePlayers(idlePlayers(gameId, ts)) + HUD.setPiecesDone(Game.getFinishedTileCount(gameId)) + if (DEBUG) Debug.checkpoint('HUD done') // --------------------------------------------------------------- if (justFinished()) { @@ -836,6 +656,23 @@ async function main(TARGET_EL) { update: onUpdate, render: onRender, }) -} -export default main + return { + setHotkeys: (state) => { + evts.setHotkeys(state) + }, + onBgChange, + onColorChange, + onNameChange, + replayOnSpeedUp, + replayOnSpeedDown, + replayOnPauseToggle, + previewImageUrl, + player: { + background: playerBgColor(), + color: playerColor(), + name: playerName(), + }, + disconnect: Communication.disconnect, + } +} diff --git a/public/index.html b/public/index.html index 94a1ac3..8647404 100644 --- a/public/index.html +++ b/public/index.html @@ -14,19 +14,32 @@ import Game from "/views/Game.vue.js" import Replay from "/views/Replay.vue.js" - const router = VueRouter.createRouter({ - history: VueRouter.createWebHashHistory(), - routes: [ - { name: 'index', path: '/', component: Index }, - { name: 'new-game', path: '/new-game', component: NewGame }, - { name: 'game', path: '/g/:id', component: Game }, - { name: 'replay', path: '/replay/:id', component: Replay }, - ], - }) + (async () => { + const res = await fetch(`/api/conf`) + const conf = await res.json() - const app = Vue.createApp(App) - app.use(router) - app.mount('#app') + const router = VueRouter.createRouter({ + history: VueRouter.createWebHashHistory(), + routes: [ + { name: 'index', path: '/', component: Index }, + { name: 'new-game', path: '/new-game', component: NewGame }, + { name: 'game', path: '/g/:id', component: Game }, + { name: 'replay', path: '/replay/:id', component: Replay }, + ], + }) + + router.beforeEach((to, from) => { + if (from.name !== undefined) { + document.documentElement.classList.remove(`view-${from.name}`) + } + document.documentElement.classList.add(`view-${to.name}`) + }) + + const app = Vue.createApp(App) + app.config.globalProperties.$config = conf + app.use(router) + app.mount('#app') + })() diff --git a/public/style.css b/public/style.css index 0ffc154..294e4a6 100644 --- a/public/style.css +++ b/public/style.css @@ -278,3 +278,10 @@ kbd { top: 0; right: 0; } + +html.view-game { overflow: hidden; } +html.view-game body { overflow: hidden; } + +html.view-replay { overflow: hidden; } +html.view-replay body { overflow: hidden; } +html.view-replay canvas { cursor: grab; } diff --git a/public/views/Game.vue.js b/public/views/Game.vue.js index 10fcf11..386b0d2 100644 --- a/public/views/Game.vue.js +++ b/public/views/Game.vue.js @@ -1,33 +1,118 @@ "use strict" -import main from './../game.js' +import Scores from './../components/Scores.vue.js' +import PuzzleStatus from './../components/PuzzleStatus.vue.js' +import SettingsOverlay from './../components/SettingsOverlay.vue.js' +import PreviewOverlay from './../components/PreviewOverlay.vue.js' +import HelpOverlay from './../components/HelpOverlay.vue.js' + +import { main, MODE_PLAY } from './../game.js' export default { name: 'game', - template: `
{{gameData}}
`, + components: { + PuzzleStatus, + Scores, + SettingsOverlay, + PreviewOverlay, + HelpOverlay, + }, + template: `
+ + + + + + + + + +
`, data() { return { - gameData: null, + activePlayers: [], + idlePlayers: [], + + finished: false, + duration: 0, + piecesDone: 0, + piecesTotal: 0, + + overlay: null, + + g: { + player: { + background: '', + color: '', + name: '', + }, + previewImageUrl: '', + setHotkeys: () => {}, + onBgChange: () => {}, + onColorChange: () => {}, + onNameChange: () => {}, + disconnect: () => {}, + }, } }, - created() { - this.$watch( - () => this.$route.params, - () => { this.fetchData() }, - { immediate: true } + async mounted() { + if (!this.$route.params.id) { + return + } + this.$watch(() => this.g.player.background, (value) => { + this.g.onBgChange(value) + }) + this.$watch(() => this.g.player.color, (value) => { + this.g.onColorChange(value) + }) + this.$watch(() => this.g.player.name, (value) => { + this.g.onNameChange(value) + }) + this.g = await main( + this.$route.params.id, + this.$config.WS_ADDRESS, + MODE_PLAY, + this.$el, + { + setActivePlayers: (v) => { this.activePlayers = v }, + setIdlePlayers: (v) => { this.idlePlayers = v }, + setFinished: (v) => { this.finished = v }, + setDuration: (v) => { this.duration = v }, + setPiecesDone: (v) => { this.piecesDone = v }, + setPiecesTotal: (v) => { this.piecesTotal = v }, + togglePreview: () => { this.toggle('preview', false) }, + } ) }, + unmounted () { + this.g.disconnect() + }, methods: { - async fetchData() { - this.gameData = null - const res = await fetch(`/api/game-data/${this.$route.params.id}`) - const json = await res.json() - this.gameData = json - - window.GAME_ID = this.gameData.GAME_ID - window.WS_ADDRESS = this.gameData.WS_ADDRESS - window.MODE = 'play' - main(this.$el) + toggle(overlay, affectsHotkeys) { + if (this.overlay === null) { + this.overlay = overlay + if (affectsHotkeys) { + this.g.setHotkeys(false) + } + } else { + // could check if overlay was the provided one + this.overlay = null + if (affectsHotkeys) { + this.g.setHotkeys(true) + } + } }, }, } diff --git a/public/views/Replay.vue.js b/public/views/Replay.vue.js index 62d1112..94e77e0 100644 --- a/public/views/Replay.vue.js +++ b/public/views/Replay.vue.js @@ -1,33 +1,142 @@ "use strict" -import main from './../game.js' +import Scores from './../components/Scores.vue.js' +import PuzzleStatus from './../components/PuzzleStatus.vue.js' +import SettingsOverlay from './../components/SettingsOverlay.vue.js' +import PreviewOverlay from './../components/PreviewOverlay.vue.js' +import HelpOverlay from './../components/HelpOverlay.vue.js' + +import { main, MODE_REPLAY } from './../game.js' export default { name: 'replay', - template: `
{{replayData}}
`, + components: { + PuzzleStatus, + Scores, + SettingsOverlay, + PreviewOverlay, + HelpOverlay, + }, + template: `
+ + + + + +
+
{{replayText}}
+ + + +
+
+ + + + +
`, data() { return { - replayData: null, + activePlayers: [], + idlePlayers: [], + + finished: false, + duration: 0, + piecesDone: 0, + piecesTotal: 0, + + overlay: null, + + g: { + player: { + background: '', + color: '', + name: '', + }, + previewImageUrl: '', + setHotkeys: () => {}, + onBgChange: () => {}, + onColorChange: () => {}, + onNameChange: () => {}, + replayOnSpeedUp: () => {}, + replayOnSpeedDown: () => {}, + replayOnPauseToggle: () => {}, + disconnect: () => {}, + }, + + replay: { + speed: 1, + paused: false, + }, } }, - created() { - this.$watch( - () => this.$route.params, - () => { this.fetchData() }, - { immediate: true } + async mounted() { + if (!this.$route.params.id) { + return + } + this.$watch(() => this.g.player.background, (value) => { + this.g.onBgChange(value) + }) + this.$watch(() => this.g.player.color, (value) => { + this.g.onColorChange(value) + }) + this.$watch(() => this.g.player.name, (value) => { + this.g.onNameChange(value) + }) + this.g = await main( + this.$route.params.id, + this.$config.WS_ADDRESS, + MODE_REPLAY, + this.$el, + { + setActivePlayers: (v) => { this.activePlayers = v }, + setIdlePlayers: (v) => { this.idlePlayers = v }, + setFinished: (v) => { this.finished = v }, + setDuration: (v) => { this.duration = v }, + setPiecesDone: (v) => { this.piecesDone = v }, + setPiecesTotal: (v) => { this.piecesTotal = v }, + togglePreview: () => { this.toggle('preview', false) }, + setReplaySpeed: (v) => { this.replay.speed = v }, + setReplayPaused: (v) => { this.replay.paused = v }, + } ) }, + unmounted () { + this.g.disconnect() + }, + computed: { + replayText () { + return 'Replay-Speed: ' + + (this.replay.speed + 'x') + + (this.replay.paused ? ' Paused' : '') + }, + }, methods: { - async fetchData() { - this.replayData = null - const res = await fetch(`/api/replay-data/${this.$route.params.id}`) - const json = await res.json() - this.replayData = json - - window.GAME_ID = this.gameData.GAME_ID - window.WS_ADDRESS = this.gameData.WS_ADDRESS - window.MODE = 'replay' - main(this.$el) + toggle(overlay, affectsHotkeys) { + if (this.overlay === null) { + this.overlay = overlay + if (affectsHotkeys) { + this.g.setHotkeys(false) + } + } else { + // could check if overlay was the provided one + this.overlay = null + if (affectsHotkeys) { + this.g.setHotkeys(true) + } + } }, }, } diff --git a/server/Dirs.js b/server/Dirs.js index 88595bf..00657fb 100644 --- a/server/Dirs.js +++ b/server/Dirs.js @@ -11,4 +11,3 @@ export const UPLOAD_DIR = `${BASE_DIR}/data/uploads` export const UPLOAD_URL = `/uploads` export const COMMON_DIR = `${BASE_DIR}/common/` export const PUBLIC_DIR = `${BASE_DIR}/public/` -export const TEMPLATE_DIR = `${BASE_DIR}/templates` diff --git a/server/index.js b/server/index.js index b1734c6..a9ea1d1 100644 --- a/server/index.js +++ b/server/index.js @@ -6,7 +6,6 @@ import config from './../config.js' import Protocol from './../common/Protocol.js' import Util, { logger } from './../common/Util.js' import Game from './Game.js' -import twing from 'twing' import bodyParser from 'body-parser' import v8 from 'v8' import GameLog from './GameLog.js' @@ -18,7 +17,6 @@ import { UPLOAD_URL, COMMON_DIR, PUBLIC_DIR, - TEMPLATE_DIR, } from './Dirs.js' import GameCommon from '../common/GameCommon.js' import GameStorage from './GameStorage.js' @@ -37,36 +35,8 @@ const storage = multer.diskStorage({ }) const upload = multer({storage}).single('file'); -const render = async (template, data) => { - const loader = new twing.TwingLoaderFilesystem(TEMPLATE_DIR) - const env = new twing.TwingEnvironment(loader) - return env.render(template, data) -} - -app.use('/g/:gid', async (req, res, next) => { - res.send(await render('game.html.twig', { - GAME_ID: req.params.gid, - WS_ADDRESS: config.ws.connectstring, - })) -}) - -app.use('/replay/:gid', async (req, res, next) => { - res.send(await render('replay.html.twig', { - GAME_ID: req.params.gid, - WS_ADDRESS: config.ws.connectstring, - })) -}) - -app.get('/api/game-data/:gid', (req, res) => { +app.get('/api/conf', (req, res) => { res.send({ - GAME_ID: req.params.gid, - WS_ADDRESS: config.ws.connectstring, - }) -}) - -app.get('/api/replay-data/:gid', (req, res) => { - res.send({ - GAME_ID: req.params.gid, WS_ADDRESS: config.ws.connectstring, }) }) diff --git a/templates/game.html.twig b/templates/game.html.twig deleted file mode 100644 index 4123a8f..0000000 --- a/templates/game.html.twig +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - 🧩 jigsaw.hyottoko.club - - -
- - - - - - diff --git a/templates/replay.html.twig b/templates/replay.html.twig deleted file mode 100644 index e851fe3..0000000 --- a/templates/replay.html.twig +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - 🧩 jigsaw.hyottoko.club - - -
- - - - - -