From da06a6eb0237123ce43fdb01cb06246b8b57f5e5 Mon Sep 17 00:00:00 2001 From: Benjamin Auder <benjamin.auder@somewhere> Date: Sat, 15 Dec 2018 15:35:59 +0100 Subject: [PATCH] On the way to problems: saving state [not functional yet] --- .gitattributes | 1 + db/create.sql | 29 + db/vchess.sqlite | 1 + package-lock.json | 573 +++++++++++++----- package.json | 6 +- public/javascripts/components/game.js | 12 +- .../javascripts/components/problemSummary.js | 17 + public/javascripts/components/problems.js | 94 ++- public/javascripts/components/rules.js | 72 +-- public/javascripts/utils/ajax.js | 55 ++ public/javascripts/utils/printDiagram.js | 46 ++ public/stylesheets/variant.sass | 22 + routes/all.js | 75 ++- sockets.js | 222 ++++--- variants.js | 16 - views/index.pug | 1 - views/rules/Chess960.pug | 2 +- views/rules/Crazyhouse.pug | 15 +- views/rules/Switching.pug | 4 + views/rules/Ultima.pug | 6 +- views/variant.pug | 4 + 21 files changed, 874 insertions(+), 399 deletions(-) create mode 100644 db/create.sql create mode 100644 db/vchess.sqlite create mode 100644 public/javascripts/components/problemSummary.js create mode 100644 public/javascripts/utils/ajax.js create mode 100644 public/javascripts/utils/printDiagram.js delete mode 100644 variants.js diff --git a/.gitattributes b/.gitattributes index d268ba5b..ac4ca112 100644 --- a/.gitattributes +++ b/.gitattributes @@ -4,3 +4,4 @@ *.wav filter=fat *.mp3 filter=fat *.png filter=fat +*.sqlite filter=fat diff --git a/db/create.sql b/db/create.sql new file mode 100644 index 00000000..aa2f3ae1 --- /dev/null +++ b/db/create.sql @@ -0,0 +1,29 @@ +create table Variants ( + name varchar primary key, + description text +); +insert into Variants values + ('Checkered', 'Shared pieces'), + ('Zen', 'Reverse captures'), + ('Atomic', 'Explosive captures'), + ('Chess960', 'Standard rules'), + ('Antiking', 'Keep antiking in check'), + ('Magnetic', 'Laws of attraction'), + ('Alice', 'Both sides of the mirror'), + ('Grand', 'Big board'), + ('Wildebeest', 'Balanced sliders & leapers'), + ('Loser', 'Lose all pieces'), + ('Crazyhouse', 'Captures reborn'), + ('Switching', 'Exchange pieces positions'), + ('Extinction', 'Capture all of a kind'), + ('Ultima', 'Exotic captures'); + +create table Problems ( + added datetime, + variant varchar, + fen varchar, + instructions text, + solution text, + foreign key (variant) references Variants(name) +); +--PRAGMA foreign_keys = ON; diff --git a/db/vchess.sqlite b/db/vchess.sqlite new file mode 100644 index 00000000..9d7e4a2a --- /dev/null +++ b/db/vchess.sqlite @@ -0,0 +1 @@ +#$# git-fat 25d1d22208da0bc58bf3076bfe684bbcb98894b2 16384 diff --git a/package-lock.json b/package-lock.json index 88d9fd81..783e4095 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { - "name": "vc", - "version": "0.0.0", + "name": "vchess", + "version": "0.1.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -304,6 +304,11 @@ } } }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" + }, "array-unique": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", @@ -816,6 +821,11 @@ "upath": "^1.0.5" } }, + "chownr": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", + "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==" + }, "ci-info": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", @@ -935,7 +945,6 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "requires": { "color-name": "1.1.3" } @@ -943,8 +952,7 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "color-support": { "version": "1.1.3", @@ -953,9 +961,9 @@ "dev": true }, "colors": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.2.tgz", - "integrity": "sha512-rhP0JSBGYvpcNQj4s5AdShMeE5ahMop96cTeDl/v9qQQm2fYClE2QXZRi8wLzc+GmXSxdIqqbOIAhyObEXDbfQ==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.3.tgz", + "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==", "dev": true }, "combined-stream": { @@ -1154,8 +1162,7 @@ "deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" }, "default-compare": { "version": "1.0.0", @@ -1262,11 +1269,54 @@ "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", "dev": true }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" + }, "doctypes": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", "integrity": "sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk=" }, + "dom-serializer": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", + "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", + "requires": { + "domelementtype": "~1.1.1", + "entities": "~1.1.1" + }, + "dependencies": { + "domelementtype": { + "version": "1.1.3", + "resolved": "http://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", + "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=" + } + } + }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + }, + "domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, "dot-prop": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", @@ -1276,12 +1326,6 @@ "is-obj": "^1.0.0" } }, - "duplexer": { - "version": "0.1.1", - "resolved": "http://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", - "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", - "dev": true - }, "duplexer3": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", @@ -1338,6 +1382,11 @@ "once": "^1.4.0" } }, + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" + }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -1410,22 +1459,6 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" }, - "event-stream": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.6.tgz", - "integrity": "sha512-dGXNg4F/FgVzlApjzItL+7naHutA3fDqbV/zAZqDDlXTjiMnQmZKu+prImWKszeBM5UQeGvAl3u1wBiKeDh61g==", - "dev": true, - "requires": { - "duplexer": "^0.1.1", - "flatmap-stream": "^0.1.0", - "from": "^0.1.7", - "map-stream": "0.0.7", - "pause-stream": "^0.0.11", - "split": "^1.0.1", - "stream-combiner": "^0.2.2", - "through": "^2.3.8" - } - }, "execa": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", @@ -1668,13 +1701,14 @@ "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" }, "fancy-log": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.2.tgz", - "integrity": "sha1-9BEl49hPLn2JpD0G2VjI94vha+E=", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", + "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", "dev": true, "requires": { "ansi-gray": "^0.1.1", "color-support": "^1.1.3", + "parse-node-version": "^1.0.0", "time-stamp": "^1.0.0" } }, @@ -1773,9 +1807,9 @@ } }, "fined": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fined/-/fined-1.1.0.tgz", - "integrity": "sha1-s33IRLdqL15wgeiE98CuNE8VNHY=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/fined/-/fined-1.1.1.tgz", + "integrity": "sha512-jQp949ZmEbiYHk3gkbdtpJ0G1+kgtLQBNdP5edFP7Fh+WAYceLQz6yO1SBj72Xkg8GVyTB3bBzAYrHJVh5Xd5g==", "dev": true, "requires": { "expand-tilde": "^2.0.2", @@ -1786,15 +1820,9 @@ } }, "flagged-respawn": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.0.tgz", - "integrity": "sha1-Tnmumy6zi/hrO7Vr8+ClaqX8q9c=", - "dev": true - }, - "flatmap-stream": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/flatmap-stream/-/flatmap-stream-0.1.1.tgz", - "integrity": "sha512-lAq4tLbm3sidmdCN8G3ExaxH7cUCtP5mgDvrYowsx84dcYkJJ4I28N7gkxA6+YlSXzaGLJYIDEi9WGfXzMiXdw==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", + "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", "dev": true }, "flush-write-stream": { @@ -1856,11 +1884,13 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, - "from": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", - "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", - "dev": true + "fs-minipass": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", + "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", + "requires": { + "minipass": "^2.2.1" + } }, "fs-mkdirp-stream": { "version": "1.0.0", @@ -1897,8 +1927,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -1919,14 +1948,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1941,20 +1968,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -2071,8 +2095,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -2084,7 +2107,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -2099,7 +2121,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -2107,14 +2128,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -2133,7 +2152,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -2214,8 +2232,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -2227,7 +2244,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -2313,8 +2329,7 @@ "safe-buffer": { "version": "5.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -2350,7 +2365,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -2370,7 +2384,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -2414,14 +2427,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, @@ -2547,13 +2558,15 @@ } }, "glob-watcher": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.1.tgz", - "integrity": "sha512-fK92r2COMC199WCyGUblrZKhjra3cyVMDiypDdqg1vsSDmexnbYivK1kNR4QItiNXLKmGlqan469ks67RtNa2g==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.3.tgz", + "integrity": "sha512-8tWsULNEPHKQ2MR4zXuzSmqbdyV5PtwwCaWSGQ1WwHsJ07ilNeN1JB8ntxhckbnpSHaf9dXFUHzIWvm1I13dsg==", "dev": true, "requires": { + "anymatch": "^2.0.0", "async-done": "^1.2.0", "chokidar": "^2.0.0", + "is-negated-glob": "^1.0.0", "just-debounce": "^1.0.0", "object.defaults": "^1.1.0" } @@ -2602,9 +2615,9 @@ } }, "glogg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.1.tgz", - "integrity": "sha512-ynYqXLoluBKf9XGR1gA59yEJisIL7YHEH4xr3ZziHB5/yl4qWfaK8Js9jGe6gBGCSCKVqiyO30WnRZADvemUNw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz", + "integrity": "sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA==", "dev": true, "requires": { "sparkles": "^1.0.0" @@ -2675,15 +2688,14 @@ } }, "gulp-nodemon": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/gulp-nodemon/-/gulp-nodemon-2.4.1.tgz", - "integrity": "sha512-IZMEfUZggvXvYDCCbb4jq8xMSsS24OltlgL0KhumhDAbZJUHMm3tHEolHbNLLL9704Wm/DgZ5bdFqCb2hXG0bQ==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/gulp-nodemon/-/gulp-nodemon-2.4.2.tgz", + "integrity": "sha512-r8ShC9yzL3lK5qUsTStMeZRwqLG6t2m4lEBVcfUYzVkiYSeYXu9xYXG5rfvzBOPZOZ2dWugTKr+zeWbnMnzWDA==", "dev": true, "requires": { "colors": "^1.2.1", - "event-stream": "^3.3.4", "gulp": "^4.0.0", - "nodemon": "^1.17.5" + "nodemon": "^1.18.7" } }, "gulplog": { @@ -2728,8 +2740,7 @@ "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, "has-symbols": { "version": "1.0.0", @@ -2788,6 +2799,31 @@ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==" }, + "htmlparser2": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.0.tgz", + "integrity": "sha512-J1nEUGv+MkXS0weHNWVKJJ+UrLfePxRWpN3C9bEi9fLxL2+ggW94DQvgYVXsaT30PGwYRIZKNZXuyMhp3Di4bQ==", + "requires": { + "domelementtype": "^1.3.0", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.0.6" + }, + "dependencies": { + "readable-stream": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.0.6.tgz", + "integrity": "sha512-9E1oLoOWfhSXHGv6QlwXJim7uNzd9EVlWK+21tCU9Ju/kR0/p2AZYPz4qSchgO8PlLIH4FpZYfzwS+rEksZjIg==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "http-errors": { "version": "1.7.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.1.tgz", @@ -2831,6 +2867,14 @@ "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=", "dev": true }, + "ignore-walk": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", + "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", + "requires": { + "minimatch": "^3.0.4" + } + }, "import-lazy": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", @@ -2873,8 +2917,7 @@ "ini": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "dev": true + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" }, "interpret": { "version": "1.1.0", @@ -3353,6 +3396,21 @@ "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", "dev": true }, + "lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" + }, "lodash.mergewith": { "version": "4.6.1", "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz", @@ -3432,12 +3490,6 @@ "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=" }, - "map-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", - "integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=", - "dev": true - }, "map-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", @@ -3558,6 +3610,30 @@ "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" }, + "minipass": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", + "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + }, + "dependencies": { + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==" + } + } + }, + "minizlib": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz", + "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", + "requires": { + "minipass": "^2.2.1" + } + }, "mixin-deep": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", @@ -3657,6 +3733,31 @@ } } }, + "needle": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.2.4.tgz", + "integrity": "sha512-HyoqEb4wr/rsoaIDfTH2aVL9nWtQqba2/HvMv+++m8u0dz808MaagKILxtfeSN7QU7nvbQ79zk3vYOJp9zsNEA==", + "requires": { + "debug": "^2.1.2", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, "negotiator": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", @@ -3694,6 +3795,53 @@ } } }, + "node-pre-gyp": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz", + "integrity": "sha512-d1xFs+C/IPS8Id0qPTZ4bUT8wWryfR/OzzAFxweG+uLN85oPzyo2Iw6bVlLQ/JOdgNonXLCoRyqDzDWq4iw72A==", + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + }, + "dependencies": { + "nopt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "tar": { + "version": "4.4.8", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz", + "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + } + }, + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==" + } + } + }, "node-sass": { "version": "4.10.0", "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.10.0.tgz", @@ -3730,21 +3878,21 @@ } }, "nodemon": { - "version": "1.18.6", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-1.18.6.tgz", - "integrity": "sha512-4pHQNYEZun+IkIC2jCaXEhkZnfA7rQe73i8RkdRyDJls/K+WxR7IpI5uNUsAvQ0zWvYcCDNGD+XVtw2ZG86/uQ==", + "version": "1.18.9", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-1.18.9.tgz", + "integrity": "sha512-oj/eEVTEI47pzYAjGkpcNw0xYwTl4XSTUQv2NPQI6PpN3b75PhpuYk3Vb3U80xHCyM2Jm+1j68ULHXl4OR3Afw==", "dev": true, "requires": { "chokidar": "^2.0.4", "debug": "^3.1.0", "ignore-by-default": "^1.0.1", "minimatch": "^3.0.4", - "pstree.remy": "^1.1.0", + "pstree.remy": "^1.1.6", "semver": "^5.5.0", "supports-color": "^5.2.0", "touch": "^3.1.0", "undefsafe": "^2.0.2", - "update-notifier": "^2.3.0" + "update-notifier": "^2.5.0" }, "dependencies": { "debug": { @@ -3804,6 +3952,20 @@ "once": "^1.3.2" } }, + "npm-bundled": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.5.tgz", + "integrity": "sha512-m/e6jgWu8/v5niCUKQi9qQl8QdeEduFA96xHDDzFGqly0OOjI7c+60KM/2sppfnUU9JJagf+zs+yGhqSOFj71g==" + }, + "npm-packlist": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.1.12.tgz", + "integrity": "sha512-WJKFOVMeAlsU/pjXuqVdzU0WfgtIBCupkEVwn+1Y0ERAbUfWw8R4GjgVbaKnUjRoD2FoQbHOCbOyT5Mbs9Lw4g==", + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, "npm-run-path": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", @@ -4023,6 +4185,12 @@ "error-ex": "^1.2.0" } }, + "parse-node-version": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.0.tgz", + "integrity": "sha512-02GTVHD1u0nWc20n2G7WX/PgdhNFG04j5fi1OkaJzPWLTcf6vh6229Lta1wTmXG/7Dg42tCssgkccVt7qvd8Kg==", + "dev": true + }, "parse-passwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", @@ -4106,15 +4274,6 @@ "pinkie-promise": "^2.0.0" } }, - "pause-stream": { - "version": "0.0.11", - "resolved": "http://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", - "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", - "dev": true, - "requires": { - "through": "~2.3" - } - }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", @@ -4144,6 +4303,49 @@ "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", "dev": true }, + "postcss": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.6.tgz", + "integrity": "sha512-Nq/rNjnHFcKgCDDZYO0lNsl6YWe6U7tTy+ESN+PnLxebL8uBtYX59HZqvrj7YLK5UCyll2hqDsJOo3ndzEW8Ug==", + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.5.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, "prepend-http": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", @@ -4178,15 +4380,6 @@ "ipaddr.js": "1.8.0" } }, - "ps-tree": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.1.0.tgz", - "integrity": "sha1-tCGyQUDWID8e08dplrRCewjowBQ=", - "dev": true, - "requires": { - "event-stream": "~3.3.0" - } - }, "pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", @@ -4198,13 +4391,10 @@ "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==" }, "pstree.remy": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.0.tgz", - "integrity": "sha512-q5I5vLRMVtdWa8n/3UEzZX7Lfghzrg9eG2IKk2ENLSofKRCXVqMvMUHxCKgXNaqH/8ebhBxrqftHWnyTFweJ5Q==", - "dev": true, - "requires": { - "ps-tree": "^1.1.0" - } + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.6.tgz", + "integrity": "sha512-NdF35+QsqD7EgNEI5mkI/X+UwaxVEbQaz9f4IooEmMUv6ZPmlTQYGjBPJGgrlzNdjSvIy4MWMg6Q6vCgBO2K+w==", + "dev": true }, "pug": { "version": "2.0.3", @@ -4384,7 +4574,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, "requires": { "deep-extend": "^0.6.0", "ini": "~1.3.0", @@ -4395,8 +4584,7 @@ "minimist": { "version": "1.2.0", "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" } } }, @@ -4670,6 +4858,51 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "sanitize-html": { + "version": "1.19.3", + "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.19.3.tgz", + "integrity": "sha512-QpIjbF1rhUSQj9V7Wey/gv4DPqOso8KTebaI4rC97p0WCLnTpmhf7BJZUhS83MTtqRvUo8MuXH316CW2Nzd48w==", + "requires": { + "chalk": "^2.4.1", + "htmlparser2": "^3.10.0", + "lodash.clonedeep": "^4.5.0", + "lodash.escaperegexp": "^4.1.2", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.mergewith": "^4.6.1", + "postcss": "^7.0.5", + "srcset": "^1.0.0", + "xtend": "^4.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, "sass-graph": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz", @@ -4681,6 +4914,11 @@ "yargs": "^7.0.0" } }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, "scss-tokenizer": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", @@ -5026,15 +5264,6 @@ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.2.tgz", "integrity": "sha512-qky9CVt0lVIECkEsYbNILVnPvycuEBkXoMFLRWsREkomQLevYhtRKC+R91a5TOAQ3bCMjikRwhyaRqj1VYatYg==" }, - "split": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", - "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", - "dev": true, - "requires": { - "through": "2" - } - }, "split-string": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", @@ -5044,6 +5273,32 @@ "extend-shallow": "^3.0.0" } }, + "sqlite3": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-4.0.4.tgz", + "integrity": "sha512-CO8vZMyUXBPC+E3iXOCc7Tz2pAdq5BWfLcQmOokCOZW5S5sZ/paijiPOCdvzpdP83RroWHYa5xYlVqCxSqpnQg==", + "requires": { + "nan": "~2.10.0", + "node-pre-gyp": "^0.10.3", + "request": "^2.87.0" + }, + "dependencies": { + "nan": { + "version": "2.10.0", + "resolved": "http://registry.npmjs.org/nan/-/nan-2.10.0.tgz", + "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==" + } + } + }, + "srcset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/srcset/-/srcset-1.0.0.tgz", + "integrity": "sha1-pWad4StC87HV6D7QPHEEb8SPQe8=", + "requires": { + "array-uniq": "^1.0.2", + "number-is-nan": "^1.0.0" + } + }, "sshpk": { "version": "1.15.2", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.15.2.tgz", @@ -5100,16 +5355,6 @@ "readable-stream": "^2.0.1" } }, - "stream-combiner": { - "version": "0.2.2", - "resolved": "http://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz", - "integrity": "sha1-rsjLrBd7Vrb0+kec7YwZEs7lKFg=", - "dev": true, - "requires": { - "duplexer": "~0.1.1", - "through": "~2.3.4" - } - }, "stream-exhaust": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", @@ -5173,8 +5418,7 @@ "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" }, "supports-color": { "version": "2.0.0", @@ -5210,12 +5454,6 @@ "execa": "^0.7.0" } }, - "through": { - "version": "2.3.8", - "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, "through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", @@ -5904,9 +6142,9 @@ } }, "ws": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.0.tgz", - "integrity": "sha512-H3dGVdGvW2H8bnYpIDc3u3LH8Wue3Qh+Zto6aXXFzvESkTVT6rAfKR6tR/+coaUvxs8yHtmNV0uioBF62ZGSTg==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.2.tgz", + "integrity": "sha512-rfUqzvz0WxmSXtJpPMX2EeASXabOrSMk1ruMOV3JBTBjo4ac2lDjGGsbQSyxj8Odhw5fBib8ZKEjDNvgouNKYw==", "requires": { "async-limiter": "~1.0.0" } @@ -5920,8 +6158,7 @@ "xtend": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", - "dev": true + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" }, "y18n": { "version": "3.2.1", diff --git a/package.json b/package.json index 62092276..72a326fc 100644 --- a/package.json +++ b/package.json @@ -13,10 +13,12 @@ "morgan": "~1.9.1", "node-sass-middleware": "~0.11.0", "pug": "~2.0.3", + "sanitize-html": "^1.19.3", "serve-favicon": "~2.5.0", - "ws": "~6.1.0" + "sqlite3": "^4.0.4", + "ws": "^6.1.2" }, "devDependencies": { - "gulp-nodemon": "~2.4.1" + "gulp-nodemon": "^2.4.2" } } diff --git a/public/javascripts/components/game.js b/public/javascripts/components/game.js index 533bc0e2..278e0782 100644 --- a/public/javascripts/components/game.js +++ b/public/javascripts/components/game.js @@ -409,7 +409,7 @@ Vue.component('my-game', { }), h('div', { - attrs: { "role": "dialog", "aria-labelledby": "modal-eog" }, + attrs: { "role": "dialog", "aria-labelledby": "eogMessage" }, }, [ h('div', @@ -425,6 +425,7 @@ Vue.component('my-game', { ), h('h3', { + attrs: { "id": "eogMessage" }, "class": { "section": true }, domProps: { innerHTML: eogMessage }, } @@ -445,7 +446,7 @@ Vue.component('my-game', { }), h('div', { - attrs: { "role": "dialog", "aria-labelledby": "modal-newgame" }, + attrs: { "role": "dialog", "aria-labelledby": "newGameTxt" }, }, [ h('div', @@ -461,6 +462,7 @@ Vue.component('my-game', { ), h('h3', { + attrs: { "id": "newGameTxt" }, "class": { "section": true }, domProps: { innerHTML: "New game" }, } @@ -485,7 +487,7 @@ Vue.component('my-game', { }), h('div', { - attrs: { "role": "dialog", "aria-labelledby": "modal-fenedit" }, + attrs: { "role": "dialog", "aria-labelledby": "titleFenedit" }, }, [ h('div', @@ -501,6 +503,7 @@ Vue.component('my-game', { ), h('h3', { + attrs: { "id": "titleFenedit" }, "class": { "section": true }, domProps: { innerHTML: "Position + flags (FEN):" }, } @@ -551,7 +554,7 @@ Vue.component('my-game', { }), h('div', { - attrs: { "role": "dialog", "aria-labelledby": "modal-settings" }, + attrs: { "role": "dialog", "aria-labelledby": "settingsTitle" }, }, [ h('div', @@ -567,6 +570,7 @@ Vue.component('my-game', { ), h('h3', { + attrs: { "id": "settingsTitle" }, "class": { "section": true }, domProps: { innerHTML: "Preferences" }, } diff --git a/public/javascripts/components/problemSummary.js b/public/javascripts/components/problemSummary.js new file mode 100644 index 00000000..6003f085 --- /dev/null +++ b/public/javascripts/components/problemSummary.js @@ -0,0 +1,17 @@ +// Show a problem summary on variant page +Vue.component('my-problem-summary', { + props: ['prob'], + template: ` + <div class="problem col-sm-12"> + <div class="diagram"> + {{ getDiagram(prob.fen) }} + </div> + <div class="problem-instructions"> + {{ prob.instructions.substr(0,32) }} + </div> + <div class="problem-time"> + {{ prob.added }} + </div> + </div> + `, +}) diff --git a/public/javascripts/components/problems.js b/public/javascripts/components/problems.js index dc320520..594d6e64 100644 --- a/public/javascripts/components/problems.js +++ b/public/javascripts/components/problems.js @@ -1,23 +1,81 @@ -//TODO: list problems as FEN (quickly rendered), by date, with possible filtering per variant(?) -//click on a problem ==> land on variant page with mode==friend, FEN prefilled... ok - -// get 10 first problems, and buttons next<>previous send date + "before" or "after" -// need database: sqlite ! - -// form "new problem" fen(position/turn/flags[guess]), instructions, solution (mandatory) -// ==> upload on server in sandbox -// -// Atomic rules, atomic game, atomic problems(list drawn position -// + summary(first chars of instructions) + timedate)... one big Vue ? with components -// -// click on problem ==> masque problems, affiche game tab, launch new game Friend with -// FEN + turn + flags + rappel instructions / solution on click sous l'échiquier - Vue.component('my-problems', { - //props: ['vobj'], + data: function () { + return { + problems: problemArray //initial value + }; + }, template: ` - <div class="variant col-sm-12"> - <p>Hello</p> + <div> + <button>Previous</button> + <button>Next</button> + <button @click="showNewproblemModal">New</button> + <my-problem-summary + v-for="(p,idx) in sortedProblems", + v-bind:prob="p", + v-bind:key="idx", + @click="showProblem(p)" + </my-problem-summary> + <input type="checkbox" id="modal-newproblem" class="modal"> + <div role="dialog" aria-labelledby="newProblemTxt"> + <div class="card newproblem"> + <label for="modal-newproblem" class="modal-close"></label> + <h3 id="newProblemTxt">Add problem</h3> + <form @submit.prevent="postNewProblem"> + <fieldset> + <label for="newpbFen">Fen</label> + <input type="text" id="newpbFen" placeholder="Position [+ flags [+ turn]]"/> + </fieldset> + <fieldset> + <p class="emphasis"> + Allowed HTML tags: + <p>,<br>,<,ul>,<ol>,<li> + </p> + <label for="newpbInstructions">Instructions</label> + <textarea id="newpbInstructions" placeholder="Explain the problem here"/> + <label for="newpbSolution">Solution</label> + <textarea id="newpbSolution" placeholder="How to solve the problem?"/> + <button class="center-btn">Send</button> + </fieldset> + <p class="mistake-newproblem"> + Note: if you made a mistake, please let me know at + <a :href="mailErrProblem">contact@vchess.club</a> + </p> + </form> + </div> + </div> </div> `, + computed: { + sortedProblems: function() { + // Newest problem first + return problems.sort((p1,p2) => { return p2.added - p1.added; }); + }, + mailErrProblem: function() { + return "mailto:contact@vchess.club?subject=[" + variant + " problems] error"; + }, + }, + methods: { + fetchProblems: function(direction) { + // TODO: ajax call return list of max 10 problems + // Do not do anything if no older problems (and store this result in cache!) + // TODO: ajax call return list of max 10 problems + // Do not do anything if no newer problems + }, + showProblem: function(prob) { + //TODO: send event with object prob.fen, prob.instructions, prob.solution + //Event should propagate to game, which set mode=="problem" + other variables + //click on a problem ==> land on variant page with mode==friend, FEN prefilled... ok + // click on problem ==> masque problems, affiche game tab, launch new game Friend with + // FEN + turn + flags + rappel instructions / solution on click sous l'échiquier + }, + showNewproblemModal: function() { + document.getElementById("modal-newproblem").checked = true; + }, + postNewProblem: function() { + const fen = document.getElementById("newpbFen").value; + const instructions = document.getElementById("newpbInstructions").value; + const solution = document.getElementById("newpbSolution").value; + + }, + }, }) diff --git a/public/javascripts/components/rules.js b/public/javascripts/components/rules.js index c3107a67..660f0be9 100644 --- a/public/javascripts/components/rules.js +++ b/public/javascripts/components/rules.js @@ -6,66 +6,22 @@ Vue.component('my-rules', { template: `<div v-html="content" class="section-content"></div>`, mounted: function() { // AJAX request to get rules content (plain text, HTML) - let xhr = new XMLHttpRequest(); - let self = this; - xhr.onreadystatechange = function() { - if (this.readyState == 4 && this.status == 200) - { - let replaceByDiag = (match, p1, p2) => { return self.drawDiag(p2); }; - self.content = xhr.responseText.replace(/(fen:)([^:]*):/g, replaceByDiag); - } - }; - xhr.open("GET", "/rules/" + variant, true); - xhr.setRequestHeader('X-Requested-With', "XMLHttpRequest"); - xhr.send(); + ajax("/rules/" + variant, "GET", response => { + let replaceByDiag = (match, p1, p2) => { + const args = self.parseFen(p2); + return getDiagram(args); + }; + self.content = response.replace(/(fen:)([^:]*):/g, replaceByDiag); + } }, methods: { - drawDiag: function(fen) { - let [sizeX,sizeY] = [V.size.x,V.size.y]; - let fenParts = fen.split(" "); - // Obtain array of pieces images names - let board = VariantRules.GetBoard(fenParts[0]); - let orientation = "w"; - if (fenParts.length >= 2) - orientation = fenParts[1]; - let markArray = []; - if (fenParts.length >= 3) - { - let marks_str = fenParts[2]; - // Turn (human) marks into coordinates - markArray = doubleArray(sizeX, sizeY, false); - let marks = marks_str.split(","); - for (let i=0; i<marks.length; i++) - { - var res = /^([a-z]+)([0-9]+)$/i.exec(marks[i]); - let x = sizeX - parseInt(res[2]); //white at bottom, so counting is reversed - let y = res[1].charCodeAt(0)-97; //always one char: max 26, big enough - markArray[x][y] = true; - } - } - let boardDiv = ""; - let [startX,startY,inc] = orientation == 'w' - ? [0, 0, 1] - : [sizeX-1, sizeY-1, -1]; - for (let i=startX; i>=0 && i<sizeX; i+=inc) - { - boardDiv += "<div class='row'>"; - for (let j=startY; j>=0 && j<sizeY; j+=inc) - { - boardDiv += "<div class='board board" + sizeY + " " + - ((i+j)%2==0 ? "light-square-diag" : "dark-square-diag") + "'>"; - if (board[i][j] != VariantRules.EMPTY) - { - boardDiv += "<img src='/images/pieces/" + - VariantRules.getPpath(board[i][j]) + ".svg' class='piece'/>"; - } - if (markArray.length>0 && markArray[i][j]) - boardDiv += "<img src='/images/mark.svg' class='mark-square'/>"; - boardDiv += "</div>"; - } - boardDiv += "</div>"; - } - return boardDiv; + parseFen(fen) { + const fenParts = fen.split(" "); + return { + position: fenParts[0], + marks: fenParts[1], + orientation: fenParts[2], + }; }, }, }) diff --git a/public/javascripts/utils/ajax.js b/public/javascripts/utils/ajax.js new file mode 100644 index 00000000..ab288bc7 --- /dev/null +++ b/public/javascripts/utils/ajax.js @@ -0,0 +1,55 @@ +// From JSON (encoded string values!) to "arg1=...&arg2=..." +function toQueryString(data) +{ + let data_str = ""; + Object.keys(data).forEach(k => { + data_str += k + "=" + data[k] + "&"; + }); + return data_str.slice(0, -1); //remove last "&" +} + +// data, error: optional +function ajax(url, method, data, success, error) +{ + let xhr = new XMLHttpRequest(); + if (typeof(data) === "function") //no data + { + error = success; + success = data; + data = {}; + } + if (!error) + error = errmsg => { alert(errmsg); }; + + xhr.onreadystatechange = function() { + if (this.readyState == 4 && this.status == 200) + { + try { + let res_json = JSON.parse(xhr.responseText); + if (!res_json.errmsg) + success(res_json); + else + error(res_json.errmsg); + } catch (e) { + // Plain text (e.g. for rules retrieval) + success(xhr.responseText); + } + } + }; + + if (["GET","DELETE"].includes(method) && !!data) + { + // Append query params to URL + url += "/?" + toQueryString(data); + } + + xhr.open(method, url, true); + xhr.setRequestHeader('X-Requested-With', "XMLHttpRequest"); + if (["POST","PUT"].includes(method)) + { + xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); + xhr.send(JSON.stringify(data)); + } + else + xhr.send(); +} diff --git a/public/javascripts/utils/printDiagram.js b/public/javascripts/utils/printDiagram.js new file mode 100644 index 00000000..ef750490 --- /dev/null +++ b/public/javascripts/utils/printDiagram.js @@ -0,0 +1,46 @@ +// Assuming V(ariantRules) class is loaded. +// args: object with position (mandatory), orientation, marks (optional) +function getDiagram(args) +{ + const [sizeX,sizeY] = [V.size.x,V.size.y]; + // Obtain array of pieces images names + const board = VariantRules.GetBoard(args.position); + const orientation = args.orientation || "w"; + let markArray = []; + if (!!args.marks) + { + // Turn (human) marks into coordinates + markArray = doubleArray(sizeX, sizeY, false); + let squares = args.marks.split(","); + for (let i=0; i<squares.length; i++) + { + const res = /^([a-z]+)([0-9]+)$/i.exec(squares[i]); + const x = sizeX - parseInt(res[2]); //white at bottom, so counting is reversed + const y = res[1].charCodeAt(0)-97; //always one char: max 26, big enough + markArray[x][y] = true; + } + } + let boardDiv = ""; + const [startX,startY,inc] = orientation == 'w' + ? [0, 0, 1] + : [sizeX-1, sizeY-1, -1]; + for (let i=startX; i>=0 && i<sizeX; i+=inc) + { + boardDiv += "<div class='row'>"; + for (let j=startY; j>=0 && j<sizeY; j+=inc) + { + boardDiv += "<div class='board board" + sizeY + " " + + ((i+j)%2==0 ? "light-square-diag" : "dark-square-diag") + "'>"; + if (board[i][j] != V.EMPTY) + { + boardDiv += "<img src='/images/pieces/" + + V.getPpath(board[i][j]) + ".svg' class='piece'/>"; + } + if (!!args.marks && markArray[i][j]) + boardDiv += "<img src='/images/mark.svg' class='mark-square'/>"; + boardDiv += "</div>"; + } + boardDiv += "</div>"; + } + return boardDiv; +} diff --git a/public/stylesheets/variant.sass b/public/stylesheets/variant.sass index f5bffc3c..f8047560 100644 --- a/public/stylesheets/variant.sass +++ b/public/stylesheets/variant.sass @@ -245,3 +245,25 @@ ul:not(.browser-default) > li #fen-div > p margin-left: 0 margin-right: 0 + +.newproblem input, .newproblem textarea + width: 100% + +.emphasis + font-style: italic + +#newpbInstructions + margin-bottom: var(--universal-margin); + +.center-btn + margin-left: 40% + +//TODO? +.center-inline + text-align: center +.center-block + margin-left: auto + margin-right: auto + +.mistake-newproblem + color: #663300 diff --git a/routes/all.js b/routes/all.js index 3e6dd001..f3e184e6 100644 --- a/routes/all.js +++ b/routes/all.js @@ -1,31 +1,78 @@ -var express = require('express'); -var router = express.Router(); -var createError = require('http-errors'); - -const Variants = require("../variants"); +let express = require('express'); +let router = express.Router(); +const createError = require('http-errors'); +const sqlite3 = require('sqlite3');//.verbose(); +const db = new sqlite3.Database('db/vchess.sqlite'); +const sanitizeHtml = require('sanitize-html'); // Home router.get('/', function(req, res, next) { - res.render('index', { - title: 'club', - variantArray: Variants, //JSON.stringify(Variants) + db.serialize(function() { + db.all("SELECT * FROM Variants", (err,variants) => { + if (!!err) + return next(err); + res.render('index', { + title: 'club', + variantArray: variants, //JSON.stringify(variants) + }); + }); }); }); // Variant router.get("/:vname([a-zA-Z0-9]+)", (req,res,next) => { const vname = req.params["vname"]; - if (!Variants.some(v => { return (v.name == vname); })) - return next(createError(404)); - res.render('variant', { - title: vname + ' Variant', - variant: vname, + db.serialize(function() { + db.all("SELECT * FROM Variants WHERE name='" + vname + "'", (err,variant) => { + if (!!err) + return next(err); + if (!variant || variant.length==0) + return next(createError(404)); + db.all("SELECT * FROM Problems WHERE variant='" + vname + "'", + (err2,problems) => { + if (!!err2) + return next(err2); + res.render('variant', { + title: vname + ' Variant', + variant: vname, + problemArray: problems, + }); + } + ); + }); }); }); // Load a rules page (AJAX) router.get("/rules/:variant([a-zA-Z0-9]+)", (req,res) => { - res.render("rules/" + req.params["variant"]); + if (!req.xhr) + return res.json({errmsg: "Unauthorized access"}); + res.render("rules/" + req.params["variant"]); +}); + +// Fetch 10 previous or next problems (AJAX) +router.get("/problems/:variant([a-zA-Z0-9]+)", (req,res) => { + if (!req.xhr) + return res.json({errmsg: "Unauthorized access"}); + // TODO: next or previous: in params + timedate (of current oldest or newest) }); +// Upload a problem (AJAX) +router.post("/problems/:variant([a-zA-Z0-9]+)", (req,res) => { + if (!req.xhr) + return res.json({errmsg: "Unauthorized access"}); + const vname = req.params["variant"]; + + // TODO: get parameters and sanitize them + sanitizeHtml(req.body["fen"]); // [/a-z0-9 ]* + sanitizeHtml(req.body["instructions"]); + db.serialize(function() { + let stmt = db.prepare("INSERT INTO Problems VALUES (?,?,?,?,?)"); + stmt.run(timestamp, vname, fen, instructions, solution); + stmt.finalize(); + }); + res.json({}); +}); + + module.exports = router; diff --git a/sockets.js b/sockets.js index 675a80eb..f1354f36 100644 --- a/sockets.js +++ b/sockets.js @@ -1,124 +1,120 @@ const url = require('url'); -const Variants = require("./variants"); +const sqlite3 = require('sqlite3'); +const db = new sqlite3.Database('db/vchess.sqlite'); module.exports = function(wss) { - - let clients = { "index": {} }; - let games = {}; //pending games (player sid) - for (const v of Variants) - clients[v.name] = {}; - -// // Safety counter (TODO: is it necessary ?) -// setInterval(() => { -// Object.keys(clients).forEach(k => { -// Object.keys(clients[k]).forEach(ck => { -// if (!clients[k][ck] || clients[k][ck].readyState != 1) -// delete clients[k][ck]; -// }); -// }); -// }, 60000); //every minute (will be lowered if a lot of users...) - - // No-op function as a callback when sending messages - const noop = () => { }; - - wss.on("connection", (socket, req) => { - const params = new URL("http://localhost" + req.url).searchParams; - const sid = params.get("sid"); - const page = params.get("page"); - // Ignore duplicate connections: - if (!!clients[page][sid]) - { - socket.send(JSON.stringify({code:"duplicate"})); - return; - } - clients[page][sid] = socket; - if (page == "index") - { - // Send counting info - const countings = {}; - for (const v of Variants) - countings[v.name] = Object.keys(clients[v.name]).length; - socket.send(JSON.stringify({code:"counts",counts:countings})); - } - else - { - // Send to every client connected on index an update message for counts - Object.keys(clients["index"]).forEach( k => { - clients["index"][k].send(JSON.stringify({code:"increase",vname:page}), noop); - }); - // Also notify potential opponents: hit all clients which check if sid corresponds - Object.keys(clients[page]).forEach( k => { - clients[page][k].send(JSON.stringify({code:"connect",id:sid}), noop); - }); - socket.on("message", objtxt => { - let obj = JSON.parse(objtxt); - switch (obj.code) + db.serialize(function() { + db.all("SELECT * FROM Variants", (err,variants) => { + let clients = { "index": {} }; + let games = {}; //pending games (player sid) + for (const v of variants) + clients[v.name] = {}; + // No-op function as a callback when sending messages + const noop = () => { }; + wss.on("connection", (socket, req) => { + const params = new URL("http://localhost" + req.url).searchParams; + const sid = params.get("sid"); + const page = params.get("page"); + // Ignore duplicate connections: + if (!!clients[page][sid]) { - case "newmove": - if (!!clients[page][obj.oppid]) - { - clients[page][obj.oppid].send( - JSON.stringify({code:"newmove",move:obj.move}), noop); - } - break; - case "ping": - if (!!clients[page][obj.oppid]) - socket.send(JSON.stringify({code:"pong"})); - break; - case "lastate": - if (!!clients[page][obj.oppid]) - { - const oppId = obj.oppid; - obj.oppid = sid; //I'm oppid for my opponent - clients[page][oppId].send(JSON.stringify(obj), noop); - } - break; - case "newgame": - if (!!games[page]) + socket.send(JSON.stringify({code:"duplicate"})); + return; + } + clients[page][sid] = socket; + if (page == "index") + { + // Send counting info + const countings = {}; + for (const v of variants) + countings[v.name] = Object.keys(clients[v.name]).length; + socket.send(JSON.stringify({code:"counts",counts:countings})); + } + else + { + // Send to every client connected on index an update message for counts + Object.keys(clients["index"]).forEach( k => { + clients["index"][k].send( + JSON.stringify({code:"increase",vname:page}), noop); + }); + // Also notify potential opponents: + // hit all clients which check if sid corresponds + Object.keys(clients[page]).forEach( k => { + clients[page][k].send(JSON.stringify({code:"connect",id:sid}), noop); + }); + socket.on("message", objtxt => { + let obj = JSON.parse(objtxt); + switch (obj.code) { - // Start a new game - const oppId = games[page]["id"]; - const fen = games[page]["fen"]; - delete games[page]; - const mycolor = Math.random() < 0.5 ? 'w' : 'b'; - socket.send( - JSON.stringify({code:"newgame",fen:fen,oppid:oppId,color:mycolor})); - if (!!clients[page][oppId]) - { - clients[page][oppId].send( - JSON.stringify( - {code:"newgame",fen:fen,oppid:sid,color:mycolor=="w"?"b":"w"}), - noop); - } + case "newmove": + if (!!clients[page][obj.oppid]) + { + clients[page][obj.oppid].send( + JSON.stringify({code:"newmove",move:obj.move}), noop); + } + break; + case "ping": + if (!!clients[page][obj.oppid]) + socket.send(JSON.stringify({code:"pong"})); + break; + case "lastate": + if (!!clients[page][obj.oppid]) + { + const oppId = obj.oppid; + obj.oppid = sid; //I'm oppid for my opponent + clients[page][oppId].send(JSON.stringify(obj), noop); + } + break; + case "newgame": + if (!!games[page]) + { + // Start a new game + const oppId = games[page]["id"]; + const fen = games[page]["fen"]; + delete games[page]; + const mycolor = Math.random() < 0.5 ? 'w' : 'b'; + socket.send(JSON.stringify( + {code:"newgame",fen:fen,oppid:oppId,color:mycolor})); + if (!!clients[page][oppId]) + { + clients[page][oppId].send( + JSON.stringify( + {code:"newgame",fen:fen,oppid:sid,color:mycolor=="w"?"b":"w"}), + noop); + } + } + else + games[page] = {id:sid, fen:obj.fen}; //wait for opponent + break; + case "cancelnewgame": //if a user cancel his seek + delete games[page]; + break; + case "resign": + if (!!clients[page][obj.oppid]) + clients[page][obj.oppid].send(JSON.stringify({code:"resign"}), noop); + break; } - else - games[page] = {id:sid, fen:obj.fen}; //wait for opponent - break; - case "cancelnewgame": //if a user cancel his seek - delete games[page]; - break; - case "resign": - if (!!clients[page][obj.oppid]) - clients[page][obj.oppid].send(JSON.stringify({code:"resign"}), noop); - break; + }); } - }); - } - socket.on("close", () => { - delete clients[page][sid]; - // Remove potential pending game - if (!!games[page] && games[page]["id"] == sid) - delete games[page]; - if (page != "index") - { - // Send to every client connected on index an update message for counts - Object.keys(clients["index"]).forEach( k => { - clients["index"][k].send(JSON.stringify({code:"decrease",vname:page}), noop); + socket.on("close", () => { + delete clients[page][sid]; + // Remove potential pending game + if (!!games[page] && games[page]["id"] == sid) + delete games[page]; + if (page != "index") + { + // Send to every client connected on index an update message for counts + Object.keys(clients["index"]).forEach( k => { + clients["index"][k].send( + JSON.stringify({code:"decrease",vname:page}), noop); + }); + } + // Also notify potential opponents: + // hit all clients which check if sid corresponds + Object.keys(clients[page]).forEach( k => { + clients[page][k].send(JSON.stringify({code:"disconnect",id:sid}), noop); + }); }); - } - // Also notify potential opponents: hit all clients which check if sid corresponds - Object.keys(clients[page]).forEach( k => { - clients[page][k].send(JSON.stringify({code:"disconnect",id:sid}), noop); }); }); }); diff --git a/variants.js b/variants.js deleted file mode 100644 index 2a20ffd9..00000000 --- a/variants.js +++ /dev/null @@ -1,16 +0,0 @@ -module.exports = [ - { "name": "Checkered", "description": "Shared pieces" }, - { "name": "Zen", "description": "Reverse captures" }, - { "name": "Atomic", "description": "Explosive captures" }, - { "name": "Chess960", "description": "Standard rules" }, - { "name": "Antiking", "description": "Keep antiking in check" }, - { "name": "Magnetic", "description": "Laws of attraction" }, - { "name": "Alice", "description": "Both sides of the mirror" }, - { "name": "Grand", "description": "Big board" }, - { "name": "Wildebeest", "description": "Balanced sliders & leapers" }, - { "name": "Loser", "description": "Lose all pieces" }, - { "name": "Crazyhouse", "description": "Captures reborn" }, - { "name": "Switching", "description": "Exchange pieces positions" }, - { "name": "Extinction", "description": "Capture all of a kind" }, - { "name": "Ultima", "description": "Non-standard captures" }, -]; diff --git a/views/index.pug b/views/index.pug index 521c1258..9ef19675 100644 --- a/views/index.pug +++ b/views/index.pug @@ -99,7 +99,6 @@ block content block javascripts script. const variantArray = !{JSON.stringify(variantArray)}; - //JSON.parse("!{variantArray}".replace(/\"/g,'"')); script(src="/javascripts/utils/misc.js") script(src="/javascripts/utils/socket_url.js") script(src="/javascripts/components/variantSummary.js") diff --git a/views/rules/Chess960.pug b/views/rules/Chess960.pug index 8b271c2f..331bc5ab 100644 --- a/views/rules/Chess960.pug +++ b/views/rules/Chess960.pug @@ -83,7 +83,7 @@ p. figure.diagram-container .diagram - | fen:k7/2p5/5q2/2b5/4N3/2R3r1/3P4/7K w f6,d6,c5,f2,g3,g5: + | fen:k7/2p5/5q2/2b5/4N3/2R3r1/3P4/7K f6,d6,c5,f2,g3,g5: figcaption Possible knight moves from e4. h4 Bishops diff --git a/views/rules/Crazyhouse.pug b/views/rules/Crazyhouse.pug index 7a3d91b7..004c0264 100644 --- a/views/rules/Crazyhouse.pug +++ b/views/rules/Crazyhouse.pug @@ -16,11 +16,20 @@ h3 Basics p | Orthodox rules apply, with only one change: - | every time you capture a piece, your "reserve" of waiting pieces is augmented - | with this figure. At every move, you may choose to land one of your reserve - | pieces anywhere on the board (except last rank for pawns), + | every time a piece is captured, the capturer "reserve" of waiting pieces is + | augmented with this figure. At every move, you may choose to land one of your + | reserve pieces anywhere on the board (except last rank for pawns), | instead of playing a regular move. +p. + Move notation: an arobase '@' indicate piece landing. For example B@d4 means + a bishop rebirth on d4 square, and @f3 is a pawn landing on f3. + +figure.diagram-container + .diagram + | fen:qnbrkbnr/ppNppp1p/2p2p2/8/8/5N2/P1PPPPPP/BRNQKBNR: + figcaption After 1.b3 Nf6 2.Bxf6 gxf6 3.Nf3 c6?? 4.N@c7# + p. Note: when a promoted pawn is captured, capturing it put a pawn in the reserve, not the promoted piece. This is to allow to gain material on last rank without diff --git a/views/rules/Switching.pug b/views/rules/Switching.pug index ce2fdbcc..8f1af19a 100644 --- a/views/rules/Switching.pug +++ b/views/rules/Switching.pug @@ -21,6 +21,10 @@ p | Switching must involves two different units. | Switching while the king is under check is not allowed. +p. + Notation: a switch move is marked by the capital letter 'S' followed by + the exchanged squares. Example in diagram: Sb8c8 + figure.diagram-container .diagram | fen:1RB3k1/5ppp/8/8/8/8/8/K7: diff --git a/views/rules/Ultima.pug b/views/rules/Ultima.pug index b3856277..cf5da24f 100644 --- a/views/rules/Ultima.pug +++ b/views/rules/Ultima.pug @@ -60,7 +60,11 @@ p | All other pieces capture passively: they land on a free square and captured | units are determined by some characteristics of the movement. -p Note: the immobilizer does not capture. +p Note 1: the immobilizer does not capture. + +p. + Note 2: for passive captures, a 'X' is added at the end of the move notation, + to indicate that something was taken (replaying the game is necessary to know where). h4 Pawns/Pincers diff --git a/views/variant.pug b/views/variant.pug index a613a0b0..37bd5e7d 100644 --- a/views/variant.pug +++ b/views/variant.pug @@ -25,13 +25,17 @@ block javascripts script(src="/javascripts/utils/socket_url.js") script(src="/javascripts/utils/array.js") script(src="/javascripts/utils/md5.js") + script(src="/javascripts/utils/printDiagram.js") + script(src="/javascripts/utils/ajax.js") script(src="/javascripts/base_rules.js") script(src="/javascripts/variants/" + variant + ".js") script. const VariantRules = #{variant}Rules; const V = VariantRules; //because this variable is often used const variant = "#{variant}"; + const problemArray = !{JSON.stringify(problemArray)}; script(src="/javascripts/components/rules.js") script(src="/javascripts/components/game.js") + script(src="/javascripts/components/problemSummary.js") script(src="/javascripts/components/problems.js") script(src="/javascripts/variant.js") -- 2.44.0