lots of changes to switch to vue

This commit is contained in:
Zutatensuppe 2021-05-13 22:45:55 +02:00
parent 5adb8806dc
commit a0118b0bdf
22 changed files with 582 additions and 920 deletions

View file

@ -17,22 +17,23 @@ export const timestamp = () => {
) )
} }
export const timeDiffStr = (from, to) => { export const durationStr = (duration) => {
let diff = to - from const d = Math.floor(duration / DAY)
const d = Math.floor(diff / DAY) duration = duration % DAY
diff = diff % DAY
const h = Math.floor(diff / HOUR) const h = Math.floor(duration / HOUR)
diff = diff % HOUR duration = duration % HOUR
const m = Math.floor(diff / MIN) const m = Math.floor(duration / MIN)
diff = diff % 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` return `${d}d ${h}h ${m}m ${s}s`
} }
export const timeDiffStr = (from, to) => durationStr(to - from)
export default { export default {
MS, MS,
SEC, SEC,
@ -42,4 +43,5 @@ export default {
timestamp, timestamp,
timeDiffStr, timeDiffStr,
durationStr,
} }

526
package-lock.json generated
View file

@ -11,7 +11,6 @@
"image-size": "^0.9.3", "image-size": "^0.9.3",
"multer": "^1.4.2", "multer": "^1.4.2",
"sharp": "^0.28.1", "sharp": "^0.28.1",
"twing": "^5.0.2",
"ws": "^7.3.1" "ws": "^7.3.1"
}, },
"devDependencies": { "devDependencies": {
@ -916,11 +915,6 @@
"@types/istanbul-lib-report": "*" "@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": { "node_modules/@types/node": {
"version": "15.0.1", "version": "15.0.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.1.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.1.tgz",
@ -1628,11 +1622,6 @@
"integrity": "sha512-c0yixVG4v9KBc/tQ2rlbB3A/bgBFRvl8h8M4IeUbqCca4gsiCfvtaheUssbnux/Mb66Vjz7x8yYjDgYcNQOhyQ==", "integrity": "sha512-c0yixVG4v9KBc/tQ2rlbB3A/bgBFRvl8h8M4IeUbqCca4gsiCfvtaheUssbnux/Mb66Vjz7x8yYjDgYcNQOhyQ==",
"dev": true "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": { "node_modules/capture-exit": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz",
@ -1825,14 +1814,6 @@
"node": ">=8" "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": { "node_modules/co": {
"version": "4.6.0", "version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
@ -2063,11 +2044,6 @@
"node": ">= 8" "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": { "node_modules/cssom": {
"version": "0.4.4", "version": "0.4.4",
"resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz",
@ -2184,14 +2160,6 @@
"node": ">=0.10.0" "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": { "node_modules/define-property": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
@ -2358,11 +2326,6 @@
"is-arrayish": "^0.2.1" "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": { "node_modules/escalade": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
@ -2421,14 +2384,6 @@
"node": ">=4" "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": { "node_modules/estraverse": {
"version": "5.2.0", "version": "5.2.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", "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", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" "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": { "node_modules/fs.realpath": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@ -3069,7 +3014,8 @@
"node_modules/graceful-fs": { "node_modules/graceful-fs": {
"version": "4.2.6", "version": "4.2.6",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", "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": { "node_modules/growly": {
"version": "1.3.0", "version": "1.3.0",
@ -3214,11 +3160,6 @@
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
"dev": true "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": { "node_modules/http-errors": {
"version": "1.7.2", "version": "1.7.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
@ -3457,17 +3398,6 @@
"node": ">=0.10.0" "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": { "node_modules/is-fullwidth-code-point": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
@ -3488,14 +3418,6 @@
"node": ">=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": { "node_modules/is-number": {
"version": "7.0.0", "version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
@ -3509,6 +3431,7 @@
"version": "2.0.4", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
"integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
"dev": true,
"dependencies": { "dependencies": {
"isobject": "^3.0.1" "isobject": "^3.0.1"
}, },
@ -3574,6 +3497,7 @@
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
"integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
"dev": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@ -4356,14 +4280,6 @@
"node": ">=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": { "node_modules/jsprim": {
"version": "1.4.1", "version": "1.4.1",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
@ -4406,14 +4322,6 @@
"node": ">=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": { "node_modules/levn": {
"version": "0.3.0", "version": "0.3.0",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
@ -4445,26 +4353,11 @@
"node": ">=8" "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": { "node_modules/lodash": {
"version": "4.17.21", "version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
}, "dev": true
"node_modules/lower-case": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz",
"integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw="
}, },
"node_modules/lru-cache": { "node_modules/lru-cache": {
"version": "6.0.0", "version": "6.0.0",
@ -4477,14 +4370,6 @@
"node": ">=10" "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": { "node_modules/make-dir": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
@ -4538,11 +4423,6 @@
"node": ">= 0.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": { "node_modules/merge-descriptors": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
@ -4741,14 +4621,6 @@
"integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
"dev": true "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": { "node_modules/node-abi": {
"version": "2.26.0", "version": "2.26.0",
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.26.0.tgz", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.26.0.tgz",
@ -4996,14 +4868,6 @@
"node": ">=0.10.0" "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": { "node_modules/object-visit": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
@ -5079,14 +4943,6 @@
"node": ">= 0.8.0" "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": { "node_modules/p-each-series": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz",
@ -5144,17 +5000,6 @@
"node": ">=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": { "node_modules/parse-json": {
"version": "5.2.0", "version": "5.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
@ -5522,11 +5367,6 @@
"node": ">=0.10.0" "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": { "node_modules/remove-trailing-separator": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
@ -5749,14 +5589,6 @@
"node": "6.* || >= 7.*" "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": { "node_modules/safe-buffer": {
"version": "5.1.2", "version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
@ -6304,14 +6136,6 @@
"node": ">=8" "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": { "node_modules/snapdragon": {
"version": "0.8.2", "version": "0.8.2",
"resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
@ -6498,6 +6322,7 @@
"version": "0.6.1", "version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@ -6969,17 +6794,6 @@
"integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==",
"dev": true "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": { "node_modules/tmpl": {
"version": "1.0.4", "version": "1.0.4",
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz",
@ -7097,70 +6911,6 @@
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
"dev": true "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": { "node_modules/type-check": {
"version": "0.3.2", "version": "0.3.2",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
@ -7248,6 +6998,7 @@
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", "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,
"engines": { "engines": {
"node": ">= 4.0.0" "node": ">= 4.0.0"
} }
@ -7339,14 +7090,6 @@
"node": ">=0.10.0" "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": { "node_modules/util-deprecate": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@ -7455,14 +7198,6 @@
"makeerror": "1.0.x" "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": { "node_modules/webidl-conversions": {
"version": "6.1.0", "version": "6.1.0",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz",
@ -8474,11 +8209,6 @@
"@types/istanbul-lib-report": "*" "@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": { "@types/node": {
"version": "15.0.1", "version": "15.0.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.1.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.1.tgz",
@ -9024,11 +8754,6 @@
"integrity": "sha512-c0yixVG4v9KBc/tQ2rlbB3A/bgBFRvl8h8M4IeUbqCca4gsiCfvtaheUssbnux/Mb66Vjz7x8yYjDgYcNQOhyQ==", "integrity": "sha512-c0yixVG4v9KBc/tQ2rlbB3A/bgBFRvl8h8M4IeUbqCca4gsiCfvtaheUssbnux/Mb66Vjz7x8yYjDgYcNQOhyQ==",
"dev": true "dev": true
}, },
"capitalize": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/capitalize/-/capitalize-1.0.0.tgz",
"integrity": "sha1-3IAsWAruEBkpAg0soUtMqKCuRL4="
},
"capture-exit": { "capture-exit": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", "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": { "co": {
"version": "4.6.0", "version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
@ -9392,11 +9112,6 @@
"which": "^2.0.1" "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": { "cssom": {
"version": "0.4.4", "version": "0.4.4",
"resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz",
@ -9491,14 +9206,6 @@
"integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==",
"dev": true "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": { "define-property": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
@ -9628,11 +9335,6 @@
"is-arrayish": "^0.2.1" "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": { "escalade": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
@ -9669,11 +9371,6 @@
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
"dev": true "dev": true
}, },
"esrever": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/esrever/-/esrever-0.2.0.tgz",
"integrity": "sha1-lunSj08bGnZ4TNXUkOquAQ50B7g="
},
"estraverse": { "estraverse": {
"version": "5.2.0", "version": "5.2.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", "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", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" "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": { "fs.realpath": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@ -10182,7 +9869,8 @@
"graceful-fs": { "graceful-fs": {
"version": "4.2.6", "version": "4.2.6",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz",
"integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==",
"dev": true
}, },
"growly": { "growly": {
"version": "1.3.0", "version": "1.3.0",
@ -10300,11 +9988,6 @@
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
"dev": true "dev": true
}, },
"htmlspecialchars": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/htmlspecialchars/-/htmlspecialchars-1.0.5.tgz",
"integrity": "sha1-9DD4wdXzcJvnvrrqaW3ASCQwajA="
},
"http-errors": { "http-errors": {
"version": "1.7.2", "version": "1.7.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
@ -10471,11 +10154,6 @@
"is-plain-object": "^2.0.4" "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": { "is-fullwidth-code-point": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "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==", "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==",
"dev": true "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": { "is-number": {
"version": "7.0.0", "version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
@ -10508,6 +10178,7 @@
"version": "2.0.4", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
"integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
"dev": true,
"requires": { "requires": {
"isobject": "^3.0.1" "isobject": "^3.0.1"
} }
@ -10560,7 +10231,8 @@
"isobject": { "isobject": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
"integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
"dev": true
}, },
"isstream": { "isstream": {
"version": "0.1.2", "version": "0.1.2",
@ -11175,14 +10847,6 @@
"minimist": "^1.2.5" "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": { "jsprim": {
"version": "1.4.1", "version": "1.4.1",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
@ -11213,11 +10877,6 @@
"integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==",
"dev": true "dev": true
}, },
"levenshtein": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/levenshtein/-/levenshtein-1.0.5.tgz",
"integrity": "sha1-ORFzepy1baNF0Aj1V4LG8TiXm6M="
},
"levn": { "levn": {
"version": "0.3.0", "version": "0.3.0",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
@ -11243,23 +10902,11 @@
"p-locate": "^4.1.0" "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": { "lodash": {
"version": "4.17.21", "version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
}, "dev": true
"lower-case": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz",
"integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw="
}, },
"lru-cache": { "lru-cache": {
"version": "6.0.0", "version": "6.0.0",
@ -11269,11 +10916,6 @@
"yallist": "^4.0.0" "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": { "make-dir": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", "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", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" "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": { "merge-descriptors": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
@ -11470,14 +11107,6 @@
"integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
"dev": true "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": { "node-abi": {
"version": "2.26.0", "version": "2.26.0",
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.26.0.tgz", "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": { "object-visit": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
@ -11746,11 +11370,6 @@
"word-wrap": "~1.2.3" "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": { "p-each-series": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", "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==", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"dev": true "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": { "parse-json": {
"version": "5.2.0", "version": "5.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
@ -12079,11 +11690,6 @@
"safe-regex": "^1.1.0" "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": { "remove-trailing-separator": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
@ -12250,11 +11856,6 @@
"integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==",
"dev": true "dev": true
}, },
"runes": {
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/runes/-/runes-0.4.3.tgz",
"integrity": "sha512-K6p9y4ZyL9wPzA+PMDloNQPfoDGTiFYDvdlXznyGKgD10BJpcAosvATKrExRKOrNLgD8E7Um7WGW0lxsnOuNLg=="
},
"safe-buffer": { "safe-buffer": {
"version": "5.1.2", "version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
@ -12695,14 +12296,6 @@
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
"dev": true "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": { "snapdragon": {
"version": "0.8.2", "version": "0.8.2",
"resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
@ -12853,7 +12446,8 @@
"source-map": { "source-map": {
"version": "0.6.1", "version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "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": { "source-map-resolve": {
"version": "0.5.3", "version": "0.5.3",
@ -13223,14 +12817,6 @@
"integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==",
"dev": true "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": { "tmpl": {
"version": "1.0.4", "version": "1.0.4",
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz",
@ -13323,65 +12909,6 @@
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
"dev": true "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": { "type-check": {
"version": "0.3.2", "version": "0.3.2",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
@ -13449,7 +12976,8 @@
"universalify": { "universalify": {
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", "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": { "unpipe": {
"version": "1.0.0", "version": "1.0.0",
@ -13523,14 +13051,6 @@
"integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
"dev": true "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": { "util-deprecate": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@ -13620,14 +13140,6 @@
"makeerror": "1.0.x" "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": { "webidl-conversions": {
"version": "6.1.0", "version": "6.1.0",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz",

View file

@ -7,7 +7,6 @@
"image-size": "^0.9.3", "image-size": "^0.9.3",
"multer": "^1.4.2", "multer": "^1.4.2",
"sharp": "^0.28.1", "sharp": "^0.28.1",
"twing": "^5.0.2",
"ws": "^7.3.1" "ws": "^7.3.1"
}, },
"devDependencies": { "devDependencies": {

View file

@ -17,10 +17,10 @@ function send(message) {
let clientSeq let clientSeq
let events let events
function connect(gameId, clientId) { function connect(address, gameId, clientId) {
clientSeq = 0 clientSeq = 0
events = {} events = {}
conn = new WsClient(WS_ADDRESS, clientId + '|' + gameId) conn = new WsClient(address, clientId + '|' + gameId)
return new Promise(resolve => { return new Promise(resolve => {
conn.connect() conn.connect()
conn.onSocket('message', async ({ data }) => { conn.onSocket('message', async ({ data }) => {
@ -46,10 +46,10 @@ function connect(gameId, clientId) {
}) })
} }
function connectReplay(gameId, clientId) { function connectReplay(address, gameId, clientId) {
clientSeq = 0 clientSeq = 0
events = {} events = {}
conn = new WsClient(WS_ADDRESS, clientId + '|' + gameId) conn = new WsClient(address, clientId + '|' + gameId)
return new Promise(resolve => { return new Promise(resolve => {
conn.connect() conn.connect()
conn.onSocket('message', async ({ data }) => { 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) { function sendClientEvent(evt) {
// when sending event, increase number of sent events // when sending event, increase number of sent events
// and add the event locally // and add the event locally
@ -78,6 +86,7 @@ function sendClientEvent(evt) {
export default { export default {
connect, connect,
connectReplay, connectReplay,
disconnect,
onServerChange, onServerChange,
sendClientEvent, sendClientEvent,
} }

View file

@ -2,6 +2,8 @@
import Time from '../common/Time.js' import Time from '../common/Time.js'
const CODE_CUSTOM_DISCONNECT = 4000
/** /**
* Wrapper around ws that * Wrapper around ws that
* - buffers 'send' until a connection is available * - buffers 'send' until a connection is available
@ -57,8 +59,16 @@ export default class WsWrapper {
} }
ws.onclose = (e) => { ws.onclose = (e) => {
this.handle = null 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) this.onclose(e)
} }
} }
disconnect() {
if (this.handle) {
this.handle.close(CODE_CUSTOM_DISCONNECT)
}
}
} }

View file

@ -2,23 +2,23 @@
import Time from './../../common/Time.js' import Time from './../../common/Time.js'
const GameTeaser = { export default {
name: 'game-teaser', name: 'game-teaser',
props: { props: {
game: Object, game: Object,
}, },
template: ` template: `
<div class="game-teaser" :style="style"> <div class="game-teaser" :style="style">
<a class="game-info" :href="'/g/' + game.id"> <router-link class="game-info" :to="{ name: 'game', params: { id: game.id } }">
<span class="game-info-text"> <span class="game-info-text">
🧩 {{game.tilesFinished}}/{{game.tilesTotal}}<br /> 🧩 {{game.tilesFinished}}/{{game.tilesTotal}}<br />
👥 {{game.players}}<br /> 👥 {{game.players}}<br />
{{time(game.started, game.finished)}}<br /> {{time(game.started, game.finished)}}<br />
</span> </span>
</a> </router-link>
<a v-if="false && game.hasReplay" class="game-replay" :href="'/replay/' + game.id"> <router-link v-if="false && game.hasReplay" class="game-replay" :to="{ name: 'replay', params: { id: game.id } }">
Watch replay Watch replay
</a> </router-link>
</div>`, </div>`,
computed: { computed: {
style() { style() {
@ -38,5 +38,3 @@ const GameTeaser = {
}, },
}, },
} }
export default GameTeaser

View file

@ -0,0 +1,26 @@
"use strict"
// ingame component
// shows the help (key bindings)
export default {
name: 'help-overlay',
template: `<div class="overlay transparent" @click="$emit('bgclick')">
<table class="help" @click.stop="">
<tr><td> Move up:</td><td><div><kbd>W</kbd>/<kbd></kbd>/🖱</div></td></tr>
<tr><td> Move down:</td><td><div><kbd>S</kbd>/<kbd></kbd>/🖱</div></td></tr>
<tr><td> Move left:</td><td><div><kbd>A</kbd>/<kbd></kbd>/🖱</div></td></tr>
<tr><td> Move right:</td><td><div><kbd>D</kbd>/<kbd></kbd>/🖱</div></td></tr>
<tr><td></td><td><div>Move faster by holding <kbd>Shift</kbd></div></td></tr>
<tr><td>🔍+ Zoom in:</td><td><div><kbd>E</kbd>/🖱-Wheel</div></td></tr>
<tr><td>🔍- Zoom out:</td><td><div><kbd>Q</kbd>/🖱-Wheel</div></td></tr>
<tr><td>🖼 Toggle preview:</td><td><div><kbd>Space</kbd></div></td></tr>
<tr><td>🧩 Toggle fixed pieces:</td><td><div><kbd>F</kbd></div></td></tr>
<tr><td>🧩 Toggle loose pieces:</td><td><div><kbd>G</kbd></div></td></tr>
</table>
</div>`,
emits: {
bgclick: null,
},
}

View file

@ -1,6 +1,6 @@
"use strict" "use strict"
const ImageTeaser = { export default {
name: 'image-teaser', name: 'image-teaser',
props: { props: {
image: Object image: Object
@ -20,5 +20,3 @@ const ImageTeaser = {
}, },
}, },
} }
export default ImageTeaser

View file

@ -0,0 +1,27 @@
"use strict"
// ingame component
// shows the preview image
export default {
name: 'preview-overlay',
template: `
<div class="overlay" @click="$emit('bgclick')">
<div class="preview">
<div class="img" :style="previewStyle"></div>
</div>
</div>`,
props: {
img: String,
},
emits: {
bgclick: null,
},
computed: {
previewStyle () {
return {
backgroundImage: `url('${this.img}')`,
}
},
},
}

View file

@ -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: `
<div class="timer">
<div>
🧩 {{piecesDone}}/{{piecesTotal}}
</div>
<div>
{{icon}} {{durationStr}}
</div>
<slot />
</div>
`,
props: {
finished: Boolean,
duration: Number,
piecesDone: Number,
piecesTotal: Number,
},
computed: {
icon () {
return this.finished ? '🏁' : '⏳'
},
durationStr () {
return Time.durationStr(this.duration)
},
}
}

View file

@ -0,0 +1,41 @@
"use strict"
// ingame component
// shows player scores
export default {
name: "scores",
template: `
<div class="scores">
<div>Scores</div>
<table>
<tr v-for="(p, idx) in actives" :key="idx" :style="{color: p.color}">
<td></td>
<td>{{p.name}}</td>
<td>{{p.points}}</td>
</tr>
<tr v-for="(p, idx) in idles" :key="idx" :style="{color: p.color}">
<td>💤</td>
<td>{{p.name}}</td>
<td>{{p.points}}</td>
</tr>
</table>
</div>
`,
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,
},
}

View file

@ -0,0 +1,38 @@
"use strict"
// ingame component
// allows to change (player) settings
export default {
name: 'settings-overlay',
template: `
<div class="overlay transparent" @click="$emit('bgclick')">
<table class="settings" @click.stop="">
<tr>
<td><label>Background: </label></td>
<td><input type="color" v-model="modelValue.background" /></td>
</tr>
<tr>
<td><label>Color: </label></td>
<td><input type="color" v-model="modelValue.color" /></td>
</tr>
<tr>
<td><label>Name: </label></td>
<td><input type="text" maxLength="16" v-model="modelValue.name" /></td>
</tr>
</table>
</div>
`,
emits: {
bgclick: null,
'update:modelValue': null,
},
props: {
modelValue: Object,
},
created () {
this.$watch('modelValue', val => {
this.$emit('update:modelValue', val)
}, { deep: true })
},
}

View file

@ -1,6 +1,6 @@
"use strict" "use strict"
const Upload = { export default {
name: 'upload', name: 'upload',
props: { props: {
accept: String, accept: String,
@ -27,5 +27,3 @@ const Upload = {
}, },
} }
} }
export default Upload

View file

@ -12,8 +12,8 @@ import fireworksController from './Fireworks.js'
import Protocol from '../common/Protocol.js' import Protocol from '../common/Protocol.js'
import Time from '../common/Time.js' import Time from '../common/Time.js'
const MODE_PLAY = 'play' export const MODE_PLAY = 'play'
const MODE_REPLAY = 'replay' export const MODE_REPLAY = 'replay'
let PIECE_VIEW_FIXED = true let PIECE_VIEW_FIXED = true
let PIECE_VIEW_LOOSE = true let PIECE_VIEW_LOOSE = true
@ -39,190 +39,6 @@ function addCanvasToDom(TARGET_EL, canvas) {
return 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() { function initme() {
let ID = localStorage.getItem('ID') let ID = localStorage.getItem('ID')
if (!ID) { if (!ID) {
@ -359,14 +175,13 @@ function EventAdapter (canvas, window, viewport) {
} }
} }
async function main(TARGET_EL) { export async function main(gameId, wsAddress, MODE, TARGET_EL, HUD) {
if (typeof GAME_ID === 'undefined') throw '[ GAME_ID not set ]' if (typeof gameId === 'undefined') throw '[ gameId not set ]'
if (typeof WS_ADDRESS === 'undefined') throw '[ WS_ADDRESS not set ]' if (typeof wsAddress === 'undefined') throw '[ wsAddress not set ]'
if (typeof MODE === 'undefined') throw '[ MODE not set ]' if (typeof MODE === 'undefined') throw '[ MODE not set ]'
if (typeof DEBUG === 'undefined') window.DEBUG = false if (typeof DEBUG === 'undefined') window.DEBUG = false
const gameId = GAME_ID
const CLIENT_ID = initme() const CLIENT_ID = initme()
const shouldDrawPlayerText = (player) => { const shouldDrawPlayerText = (player) => {
return MODE === MODE_REPLAY || player.id !== CLIENT_ID return MODE === MODE_REPLAY || player.id !== CLIENT_ID
@ -412,12 +227,12 @@ async function main(TARGET_EL) {
let TIME let TIME
if (MODE === MODE_PLAY) { 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) const gameObject = Util.decodeGame(game)
Game.setGame(gameObject.id, gameObject) Game.setGame(gameObject.id, gameObject)
TIME = () => Time.timestamp() TIME = () => Time.timestamp()
} else if (MODE === MODE_REPLAY) { } 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) const gameObject = Util.decodeGame(game)
Game.setGame(gameObject.id, gameObject) Game.setGame(gameObject.id, gameObject)
REPLAY.log = log REPLAY.log = log
@ -468,25 +283,33 @@ async function main(TARGET_EL) {
const evts = new EventAdapter(canvas, window, viewport) const evts = new EventAdapter(canvas, window, viewport)
const { const previewImageUrl = Game.getImageUrl(gameId)
bgColorPickerEl,
playerColorPickerEl,
nameChangeEl,
updateScoreBoard,
updateTimer,
udateTilesDone,
togglePreview,
replayControl,
} = addMenuToDom(TARGET_EL, Game.getImageUrl(gameId), evts.setHotkeys)
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 = () => { 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() updateTimerElements()
udateTilesDone(Game.getFinishedTileCount(gameId), Game.getTileCount(gameId)) HUD.setPiecesDone(Game.getFinishedTileCount(gameId))
HUD.setPiecesTotal(Game.getTileCount(gameId))
const ts = TIME() const ts = TIME()
updateScoreBoard(Game.getRelevantPlayers(gameId, ts), ts) HUD.setActivePlayers(activePlayers(gameId, ts))
HUD.setIdlePlayers(idlePlayers(gameId, ts))
const longFinished = !! Game.getFinishTs(gameId) const longFinished = !! Game.getFinishTs(gameId)
let finished = longFinished let finished = longFinished
@ -508,49 +331,45 @@ async function main(TARGET_EL) {
|| 'anon') || '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) { 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) setInterval(updateTimerElements, 1000)
} else if (MODE === MODE_REPLAY) { } else if (MODE === MODE_REPLAY) {
const setSpeedStatus = () => { doSetSpeedStatus()
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()
})
} }
if (MODE === MODE_PLAY) { if (MODE === MODE_PLAY) {
@ -678,7 +497,7 @@ async function main(TARGET_EL) {
Game.changePlayer(gameId, CLIENT_ID, pos) Game.changePlayer(gameId, CLIENT_ID, pos)
} }
} else if (type === Protocol.INPUT_EV_TOGGLE_PREVIEW) { } else if (type === Protocol.INPUT_EV_TOGGLE_PREVIEW) {
togglePreview() HUD.togglePreview()
} }
// LOCAL + SERVER CHANGES // LOCAL + SERVER CHANGES
@ -726,7 +545,7 @@ async function main(TARGET_EL) {
RERENDER = true RERENDER = true
} }
} else if (type === Protocol.INPUT_EV_TOGGLE_PREVIEW) { } 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') if (DEBUG) Debug.checkpoint('players done')
// DRAW PLAYERS // propagate HUD changes
// --------------------------------------------------------------- // ---------------------------------------------------------------
updateScoreBoard(Game.getRelevantPlayers(gameId, ts), ts) HUD.setActivePlayers(activePlayers(gameId, ts))
udateTilesDone(Game.getFinishedTileCount(gameId), Game.getTileCount(gameId)) HUD.setIdlePlayers(idlePlayers(gameId, ts))
if (DEBUG) Debug.checkpoint('scores done') HUD.setPiecesDone(Game.getFinishedTileCount(gameId))
if (DEBUG) Debug.checkpoint('HUD done')
// --------------------------------------------------------------- // ---------------------------------------------------------------
if (justFinished()) { if (justFinished()) {
@ -836,6 +656,23 @@ async function main(TARGET_EL) {
update: onUpdate, update: onUpdate,
render: onRender, 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,
}
}

View file

@ -14,19 +14,32 @@
import Game from "/views/Game.vue.js" import Game from "/views/Game.vue.js"
import Replay from "/views/Replay.vue.js" import Replay from "/views/Replay.vue.js"
const router = VueRouter.createRouter({ (async () => {
history: VueRouter.createWebHashHistory(), const res = await fetch(`/api/conf`)
routes: [ const conf = await res.json()
{ 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 },
],
})
const app = Vue.createApp(App) const router = VueRouter.createRouter({
app.use(router) history: VueRouter.createWebHashHistory(),
app.mount('#app') 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')
})()
</script> </script>
</body> </body>
</html> </html>

View file

@ -278,3 +278,10 @@ kbd {
top: 0; top: 0;
right: 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; }

View file

@ -1,33 +1,118 @@
"use strict" "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 { export default {
name: 'game', name: 'game',
template: `<div>{{gameData}}</div>`, components: {
PuzzleStatus,
Scores,
SettingsOverlay,
PreviewOverlay,
HelpOverlay,
},
template: `<div id="game">
<settings-overlay v-show="overlay === 'settings'" @bgclick="toggle('settings', true)" v-model="g.player" />
<preview-overlay v-show="overlay === 'preview'" @bgclick="toggle('preview', false)" :img="g.previewImageUrl" />
<help-overlay v-show="overlay === 'help'" @bgclick="toggle('help', true)" />
<puzzle-status
:finished="finished"
:duration="duration"
:piecesDone="piecesDone"
:piecesTotal="piecesTotal"
/>
<div class="menu">
<div class="tabs">
<router-link class="opener" :to="{name: 'index'}" target="_blank">🧩 Puzzles</router-link>
<div class="opener" @click="toggle('preview', false)">🖼 Preview</div>
<div class="opener" @click="toggle('settings', true)">🛠 Settings</div>
<div class="opener" @click="toggle('help', true)"> Help</div>
</div>
</div>
<scores :activePlayers="activePlayers" :idlePlayers="idlePlayers" />
</div>`,
data() { data() {
return { 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() { async mounted() {
this.$watch( if (!this.$route.params.id) {
() => this.$route.params, return
() => { this.fetchData() }, }
{ immediate: true } 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: { methods: {
async fetchData() { toggle(overlay, affectsHotkeys) {
this.gameData = null if (this.overlay === null) {
const res = await fetch(`/api/game-data/${this.$route.params.id}`) this.overlay = overlay
const json = await res.json() if (affectsHotkeys) {
this.gameData = json this.g.setHotkeys(false)
}
window.GAME_ID = this.gameData.GAME_ID } else {
window.WS_ADDRESS = this.gameData.WS_ADDRESS // could check if overlay was the provided one
window.MODE = 'play' this.overlay = null
main(this.$el) if (affectsHotkeys) {
this.g.setHotkeys(true)
}
}
}, },
}, },
} }

View file

@ -1,33 +1,142 @@
"use strict" "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 { export default {
name: 'replay', name: 'replay',
template: `<div>{{replayData}}</div>`, components: {
PuzzleStatus,
Scores,
SettingsOverlay,
PreviewOverlay,
HelpOverlay,
},
template: `<div id="replay">
<settings-overlay v-show="overlay === 'settings'" @bgclick="toggle('settings', true)" v-model="g.player" />
<preview-overlay v-show="overlay === 'preview'" @bgclick="toggle('preview', false)" :img="g.previewImageUrl" />
<help-overlay v-show="overlay === 'help'" @bgclick="toggle('help', true)" />
<puzzle-status
:finished="finished"
:duration="duration"
:piecesDone="piecesDone"
:piecesTotal="piecesTotal"
>
<div>
<div>{{replayText}}</div>
<button class="btn" @click="g.replayOnSpeedUp()"></button>
<button class="btn" @click="g.replayOnSpeedDown()"></button>
<button class="btn" @click="g.replayOnPauseToggle()"></button>
</div>
</puzzle-status>
<div class="menu">
<div class="tabs">
<router-link class="opener" :to="{name: 'index'}" target="_blank">🧩 Puzzles</router-link>
<div class="opener" @click="toggle('preview', false)">🖼 Preview</div>
<div class="opener" @click="toggle('settings', true)">🛠 Settings</div>
<div class="opener" @click="toggle('help', true)"> Help</div>
</div>
</div>
<scores :activePlayers="activePlayers" :idlePlayers="idlePlayers" />
</div>`,
data() { data() {
return { 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() { async mounted() {
this.$watch( if (!this.$route.params.id) {
() => this.$route.params, return
() => { this.fetchData() }, }
{ immediate: true } 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: { methods: {
async fetchData() { toggle(overlay, affectsHotkeys) {
this.replayData = null if (this.overlay === null) {
const res = await fetch(`/api/replay-data/${this.$route.params.id}`) this.overlay = overlay
const json = await res.json() if (affectsHotkeys) {
this.replayData = json this.g.setHotkeys(false)
}
window.GAME_ID = this.gameData.GAME_ID } else {
window.WS_ADDRESS = this.gameData.WS_ADDRESS // could check if overlay was the provided one
window.MODE = 'replay' this.overlay = null
main(this.$el) if (affectsHotkeys) {
this.g.setHotkeys(true)
}
}
}, },
}, },
} }

View file

@ -11,4 +11,3 @@ export const UPLOAD_DIR = `${BASE_DIR}/data/uploads`
export const UPLOAD_URL = `/uploads` export const UPLOAD_URL = `/uploads`
export const COMMON_DIR = `${BASE_DIR}/common/` export const COMMON_DIR = `${BASE_DIR}/common/`
export const PUBLIC_DIR = `${BASE_DIR}/public/` export const PUBLIC_DIR = `${BASE_DIR}/public/`
export const TEMPLATE_DIR = `${BASE_DIR}/templates`

View file

@ -6,7 +6,6 @@ import config from './../config.js'
import Protocol from './../common/Protocol.js' import Protocol from './../common/Protocol.js'
import Util, { logger } from './../common/Util.js' import Util, { logger } from './../common/Util.js'
import Game from './Game.js' import Game from './Game.js'
import twing from 'twing'
import bodyParser from 'body-parser' import bodyParser from 'body-parser'
import v8 from 'v8' import v8 from 'v8'
import GameLog from './GameLog.js' import GameLog from './GameLog.js'
@ -18,7 +17,6 @@ import {
UPLOAD_URL, UPLOAD_URL,
COMMON_DIR, COMMON_DIR,
PUBLIC_DIR, PUBLIC_DIR,
TEMPLATE_DIR,
} from './Dirs.js' } from './Dirs.js'
import GameCommon from '../common/GameCommon.js' import GameCommon from '../common/GameCommon.js'
import GameStorage from './GameStorage.js' import GameStorage from './GameStorage.js'
@ -37,36 +35,8 @@ const storage = multer.diskStorage({
}) })
const upload = multer({storage}).single('file'); const upload = multer({storage}).single('file');
const render = async (template, data) => { app.get('/api/conf', (req, res) => {
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) => {
res.send({ 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, WS_ADDRESS: config.ws.connectstring,
}) })
}) })

View file

@ -1,23 +0,0 @@
<html>
<head>
<link rel="stylesheet" href="/style.css" />
<script src="https://unpkg.com/vue@3.0.11"></script>
<style type="text/css">
html,
body {
overflow: hidden;
}
</style>
<title>🧩 jigsaw.hyottoko.club</title>
</head>
<body>
<div id="app"></div>
<script>window.GAME_ID = '{{GAME_ID}}'</script>
<script>window.WS_ADDRESS = '{{WS_ADDRESS}}'</script>
<script>window.MODE = 'play'</script>
<script type="module">
import main from '/game.js'
main(document.getElementById('app'))
</script>
</body>
</html>

View file

@ -1,29 +0,0 @@
<html>
<head>
<link rel="stylesheet" href="/style.css" />
<script src="https://unpkg.com/vue@3.0.11"></script>
<style type="text/css">
html,
body {
overflow: hidden;
}
canvas {
cursor: grab;
}
canvas.loaded {
cursor: grab;
}
</style>
<title>🧩 jigsaw.hyottoko.club</title>
</head>
<body>
<div id="app"></div>
<script>window.GAME_ID = '{{GAME_ID}}'</script>
<script>window.WS_ADDRESS = '{{WS_ADDRESS}}'</script>
<script>window.MODE = 'replay'</script>
<script type="module">
import main from '/game.js'
main(document.getElementById('app'))
</script>
</body>
</html>