From 3218fd131939b3e777d90c80da7bd3b617083d46 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 30 Sep 2021 14:15:34 -0600 Subject: [PATCH 001/255] Add launch configuration for VSCode --- .vscode/launch.json | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..b05495f --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,17 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "pwa-node", + "request": "launch", + "name": "Launch Program", + "skipFiles": [ + "/**" + ], + "program": "${workspaceFolder}/bin/www" + } + ] +} \ No newline at end of file From dc3897fe77b0cb61fb53533f714c2bbfb6a42ff3 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 30 Sep 2021 14:18:03 -0600 Subject: [PATCH 002/255] Add package-lock.json after npm install --- package-lock.json | 1801 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 1642 insertions(+), 159 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4a8d7ba..068a1c7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,8 +1,1491 @@ { "name": "demo", "version": "0.0.0", - "lockfileVersion": 1, + "lockfileVersion": 2, "requires": true, + "packages": { + "": { + "name": "demo", + "version": "0.0.0", + "dependencies": { + "cookie-parser": "~1.4.3", + "debug": "~2.6.9", + "express": "~4.16.0", + "http-errors": "~1.6.2", + "morgan": "~1.9.0", + "pug": "2.0.0-beta11" + }, + "devDependencies": { + "mocha": "^5.1.1", + "supertest": "^3.0.0" + } + }, + "node_modules/@types/babel-types": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/@types/babel-types/-/babel-types-7.0.4.tgz", + "integrity": "sha512-WiZhq3SVJHFRgRYLXvpf65XnV6ipVHhnNaNvE8yCimejrGglkg38kEj0JcizqwSHxmPSjcTlig/6JouxLGEhGw==" + }, + "node_modules/@types/babylon": { + "version": "6.16.3", + "resolved": "https://registry.npmjs.org/@types/babylon/-/babylon-6.16.3.tgz", + "integrity": "sha512-lyJ8sW1PbY3uwuvpOBZ9zMYKshMnQpXmeDHh8dj9j2nJm/xrW0FgB5gLSYOArj5X0IfaXnmhFoJnhS4KbqIMug==", + "dependencies": { + "@types/babel-types": "*" + } + }, + "node_modules/accepts": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", + "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", + "dependencies": { + "mime-types": "~2.1.18", + "negotiator": "0.6.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-3.1.0.tgz", + "integrity": "sha1-/YJw9x+7SZawBPqIDuXUZXOnMb8=", + "dependencies": { + "acorn": "^4.0.4" + } + }, + "node_modules/acorn-globals/node_modules/acorn": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "dependencies": { + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "engines": { + "node": ">=0.4.2" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "node_modules/babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "dependencies": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } + }, + "node_modules/babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "dependencies": { + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" + } + }, + "node_modules/babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "bin": { + "babylon": "bin/babylon.js" + } + }, + "node_modules/balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "node_modules/basic-auth": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.0.tgz", + "integrity": "sha1-AV2z81PgLlY3d1X5YnQuiYHnu7o=", + "dependencies": { + "safe-buffer": "5.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", + "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", + "dependencies": { + "bytes": "3.0.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.1", + "http-errors": "~1.6.2", + "iconv-lite": "0.4.19", + "on-finished": "~2.3.0", + "qs": "6.5.1", + "raw-body": "2.3.2", + "type-is": "~1.6.15" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "dependencies": { + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/character-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", + "integrity": "sha1-x84o821LzZdE5f/CxfzeHHMmH8A=", + "dependencies": { + "is-regex": "^1.0.3" + } + }, + "node_modules/clean-css": { + "version": "3.4.28", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-3.4.28.tgz", + "integrity": "sha1-vxlF6C/ICPVWlebd6uwBQA79A/8=", + "dependencies": { + "commander": "2.8.x", + "source-map": "0.4.x" + }, + "bin": { + "cleancss": "bin/cleancss" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "dependencies": { + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" + } + }, + "node_modules/combined-stream": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", + "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", + "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", + "dependencies": { + "graceful-readlink": ">= 1.0.0" + }, + "engines": { + "node": ">= 0.6.x" + } + }, + "node_modules/component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "node_modules/constantinople": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-3.1.2.tgz", + "integrity": "sha512-yePcBqEFhLOqSBtwYOGGS1exHo/s1xjekXiinh4itpNQGCu4KA1euPh1fg07N2wMITZXQkBz75Ntdt1ctGZouw==", + "dependencies": { + "@types/babel-types": "^7.0.0", + "@types/babylon": "^6.16.2", + "babel-types": "^6.26.0", + "babylon": "^6.18.0" + } + }, + "node_modules/content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-parser": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.3.tgz", + "integrity": "sha1-D+MfoZ0AC5X0qt8fU/3CuKIDuqU=", + "dependencies": { + "cookie": "0.3.1", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "node_modules/cookiejar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", + "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==", + "dev": true + }, + "node_modules/core-js": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", + "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==", + "deprecated": "core-js@<3.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Please, upgrade your dependencies to the actual version of core-js." + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "node_modules/diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/doctypes": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", + "integrity": "sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk=" + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.16.3", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz", + "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=", + "dependencies": { + "accepts": "~1.3.5", + "array-flatten": "1.1.1", + "body-parser": "1.18.2", + "content-disposition": "0.5.2", + "content-type": "~1.0.4", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.1.1", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.3", + "qs": "6.5.1", + "range-parser": "~1.2.0", + "safe-buffer": "5.1.1", + "send": "0.16.2", + "serve-static": "1.13.2", + "setprototypeof": "1.1.0", + "statuses": "~1.4.0", + "type-is": "~1.6.16", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "node_modules/finalhandler": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.4.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/form-data": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", + "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/formidable": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.1.tgz", + "integrity": "sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg==", + "deprecated": "Please upgrade to the upcoming v2, currently (until end of February) install using formidable@canary!", + "dev": true + }, + "node_modules/forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/graceful-readlink": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" + }, + "node_modules/growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true, + "engines": { + "node": ">=4.x" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", + "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "node_modules/ipaddr.js": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", + "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "node_modules/is-expression": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-3.0.0.tgz", + "integrity": "sha1-Oayqa+f9HzRx3ELHQW5hwkMXrJ8=", + "dependencies": { + "acorn": "~4.0.2", + "object-assign": "^4.0.1" + } + }, + "node_modules/is-expression/node_modules/acorn": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" + }, + "node_modules/is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "dependencies": { + "has": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "node_modules/js-stringify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", + "integrity": "sha1-Fzb939lyTyijaCrcYjCufk6Weds=" + }, + "node_modules/jstransformer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", + "integrity": "sha1-7Yvwkh4vPx7U1cGkT2hwntJHIsM=", + "dependencies": { + "is-promise": "^2.0.0", + "promise": "^7.0.1" + } + }, + "node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" + }, + "node_modules/longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", + "bin": { + "mime": "cli.js" + } + }, + "node_modules/mime-db": { + "version": "1.35.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.35.0.tgz", + "integrity": "sha512-JWT/IcCTsB0Io3AhWUMjRqucrHSPsSf2xKLaRldJVULioggvkJvggZ3VXNNSRkCddE6D+BUI4HEIZIA2OjwIvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.19", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.19.tgz", + "integrity": "sha512-P1tKYHVSZ6uFo26mtnve4HQFE3koh1UWVkp8YUC+ESBHe945xWSoXuHHiGarDqcEZ+whpCDnlNw5LON0kLo+sw==", + "dependencies": { + "mime-db": "~1.35.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "node_modules/mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)", + "dev": true, + "dependencies": { + "minimist": "0.0.8" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mocha": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", + "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", + "dev": true, + "dependencies": { + "browser-stdout": "1.3.1", + "commander": "2.15.1", + "debug": "3.1.0", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "glob": "7.1.2", + "growl": "1.10.5", + "he": "1.1.1", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "supports-color": "5.4.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/mocha/node_modules/commander": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "dev": true + }, + "node_modules/mocha/node_modules/debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/morgan": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.0.tgz", + "integrity": "sha1-0B+mxlhZt2/PMbPLU6OCGjEdgFE=", + "dependencies": { + "basic-auth": "~2.0.0", + "debug": "2.6.9", + "depd": "~1.1.1", + "on-finished": "~2.3.0", + "on-headers": "~1.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node_modules/negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz", + "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-parse": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", + "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=" + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "node_modules/process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true + }, + "node_modules/promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "dependencies": { + "asap": "~2.0.3" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", + "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", + "dependencies": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.8.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pug": { + "version": "2.0.0-beta11", + "resolved": "https://registry.npmjs.org/pug/-/pug-2.0.0-beta11.tgz", + "integrity": "sha1-Favmr1AEx+LPRhPksnRlyVRrXwE=", + "dependencies": { + "pug-code-gen": "^1.1.1", + "pug-filters": "^2.1.1", + "pug-lexer": "^3.0.0", + "pug-linker": "^2.0.2", + "pug-load": "^2.0.5", + "pug-parser": "^2.0.2", + "pug-runtime": "^2.0.3", + "pug-strip-comments": "^1.0.2" + } + }, + "node_modules/pug-attrs": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-2.0.3.tgz", + "integrity": "sha1-owlflw5kFR972tlX7vVftdeQXRU=", + "dependencies": { + "constantinople": "^3.0.1", + "js-stringify": "^1.0.1", + "pug-runtime": "^2.0.4" + } + }, + "node_modules/pug-code-gen": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-1.1.1.tgz", + "integrity": "sha1-HPcnRO8qA56uajNAyqoRBYcSWOg=", + "dependencies": { + "constantinople": "^3.0.1", + "doctypes": "^1.1.0", + "js-stringify": "^1.0.1", + "pug-attrs": "^2.0.2", + "pug-error": "^1.3.2", + "pug-runtime": "^2.0.3", + "void-elements": "^2.0.1", + "with": "^5.0.0" + } + }, + "node_modules/pug-error": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-1.3.2.tgz", + "integrity": "sha1-U659nSm7A89WRJOgJhCfVMR/XyY=" + }, + "node_modules/pug-filters": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-2.1.5.tgz", + "integrity": "sha512-xkw71KtrC4sxleKiq+cUlQzsiLn8pM5+vCgkChW2E6oNOzaqTSIBKIQ5cl4oheuDzvJYCTSYzRaVinMUrV4YLQ==", + "dependencies": { + "clean-css": "^3.3.0", + "constantinople": "^3.0.1", + "jstransformer": "1.0.0", + "pug-error": "^1.3.2", + "pug-walk": "^1.1.5", + "resolve": "^1.1.6", + "uglify-js": "^2.6.1" + } + }, + "node_modules/pug-lexer": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-3.1.0.tgz", + "integrity": "sha1-/QhzdtSmdbT1n4/vQiiDQ06VgaI=", + "dependencies": { + "character-parser": "^2.1.1", + "is-expression": "^3.0.0", + "pug-error": "^1.3.2" + } + }, + "node_modules/pug-linker": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-2.0.3.tgz", + "integrity": "sha1-szH/olc33eacEntWwQ/xf652bco=", + "dependencies": { + "pug-error": "^1.3.2", + "pug-walk": "^1.1.2" + } + }, + "node_modules/pug-load": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-2.0.11.tgz", + "integrity": "sha1-5kjlftET/iwfRdV4WOorrWvAFSc=", + "dependencies": { + "object-assign": "^4.1.0", + "pug-walk": "^1.1.7" + } + }, + "node_modules/pug-parser": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-2.0.2.tgz", + "integrity": "sha1-U6aAz9BQOdywwn0CkJS8SnkmibA=", + "dependencies": { + "pug-error": "^1.3.2", + "token-stream": "0.0.1" + } + }, + "node_modules/pug-runtime": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-2.0.4.tgz", + "integrity": "sha1-4XjhvaaKsujArPybztLFT9iM61g=" + }, + "node_modules/pug-strip-comments": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-1.0.3.tgz", + "integrity": "sha1-8VWVkiBu3G+FMQ2s9K+0igJa9Z8=", + "dependencies": { + "pug-error": "^1.3.2" + } + }, + "node_modules/pug-walk": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-1.1.7.tgz", + "integrity": "sha1-wA1cUSi6xYBr7BXSt+fNq+QlMfM=" + }, + "node_modules/qs": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", + "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", + "dependencies": { + "bytes": "3.0.0", + "http-errors": "1.6.2", + "iconv-lite": "0.4.19", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/depd": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", + "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body/node_modules/http-errors": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", + "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", + "dependencies": { + "depd": "1.1.1", + "inherits": "2.0.3", + "setprototypeof": "1.0.3", + "statuses": ">= 1.3.1 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body/node_modules/setprototypeof": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", + "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" + }, + "node_modules/readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/resolve": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", + "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==", + "dependencies": { + "path-parse": "^1.0.5" + } + }, + "node_modules/right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", + "dependencies": { + "align-text": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + }, + "node_modules/send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "dependencies": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", + "send": "0.16.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "node_modules/source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "dependencies": { + "amdefine": ">=0.0.4" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/superagent": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.2.tgz", + "integrity": "sha512-gVH4QfYHcY3P0f/BZzavLreHW3T1v7hG9B+hpMQotGQqurOvhv87GcMCd6LWySmBuf+BDR44TQd0aISjVHLeNQ==", + "dev": true, + "dependencies": { + "component-emitter": "^1.2.0", + "cookiejar": "^2.1.0", + "debug": "^3.1.0", + "extend": "^3.0.0", + "form-data": "^2.3.1", + "formidable": "^1.1.1", + "methods": "^1.1.1", + "mime": "^1.4.1", + "qs": "^6.5.1", + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/superagent/node_modules/debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/supertest": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-3.1.0.tgz", + "integrity": "sha512-O44AMnmJqx294uJQjfUmEyYOg7d9mylNFsMw/Wkz4evKd1njyPrtCN+U6ZIC7sKtfEVQhfTqFFijlXx8KP/Czw==", + "dev": true, + "dependencies": { + "methods": "~1.1.2", + "superagent": "3.8.2" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/supports-color": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/token-stream": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-0.0.1.tgz", + "integrity": "sha1-zu78cXp2xDFvEm0LnbqlXX598Bo=" + }, + "node_modules/type-is": { + "version": "1.6.16", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", + "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.18" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "dependencies": { + "source-map": "~0.5.1", + "yargs": "~3.10.0" + }, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + }, + "optionalDependencies": { + "uglify-to-browserify": "~1.0.0" + } + }, + "node_modules/uglify-js/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", + "optional": true + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/with": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/with/-/with-5.1.1.tgz", + "integrity": "sha1-+k2qktrzLE6pTtRTyB8EaGtXXf4=", + "dependencies": { + "acorn": "^3.1.0", + "acorn-globals": "^3.0.0" + } + }, + "node_modules/wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "node_modules/yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "dependencies": { + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "window-size": "0.1.0" + } + } + }, "dependencies": { "@types/babel-types": { "version": "7.0.4", @@ -14,7 +1497,7 @@ "resolved": "https://registry.npmjs.org/@types/babylon/-/babylon-6.16.3.tgz", "integrity": "sha512-lyJ8sW1PbY3uwuvpOBZ9zMYKshMnQpXmeDHh8dj9j2nJm/xrW0FgB5gLSYOArj5X0IfaXnmhFoJnhS4KbqIMug==", "requires": { - "@types/babel-types": "7.0.4" + "@types/babel-types": "*" } }, "accepts": { @@ -22,7 +1505,7 @@ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", "requires": { - "mime-types": "2.1.19", + "mime-types": "~2.1.18", "negotiator": "0.6.1" } }, @@ -36,7 +1519,7 @@ "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-3.1.0.tgz", "integrity": "sha1-/YJw9x+7SZawBPqIDuXUZXOnMb8=", "requires": { - "acorn": "4.0.13" + "acorn": "^4.0.4" }, "dependencies": { "acorn": { @@ -51,9 +1534,9 @@ "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", "requires": { - "kind-of": "3.2.2", - "longest": "1.0.1", - "repeat-string": "1.6.1" + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.2" } }, "amdefine": { @@ -82,8 +1565,8 @@ "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "requires": { - "core-js": "2.5.7", - "regenerator-runtime": "0.11.1" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } }, "babel-types": { @@ -91,10 +1574,10 @@ "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", "requires": { - "babel-runtime": "6.26.0", - "esutils": "2.0.2", - "lodash": "4.17.10", - "to-fast-properties": "1.0.3" + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" } }, "babylon": { @@ -122,15 +1605,15 @@ "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", "requires": { "bytes": "3.0.0", - "content-type": "1.0.4", + "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "1.1.2", - "http-errors": "1.6.3", + "depd": "~1.1.1", + "http-errors": "~1.6.2", "iconv-lite": "0.4.19", - "on-finished": "2.3.0", + "on-finished": "~2.3.0", "qs": "6.5.1", "raw-body": "2.3.2", - "type-is": "1.6.16" + "type-is": "~1.6.15" } }, "brace-expansion": { @@ -139,7 +1622,7 @@ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { - "balanced-match": "1.0.0", + "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, @@ -164,8 +1647,8 @@ "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", "requires": { - "align-text": "0.1.4", - "lazy-cache": "1.0.4" + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" } }, "character-parser": { @@ -173,7 +1656,7 @@ "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", "integrity": "sha1-x84o821LzZdE5f/CxfzeHHMmH8A=", "requires": { - "is-regex": "1.0.4" + "is-regex": "^1.0.3" } }, "clean-css": { @@ -181,8 +1664,8 @@ "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-3.4.28.tgz", "integrity": "sha1-vxlF6C/ICPVWlebd6uwBQA79A/8=", "requires": { - "commander": "2.8.1", - "source-map": "0.4.4" + "commander": "2.8.x", + "source-map": "0.4.x" } }, "cliui": { @@ -190,8 +1673,8 @@ "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", "requires": { - "center-align": "0.1.3", - "right-align": "0.1.3", + "center-align": "^0.1.1", + "right-align": "^0.1.1", "wordwrap": "0.0.2" } }, @@ -201,7 +1684,7 @@ "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", "dev": true, "requires": { - "delayed-stream": "1.0.0" + "delayed-stream": "~1.0.0" } }, "commander": { @@ -209,7 +1692,7 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", "requires": { - "graceful-readlink": "1.0.1" + "graceful-readlink": ">= 1.0.0" } }, "component-emitter": { @@ -229,10 +1712,10 @@ "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-3.1.2.tgz", "integrity": "sha512-yePcBqEFhLOqSBtwYOGGS1exHo/s1xjekXiinh4itpNQGCu4KA1euPh1fg07N2wMITZXQkBz75Ntdt1ctGZouw==", "requires": { - "@types/babel-types": "7.0.4", - "@types/babylon": "6.16.3", - "babel-types": "6.26.0", - "babylon": "6.18.0" + "@types/babel-types": "^7.0.0", + "@types/babylon": "^6.16.2", + "babel-types": "^6.26.0", + "babylon": "^6.18.0" } }, "content-disposition": { @@ -357,36 +1840,36 @@ "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz", "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=", "requires": { - "accepts": "1.3.5", + "accepts": "~1.3.5", "array-flatten": "1.1.1", "body-parser": "1.18.2", "content-disposition": "0.5.2", - "content-type": "1.0.4", + "content-type": "~1.0.4", "cookie": "0.3.1", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "1.1.2", - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "etag": "1.8.1", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", "finalhandler": "1.1.1", "fresh": "0.5.2", "merge-descriptors": "1.0.1", - "methods": "1.1.2", - "on-finished": "2.3.0", - "parseurl": "1.3.2", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", "path-to-regexp": "0.1.7", - "proxy-addr": "2.0.4", + "proxy-addr": "~2.0.3", "qs": "6.5.1", - "range-parser": "1.2.0", + "range-parser": "~1.2.0", "safe-buffer": "5.1.1", "send": "0.16.2", "serve-static": "1.13.2", "setprototypeof": "1.1.0", - "statuses": "1.4.0", - "type-is": "1.6.16", + "statuses": "~1.4.0", + "type-is": "~1.6.16", "utils-merge": "1.0.1", - "vary": "1.1.2" + "vary": "~1.1.2" } }, "extend": { @@ -401,12 +1884,12 @@ "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", "requires": { "debug": "2.6.9", - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "on-finished": "2.3.0", - "parseurl": "1.3.2", - "statuses": "1.4.0", - "unpipe": "1.0.0" + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.4.0", + "unpipe": "~1.0.0" } }, "form-data": { @@ -415,9 +1898,9 @@ "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", "dev": true, "requires": { - "asynckit": "0.4.0", + "asynckit": "^0.4.0", "combined-stream": "1.0.6", - "mime-types": "2.1.19" + "mime-types": "^2.1.12" } }, "formidable": { @@ -453,12 +1936,12 @@ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, "graceful-readlink": { @@ -477,7 +1960,7 @@ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "requires": { - "function-bind": "1.1.1" + "function-bind": "^1.1.1" } }, "has-flag": { @@ -497,10 +1980,10 @@ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "requires": { - "depd": "1.1.2", + "depd": "~1.1.2", "inherits": "2.0.3", "setprototypeof": "1.1.0", - "statuses": "1.4.0" + "statuses": ">= 1.4.0 < 2" } }, "iconv-lite": { @@ -514,8 +1997,8 @@ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" + "once": "^1.3.0", + "wrappy": "1" } }, "inherits": { @@ -538,8 +2021,8 @@ "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-3.0.0.tgz", "integrity": "sha1-Oayqa+f9HzRx3ELHQW5hwkMXrJ8=", "requires": { - "acorn": "4.0.13", - "object-assign": "4.1.1" + "acorn": "~4.0.2", + "object-assign": "^4.0.1" }, "dependencies": { "acorn": { @@ -559,7 +2042,7 @@ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", "requires": { - "has": "1.0.3" + "has": "^1.0.1" } }, "isarray": { @@ -578,8 +2061,8 @@ "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", "integrity": "sha1-7Yvwkh4vPx7U1cGkT2hwntJHIsM=", "requires": { - "is-promise": "2.1.0", - "promise": "7.3.1" + "is-promise": "^2.0.0", + "promise": "^7.0.1" } }, "kind-of": { @@ -587,7 +2070,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } }, "lazy-cache": { @@ -635,7 +2118,7 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.19.tgz", "integrity": "sha512-P1tKYHVSZ6uFo26mtnve4HQFE3koh1UWVkp8YUC+ESBHe945xWSoXuHHiGarDqcEZ+whpCDnlNw5LON0kLo+sw==", "requires": { - "mime-db": "1.35.0" + "mime-db": "~1.35.0" } }, "minimatch": { @@ -644,7 +2127,7 @@ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { - "brace-expansion": "1.1.11" + "brace-expansion": "^1.1.7" } }, "minimist": { @@ -703,11 +2186,11 @@ "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.0.tgz", "integrity": "sha1-0B+mxlhZt2/PMbPLU6OCGjEdgFE=", "requires": { - "basic-auth": "2.0.0", + "basic-auth": "~2.0.0", "debug": "2.6.9", - "depd": "1.1.2", - "on-finished": "2.3.0", - "on-headers": "1.0.1" + "depd": "~1.1.1", + "on-finished": "~2.3.0", + "on-headers": "~1.0.1" } }, "ms": { @@ -744,7 +2227,7 @@ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { - "wrappy": "1.0.2" + "wrappy": "1" } }, "parseurl": { @@ -779,7 +2262,7 @@ "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", "requires": { - "asap": "2.0.6" + "asap": "~2.0.3" } }, "proxy-addr": { @@ -787,7 +2270,7 @@ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", "requires": { - "forwarded": "0.1.2", + "forwarded": "~0.1.2", "ipaddr.js": "1.8.0" } }, @@ -796,14 +2279,14 @@ "resolved": "https://registry.npmjs.org/pug/-/pug-2.0.0-beta11.tgz", "integrity": "sha1-Favmr1AEx+LPRhPksnRlyVRrXwE=", "requires": { - "pug-code-gen": "1.1.1", - "pug-filters": "2.1.5", - "pug-lexer": "3.1.0", - "pug-linker": "2.0.3", - "pug-load": "2.0.11", - "pug-parser": "2.0.2", - "pug-runtime": "2.0.4", - "pug-strip-comments": "1.0.3" + "pug-code-gen": "^1.1.1", + "pug-filters": "^2.1.1", + "pug-lexer": "^3.0.0", + "pug-linker": "^2.0.2", + "pug-load": "^2.0.5", + "pug-parser": "^2.0.2", + "pug-runtime": "^2.0.3", + "pug-strip-comments": "^1.0.2" } }, "pug-attrs": { @@ -811,9 +2294,9 @@ "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-2.0.3.tgz", "integrity": "sha1-owlflw5kFR972tlX7vVftdeQXRU=", "requires": { - "constantinople": "3.1.2", - "js-stringify": "1.0.2", - "pug-runtime": "2.0.4" + "constantinople": "^3.0.1", + "js-stringify": "^1.0.1", + "pug-runtime": "^2.0.4" } }, "pug-code-gen": { @@ -821,14 +2304,14 @@ "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-1.1.1.tgz", "integrity": "sha1-HPcnRO8qA56uajNAyqoRBYcSWOg=", "requires": { - "constantinople": "3.1.2", - "doctypes": "1.1.0", - "js-stringify": "1.0.2", - "pug-attrs": "2.0.3", - "pug-error": "1.3.2", - "pug-runtime": "2.0.4", - "void-elements": "2.0.1", - "with": "5.1.1" + "constantinople": "^3.0.1", + "doctypes": "^1.1.0", + "js-stringify": "^1.0.1", + "pug-attrs": "^2.0.2", + "pug-error": "^1.3.2", + "pug-runtime": "^2.0.3", + "void-elements": "^2.0.1", + "with": "^5.0.0" } }, "pug-error": { @@ -841,13 +2324,13 @@ "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-2.1.5.tgz", "integrity": "sha512-xkw71KtrC4sxleKiq+cUlQzsiLn8pM5+vCgkChW2E6oNOzaqTSIBKIQ5cl4oheuDzvJYCTSYzRaVinMUrV4YLQ==", "requires": { - "clean-css": "3.4.28", - "constantinople": "3.1.2", + "clean-css": "^3.3.0", + "constantinople": "^3.0.1", "jstransformer": "1.0.0", - "pug-error": "1.3.2", - "pug-walk": "1.1.7", - "resolve": "1.8.1", - "uglify-js": "2.8.29" + "pug-error": "^1.3.2", + "pug-walk": "^1.1.5", + "resolve": "^1.1.6", + "uglify-js": "^2.6.1" } }, "pug-lexer": { @@ -855,9 +2338,9 @@ "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-3.1.0.tgz", "integrity": "sha1-/QhzdtSmdbT1n4/vQiiDQ06VgaI=", "requires": { - "character-parser": "2.2.0", - "is-expression": "3.0.0", - "pug-error": "1.3.2" + "character-parser": "^2.1.1", + "is-expression": "^3.0.0", + "pug-error": "^1.3.2" } }, "pug-linker": { @@ -865,8 +2348,8 @@ "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-2.0.3.tgz", "integrity": "sha1-szH/olc33eacEntWwQ/xf652bco=", "requires": { - "pug-error": "1.3.2", - "pug-walk": "1.1.7" + "pug-error": "^1.3.2", + "pug-walk": "^1.1.2" } }, "pug-load": { @@ -874,8 +2357,8 @@ "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-2.0.11.tgz", "integrity": "sha1-5kjlftET/iwfRdV4WOorrWvAFSc=", "requires": { - "object-assign": "4.1.1", - "pug-walk": "1.1.7" + "object-assign": "^4.1.0", + "pug-walk": "^1.1.7" } }, "pug-parser": { @@ -883,7 +2366,7 @@ "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-2.0.2.tgz", "integrity": "sha1-U6aAz9BQOdywwn0CkJS8SnkmibA=", "requires": { - "pug-error": "1.3.2", + "pug-error": "^1.3.2", "token-stream": "0.0.1" } }, @@ -897,7 +2380,7 @@ "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-1.0.3.tgz", "integrity": "sha1-8VWVkiBu3G+FMQ2s9K+0igJa9Z8=", "requires": { - "pug-error": "1.3.2" + "pug-error": "^1.3.2" } }, "pug-walk": { @@ -939,7 +2422,7 @@ "depd": "1.1.1", "inherits": "2.0.3", "setprototypeof": "1.0.3", - "statuses": "1.4.0" + "statuses": ">= 1.3.1 < 2" } }, "setprototypeof": { @@ -955,13 +2438,13 @@ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "2.0.0", - "safe-buffer": "5.1.1", - "string_decoder": "1.1.1", - "util-deprecate": "1.0.2" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, "regenerator-runtime": { @@ -979,7 +2462,7 @@ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==", "requires": { - "path-parse": "1.0.5" + "path-parse": "^1.0.5" } }, "right-align": { @@ -987,7 +2470,7 @@ "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", "requires": { - "align-text": "0.1.4" + "align-text": "^0.1.1" } }, "safe-buffer": { @@ -1001,18 +2484,18 @@ "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", "requires": { "debug": "2.6.9", - "depd": "1.1.2", - "destroy": "1.0.4", - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "etag": "1.8.1", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "1.6.3", + "http-errors": "~1.6.2", "mime": "1.4.1", "ms": "2.0.0", - "on-finished": "2.3.0", - "range-parser": "1.2.0", - "statuses": "1.4.0" + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" } }, "serve-static": { @@ -1020,9 +2503,9 @@ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", "requires": { - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "parseurl": "1.3.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", "send": "0.16.2" } }, @@ -1036,7 +2519,7 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", "requires": { - "amdefine": "1.0.1" + "amdefine": ">=0.0.4" } }, "statuses": { @@ -1050,7 +2533,7 @@ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { - "safe-buffer": "5.1.1" + "safe-buffer": "~5.1.0" } }, "superagent": { @@ -1059,16 +2542,16 @@ "integrity": "sha512-gVH4QfYHcY3P0f/BZzavLreHW3T1v7hG9B+hpMQotGQqurOvhv87GcMCd6LWySmBuf+BDR44TQd0aISjVHLeNQ==", "dev": true, "requires": { - "component-emitter": "1.2.1", - "cookiejar": "2.1.2", - "debug": "3.1.0", - "extend": "3.0.2", - "form-data": "2.3.2", - "formidable": "1.2.1", - "methods": "1.1.2", - "mime": "1.4.1", - "qs": "6.5.1", - "readable-stream": "2.3.6" + "component-emitter": "^1.2.0", + "cookiejar": "^2.1.0", + "debug": "^3.1.0", + "extend": "^3.0.0", + "form-data": "^2.3.1", + "formidable": "^1.1.1", + "methods": "^1.1.1", + "mime": "^1.4.1", + "qs": "^6.5.1", + "readable-stream": "^2.0.5" }, "dependencies": { "debug": { @@ -1088,7 +2571,7 @@ "integrity": "sha512-O44AMnmJqx294uJQjfUmEyYOg7d9mylNFsMw/Wkz4evKd1njyPrtCN+U6ZIC7sKtfEVQhfTqFFijlXx8KP/Czw==", "dev": true, "requires": { - "methods": "1.1.2", + "methods": "~1.1.2", "superagent": "3.8.2" } }, @@ -1098,7 +2581,7 @@ "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } }, "to-fast-properties": { @@ -1117,7 +2600,7 @@ "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", "requires": { "media-typer": "0.3.0", - "mime-types": "2.1.19" + "mime-types": "~2.1.18" } }, "uglify-js": { @@ -1125,9 +2608,9 @@ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", "requires": { - "source-map": "0.5.7", - "uglify-to-browserify": "1.0.2", - "yargs": "3.10.0" + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" }, "dependencies": { "source-map": { @@ -1179,8 +2662,8 @@ "resolved": "https://registry.npmjs.org/with/-/with-5.1.1.tgz", "integrity": "sha1-+k2qktrzLE6pTtRTyB8EaGtXXf4=", "requires": { - "acorn": "3.3.0", - "acorn-globals": "3.1.0" + "acorn": "^3.1.0", + "acorn-globals": "^3.0.0" } }, "wordwrap": { @@ -1199,9 +2682,9 @@ "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", "requires": { - "camelcase": "1.2.1", - "cliui": "2.1.0", - "decamelize": "1.2.0", + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", "window-size": "0.1.0" } } From 746b8a97bffdaeeb4504865690a26852bf89f714 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 30 Sep 2021 14:43:10 -0600 Subject: [PATCH 003/255] Add basic form --- views/index.pug | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/views/index.pug b/views/index.pug index 3d63b9a..779b6d2 100644 --- a/views/index.pug +++ b/views/index.pug @@ -1,5 +1,30 @@ extends layout block content - h1= title - p Welcome to #{title} + form(action='/submit', method='POST') + span + label Sport + select(name="sport") + option(value="football" selected) Football + select(name="gender") + option(value="male" selected) Male + option(value="female") Female + select(name="division") + option(value="varsity") Varsity + option(value="jv-a") JV-A + option(value="jv-b") JV-B + span + label Home Team + input(type="text", name="home-team") + input(type="number", name="home-team-score") + span + label Visiting Team + input(type="text", name="visiting-team") + input(type="number", name="visiting-team-score") + span + label Submitter + input(type="text", name="submitter") + span + label Send info to + input(type="text", name="email", placeholder="email@example.com") + button(type="submit") Submit From 3cd5c4f275f3300e673b160a489d32fea76efc25 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 30 Sep 2021 14:43:23 -0600 Subject: [PATCH 004/255] Add basic positioning to form --- public/stylesheets/style.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css index 9453385..5f4624e 100644 --- a/public/stylesheets/style.css +++ b/public/stylesheets/style.css @@ -6,3 +6,8 @@ body { a { color: #00B7FF; } + +form { + display: flex; + flex-direction: column; +} \ No newline at end of file From e175bb80ff83c37160cf84d25407e6becef3067d Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 30 Sep 2021 14:46:17 -0600 Subject: [PATCH 005/255] Change email form from "text" to "email" --- views/index.pug | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/index.pug b/views/index.pug index 779b6d2..fcae639 100644 --- a/views/index.pug +++ b/views/index.pug @@ -26,5 +26,5 @@ block content input(type="text", name="submitter") span label Send info to - input(type="text", name="email", placeholder="email@example.com") + input(type="email", name="email", placeholder="email@example.com") button(type="submit") Submit From 14b099d72097ac63883576bc3ad572dda7ea776e Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 30 Sep 2021 14:48:09 -0600 Subject: [PATCH 006/255] Add default "0" value to scores --- views/index.pug | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/views/index.pug b/views/index.pug index fcae639..5e0985a 100644 --- a/views/index.pug +++ b/views/index.pug @@ -16,11 +16,11 @@ block content span label Home Team input(type="text", name="home-team") - input(type="number", name="home-team-score") + input(type="number", name="home-team-score", value="0") span label Visiting Team input(type="text", name="visiting-team") - input(type="number", name="visiting-team-score") + input(type="number", name="visiting-team-score", value="0") span label Submitter input(type="text", name="submitter") From a23bf1ae3bb9cff4368966dd922b9ad099a57718 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 30 Sep 2021 14:49:44 -0600 Subject: [PATCH 007/255] Add submit page --- app.js | 2 ++ routes/submit.js | 9 +++++++++ views/submit.pug | 0 3 files changed, 11 insertions(+) create mode 100644 routes/submit.js create mode 100644 views/submit.pug diff --git a/app.js b/app.js index ab7aed4..3cff1ca 100644 --- a/app.js +++ b/app.js @@ -6,6 +6,7 @@ var logger = require('morgan'); var indexRouter = require('./routes/index'); var usersRouter = require('./routes/users'); +var submitRouter = require('./routes/submit') var app = express(); @@ -21,6 +22,7 @@ app.use(express.static(path.join(__dirname, 'public'))); app.use('/', indexRouter); app.use('/users', usersRouter); +app.use('/submit', submitRouter); // catch 404 and forward to error handler app.use(function(req, res, next) { diff --git a/routes/submit.js b/routes/submit.js new file mode 100644 index 0000000..9f2355b --- /dev/null +++ b/routes/submit.js @@ -0,0 +1,9 @@ +var express = require('express'); +var router = express.Router(); + +/* GET submit page. */ +router.get('/', function(req, res, next) { + res.render('submit', { title: 'Express' }); +}); + +module.exports = router; diff --git a/views/submit.pug b/views/submit.pug new file mode 100644 index 0000000..e69de29 From a12df8aab27547bb324f2ce1e06a4ad0423b27d2 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 30 Sep 2021 14:55:01 -0600 Subject: [PATCH 008/255] Add submit messages --- routes/submit.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/routes/submit.js b/routes/submit.js index 9f2355b..bdd7012 100644 --- a/routes/submit.js +++ b/routes/submit.js @@ -3,7 +3,12 @@ var router = express.Router(); /* GET submit page. */ router.get('/', function(req, res, next) { - res.render('submit', { title: 'Express' }); + res.send('Nothing to send'); +}); + +/* POST submit page. */ +router.post('/', function(req, res, next) { + res.send('Score sent'); }); module.exports = router; From 539f20707194b8169550c7741d4d799ae8596e7c Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sun, 3 Oct 2021 21:50:24 -0600 Subject: [PATCH 009/255] Add "nodemailer" and "dotenv" packages --- package-lock.json | 28 ++++++++++++++++++++++++++++ package.json | 2 ++ 2 files changed, 30 insertions(+) diff --git a/package-lock.json b/package-lock.json index 068a1c7..006fc12 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,9 +10,11 @@ "dependencies": { "cookie-parser": "~1.4.3", "debug": "~2.6.9", + "dotenv": "^10.0.0", "express": "~4.16.0", "http-errors": "~1.6.2", "morgan": "~1.9.0", + "nodemailer": "^6.6.5", "pug": "2.0.0-beta11" }, "devDependencies": { @@ -411,6 +413,14 @@ "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", "integrity": "sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk=" }, + "node_modules/dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", + "engines": { + "node": ">=10" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -911,6 +921,14 @@ "node": ">= 0.6" } }, + "node_modules/nodemailer": { + "version": "6.6.5", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.6.5.tgz", + "integrity": "sha512-C/v856DBijUzHcHIgGpQoTrfsH3suKIRAGliIzCstatM2cAa+MYX3LuyCrABiO/cdJTxgBBHXxV1ztiqUwst5A==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -1804,6 +1822,11 @@ "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", "integrity": "sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk=" }, + "dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==" + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -2203,6 +2226,11 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" }, + "nodemailer": { + "version": "6.6.5", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.6.5.tgz", + "integrity": "sha512-C/v856DBijUzHcHIgGpQoTrfsH3suKIRAGliIzCstatM2cAa+MYX3LuyCrABiO/cdJTxgBBHXxV1ztiqUwst5A==" + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", diff --git a/package.json b/package.json index 20f3026..0a035a8 100644 --- a/package.json +++ b/package.json @@ -9,9 +9,11 @@ "dependencies": { "cookie-parser": "~1.4.3", "debug": "~2.6.9", + "dotenv": "^10.0.0", "express": "~4.16.0", "http-errors": "~1.6.2", "morgan": "~1.9.0", + "nodemailer": "^6.6.5", "pug": "2.0.0-beta11" }, "devDependencies": { From 7207a1b7352b0500f4db25df6040fec53216e714 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sun, 3 Oct 2021 21:50:55 -0600 Subject: [PATCH 010/255] Add mail functions --- mail/mail.js | 33 +++++++++++++++++++++++++++++++++ routes/submit.js | 24 ++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 mail/mail.js diff --git a/mail/mail.js b/mail/mail.js new file mode 100644 index 0000000..caac4d4 --- /dev/null +++ b/mail/mail.js @@ -0,0 +1,33 @@ +const app = require('../app'); +const nodemailer = require('nodemailer'); + +if (process.env.NODE_ENV !== 'production') { + require('dotenv').config(); +} + +module.exports = { + send: function (recipient, subject, message) { + send(recipient, subject, message); + } + }; + + +var send = function (recipient, subject, message) { + transporter.sendMail({ + to: recipient, // list of receivers + subject: subject, // Subject line + html: message, // html body + }); +} + +let transporter = nodemailer.createTransport({ + host: process.env.MAIL_HOST, + port: process.env.MAIL_PORT, + secure: process.env.MAIL_SECURE, + auth: { + user: process.env.MAIL_USER, + pass: process.env.MAIL_PASS, + }, +}, { + from: process.env.MAIL_FROM +}); \ No newline at end of file diff --git a/routes/submit.js b/routes/submit.js index bdd7012..30901be 100644 --- a/routes/submit.js +++ b/routes/submit.js @@ -1,4 +1,5 @@ var express = require('express'); +var mail = require('../mail/mail'); var router = express.Router(); /* GET submit page. */ @@ -8,7 +9,30 @@ router.get('/', function(req, res, next) { /* POST submit page. */ router.post('/', function(req, res, next) { + let sport = req.body.sport; + let gender = req.body.gender; + let division = req.body.division; + let home = req.body['home-team']; + let homeScore = req.body['home-team-score']; + let visiting = req.body['visiting-team']; + let visitingScore = req.body['visiting-team-score']; + let submitter = req.body['submitter']; + let recipient = req.body['email']; + + let message = prepMailBody(sport, gender, division, home, homeScore, visiting, visitingScore, submitter); + + mail.send(recipient, "Score Report", message); + res.send('Score sent'); }); +var prepMailBody = function(sport, gender, division, home, homeScore, visiting, visitingScore, submitter) { + return( + "Score report from " + submitter + "

" + + "Sport:
" + sport + " - " + gender + " - " + division + "

" + + "Home team:
" + home + " - " + homeScore + "

" + + "Visiting team:
" + visiting + " - " + visitingScore + "

"); +}; + + module.exports = router; From 58c2bac2d2f4c77dc15a10bec2669f82a0881d2d Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sun, 3 Oct 2021 21:51:16 -0600 Subject: [PATCH 011/255] Capitalize values for dropdown menus --- views/index.pug | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/views/index.pug b/views/index.pug index 5e0985a..fa4e619 100644 --- a/views/index.pug +++ b/views/index.pug @@ -5,14 +5,14 @@ block content span label Sport select(name="sport") - option(value="football" selected) Football + option(value="Football" selected) Football select(name="gender") - option(value="male" selected) Male - option(value="female") Female + option(value="Male" selected) Male + option(value="Female") Female select(name="division") - option(value="varsity") Varsity - option(value="jv-a") JV-A - option(value="jv-b") JV-B + option(value="Varsity") Varsity + option(value="JV-A") JV-A + option(value="JV-B") JV-B span label Home Team input(type="text", name="home-team") From c1692230458f556244fe024105ff0562c3fd007a Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sun, 3 Oct 2021 21:54:40 -0600 Subject: [PATCH 012/255] Add .env.example --- .env.example | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .env.example diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..9ef5217 --- /dev/null +++ b/.env.example @@ -0,0 +1,8 @@ +NODE_ENV=development + +MAIL_FROM=fromaddress@example.com +MAIL_HOST=smtp.smtphost.net +MAIL_PORT=465 +MAIL_SECURE=true +MAIL_USER=username +MAIL_PASS=password From 2ed0046c095417f2c1f6610111ddf7c0bf6c0e29 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 4 Oct 2021 11:24:04 -0600 Subject: [PATCH 013/255] Adjust titles --- routes/index.js | 2 +- views/index.pug | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/routes/index.js b/routes/index.js index ecca96a..9399195 100644 --- a/routes/index.js +++ b/routes/index.js @@ -3,7 +3,7 @@ var router = express.Router(); /* GET home page. */ router.get('/', function(req, res, next) { - res.render('index', { title: 'Express' }); + res.render('index', { title: 'Submit Score' }); }); module.exports = router; diff --git a/views/index.pug b/views/index.pug index fa4e619..0b587c6 100644 --- a/views/index.pug +++ b/views/index.pug @@ -1,6 +1,7 @@ extends layout block content + h1 Submit Score form(action='/submit', method='POST') span label Sport From 5a39c4983bbed11624f9fb2f2ceb5cea7e018bf9 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 4 Oct 2021 11:30:35 -0600 Subject: [PATCH 014/255] Start separate stylesheet for submit page --- public/stylesheets/style.css | 4 ---- public/stylesheets/submit.css | 4 ++++ views/index.pug | 3 +++ views/layout.pug | 3 ++- 4 files changed, 9 insertions(+), 5 deletions(-) create mode 100644 public/stylesheets/submit.css diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css index 5f4624e..4f3025f 100644 --- a/public/stylesheets/style.css +++ b/public/stylesheets/style.css @@ -7,7 +7,3 @@ a { color: #00B7FF; } -form { - display: flex; - flex-direction: column; -} \ No newline at end of file diff --git a/public/stylesheets/submit.css b/public/stylesheets/submit.css new file mode 100644 index 0000000..df8ee7a --- /dev/null +++ b/public/stylesheets/submit.css @@ -0,0 +1,4 @@ +form { + display: flex; + flex-direction: column; +} \ No newline at end of file diff --git a/views/index.pug b/views/index.pug index 0b587c6..9cc4f1b 100644 --- a/views/index.pug +++ b/views/index.pug @@ -1,5 +1,8 @@ extends layout +block stylesheets + link(rel='stylesheet', href='/stylesheets/submit.css') + block content h1 Submit Score form(action='/submit', method='POST') diff --git a/views/layout.pug b/views/layout.pug index 15af079..f91cc8c 100644 --- a/views/layout.pug +++ b/views/layout.pug @@ -2,6 +2,7 @@ doctype html html head title= title - link(rel='stylesheet', href='/stylesheets/style.css') + block stylesheets + link(rel='stylesheet', href='/stylesheets/style.css') body block content From 6b9841bf827792ee54538a61b15b76de8006edce Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 4 Oct 2021 13:31:22 -0600 Subject: [PATCH 015/255] Add simple styling to submit page --- public/stylesheets/style.css | 2 +- public/stylesheets/submit.css | 37 +++++++++++++++++++++++++++ views/index.pug | 48 ++++++++++++++++++++--------------- views/layout.pug | 2 +- 4 files changed, 66 insertions(+), 23 deletions(-) diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css index 4f3025f..6a727df 100644 --- a/public/stylesheets/style.css +++ b/public/stylesheets/style.css @@ -1,5 +1,5 @@ body { - padding: 50px; + padding: 1em; font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; } diff --git a/public/stylesheets/submit.css b/public/stylesheets/submit.css index df8ee7a..89dd41c 100644 --- a/public/stylesheets/submit.css +++ b/public/stylesheets/submit.css @@ -1,4 +1,41 @@ +h1 { + text-align: center; +} + form { display: flex; flex-direction: column; + max-width: 20em; + margin-left: auto; + margin-right: auto; +} + +span { + display: flex; + flex-direction: column; +} + + +#sport-dropdown { + width: 100%; +} + +.form-section { + margin-bottom: 0.75em; +} + +.form-section-input { + flex-direction: row; +} + +input { + width: 100%; +} + +.score-input{ + width: 35%; +} + +button { + margin-top: 1.5em; } \ No newline at end of file diff --git a/views/index.pug b/views/index.pug index 9cc4f1b..7f6baf1 100644 --- a/views/index.pug +++ b/views/index.pug @@ -6,29 +6,35 @@ block stylesheets block content h1 Submit Score form(action='/submit', method='POST') - span + span(class='form-section') label Sport - select(name="sport") - option(value="Football" selected) Football - select(name="gender") - option(value="Male" selected) Male - option(value="Female") Female - select(name="division") - option(value="Varsity") Varsity - option(value="JV-A") JV-A - option(value="JV-B") JV-B - span + span(class='form-section-input') + select#sport-dropdown(name="sport") + option(value="Football" selected) Football + select#gender-dropdown(name="gender") + option(value="Male" selected) Male + option(value="Female") Female + select#division-dropdown(name="division") + option(value="Varsity") Varsity + option(value="JV-A") JV-A + option(value="JV-B") JV-B + span(class='form-section') label Home Team - input(type="text", name="home-team") - input(type="number", name="home-team-score", value="0") - span - label Visiting Team - input(type="text", name="visiting-team") - input(type="number", name="visiting-team-score", value="0") - span + span(class='form-section-input') + input(type="text", name="home-team") + input(class="score-input", type="number", name="home-team-score", value="0") + span(class='form-section') + label Visiting Team + span(class='form-section-input') + input(type="text", name="visiting-team") + input(class="score-input", type="number", name="visiting-team-score", value="0") + span(class='form-section') label Submitter - input(type="text", name="submitter") - span + span(class='form-section-input') + input(type="text", name="submitter") + span(class='form-section') label Send info to - input(type="email", name="email", placeholder="email@example.com") + span(class='form-section-input') + input(type="email", name="email", placeholder="email@example.com") + span(class='form-section') button(type="submit") Submit diff --git a/views/layout.pug b/views/layout.pug index f91cc8c..2125107 100644 --- a/views/layout.pug +++ b/views/layout.pug @@ -2,7 +2,7 @@ doctype html html head title= title + link(rel='stylesheet', href='/stylesheets/style.css') block stylesheets - link(rel='stylesheet', href='/stylesheets/style.css') body block content From 1136513ded4a5526fed7a2a9232e23ba7f2fecfa Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 4 Oct 2021 13:42:52 -0600 Subject: [PATCH 016/255] Fix scaling for mobile --- views/layout.pug | 1 + 1 file changed, 1 insertion(+) diff --git a/views/layout.pug b/views/layout.pug index 2125107..69f5a59 100644 --- a/views/layout.pug +++ b/views/layout.pug @@ -2,6 +2,7 @@ doctype html html head title= title + meta(name='viewport', content='width=device-width, initial-scale=1') link(rel='stylesheet', href='/stylesheets/style.css') block stylesheets body From 1c4694aff58b318cbde056f6903ba7900964e569 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 4 Oct 2021 13:59:38 -0600 Subject: [PATCH 017/255] Do not import .env file in testing server --- mail/mail.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mail/mail.js b/mail/mail.js index caac4d4..1c9ccef 100644 --- a/mail/mail.js +++ b/mail/mail.js @@ -1,7 +1,7 @@ const app = require('../app'); const nodemailer = require('nodemailer'); -if (process.env.NODE_ENV !== 'production') { +if (process.env.NODE_ENV !== 'production' || process.env.NODE_ENV !== 'testing') { require('dotenv').config(); } From 8b1bd3bc13d1790ffefdeade5d1c48aac57ab4d2 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sat, 13 Nov 2021 20:47:42 -0700 Subject: [PATCH 018/255] install "pg" module --- package-lock.json | 273 +++++++++++++++++++++++++++++++++++++++++++++- package.json | 1 + 2 files changed, 268 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 006fc12..6499394 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "http-errors": "~1.6.2", "morgan": "~1.9.0", "nodemailer": "^6.6.5", + "pg": "^8.7.1", "pug": "2.0.0-beta11" }, "devDependencies": { @@ -195,6 +196,14 @@ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, + "node_modules/buffer-writer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==", + "engines": { + "node": ">=4" + } + }, "node_modules/bytes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", @@ -965,6 +974,11 @@ "wrappy": "1" } }, + "node_modules/packet-reader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", + "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" + }, "node_modules/parseurl": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", @@ -992,6 +1006,115 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "node_modules/pg": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.7.1.tgz", + "integrity": "sha512-7bdYcv7V6U3KAtWjpQJJBww0UEsWuh4yQ/EjNf2HeO/NnvKjpvhEIe/A/TleP6wtmSKnUnghs5A9jUoK6iDdkA==", + "dependencies": { + "buffer-writer": "2.0.0", + "packet-reader": "1.0.0", + "pg-connection-string": "^2.5.0", + "pg-pool": "^3.4.1", + "pg-protocol": "^1.5.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + }, + "engines": { + "node": ">= 8.0.0" + }, + "peerDependencies": { + "pg-native": ">=2.0.0" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-connection-string": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", + "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.4.1.tgz", + "integrity": "sha512-TVHxR/gf3MeJRvchgNHxsYsTCHQ+4wm3VIHSS19z8NC0+gioEhq1okDY1sm/TYbfoP6JLFx01s0ShvZ3puP/iQ==", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.5.0.tgz", + "integrity": "sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ==" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.4.tgz", + "integrity": "sha512-YmuA56alyBq7M59vxVBfPJrGSozru8QAdoNlWuW3cz8l+UX3cWge0vTvjKhsSHSJpo3Bom8/Mm6hf0TR5GY0+w==", + "dependencies": { + "split2": "^3.1.1" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/process-nextick-args": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", @@ -1294,6 +1417,27 @@ "node": ">=0.8.0" } }, + "node_modules/split2": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "dependencies": { + "readable-stream": "^3.0.0" + } + }, + "node_modules/split2/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/statuses": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", @@ -1306,7 +1450,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, "dependencies": { "safe-buffer": "~5.1.0" } @@ -1434,8 +1577,7 @@ "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "node_modules/utils-merge": { "version": "1.0.1", @@ -1492,6 +1634,14 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, "node_modules/yargs": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", @@ -1650,6 +1800,11 @@ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, + "buffer-writer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==" + }, "bytes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", @@ -2258,6 +2413,11 @@ "wrappy": "1" } }, + "packet-reader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", + "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" + }, "parseurl": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", @@ -2279,6 +2439,84 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "pg": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.7.1.tgz", + "integrity": "sha512-7bdYcv7V6U3KAtWjpQJJBww0UEsWuh4yQ/EjNf2HeO/NnvKjpvhEIe/A/TleP6wtmSKnUnghs5A9jUoK6iDdkA==", + "requires": { + "buffer-writer": "2.0.0", + "packet-reader": "1.0.0", + "pg-connection-string": "^2.5.0", + "pg-pool": "^3.4.1", + "pg-protocol": "^1.5.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + } + }, + "pg-connection-string": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", + "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==" + }, + "pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==" + }, + "pg-pool": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.4.1.tgz", + "integrity": "sha512-TVHxR/gf3MeJRvchgNHxsYsTCHQ+4wm3VIHSS19z8NC0+gioEhq1okDY1sm/TYbfoP6JLFx01s0ShvZ3puP/iQ==", + "requires": {} + }, + "pg-protocol": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.5.0.tgz", + "integrity": "sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ==" + }, + "pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "requires": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + } + }, + "pgpass": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.4.tgz", + "integrity": "sha512-YmuA56alyBq7M59vxVBfPJrGSozru8QAdoNlWuW3cz8l+UX3cWge0vTvjKhsSHSJpo3Bom8/Mm6hf0TR5GY0+w==", + "requires": { + "split2": "^3.1.1" + } + }, + "postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==" + }, + "postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU=" + }, + "postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==" + }, + "postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "requires": { + "xtend": "^4.0.0" + } + }, "process-nextick-args": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", @@ -2550,6 +2788,26 @@ "amdefine": ">=0.0.4" } }, + "split2": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "requires": { + "readable-stream": "^3.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "statuses": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", @@ -2559,7 +2817,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, "requires": { "safe-buffer": "~5.1.0" } @@ -2662,8 +2919,7 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "utils-merge": { "version": "1.0.1", @@ -2705,6 +2961,11 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, "yargs": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", diff --git a/package.json b/package.json index 0a035a8..8fbe5ba 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "http-errors": "~1.6.2", "morgan": "~1.9.0", "nodemailer": "^6.6.5", + "pg": "^8.7.1", "pug": "2.0.0-beta11" }, "devDependencies": { From 95d0979f53e82219f168573d9bfb9bd1fd5f453b Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 15 Nov 2021 10:39:03 -0700 Subject: [PATCH 019/255] Begin adding support for PostgreSQL database storage --- database/database.js | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 database/database.js diff --git a/database/database.js b/database/database.js new file mode 100644 index 0000000..e653b9a --- /dev/null +++ b/database/database.js @@ -0,0 +1,9 @@ +const app = require('../app'); +const { Client } = require('pg'); + +if (process.env.NODE_ENV !== 'production' || process.env.NODE_ENV !== 'testing') { + require('dotenv').config(); +} + +const client = new Client(); +client.connect(); \ No newline at end of file From 80632bf8b37b359f2a5e344b932796f91a8df0a8 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 15 Nov 2021 17:34:05 -0700 Subject: [PATCH 020/255] Add PG variables to .env.example --- .env.example | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.env.example b/.env.example index 9ef5217..11d1e69 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,11 @@ NODE_ENV=development +PGUSER=dbuser +PGHOST=database.server.com +PGPASSWORD=dbuserpassword +PGDATABASE=mydatabase +PGPORT=5432 + MAIL_FROM=fromaddress@example.com MAIL_HOST=smtp.smtphost.net MAIL_PORT=465 From 2557a83e3763f98b39db08267b8365f7d7c3318c Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 18 Nov 2021 14:12:46 -0700 Subject: [PATCH 021/255] Add packages "async" and "passport" --- package-lock.json | 60 +++++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 ++ 2 files changed, 62 insertions(+) diff --git a/package-lock.json b/package-lock.json index 6499394..225db7b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "demo", "version": "0.0.0", "dependencies": { + "async": "^3.2.2", "cookie-parser": "~1.4.3", "debug": "~2.6.9", "dotenv": "^10.0.0", @@ -15,6 +16,7 @@ "http-errors": "~1.6.2", "morgan": "~1.9.0", "nodemailer": "^6.6.5", + "passport": "^0.5.0", "pg": "^8.7.1", "pug": "2.0.0-beta11" }, @@ -109,6 +111,11 @@ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" }, + "node_modules/async": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.2.tgz", + "integrity": "sha512-H0E+qZaDEfx/FY4t7iLRv1W2fFI6+pyCeTw1uN20AQPiwqwM6ojPxHxdLv4z8hi2DtnW9BOckSspLucW7pIE5g==" + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -987,6 +994,30 @@ "node": ">= 0.8" } }, + "node_modules/passport": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.5.0.tgz", + "integrity": "sha512-ln+ue5YaNDS+fes6O5PCzXKSseY5u8MYhX9H5Co4s+HfYI5oqvnHKoOORLYDUPh+8tHvrxugF2GFcUA1Q1Gqfg==", + "dependencies": { + "passport-strategy": "1.x.x", + "pause": "0.0.1" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, + "node_modules/passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -1006,6 +1037,11 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "node_modules/pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" + }, "node_modules/pg": { "version": "8.7.1", "resolved": "https://registry.npmjs.org/pg/-/pg-8.7.1.tgz", @@ -1722,6 +1758,11 @@ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" }, + "async": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.2.tgz", + "integrity": "sha512-H0E+qZaDEfx/FY4t7iLRv1W2fFI6+pyCeTw1uN20AQPiwqwM6ojPxHxdLv4z8hi2DtnW9BOckSspLucW7pIE5g==" + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -2423,6 +2464,20 @@ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" }, + "passport": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.5.0.tgz", + "integrity": "sha512-ln+ue5YaNDS+fes6O5PCzXKSseY5u8MYhX9H5Co4s+HfYI5oqvnHKoOORLYDUPh+8tHvrxugF2GFcUA1Q1Gqfg==", + "requires": { + "passport-strategy": "1.x.x", + "pause": "0.0.1" + } + }, + "passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=" + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -2439,6 +2494,11 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" + }, "pg": { "version": "8.7.1", "resolved": "https://registry.npmjs.org/pg/-/pg-8.7.1.tgz", diff --git a/package.json b/package.json index 8fbe5ba..c94c979 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "test": "mocha" }, "dependencies": { + "async": "^3.2.2", "cookie-parser": "~1.4.3", "debug": "~2.6.9", "dotenv": "^10.0.0", @@ -14,6 +15,7 @@ "http-errors": "~1.6.2", "morgan": "~1.9.0", "nodemailer": "^6.6.5", + "passport": "^0.5.0", "pg": "^8.7.1", "pug": "2.0.0-beta11" }, From 9554d54496ace8ea808c540e9e4a4b89313dcbde Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 18 Nov 2021 14:13:03 -0700 Subject: [PATCH 022/255] Add database initialization --- database/database.js | 34 +++++++++++- database/init_database.sql | 105 +++++++++++++++++++++++++++++++++++++ routes/index.js | 1 + 3 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 database/init_database.sql diff --git a/database/database.js b/database/database.js index e653b9a..766168d 100644 --- a/database/database.js +++ b/database/database.js @@ -1,9 +1,41 @@ const app = require('../app'); const { Client } = require('pg'); +const fs = require('fs'); if (process.env.NODE_ENV !== 'production' || process.env.NODE_ENV !== 'testing') { require('dotenv').config(); } const client = new Client(); -client.connect(); \ No newline at end of file +client.connect(); + +async function executeQuery(query, values = []) { + const result = await client.query({ + rowMode: 'array', + text: query, + values: values + }); + return result.rows; +} + +async function InitializeDatabase() { + console.log("Initializing database...") + const sql = fs.readFileSync('database/init_database.sql').toString(); + await executeQuery(sql); + console.log("Database initialized.") +} + + + + +async function checkForDatabaseInitialization() { + const query = `SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'scores'`; + let result = await executeQuery(query); + + const scoresSchemaExists = result.length !== 0; + + if(!scoresSchemaExists) { + InitializeDatabase(); + } +} +checkForDatabaseInitialization(); \ No newline at end of file diff --git a/database/init_database.sql b/database/init_database.sql new file mode 100644 index 0000000..5df20f8 --- /dev/null +++ b/database/init_database.sql @@ -0,0 +1,105 @@ +/* SCORE TRACKER DATABASE LAYOUT + +scores: + + sports: + *sport_id* | name | currently_active + + divisions: + *division_id* | name | gender | *sport_id* | currently_active + + teams: + *team_id* | name | ~sport_id~ | currently_active + + seasons: + *season_id* | school_year + + games: + *game_id* | ~division_id~ | ~season_id~ | date | ~team1_id~ | ~team2_id~ | team1_score | team2_score | ~submitter_id~ | updated_timestamp + + + +accounts: + + users: + *user_id* | email | salt | password_hash | role | approved + + sessions: + *session_id* | ~user_id~ | expiration_date + +*/ + + +BEGIN; + + +CREATE SCHEMA IF NOT EXISTS scores; + + +CREATE TABLE IF NOT EXISTS scores.sports( + sport_id BIGINT GENERATED ALWAYS AS IDENTITY, + sport_name TEXT UNIQUE NOT NULL, + currently_active BOOLEAN DEFAULT TRUE, + PRIMARY KEY(sport_id) + ); + +CREATE TABLE IF NOT EXISTS scores.divisions( + division_id BIGINT GENERATED ALWAYS AS IDENTITY, + division_name TEXT NOT NULL, + gender VARCHAR(1) CHECK (gender IN ( 'F', 'M' ) ), + sport_id BIGINT, + currently_active BOOLEAN DEFAULT TRUE, + PRIMARY KEY(division_id), + CONSTRAINT fk_sport + FOREIGN KEY(sport_id) + REFERENCES scores.sports(sport_id) + ); + +CREATE TABLE IF NOT EXISTS scores.teams( + team_id BIGINT GENERATED ALWAYS AS IDENTITY, + team_name TEXT NOT NULL, + sport_id BIGINT, + currently_active BOOLEAN DEFAULT TRUE, + PRIMARY KEY(team_id), + CONSTRAINT fk_sport + FOREIGN KEY(sport_id) + REFERENCES scores.sports(sport_id) + ); + +CREATE TABLE IF NOT EXISTS scores.seasons( + season_id BIGINT GENERATED ALWAYS AS IDENTITY, + school_year INTEGER NOT NULL, + PRIMARY KEY(season_id) + ); + +CREATE TABLE IF NOT EXISTS scores.games( + game_id BIGINT GENERATED ALWAYS AS IDENTITY, + division_id BIGINT, + season_id BIGINT, + game_date DATE, + team1_id BIGINT, + team2_id BIGINT, + team1_score INTEGER, + team2_score INTEGER, +/* submitter_id BIGINT,*/ + updated_timestamp TIMESTAMP WITH TIME ZONE DEFAULT now(), + PRIMARY KEY(game_id), + CONSTRAINT fk_division + FOREIGN KEY(division_id) + REFERENCES scores.divisions(division_id), + CONSTRAINT fk_season + FOREIGN KEY(season_id) + REFERENCES scores.seasons(season_id), + CONSTRAINT fk_team1 + FOREIGN KEY(team1_id) + REFERENCES scores.teams(team_id), + CONSTRAINT fk_team2 + FOREIGN KEY(team2_id) + REFERENCES scores.teams(team_id) +/* CONSTRAINT fk_submitter + FOREIGN KEY(submitter_id) + REFERENCES accounts.users(user_id)*/ + ); + + +COMMIT; \ No newline at end of file diff --git a/routes/index.js b/routes/index.js index 9399195..c806b1c 100644 --- a/routes/index.js +++ b/routes/index.js @@ -1,5 +1,6 @@ var express = require('express'); var router = express.Router(); +var database = require('../database/database'); /* GET home page. */ router.get('/', function(req, res, next) { From 7ceccd2928e7af8860f5dbefe8716caf88427ae0 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 19 Nov 2021 11:42:08 -0700 Subject: [PATCH 023/255] Fix database layout comment --- database/init_database.sql | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/database/init_database.sql b/database/init_database.sql index 5df20f8..ad4f0e7 100644 --- a/database/init_database.sql +++ b/database/init_database.sql @@ -3,19 +3,19 @@ scores: sports: - *sport_id* | name | currently_active + *sport_id* | sport_name | currently_active divisions: - *division_id* | name | gender | *sport_id* | currently_active + *division_id* | division_name | gender | *sport_id* | currently_active teams: - *team_id* | name | ~sport_id~ | currently_active + *team_id* | team_name | ~sport_id~ | currently_active seasons: *season_id* | school_year games: - *game_id* | ~division_id~ | ~season_id~ | date | ~team1_id~ | ~team2_id~ | team1_score | team2_score | ~submitter_id~ | updated_timestamp + *game_id* | ~division_id~ | ~season_id~ | game_date | ~team1_id~ | ~team2_id~ | team1_score | team2_score | ~submitter_id~ | updated_timestamp From cc0b5ee087dc69aac6d6943f31fe8ac15d81eee6 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sat, 20 Nov 2021 16:48:48 -0700 Subject: [PATCH 024/255] Edit submit page to better match database values --- views/index.pug | 40 ---------------------------------------- views/submit.pug | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 40 deletions(-) diff --git a/views/index.pug b/views/index.pug index 7f6baf1..e69de29 100644 --- a/views/index.pug +++ b/views/index.pug @@ -1,40 +0,0 @@ -extends layout - -block stylesheets - link(rel='stylesheet', href='/stylesheets/submit.css') - -block content - h1 Submit Score - form(action='/submit', method='POST') - span(class='form-section') - label Sport - span(class='form-section-input') - select#sport-dropdown(name="sport") - option(value="Football" selected) Football - select#gender-dropdown(name="gender") - option(value="Male" selected) Male - option(value="Female") Female - select#division-dropdown(name="division") - option(value="Varsity") Varsity - option(value="JV-A") JV-A - option(value="JV-B") JV-B - span(class='form-section') - label Home Team - span(class='form-section-input') - input(type="text", name="home-team") - input(class="score-input", type="number", name="home-team-score", value="0") - span(class='form-section') - label Visiting Team - span(class='form-section-input') - input(type="text", name="visiting-team") - input(class="score-input", type="number", name="visiting-team-score", value="0") - span(class='form-section') - label Submitter - span(class='form-section-input') - input(type="text", name="submitter") - span(class='form-section') - label Send info to - span(class='form-section-input') - input(type="email", name="email", placeholder="email@example.com") - span(class='form-section') - button(type="submit") Submit diff --git a/views/submit.pug b/views/submit.pug index e69de29..385ad51 100644 --- a/views/submit.pug +++ b/views/submit.pug @@ -0,0 +1,41 @@ +extends layout + +block stylesheets + link(rel='stylesheet', href='/stylesheets/submit.css') + +block content + h1 Submit Score + form(action='/submit', method='POST') + span(class='form-section') + label Year + span(class='form-section-input') + select#year-dropdown(name="year" class="main-dropdown") + option(value="2022" selected) 2021-2022 + span(class='form-section') + label Sport + span(class='form-section-input') + select#sport-dropdown(name="sport" class="main-dropdown") + option(value="Basketball" selected) Basketball + select#gender-dropdown(name="gender") + option(value="Male" selected) Male + option(value="Female") Female + select#division-dropdown(name="division") + option(value="Varsity") Varsity + option(value="JV-A") JV-A + option(value="JV-B") JV-B + span(class='form-section') + label Date of match + span(class='form-section-input') + input(type="date", name="date", value=date) + span(class='form-section') + label Your team + span(class='form-section-input') + select#team1-dropdown(name="team1" class="main-dropdown") + input(class="score-input", type="number", name="team1-score", value="0") + span(class='form-section') + label Opponent + span(class='form-section-input') + select#team2-dropdown(name="team2" class="main-dropdown") + input(class="score-input", type="number", name="team2-score", value="0") + span(class='form-section') + button(type="submit") Submit From 93521dc431e155ee1c7f220fa607f9103324cbc5 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sat, 20 Nov 2021 16:50:23 -0700 Subject: [PATCH 025/255] Edit CSS for submit page --- public/stylesheets/submit.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/stylesheets/submit.css b/public/stylesheets/submit.css index 89dd41c..80a8146 100644 --- a/public/stylesheets/submit.css +++ b/public/stylesheets/submit.css @@ -16,7 +16,7 @@ span { } -#sport-dropdown { +.main-dropdown { width: 100%; } From 82137290098bc3da97c9d8db8c384c4c4e8955c3 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sat, 20 Nov 2021 16:51:08 -0700 Subject: [PATCH 026/255] Remove mail functions for submit page --- routes/submit.js | 41 +++++++++++++++++------------------------ 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/routes/submit.js b/routes/submit.js index 30901be..e79ff92 100644 --- a/routes/submit.js +++ b/routes/submit.js @@ -1,38 +1,31 @@ var express = require('express'); -var mail = require('../mail/mail'); var router = express.Router(); +var database = require('../database/database'); /* GET submit page. */ router.get('/', function(req, res, next) { - res.send('Nothing to send'); + const date_ob = new Date(); + const date = ("0" + date_ob.getDate()).slice(-2); + const month = ("0" + (date_ob.getMonth() + 1)).slice(-2); + const year = date_ob.getFullYear(); + + const currentDate = year + '-' + month + '-' + date; + res.render('submit', { title: 'Submit Score', date: currentDate }); }); /* POST submit page. */ router.post('/', function(req, res, next) { - let sport = req.body.sport; - let gender = req.body.gender; - let division = req.body.division; - let home = req.body['home-team']; - let homeScore = req.body['home-team-score']; - let visiting = req.body['visiting-team']; - let visitingScore = req.body['visiting-team-score']; - let submitter = req.body['submitter']; - let recipient = req.body['email']; + const year = req.body['year']; + const sport = req.body['sport']; + const gender = req.body['gender']; + const division = req.body['division']; + const date = req.body['date']; + const team1 = req.body['team1']; + const team1Score = req.body['team1-score']; + const team2 = req.body['team2']; + const team2Score = req.body['team2-score']; - let message = prepMailBody(sport, gender, division, home, homeScore, visiting, visitingScore, submitter); - mail.send(recipient, "Score Report", message); - - res.send('Score sent'); }); -var prepMailBody = function(sport, gender, division, home, homeScore, visiting, visitingScore, submitter) { - return( - "Score report from " + submitter + "

" + - "Sport:
" + sport + " - " + gender + " - " + division + "

" + - "Home team:
" + home + " - " + homeScore + "

" + - "Visiting team:
" + visiting + " - " + visitingScore + "

"); -}; - - module.exports = router; From 6f4a41fbd7e19b6ae7428b39406840e37fa41c5a Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sat, 20 Nov 2021 16:51:49 -0700 Subject: [PATCH 027/255] Edit database.js --- database/database.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/database/database.js b/database/database.js index 766168d..f166980 100644 --- a/database/database.js +++ b/database/database.js @@ -18,7 +18,7 @@ async function executeQuery(query, values = []) { return result.rows; } -async function InitializeDatabase() { +async function Initialize() { console.log("Initializing database...") const sql = fs.readFileSync('database/init_database.sql').toString(); await executeQuery(sql); @@ -35,7 +35,13 @@ async function checkForDatabaseInitialization() { const scoresSchemaExists = result.length !== 0; if(!scoresSchemaExists) { - InitializeDatabase(); + Initialize(); } } -checkForDatabaseInitialization(); \ No newline at end of file +checkForDatabaseInitialization(); + + + + + +exports.executeQuery = executeQuery; \ No newline at end of file From 64f4f9fb32dcee891a822e6df9b2d5744016b5d7 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sat, 20 Nov 2021 16:53:13 -0700 Subject: [PATCH 028/255] Add createSport function --- database/scores/sports.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 database/scores/sports.js diff --git a/database/scores/sports.js b/database/scores/sports.js new file mode 100644 index 0000000..abdea6b --- /dev/null +++ b/database/scores/sports.js @@ -0,0 +1,26 @@ +const database = require('./database'); + + + +class Sport { + constructor(id) { + this.id = id; + } +} + +async function createSport(name) { + query = `INSERT INTO scores.sports(sport_name) + VALUES($1);`; + await database.executeQuery(query, [name]); + + query = `SELECT sport_id FROM scores.sports + WHERE sport_name = $1` + const sportId = await database.executeQuery(query, [name]); + console.log(sportId); + + return new Sport(sportId[0][0]); +} + + + +exports.createSport = createSport; \ No newline at end of file From 2191218fe1809c7cfb049d5ac47bb7fd30729658 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sat, 20 Nov 2021 17:52:09 -0700 Subject: [PATCH 029/255] Add rename sport rename function --- database/scores/sports.js | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/database/scores/sports.js b/database/scores/sports.js index abdea6b..b1be445 100644 --- a/database/scores/sports.js +++ b/database/scores/sports.js @@ -1,26 +1,35 @@ -const database = require('./database'); +const database = require('./../database'); class Sport { - constructor(id) { + constructor(id, name) { this.id = id; + this.name = name; } } -async function createSport(name) { + + +async function create(name) { query = `INSERT INTO scores.sports(sport_name) - VALUES($1);`; - await database.executeQuery(query, [name]); - - query = `SELECT sport_id FROM scores.sports - WHERE sport_name = $1` + VALUES($1) + RETURNING sport_id;`; const sportId = await database.executeQuery(query, [name]); - console.log(sportId); + return new Sport(sportId[0][0], name); +} - return new Sport(sportId[0][0]); +async function rename(id, name) { + query = `UPDATE scores.sports + SET sport_name = $2 + WHERE sport_id = $1;` + await database.executeQuery(query, [id, name]); + return new Sport(id, name); } -exports.createSport = createSport; \ No newline at end of file + + +exports.create = create; +exports.rename = rename; \ No newline at end of file From c176e279877db26d5f4066656ad7eb06b4a67fed Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sat, 20 Nov 2021 17:58:29 -0700 Subject: [PATCH 030/255] Add function to remove sport --- database/scores/sports.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/database/scores/sports.js b/database/scores/sports.js index b1be445..bd719fc 100644 --- a/database/scores/sports.js +++ b/database/scores/sports.js @@ -15,21 +15,30 @@ async function create(name) { query = `INSERT INTO scores.sports(sport_name) VALUES($1) RETURNING sport_id;`; - const sportId = await database.executeQuery(query, [name]); - return new Sport(sportId[0][0], name); + const id = (await database.executeQuery(query, [name]))[0][0]; + return new Sport(id, name); } async function rename(id, name) { query = `UPDATE scores.sports SET sport_name = $2 - WHERE sport_id = $1;` + WHERE sport_id = $1;`; await database.executeQuery(query, [id, name]); return new Sport(id, name); } +async function remove(id) { + query = `DELETE FROM scores.sports + WHERE sport_id = $1 + RETURNING sport_name;`; + name = (await database.executeQuery(query, [id]))[0][0]; + return new Sport(id, name); +} + exports.create = create; -exports.rename = rename; \ No newline at end of file +exports.rename = rename; +exports.remove = remove; \ No newline at end of file From 2d14395b61c930862c072dd66561796027fe8383 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sat, 20 Nov 2021 18:07:37 -0700 Subject: [PATCH 031/255] Add function to retrieve all sports from database --- database/scores/sports.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/database/scores/sports.js b/database/scores/sports.js index bd719fc..38f35cf 100644 --- a/database/scores/sports.js +++ b/database/scores/sports.js @@ -35,10 +35,24 @@ async function remove(id) { return new Sport(id, name); } +async function retrieveAll() { + query = `SELECT * + FROM scores.sports + ORDER BY sport_name;`; + const table = await database.executeQuery(query); + + const sportsList = []; + table.forEach((row) => { + sportsList.push(new Sport(row[0], row[1])); + }); + return sportsList; +} + exports.create = create; exports.rename = rename; -exports.remove = remove; \ No newline at end of file +exports.remove = remove; +exports.retrieveAll = retrieveAll; \ No newline at end of file From 65238e374561e7f95dcde76c0bf2144996498c6a Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sat, 20 Nov 2021 18:53:22 -0700 Subject: [PATCH 032/255] Add module for defining genders --- database/scores/genders.js | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 database/scores/genders.js diff --git a/database/scores/genders.js b/database/scores/genders.js new file mode 100644 index 0000000..402d58f --- /dev/null +++ b/database/scores/genders.js @@ -0,0 +1,8 @@ +class Gender { + constructor(name) { + this.name = name; + } +} + +exports.male = new Gender("male"); +exports.female = new Gender("female"); \ No newline at end of file From f86717d99f0fddbbd2c9ecb6b8bb48c61e82489b Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sat, 20 Nov 2021 19:00:06 -0700 Subject: [PATCH 033/255] Move genders definition to dedicated constants folder --- {database/scores => constants}/genders.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {database/scores => constants}/genders.js (100%) diff --git a/database/scores/genders.js b/constants/genders.js similarity index 100% rename from database/scores/genders.js rename to constants/genders.js From 2db5bc4480e69d00e2ccfc470e0b819abe6f4993 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sat, 20 Nov 2021 19:14:43 -0700 Subject: [PATCH 034/255] Create functions for database management of divisions --- database/scores/divisions.js | 40 ++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 database/scores/divisions.js diff --git a/database/scores/divisions.js b/database/scores/divisions.js new file mode 100644 index 0000000..0a0e8be --- /dev/null +++ b/database/scores/divisions.js @@ -0,0 +1,40 @@ +const database = require('./../database'); +const genders = require('../../constants/genders'); + + + + + +class Division { + constructor(id, name) { + this.id = id; + this.name = name; + } +} + + + +async function create(name, gender, sportID) { + query = `INSERT INTO scores.divisions(division_name,gender,sport_id) + VALUES($1,$2,$3) + RETURNING division_id;`; + const genderID = (gender == genders.male) ? "M" : "F"; + const id = (await database.executeQuery(query, [name, genderID, sportID]))[0][0]; + return new Division(id, name); +} + +async function rename(id, division) { + query = `UPDATE scores.divisions + SET division_name = $2 + WHERE division_id = $1;`; + await database.executeQuery(query, [id, name]); + return new Division(id, name); +} + +async function remove(id) { + query = `DELETE FROM scores.divisions + WHERE division_id = $1 + RETURNING division_name;`; + name = (await database.executeQuery(query, [id]))[0][0]; + return new Division(id, name); +} \ No newline at end of file From 6e108ef975a7415a023964baab65f6c63b998253 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sat, 20 Nov 2021 21:25:29 -0700 Subject: [PATCH 035/255] Create function to retrieve divisions from database --- database/scores/divisions.js | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/database/scores/divisions.js b/database/scores/divisions.js index 0a0e8be..8c5510d 100644 --- a/database/scores/divisions.js +++ b/database/scores/divisions.js @@ -14,11 +14,17 @@ class Division { +function getGenderID(gender) { + return (gender == genders.male) ? "M" : "F"; +} + + + async function create(name, gender, sportID) { query = `INSERT INTO scores.divisions(division_name,gender,sport_id) VALUES($1,$2,$3) RETURNING division_id;`; - const genderID = (gender == genders.male) ? "M" : "F"; + const genderID = getGenderID(gender); const id = (await database.executeQuery(query, [name, genderID, sportID]))[0][0]; return new Division(id, name); } @@ -37,4 +43,19 @@ async function remove(id) { RETURNING division_name;`; name = (await database.executeQuery(query, [id]))[0][0]; return new Division(id, name); +} + +async function retrieveBySportAndGender(sportID, gender) { + query = `SELECT * + FROM scores.divisions + WHERE sport_id = $1 AND gender = $2 + ORDER BY division_name;`; + const genderID = getGenderID(gender); + const table = await database.executeQuery(query, [sportID, genderID]); + + const divisionsList = []; + table.forEach((row) => { + divisionsList.push(new Division(row[0], row[1])); + }); + return divisionsList; } \ No newline at end of file From 9c55999ad88deea85a50cc6e0c38015a92efc43f Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sat, 20 Nov 2021 21:35:56 -0700 Subject: [PATCH 036/255] Add function to create teams --- database/scores/teams.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 database/scores/teams.js diff --git a/database/scores/teams.js b/database/scores/teams.js new file mode 100644 index 0000000..8d9858b --- /dev/null +++ b/database/scores/teams.js @@ -0,0 +1,22 @@ +const database = require('./../database'); + + + + + +class Team { + constructor(id, name) { + this.id = id; + this.name = name; + } +} + + + +async function create(name, sportID) { + query = `INSERT INTO scores.teams(team_name, sport_id) + VALUES($1, $2) + RETURNING team_id;`; + const id = (await database.executeQuery(query, [name, sportID]))[0][0]; + return new Team(id, name); +} \ No newline at end of file From 7e07236779421710d06b8559e7a5ba4c5c1a7d1e Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sat, 20 Nov 2021 21:38:08 -0700 Subject: [PATCH 037/255] Add function to rename teams --- database/scores/teams.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/database/scores/teams.js b/database/scores/teams.js index 8d9858b..53450fd 100644 --- a/database/scores/teams.js +++ b/database/scores/teams.js @@ -19,4 +19,12 @@ async function create(name, sportID) { RETURNING team_id;`; const id = (await database.executeQuery(query, [name, sportID]))[0][0]; return new Team(id, name); +} + +async function rename(id, name) { + query = `UPDATE scores.teams + SET team_name = $2 + WHERE team_id = $1;`; + await database.executeQuery(query, [id, name]); + return new Team(id, name); } \ No newline at end of file From 16c90cf8839d697f11b6c2a0c62cfd9e81fbe1e7 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sat, 20 Nov 2021 21:39:30 -0700 Subject: [PATCH 038/255] Add function to remove teams --- database/scores/teams.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/database/scores/teams.js b/database/scores/teams.js index 53450fd..3e09496 100644 --- a/database/scores/teams.js +++ b/database/scores/teams.js @@ -27,4 +27,12 @@ async function rename(id, name) { WHERE team_id = $1;`; await database.executeQuery(query, [id, name]); return new Team(id, name); +} + +async function remove(id) { + query = `DELETE FROM scores.teams + WHERE team_id = $1 + RETURNING team_name;`; + name = (await database.executeQuery(query, [id]))[0][0]; + return new Team(id, name); } \ No newline at end of file From e37b10596e7f45668bf5a7235eb512947353414e Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sat, 20 Nov 2021 21:43:19 -0700 Subject: [PATCH 039/255] Add function to retrieve teams by sport --- database/scores/teams.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/database/scores/teams.js b/database/scores/teams.js index 3e09496..fe27d6b 100644 --- a/database/scores/teams.js +++ b/database/scores/teams.js @@ -35,4 +35,18 @@ async function remove(id) { RETURNING team_name;`; name = (await database.executeQuery(query, [id]))[0][0]; return new Team(id, name); +} + +async function retrieveBySport(sportID) { + query = `SELECT * + FROM scores.teams + WHERE sport_id = $1 + ORDER BY team_name;`; + const table = await database.executeQuery(query); + + const teamsList = []; + table.forEach((row) => { + teamsList.push(new Team(row[0], row[1])); + }); + return teamsList; } \ No newline at end of file From e056743022059243f6545985748eb1e79f51f4ca Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sat, 20 Nov 2021 21:45:28 -0700 Subject: [PATCH 040/255] Add "const" to accidental global variables --- database/scores/divisions.js | 10 +++++----- database/scores/sports.js | 10 +++++----- database/scores/teams.js | 10 +++++----- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/database/scores/divisions.js b/database/scores/divisions.js index 8c5510d..9003da9 100644 --- a/database/scores/divisions.js +++ b/database/scores/divisions.js @@ -21,7 +21,7 @@ function getGenderID(gender) { async function create(name, gender, sportID) { - query = `INSERT INTO scores.divisions(division_name,gender,sport_id) + const query = `INSERT INTO scores.divisions(division_name,gender,sport_id) VALUES($1,$2,$3) RETURNING division_id;`; const genderID = getGenderID(gender); @@ -30,7 +30,7 @@ async function create(name, gender, sportID) { } async function rename(id, division) { - query = `UPDATE scores.divisions + const query = `UPDATE scores.divisions SET division_name = $2 WHERE division_id = $1;`; await database.executeQuery(query, [id, name]); @@ -38,15 +38,15 @@ async function rename(id, division) { } async function remove(id) { - query = `DELETE FROM scores.divisions + const query = `DELETE FROM scores.divisions WHERE division_id = $1 RETURNING division_name;`; - name = (await database.executeQuery(query, [id]))[0][0]; + const name = (await database.executeQuery(query, [id]))[0][0]; return new Division(id, name); } async function retrieveBySportAndGender(sportID, gender) { - query = `SELECT * + const query = `SELECT * FROM scores.divisions WHERE sport_id = $1 AND gender = $2 ORDER BY division_name;`; diff --git a/database/scores/sports.js b/database/scores/sports.js index 38f35cf..c801612 100644 --- a/database/scores/sports.js +++ b/database/scores/sports.js @@ -12,7 +12,7 @@ class Sport { async function create(name) { - query = `INSERT INTO scores.sports(sport_name) + const query = `INSERT INTO scores.sports(sport_name) VALUES($1) RETURNING sport_id;`; const id = (await database.executeQuery(query, [name]))[0][0]; @@ -20,7 +20,7 @@ async function create(name) { } async function rename(id, name) { - query = `UPDATE scores.sports + const query = `UPDATE scores.sports SET sport_name = $2 WHERE sport_id = $1;`; await database.executeQuery(query, [id, name]); @@ -28,15 +28,15 @@ async function rename(id, name) { } async function remove(id) { - query = `DELETE FROM scores.sports + const query = `DELETE FROM scores.sports WHERE sport_id = $1 RETURNING sport_name;`; - name = (await database.executeQuery(query, [id]))[0][0]; + const name = (await database.executeQuery(query, [id]))[0][0]; return new Sport(id, name); } async function retrieveAll() { - query = `SELECT * + const query = `SELECT * FROM scores.sports ORDER BY sport_name;`; const table = await database.executeQuery(query); diff --git a/database/scores/teams.js b/database/scores/teams.js index fe27d6b..0bdb33d 100644 --- a/database/scores/teams.js +++ b/database/scores/teams.js @@ -14,7 +14,7 @@ class Team { async function create(name, sportID) { - query = `INSERT INTO scores.teams(team_name, sport_id) + const query = `INSERT INTO scores.teams(team_name, sport_id) VALUES($1, $2) RETURNING team_id;`; const id = (await database.executeQuery(query, [name, sportID]))[0][0]; @@ -22,7 +22,7 @@ async function create(name, sportID) { } async function rename(id, name) { - query = `UPDATE scores.teams + const query = `UPDATE scores.teams SET team_name = $2 WHERE team_id = $1;`; await database.executeQuery(query, [id, name]); @@ -30,15 +30,15 @@ async function rename(id, name) { } async function remove(id) { - query = `DELETE FROM scores.teams + const query = `DELETE FROM scores.teams WHERE team_id = $1 RETURNING team_name;`; - name = (await database.executeQuery(query, [id]))[0][0]; + const name = (await database.executeQuery(query, [id]))[0][0]; return new Team(id, name); } async function retrieveBySport(sportID) { - query = `SELECT * + const query = `SELECT * FROM scores.teams WHERE sport_id = $1 ORDER BY team_name;`; From c2e3c95cfdaa65764f67fdee49c3ec79fd51df14 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sat, 20 Nov 2021 21:46:28 -0700 Subject: [PATCH 041/255] Add module exports to divisions.js --- database/scores/divisions.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/database/scores/divisions.js b/database/scores/divisions.js index 9003da9..6f0a443 100644 --- a/database/scores/divisions.js +++ b/database/scores/divisions.js @@ -58,4 +58,13 @@ async function retrieveBySportAndGender(sportID, gender) { divisionsList.push(new Division(row[0], row[1])); }); return divisionsList; -} \ No newline at end of file +} + + + + + +exports.create = create; +exports.rename = rename; +exports.remove = remove; +exports.retrieveBySportAndGender = retrieveBySportAndGender; \ No newline at end of file From 90e302c3ae535ad70545f3e0edebae521eeb1f02 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sat, 20 Nov 2021 21:47:15 -0700 Subject: [PATCH 042/255] Add module exports to teams.js --- database/scores/teams.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/database/scores/teams.js b/database/scores/teams.js index 0bdb33d..a6dac68 100644 --- a/database/scores/teams.js +++ b/database/scores/teams.js @@ -49,4 +49,13 @@ async function retrieveBySport(sportID) { teamsList.push(new Team(row[0], row[1])); }); return teamsList; -} \ No newline at end of file +} + + + + + +exports.create = create; +exports.rename = rename; +exports.remove = remove; +exports.retrieveBySport = retrieveBySport; \ No newline at end of file From b533668445755edec9069951b212dcbe8870578b Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sat, 20 Nov 2021 21:52:32 -0700 Subject: [PATCH 043/255] Add function to create season --- database/scores/seasons.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 database/scores/seasons.js diff --git a/database/scores/seasons.js b/database/scores/seasons.js new file mode 100644 index 0000000..de32b2b --- /dev/null +++ b/database/scores/seasons.js @@ -0,0 +1,22 @@ +const database = require('./../database'); + + + + + +class Season { + constructor(id, year) { + this.id = id; + this.year = year; + } +} + + + +async function create(year) { + const query = `INSERT INTO scores.seasons(school_year) + VALUES($1) + RETURNING season_id;`; + const id = (await database.executeQuery(query, [year]))[0][0]; + return new Season(id, year); +} \ No newline at end of file From e9bda3c807bcf18c7f5cec6c402ddab12a0ede51 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sat, 20 Nov 2021 21:55:31 -0700 Subject: [PATCH 044/255] Add function to remove season --- database/scores/seasons.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/database/scores/seasons.js b/database/scores/seasons.js index de32b2b..55e9941 100644 --- a/database/scores/seasons.js +++ b/database/scores/seasons.js @@ -19,4 +19,12 @@ async function create(year) { RETURNING season_id;`; const id = (await database.executeQuery(query, [year]))[0][0]; return new Season(id, year); +} + +async function remove(id) { + const query = `DELETE FROM scores.seasons + WHERE season_id = $1 + RETURNING school_year;`; + const year = (await database.executeQuery(query, [id]))[0][0]; + return new Season(id, year); } \ No newline at end of file From c046eeb6acd42f2e17fd8698ea3e3a9cabc73f96 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sat, 20 Nov 2021 21:57:27 -0700 Subject: [PATCH 045/255] Create function to retrive seasons --- database/scores/seasons.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/database/scores/seasons.js b/database/scores/seasons.js index 55e9941..b8ff917 100644 --- a/database/scores/seasons.js +++ b/database/scores/seasons.js @@ -27,4 +27,17 @@ async function remove(id) { RETURNING school_year;`; const year = (await database.executeQuery(query, [id]))[0][0]; return new Season(id, year); +} + +async function retrieveAll() { + const query = `SELECT * + FROM scores.seasons + ORDER BY school_year;`; + const table = await database.executeQuery(query); + + const seasonsList = []; + table.forEach((row) => { + seasonsList.push(new Season(row[0], row[1])); + }); + return seasonsList; } \ No newline at end of file From 4eadf64fac3692869bcf38fb6846e867c63683d9 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sat, 20 Nov 2021 22:01:55 -0700 Subject: [PATCH 046/255] Rename "create" function to "add" in "scores" modules --- database/scores/divisions.js | 4 ++-- database/scores/seasons.js | 2 +- database/scores/sports.js | 4 ++-- database/scores/teams.js | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/database/scores/divisions.js b/database/scores/divisions.js index 6f0a443..b04d6a5 100644 --- a/database/scores/divisions.js +++ b/database/scores/divisions.js @@ -20,7 +20,7 @@ function getGenderID(gender) { -async function create(name, gender, sportID) { +async function add(name, gender, sportID) { const query = `INSERT INTO scores.divisions(division_name,gender,sport_id) VALUES($1,$2,$3) RETURNING division_id;`; @@ -64,7 +64,7 @@ async function retrieveBySportAndGender(sportID, gender) { -exports.create = create; +exports.add = add; exports.rename = rename; exports.remove = remove; exports.retrieveBySportAndGender = retrieveBySportAndGender; \ No newline at end of file diff --git a/database/scores/seasons.js b/database/scores/seasons.js index b8ff917..bd9704d 100644 --- a/database/scores/seasons.js +++ b/database/scores/seasons.js @@ -13,7 +13,7 @@ class Season { -async function create(year) { +async function add(year) { const query = `INSERT INTO scores.seasons(school_year) VALUES($1) RETURNING season_id;`; diff --git a/database/scores/sports.js b/database/scores/sports.js index c801612..14a6e45 100644 --- a/database/scores/sports.js +++ b/database/scores/sports.js @@ -11,7 +11,7 @@ class Sport { -async function create(name) { +async function add(name) { const query = `INSERT INTO scores.sports(sport_name) VALUES($1) RETURNING sport_id;`; @@ -52,7 +52,7 @@ async function retrieveAll() { -exports.create = create; +exports.add = add; exports.rename = rename; exports.remove = remove; exports.retrieveAll = retrieveAll; \ No newline at end of file diff --git a/database/scores/teams.js b/database/scores/teams.js index a6dac68..1bad3fa 100644 --- a/database/scores/teams.js +++ b/database/scores/teams.js @@ -13,7 +13,7 @@ class Team { -async function create(name, sportID) { +async function add(name, sportID) { const query = `INSERT INTO scores.teams(team_name, sport_id) VALUES($1, $2) RETURNING team_id;`; @@ -55,7 +55,7 @@ async function retrieveBySport(sportID) { -exports.create = create; +exports.add = add; exports.rename = rename; exports.remove = remove; exports.retrieveBySport = retrieveBySport; \ No newline at end of file From 5f68b24d8badf4fb03f0c402d32d14ad9bf8a436 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sat, 20 Nov 2021 22:02:20 -0700 Subject: [PATCH 047/255] Add exports to seasons.js --- database/scores/seasons.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/database/scores/seasons.js b/database/scores/seasons.js index bd9704d..381d064 100644 --- a/database/scores/seasons.js +++ b/database/scores/seasons.js @@ -40,4 +40,12 @@ async function retrieveAll() { seasonsList.push(new Season(row[0], row[1])); }); return seasonsList; -} \ No newline at end of file +} + + + + + +exports.add = add; +exports.remove = remove; +exports.retrieveAll = retrieveAll; \ No newline at end of file From acb987ffd40854aeeab1541f596eb2ebc1db28c2 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sat, 20 Nov 2021 22:14:12 -0700 Subject: [PATCH 048/255] Add function to create game --- database/scores/games.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 database/scores/games.js diff --git a/database/scores/games.js b/database/scores/games.js new file mode 100644 index 0000000..256919b --- /dev/null +++ b/database/scores/games.js @@ -0,0 +1,26 @@ +const database = require('./../database'); + + + + + +class Game { + constructor(id, date, team1ID, team2ID, team1Score, team2Score) { + this.id = id; + this.date = date; + this.team1ID = team1ID; + this.team2ID = team2ID; + this.team1Score = team1Score; + this.team2Score = team2Score; + } +} + + + +async function add(divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score) { + const query = `INSERT INTO scores.games(division_id, season_id, game_date, team1_id, team2_id, team1_score, team2_score) + VALUES($1, $2, $3, $4, $5, $6, $7) + RETURNING game_id;`; + const id = (await database.executeQuery(query, [divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score]))[0][0]; + return new Game(id, date, team1ID, team2ID, team1Score, team2Score); +} \ No newline at end of file From 9ca8641f207a45b2f808454719da9f30f13dafcb Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sat, 20 Nov 2021 22:20:38 -0700 Subject: [PATCH 049/255] Create function to remove games from database --- database/scores/games.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/database/scores/games.js b/database/scores/games.js index 256919b..c0ad895 100644 --- a/database/scores/games.js +++ b/database/scores/games.js @@ -23,4 +23,12 @@ async function add(divisionID, seasonID, date, team1ID, team2ID, team1Score, tea RETURNING game_id;`; const id = (await database.executeQuery(query, [divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score]))[0][0]; return new Game(id, date, team1ID, team2ID, team1Score, team2Score); +} + +async function remove(id) { + const query = `DELETE FROM scores.games + WHERE game_id = $1 + RETURNING * ;`; + const row = (await database.executeQuery(query, [id]))[0]; + return new Game(id, row[3], row[4], row[5], row[6], row[7]); } \ No newline at end of file From 42ccda2aba492aa788982fc879e139d4d59e5941 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sat, 20 Nov 2021 23:01:13 -0700 Subject: [PATCH 050/255] Add proper date handling functionality to "add" function of "games.js" --- database/scores/games.js | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/database/scores/games.js b/database/scores/games.js index c0ad895..94e017c 100644 --- a/database/scores/games.js +++ b/database/scores/games.js @@ -21,7 +21,14 @@ async function add(divisionID, seasonID, date, team1ID, team2ID, team1Score, tea const query = `INSERT INTO scores.games(division_id, season_id, game_date, team1_id, team2_id, team1_score, team2_score) VALUES($1, $2, $3, $4, $5, $6, $7) RETURNING game_id;`; - const id = (await database.executeQuery(query, [divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score]))[0][0]; + + const day = ("0" + date.getDate()).slice(-2); + const month = ("0" + (date.getMonth() + 1)).slice(-2); + const year = date.getFullYear(); + + const dateISO = year + '-' + month + '-' + day; + + const id = (await database.executeQuery(query, [divisionID, seasonID, dateISO, team1ID, team2ID, team1Score, team2Score]))[0][0]; return new Game(id, date, team1ID, team2ID, team1Score, team2Score); } @@ -31,4 +38,23 @@ async function remove(id) { RETURNING * ;`; const row = (await database.executeQuery(query, [id]))[0]; return new Game(id, row[3], row[4], row[5], row[6], row[7]); +} + +async function retrieveByTeamDivisionAndSeason(teamID, divisionID, seasonID) { + const query = `SELECT * + FROM scores.games + WHERE (team1_id = $1 OR team2_id = $1) AND division_id = $2 AND season_id = $3 + ORDER BY game_date DESC;`; + const table = (await database.executeQuery(query, [teamID,divisionID,seasonID])); + + const gamesList = []; + table.forEach((row) => { + opponentIsTeam2 = teamID != row[5]; + opponentID = opponentIsTeam2 ? row[5] : row[4]; + teamScore = opponentIsTeam2 ? row[6] : row[7]; + opponentScore = opponentIsTeam2 ? row[7] : row[6]; + + gamesList.push(new Game(row[0], row[3], teamID, opponentID, teamScore, opponentScore)); + }); + return gamesList; } \ No newline at end of file From 78136df5fd809fe526cd3815c562fd4e2a9da467 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sat, 20 Nov 2021 23:02:22 -0700 Subject: [PATCH 051/255] Add module exports for games.js --- database/scores/games.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/database/scores/games.js b/database/scores/games.js index 94e017c..b990a31 100644 --- a/database/scores/games.js +++ b/database/scores/games.js @@ -57,4 +57,12 @@ async function retrieveByTeamDivisionAndSeason(teamID, divisionID, seasonID) { gamesList.push(new Game(row[0], row[3], teamID, opponentID, teamScore, opponentScore)); }); return gamesList; -} \ No newline at end of file +} + + + + + +exports.add = add; +exports.remove = remove; +exports.retrieveByTeamDivisionAndSeason = retrieveByTeamDivisionAndSeason; \ No newline at end of file From 8c3ffcc5fada71f9bf9a957d99a0a47a0c07ea37 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sun, 21 Nov 2021 14:37:35 -0700 Subject: [PATCH 052/255] Start script to fetch data from server --- app.js | 4 +++- public/scripts/submit.js | 0 routes/data.js | 10 ++++++++++ views/layout.pug | 1 + views/submit.pug | 3 +++ 5 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 public/scripts/submit.js create mode 100644 routes/data.js diff --git a/app.js b/app.js index 3cff1ca..29f7b4f 100644 --- a/app.js +++ b/app.js @@ -6,7 +6,8 @@ var logger = require('morgan'); var indexRouter = require('./routes/index'); var usersRouter = require('./routes/users'); -var submitRouter = require('./routes/submit') +var submitRouter = require('./routes/submit'); +var dataRouter = require('./routes/data'); var app = express(); @@ -23,6 +24,7 @@ app.use(express.static(path.join(__dirname, 'public'))); app.use('/', indexRouter); app.use('/users', usersRouter); app.use('/submit', submitRouter); +app.use('/data', dataRouter); // catch 404 and forward to error handler app.use(function(req, res, next) { diff --git a/public/scripts/submit.js b/public/scripts/submit.js new file mode 100644 index 0000000..e69de29 diff --git a/routes/data.js b/routes/data.js new file mode 100644 index 0000000..05f5291 --- /dev/null +++ b/routes/data.js @@ -0,0 +1,10 @@ +var express = require('express'); +var router = express.Router(); +var database = require('../database/database'); + +/* GET submit page. */ +router.get('/', function(req, res, next) { + res.json( { message : "test" }); +}); + +module.exports = router; \ No newline at end of file diff --git a/views/layout.pug b/views/layout.pug index 69f5a59..e4f85eb 100644 --- a/views/layout.pug +++ b/views/layout.pug @@ -7,3 +7,4 @@ html block stylesheets body block content + block scripts diff --git a/views/submit.pug b/views/submit.pug index 385ad51..149fde7 100644 --- a/views/submit.pug +++ b/views/submit.pug @@ -39,3 +39,6 @@ block content input(class="score-input", type="number", name="team2-score", value="0") span(class='form-section') button(type="submit") Submit + +block scripts + script(src='/scripts/submit.js') \ No newline at end of file From 30ddb13ec6815007a80c4db7de6a3b2b64108fa1 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sun, 21 Nov 2021 15:30:54 -0700 Subject: [PATCH 053/255] Add function to list sports in submit form dropdown --- public/scripts/data.js | 5 +++++ public/scripts/submit.js | 20 ++++++++++++++++++++ routes/data.js | 7 ++++--- views/submit.pug | 3 +-- 4 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 public/scripts/data.js diff --git a/public/scripts/data.js b/public/scripts/data.js new file mode 100644 index 0000000..01b681b --- /dev/null +++ b/public/scripts/data.js @@ -0,0 +1,5 @@ +export async function getSports() { + const response = await fetch('/data/sports'); + const sportsList = await response.json(); + return sportsList; +} \ No newline at end of file diff --git a/public/scripts/submit.js b/public/scripts/submit.js index e69de29..dc3b2b7 100644 --- a/public/scripts/submit.js +++ b/public/scripts/submit.js @@ -0,0 +1,20 @@ +import * as Data from "./data.js"; + + +const sportDropdown = document.getElementById('sport-dropdown'); + + + + + +async function listSports() { + let sportsList = await Data.getSports(); + + sportsList.forEach(sport => { + const option = document.createElement('option'); + option.text = sport.name; + option.value = sport.id; + sportDropdown.appendChild(option); + }); +} +listSports(); \ No newline at end of file diff --git a/routes/data.js b/routes/data.js index 05f5291..072a63e 100644 --- a/routes/data.js +++ b/routes/data.js @@ -1,10 +1,11 @@ var express = require('express'); var router = express.Router(); -var database = require('../database/database'); +var sports = require('../database/scores/sports'); /* GET submit page. */ -router.get('/', function(req, res, next) { - res.json( { message : "test" }); +router.get('/sports', function(req, res, next) { + sports.retrieveAll() + .then(data => res.json(data)); }); module.exports = router; \ No newline at end of file diff --git a/views/submit.pug b/views/submit.pug index 149fde7..25ebe71 100644 --- a/views/submit.pug +++ b/views/submit.pug @@ -15,7 +15,6 @@ block content label Sport span(class='form-section-input') select#sport-dropdown(name="sport" class="main-dropdown") - option(value="Basketball" selected) Basketball select#gender-dropdown(name="gender") option(value="Male" selected) Male option(value="Female") Female @@ -41,4 +40,4 @@ block content button(type="submit") Submit block scripts - script(src='/scripts/submit.js') \ No newline at end of file + script(src='/scripts/submit.js' type="module") \ No newline at end of file From 0d9e70896a0d9fd853a48f26cfe49f1cd7ca7467 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sun, 21 Nov 2021 15:40:48 -0700 Subject: [PATCH 054/255] Add function to list seasons in submit form --- public/scripts/data.js | 6 ++++++ public/scripts/submit.js | 17 +++++++++++++++-- routes/data.js | 8 +++++++- views/submit.pug | 1 - 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/public/scripts/data.js b/public/scripts/data.js index 01b681b..44d50a1 100644 --- a/public/scripts/data.js +++ b/public/scripts/data.js @@ -2,4 +2,10 @@ export async function getSports() { const response = await fetch('/data/sports'); const sportsList = await response.json(); return sportsList; +} + +export async function getSeasons() { + const response = await fetch('/data/seasons'); + const seasonsList = await response.json(); + return seasonsList; } \ No newline at end of file diff --git a/public/scripts/submit.js b/public/scripts/submit.js index dc3b2b7..64b62ac 100644 --- a/public/scripts/submit.js +++ b/public/scripts/submit.js @@ -2,13 +2,14 @@ import * as Data from "./data.js"; const sportDropdown = document.getElementById('sport-dropdown'); +const seasonDropdown = document.getElementById('year-dropdown'); async function listSports() { - let sportsList = await Data.getSports(); + const sportsList = await Data.getSports(); sportsList.forEach(sport => { const option = document.createElement('option'); @@ -17,4 +18,16 @@ async function listSports() { sportDropdown.appendChild(option); }); } -listSports(); \ No newline at end of file +listSports(); + +async function listSeasons() { + const seasonsList = await Data.getSeasons(); + + seasonsList.forEach(season => { + const option = document.createElement('option'); + option.text = season.year - 1 + "-" + season.year; + option.value = season.id; + seasonDropdown.appendChild(option); + }); +} +listSeasons(); \ No newline at end of file diff --git a/routes/data.js b/routes/data.js index 072a63e..aa61618 100644 --- a/routes/data.js +++ b/routes/data.js @@ -1,11 +1,17 @@ var express = require('express'); var router = express.Router(); var sports = require('../database/scores/sports'); +var seasons = require('../database/scores/seasons'); + -/* GET submit page. */ router.get('/sports', function(req, res, next) { sports.retrieveAll() .then(data => res.json(data)); }); +router.get('/seasons', function(req, res, next) { + seasons.retrieveAll() + .then(data => res.json(data)); +}) + module.exports = router; \ No newline at end of file diff --git a/views/submit.pug b/views/submit.pug index 25ebe71..5ac6ccf 100644 --- a/views/submit.pug +++ b/views/submit.pug @@ -10,7 +10,6 @@ block content label Year span(class='form-section-input') select#year-dropdown(name="year" class="main-dropdown") - option(value="2022" selected) 2021-2022 span(class='form-section') label Sport span(class='form-section-input') From c0ab9d45816ff0051a4ebdcaaa64e03d6ea19f77 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sun, 21 Nov 2021 15:52:54 -0700 Subject: [PATCH 055/255] Create module to retrieve a list of genders by sport --- database/scores/genders.js | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 database/scores/genders.js diff --git a/database/scores/genders.js b/database/scores/genders.js new file mode 100644 index 0000000..857190f --- /dev/null +++ b/database/scores/genders.js @@ -0,0 +1,34 @@ +const database = require('./../database'); +const genders = require('../../constants/genders'); + + + + + +function getGendersBySport(sportID) { + const query = `SELECT gender + from scores.divisions + WHERE sport_id = $1;`; + const table = await database.executeQuery(query, [sportID]); + + const gendersList = []; + + if(table.length() == 2) { + gendersList.push(genders.female); + gendersList.push(genders.male); + } + else if(table[0][0] = "F") { + gendersList.push(genders.female); + } + else if(table[0][0] = "M") { + gendersList.push(genders.male); + } + + return gendersList; +} + + + + + +exports.getGendersBySport = getGendersBySport; \ No newline at end of file From 07c4d9b6221987da465806de8f4be2cfebf52ba7 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sun, 21 Nov 2021 15:53:58 -0700 Subject: [PATCH 056/255] Retrieve seasons in descending order --- database/scores/seasons.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/scores/seasons.js b/database/scores/seasons.js index 381d064..f787ade 100644 --- a/database/scores/seasons.js +++ b/database/scores/seasons.js @@ -32,7 +32,7 @@ async function remove(id) { async function retrieveAll() { const query = `SELECT * FROM scores.seasons - ORDER BY school_year;`; + ORDER BY school_year DESC;`; const table = await database.executeQuery(query); const seasonsList = []; From a8eb7dae0a9e65fcb88f73b065e16c1d38c59bfe Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sun, 21 Nov 2021 15:57:09 -0700 Subject: [PATCH 057/255] Rename function in genders.js from "getGenderBySport" to "retrieveBySport" --- database/scores/genders.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/database/scores/genders.js b/database/scores/genders.js index 857190f..9e6ba44 100644 --- a/database/scores/genders.js +++ b/database/scores/genders.js @@ -5,7 +5,7 @@ const genders = require('../../constants/genders'); -function getGendersBySport(sportID) { +function retrieveBySport(sportID) { const query = `SELECT gender from scores.divisions WHERE sport_id = $1;`; @@ -31,4 +31,4 @@ function getGendersBySport(sportID) { -exports.getGendersBySport = getGendersBySport; \ No newline at end of file +exports.retrieveBySport = retrieveBySport; \ No newline at end of file From e5b17912598205daa24502fecd4468fd14f0f6b8 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sun, 21 Nov 2021 15:59:07 -0700 Subject: [PATCH 058/255] Make "retrieveBySport" function in genders.js async --- database/scores/genders.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/scores/genders.js b/database/scores/genders.js index 9e6ba44..c86221a 100644 --- a/database/scores/genders.js +++ b/database/scores/genders.js @@ -5,7 +5,7 @@ const genders = require('../../constants/genders'); -function retrieveBySport(sportID) { +async function retrieveBySport(sportID) { const query = `SELECT gender from scores.divisions WHERE sport_id = $1;`; From ff070acdf7ea9889c7e3b604f28d300acb732577 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sun, 21 Nov 2021 16:24:43 -0700 Subject: [PATCH 059/255] Add function to get genders per sport in submit form --- database/scores/genders.js | 10 +++++++--- public/scripts/data.js | 6 ++++++ public/scripts/submit.js | 23 ++++++++++++++++++++++- routes/data.js | 6 ++++++ views/submit.pug | 2 -- 5 files changed, 41 insertions(+), 6 deletions(-) diff --git a/database/scores/genders.js b/database/scores/genders.js index c86221a..27e811e 100644 --- a/database/scores/genders.js +++ b/database/scores/genders.js @@ -13,18 +13,22 @@ async function retrieveBySport(sportID) { const gendersList = []; - if(table.length() == 2) { + if(table.length == 0) { + return gendersList; + } + if(table.length == 2) { gendersList.push(genders.female); gendersList.push(genders.male); + return gendersList; } else if(table[0][0] = "F") { gendersList.push(genders.female); + return gendersList; } else if(table[0][0] = "M") { gendersList.push(genders.male); + return gendersList; } - - return gendersList; } diff --git a/public/scripts/data.js b/public/scripts/data.js index 44d50a1..cccd7cf 100644 --- a/public/scripts/data.js +++ b/public/scripts/data.js @@ -8,4 +8,10 @@ export async function getSeasons() { const response = await fetch('/data/seasons'); const seasonsList = await response.json(); return seasonsList; +} + +export async function getGenders(sportID) { + const response = await fetch(`/data/genders?sport=${+sportID}`); + const gendersList = await response.json(); + return gendersList; } \ No newline at end of file diff --git a/public/scripts/submit.js b/public/scripts/submit.js index 64b62ac..e51fda6 100644 --- a/public/scripts/submit.js +++ b/public/scripts/submit.js @@ -3,6 +3,7 @@ import * as Data from "./data.js"; const sportDropdown = document.getElementById('sport-dropdown'); const seasonDropdown = document.getElementById('year-dropdown'); +const genderDropdown = document.getElementById('gender-dropdown'); @@ -30,4 +31,24 @@ async function listSeasons() { seasonDropdown.appendChild(option); }); } -listSeasons(); \ No newline at end of file +listSeasons(); + +async function listGenders() { + genderDropdown.innerHTML = ""; + + const selectedSportID = sportDropdown.value; + const gendersList = await Data.getGenders(selectedSportID); + + gendersList.forEach(gender => { + const option = document.createElement('option'); + option.text = (gender.name == "female") ? "Female" : (gender.name == "male") ? "Male" : ""; + option.value = gender.name; + genderDropdown.appendChild(option); + }); +} + + + + + +sportDropdown.onchange = listGenders; \ No newline at end of file diff --git a/routes/data.js b/routes/data.js index aa61618..8b4ea87 100644 --- a/routes/data.js +++ b/routes/data.js @@ -2,6 +2,7 @@ var express = require('express'); var router = express.Router(); var sports = require('../database/scores/sports'); var seasons = require('../database/scores/seasons'); +var genders = require('../database/scores/genders'); router.get('/sports', function(req, res, next) { @@ -14,4 +15,9 @@ router.get('/seasons', function(req, res, next) { .then(data => res.json(data)); }) +router.get('/genders', function(req, res, next) { + genders.retrieveBySport(req.query.sport) + .then(data => res.json(data)); +}) + module.exports = router; \ No newline at end of file diff --git a/views/submit.pug b/views/submit.pug index 5ac6ccf..93fdec1 100644 --- a/views/submit.pug +++ b/views/submit.pug @@ -15,8 +15,6 @@ block content span(class='form-section-input') select#sport-dropdown(name="sport" class="main-dropdown") select#gender-dropdown(name="gender") - option(value="Male" selected) Male - option(value="Female") Female select#division-dropdown(name="division") option(value="Varsity") Varsity option(value="JV-A") JV-A From bdd943d422fefbec529b5309cb587e63b41d7f46 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sun, 21 Nov 2021 16:30:36 -0700 Subject: [PATCH 060/255] Move gender constants into genders.js file in database directory --- constants/genders.js | 8 -------- database/scores/divisions.js | 4 ++-- database/scores/genders.js | 22 ++++++++++++++++------ 3 files changed, 18 insertions(+), 16 deletions(-) delete mode 100644 constants/genders.js diff --git a/constants/genders.js b/constants/genders.js deleted file mode 100644 index 402d58f..0000000 --- a/constants/genders.js +++ /dev/null @@ -1,8 +0,0 @@ -class Gender { - constructor(name) { - this.name = name; - } -} - -exports.male = new Gender("male"); -exports.female = new Gender("female"); \ No newline at end of file diff --git a/database/scores/divisions.js b/database/scores/divisions.js index b04d6a5..38fa8be 100644 --- a/database/scores/divisions.js +++ b/database/scores/divisions.js @@ -1,5 +1,5 @@ const database = require('./../database'); -const genders = require('../../constants/genders'); +const genders = require('./genders'); @@ -15,7 +15,7 @@ class Division { function getGenderID(gender) { - return (gender == genders.male) ? "M" : "F"; + return (gender == genders.MALE) ? "M" : "F"; } diff --git a/database/scores/genders.js b/database/scores/genders.js index 27e811e..126e57b 100644 --- a/database/scores/genders.js +++ b/database/scores/genders.js @@ -1,10 +1,19 @@ const database = require('./../database'); -const genders = require('../../constants/genders'); +class Gender { + constructor(name) { + this.name = name; + } +} +const MALE = new Gender("male"); +const FEMALE = new Gender("female"); + + + async function retrieveBySport(sportID) { const query = `SELECT gender from scores.divisions @@ -17,16 +26,16 @@ async function retrieveBySport(sportID) { return gendersList; } if(table.length == 2) { - gendersList.push(genders.female); - gendersList.push(genders.male); + gendersList.push(genders.FEMALE); + gendersList.push(genders.MALE); return gendersList; } else if(table[0][0] = "F") { - gendersList.push(genders.female); + gendersList.push(genders.FEMALE); return gendersList; } else if(table[0][0] = "M") { - gendersList.push(genders.male); + gendersList.push(genders.MALE); return gendersList; } } @@ -34,5 +43,6 @@ async function retrieveBySport(sportID) { - +exports.MALE = MALE; +exports.FEMALE = FEMALE; exports.retrieveBySport = retrieveBySport; \ No newline at end of file From d561a34055fc0086cd8edf2322f1bd8d52461afc Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sun, 21 Nov 2021 16:39:51 -0700 Subject: [PATCH 061/255] Fix bug in genders.js regarding constants --- database/scores/genders.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/database/scores/genders.js b/database/scores/genders.js index 126e57b..b59bdb0 100644 --- a/database/scores/genders.js +++ b/database/scores/genders.js @@ -26,16 +26,16 @@ async function retrieveBySport(sportID) { return gendersList; } if(table.length == 2) { - gendersList.push(genders.FEMALE); - gendersList.push(genders.MALE); + gendersList.push(FEMALE); + gendersList.push(MALE); return gendersList; } else if(table[0][0] = "F") { - gendersList.push(genders.FEMALE); + gendersList.push(FEMALE); return gendersList; } else if(table[0][0] = "M") { - gendersList.push(genders.MALE); + gendersList.push(MALE); return gendersList; } } From 9b7ec5f3d679fa0e13f028900dc4c1ba8e88e0e8 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sun, 21 Nov 2021 16:48:03 -0700 Subject: [PATCH 062/255] Add function to list divisions in submit form --- public/scripts/data.js | 6 ++++++ public/scripts/submit.js | 26 +++++++++++++++++++++++++- routes/data.js | 8 ++++++++ views/submit.pug | 3 --- 4 files changed, 39 insertions(+), 4 deletions(-) diff --git a/public/scripts/data.js b/public/scripts/data.js index cccd7cf..27ba099 100644 --- a/public/scripts/data.js +++ b/public/scripts/data.js @@ -14,4 +14,10 @@ export async function getGenders(sportID) { const response = await fetch(`/data/genders?sport=${+sportID}`); const gendersList = await response.json(); return gendersList; +} + +export async function getDivisions(sportID, gender) { + const response = await fetch(`/data/divisions?sport=${+sportID}&gender=${gender}`); + const divisionsList = await response.json(); + return divisionsList; } \ No newline at end of file diff --git a/public/scripts/submit.js b/public/scripts/submit.js index e51fda6..85fc410 100644 --- a/public/scripts/submit.js +++ b/public/scripts/submit.js @@ -4,6 +4,7 @@ import * as Data from "./data.js"; const sportDropdown = document.getElementById('sport-dropdown'); const seasonDropdown = document.getElementById('year-dropdown'); const genderDropdown = document.getElementById('gender-dropdown'); +const divisionDropdown = document.getElementById('division-dropdown'); @@ -18,6 +19,8 @@ async function listSports() { option.value = sport.id; sportDropdown.appendChild(option); }); + + listSeasons(); } listSports(); @@ -45,10 +48,31 @@ async function listGenders() { option.value = gender.name; genderDropdown.appendChild(option); }); + + listDivisions(); +} + +async function listDivisions() { + divisionDropdown.innerHTML = ""; + + const selectedSportID = sportDropdown.value; + const selectedGender = genderDropdown.value; + + if(!selectedGender) return; + + const divisionsList = await Data.getDivisions(selectedSportID, selectedGender); + + divisionsList.forEach(division => { + const option = document.createElement('option'); + option.text = division.name; + option.value = division.id; + divisionDropdown.appendChild(option); + }) } -sportDropdown.onchange = listGenders; \ No newline at end of file +sportDropdown.onchange = listGenders; +genderDropdown.onchange = listDivisions; \ No newline at end of file diff --git a/routes/data.js b/routes/data.js index 8b4ea87..68a7e6d 100644 --- a/routes/data.js +++ b/routes/data.js @@ -3,6 +3,7 @@ var router = express.Router(); var sports = require('../database/scores/sports'); var seasons = require('../database/scores/seasons'); var genders = require('../database/scores/genders'); +var divisions = require('../database/scores/divisions'); router.get('/sports', function(req, res, next) { @@ -20,4 +21,11 @@ router.get('/genders', function(req, res, next) { .then(data => res.json(data)); }) +router.get('/divisions', function(req, res, next) { + const gender = req.body.gender == 'female' ? genders.FEMALE : genders.MALE; + + divisions.retrieveBySportAndGender(req.query.sport, gender) + .then(data => res.json(data)); +}) + module.exports = router; \ No newline at end of file diff --git a/views/submit.pug b/views/submit.pug index 93fdec1..97d7b37 100644 --- a/views/submit.pug +++ b/views/submit.pug @@ -16,9 +16,6 @@ block content select#sport-dropdown(name="sport" class="main-dropdown") select#gender-dropdown(name="gender") select#division-dropdown(name="division") - option(value="Varsity") Varsity - option(value="JV-A") JV-A - option(value="JV-B") JV-B span(class='form-section') label Date of match span(class='form-section-input') From e1acf1951ccaf49a61318af046d1507149d1ebc9 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sun, 21 Nov 2021 16:51:35 -0700 Subject: [PATCH 063/255] Fix bugs in submit form dropdowns --- public/scripts/submit.js | 64 ++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/public/scripts/submit.js b/public/scripts/submit.js index 85fc410..678fa16 100644 --- a/public/scripts/submit.js +++ b/public/scripts/submit.js @@ -10,21 +10,9 @@ const divisionDropdown = document.getElementById('division-dropdown'); -async function listSports() { - const sportsList = await Data.getSports(); - - sportsList.forEach(sport => { - const option = document.createElement('option'); - option.text = sport.name; - option.value = sport.id; - sportDropdown.appendChild(option); - }); - - listSeasons(); -} -listSports(); - async function listSeasons() { + seasonDropdown.innerHTML = ""; + const seasonsList = await Data.getSeasons(); seasonsList.forEach(season => { @@ -36,18 +24,36 @@ async function listSeasons() { } listSeasons(); +async function listSports() { + sportDropdown.innerHTML = ""; + + const sportsList = await Data.getSports(); + + sportsList.forEach(sport => { + const option = document.createElement('option'); + option.text = sport.name; + option.value = sport.id; + sportDropdown.appendChild(option); + }); + + listGenders(); +} +listSports(); + async function listGenders() { genderDropdown.innerHTML = ""; const selectedSportID = sportDropdown.value; const gendersList = await Data.getGenders(selectedSportID); - gendersList.forEach(gender => { - const option = document.createElement('option'); - option.text = (gender.name == "female") ? "Female" : (gender.name == "male") ? "Male" : ""; - option.value = gender.name; - genderDropdown.appendChild(option); - }); + if(selectedSportID) { + gendersList.forEach(gender => { + const option = document.createElement('option'); + option.text = (gender.name == "female") ? "Female" : (gender.name == "male") ? "Male" : ""; + option.value = gender.name; + genderDropdown.appendChild(option); + }); + } listDivisions(); } @@ -58,16 +64,16 @@ async function listDivisions() { const selectedSportID = sportDropdown.value; const selectedGender = genderDropdown.value; - if(!selectedGender) return; + if(selectedGender) { + const divisionsList = await Data.getDivisions(selectedSportID, selectedGender); - const divisionsList = await Data.getDivisions(selectedSportID, selectedGender); - - divisionsList.forEach(division => { - const option = document.createElement('option'); - option.text = division.name; - option.value = division.id; - divisionDropdown.appendChild(option); - }) + divisionsList.forEach(division => { + const option = document.createElement('option'); + option.text = division.name; + option.value = division.id; + divisionDropdown.appendChild(option); + }); + } } From edfd42a6232676053b0e5808e2b3591208d8c6dc Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sun, 21 Nov 2021 17:08:16 -0700 Subject: [PATCH 064/255] Fix bug in "retrieveBySport" function in teams.js --- database/scores/teams.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/scores/teams.js b/database/scores/teams.js index 1bad3fa..89f2c4c 100644 --- a/database/scores/teams.js +++ b/database/scores/teams.js @@ -42,7 +42,7 @@ async function retrieveBySport(sportID) { FROM scores.teams WHERE sport_id = $1 ORDER BY team_name;`; - const table = await database.executeQuery(query); + const table = await database.executeQuery(query, [sportID]); const teamsList = []; table.forEach((row) => { From 27d05752b59d773855dde52bc5f749be87102145 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sun, 21 Nov 2021 17:15:02 -0700 Subject: [PATCH 065/255] Add function to list teams on submit form --- public/scripts/data.js | 6 ++++++ public/scripts/submit.js | 31 ++++++++++++++++++++++++++++++- routes/data.js | 6 ++++++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/public/scripts/data.js b/public/scripts/data.js index 27ba099..7e21436 100644 --- a/public/scripts/data.js +++ b/public/scripts/data.js @@ -20,4 +20,10 @@ export async function getDivisions(sportID, gender) { const response = await fetch(`/data/divisions?sport=${+sportID}&gender=${gender}`); const divisionsList = await response.json(); return divisionsList; +} + +export async function getTeams(sportID) { + const response = await fetch(`/data/teams?sport=${+sportID}`); + const teamsList = await response.json(); + return teamsList; } \ No newline at end of file diff --git a/public/scripts/submit.js b/public/scripts/submit.js index 678fa16..bd4d08a 100644 --- a/public/scripts/submit.js +++ b/public/scripts/submit.js @@ -5,6 +5,8 @@ const sportDropdown = document.getElementById('sport-dropdown'); const seasonDropdown = document.getElementById('year-dropdown'); const genderDropdown = document.getElementById('gender-dropdown'); const divisionDropdown = document.getElementById('division-dropdown'); +const team1Dropdown = document.getElementById('team1-dropdown'); +const team2Dropdown = document.getElementById('team2-dropdown'); @@ -37,6 +39,7 @@ async function listSports() { }); listGenders(); + listTeams(); } listSports(); @@ -76,9 +79,35 @@ async function listDivisions() { } } +async function listTeams() { + team1Dropdown.innerHTML = ""; + team2Dropdown.innerHTML = ""; + + const selectedSportID = sportDropdown.value; + + if(selectedSportID) { + const teamsList = await Data.getTeams(selectedSportID); + + teamsList.forEach(team => { + const optionT1 = document.createElement('option'); + optionT1.text = team.name; + optionT1.value = team.id; + team1Dropdown.appendChild(optionT1); + + const optionT2 = document.createElement('option'); + optionT2.text = team.name; + optionT2.value = team.id; + team2Dropdown.appendChild(optionT2); + }); + } +} -sportDropdown.onchange = listGenders; + +sportDropdown.onchange = (() => { + listGenders(); + listTeams(); +}); genderDropdown.onchange = listDivisions; \ No newline at end of file diff --git a/routes/data.js b/routes/data.js index 68a7e6d..fc6c947 100644 --- a/routes/data.js +++ b/routes/data.js @@ -4,6 +4,7 @@ var sports = require('../database/scores/sports'); var seasons = require('../database/scores/seasons'); var genders = require('../database/scores/genders'); var divisions = require('../database/scores/divisions'); +var teams = require('../database/scores/teams'); router.get('/sports', function(req, res, next) { @@ -28,4 +29,9 @@ router.get('/divisions', function(req, res, next) { .then(data => res.json(data)); }) +router.get('/teams', function(req, res, next) { + teams.retrieveBySport(req.query.sport) + .then(data => res.json(data)); +}) + module.exports = router; \ No newline at end of file From 7d4dcc4d82826c000b5263a0a2d6a247161ba765 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sun, 21 Nov 2021 17:20:14 -0700 Subject: [PATCH 066/255] Add moment.js --- package-lock.json | 14 ++++++++++++++ package.json | 1 + 2 files changed, 15 insertions(+) diff --git a/package-lock.json b/package-lock.json index 225db7b..821a54c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "dotenv": "^10.0.0", "express": "~4.16.0", "http-errors": "~1.6.2", + "moment": "^2.29.1", "morgan": "~1.9.0", "nodemailer": "^6.6.5", "passport": "^0.5.0", @@ -909,6 +910,14 @@ "ms": "2.0.0" } }, + "node_modules/moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==", + "engines": { + "node": "*" + } + }, "node_modules/morgan": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.0.tgz", @@ -2400,6 +2409,11 @@ } } }, + "moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" + }, "morgan": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.0.tgz", diff --git a/package.json b/package.json index c94c979..2e3323a 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "dotenv": "^10.0.0", "express": "~4.16.0", "http-errors": "~1.6.2", + "moment": "^2.29.1", "morgan": "~1.9.0", "nodemailer": "^6.6.5", "passport": "^0.5.0", From 4576b7261287dc59d5396162635cb935fab597c6 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sun, 21 Nov 2021 17:37:45 -0700 Subject: [PATCH 067/255] Switch date from native object to moment.js --- database/scores/games.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/database/scores/games.js b/database/scores/games.js index b990a31..8d49aeb 100644 --- a/database/scores/games.js +++ b/database/scores/games.js @@ -1,4 +1,5 @@ const database = require('./../database'); +const moment = require('moment'); @@ -22,11 +23,7 @@ async function add(divisionID, seasonID, date, team1ID, team2ID, team1Score, tea VALUES($1, $2, $3, $4, $5, $6, $7) RETURNING game_id;`; - const day = ("0" + date.getDate()).slice(-2); - const month = ("0" + (date.getMonth() + 1)).slice(-2); - const year = date.getFullYear(); - - const dateISO = year + '-' + month + '-' + day; + const dateISO = date.format(YYYY-MM-DD); const id = (await database.executeQuery(query, [divisionID, seasonID, dateISO, team1ID, team2ID, team1Score, team2Score]))[0][0]; return new Game(id, date, team1ID, team2ID, team1Score, team2Score); From 344e8b66d394515176924ac632d1a77bbb2537e0 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sun, 21 Nov 2021 17:39:47 -0700 Subject: [PATCH 068/255] Report date as moment object in list generated by "retrieveByTeamDivisionAndSeason" function in games.js --- database/scores/games.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/scores/games.js b/database/scores/games.js index 8d49aeb..1560768 100644 --- a/database/scores/games.js +++ b/database/scores/games.js @@ -51,7 +51,7 @@ async function retrieveByTeamDivisionAndSeason(teamID, divisionID, seasonID) { teamScore = opponentIsTeam2 ? row[6] : row[7]; opponentScore = opponentIsTeam2 ? row[7] : row[6]; - gamesList.push(new Game(row[0], row[3], teamID, opponentID, teamScore, opponentScore)); + gamesList.push(new Game(row[0], moment(row[3]), teamID, opponentID, teamScore, opponentScore)); }); return gamesList; } From 49aff3f741c95c52d480cbc35d4c0bd5be5695bf Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sun, 21 Nov 2021 17:41:45 -0700 Subject: [PATCH 069/255] Fix bug regarding date formatting in games.js --- database/scores/games.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/scores/games.js b/database/scores/games.js index 1560768..96463ec 100644 --- a/database/scores/games.js +++ b/database/scores/games.js @@ -23,7 +23,7 @@ async function add(divisionID, seasonID, date, team1ID, team2ID, team1Score, tea VALUES($1, $2, $3, $4, $5, $6, $7) RETURNING game_id;`; - const dateISO = date.format(YYYY-MM-DD); + const dateISO = date.format('YYYY-MM-DD'); const id = (await database.executeQuery(query, [divisionID, seasonID, dateISO, team1ID, team2ID, team1Score, team2Score]))[0][0]; return new Game(id, date, team1ID, team2ID, team1Score, team2Score); From 12c39e271e63845819118fa2962e22c3878a06a9 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sun, 21 Nov 2021 17:43:34 -0700 Subject: [PATCH 070/255] Complete submit game feature --- routes/submit.js | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/routes/submit.js b/routes/submit.js index e79ff92..8510303 100644 --- a/routes/submit.js +++ b/routes/submit.js @@ -1,31 +1,28 @@ var express = require('express'); var router = express.Router(); -var database = require('../database/database'); +var genders = require('../database/scores/genders'); +var games = require('../database/scores/games'); +var moment = require('moment'); /* GET submit page. */ router.get('/', function(req, res, next) { - const date_ob = new Date(); - const date = ("0" + date_ob.getDate()).slice(-2); - const month = ("0" + (date_ob.getMonth() + 1)).slice(-2); - const year = date_ob.getFullYear(); - - const currentDate = year + '-' + month + '-' + date; - res.render('submit', { title: 'Submit Score', date: currentDate }); + res.render('submit', { title: 'Submit Score', date: moment().format('YYYY-MM-DD') }); }); /* POST submit page. */ router.post('/', function(req, res, next) { - const year = req.body['year']; - const sport = req.body['sport']; - const gender = req.body['gender']; - const division = req.body['division']; - const date = req.body['date']; - const team1 = req.body['team1']; + const seasonID = req.body['year']; + const sportID = req.body['sport']; + const gender = (req.body['gender'] == "female") ? genders.FEMALE : genders.MALE; + const divisionID = req.body['division']; + const date = moment(req.body['date']); + const team1ID = req.body['team1']; const team1Score = req.body['team1-score']; - const team2 = req.body['team2']; + const team2ID = req.body['team2']; const team2Score = req.body['team2-score']; - + games.add(divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score) + .then(res.send("SUCCESS")); }); module.exports = router; From 9b4575c666d15044823e251a61026a3d53f66d47 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sun, 21 Nov 2021 19:07:44 -0700 Subject: [PATCH 071/255] Begin index page --- public/scripts/index.js | 106 ++++++++++++++++++++++++++++++++++ public/stylesheets/form.css | 34 +++++++++++ public/stylesheets/index.css | 3 + public/stylesheets/style.css | 8 +++ public/stylesheets/submit.css | 38 ------------ routes/index.js | 2 +- views/index.pug | 36 ++++++++++++ views/submit.pug | 58 ++++++++++--------- 8 files changed, 218 insertions(+), 67 deletions(-) create mode 100644 public/scripts/index.js create mode 100644 public/stylesheets/form.css create mode 100644 public/stylesheets/index.css diff --git a/public/scripts/index.js b/public/scripts/index.js new file mode 100644 index 0000000..38627c0 --- /dev/null +++ b/public/scripts/index.js @@ -0,0 +1,106 @@ +import * as Data from "./data.js"; + + +const sportDropdown = document.getElementById('sport-dropdown'); +const seasonDropdown = document.getElementById('year-dropdown'); +const genderDropdown = document.getElementById('gender-dropdown'); +const divisionDropdown = document.getElementById('division-dropdown'); +const teamDropdown = document.getElementById('team-dropdown'); + + + + + +async function listSeasons() { + seasonDropdown.innerHTML = ""; + + const seasonsList = await Data.getSeasons(); + + seasonsList.forEach(season => { + const option = document.createElement('option'); + option.text = season.year - 1 + "-" + season.year; + option.value = season.id; + seasonDropdown.appendChild(option); + }); +} +listSeasons(); + +async function listSports() { + sportDropdown.innerHTML = ""; + + const sportsList = await Data.getSports(); + + sportsList.forEach(sport => { + const option = document.createElement('option'); + option.text = sport.name; + option.value = sport.id; + sportDropdown.appendChild(option); + }); + + listGenders(); + listTeams(); +} +listSports(); + +async function listGenders() { + genderDropdown.innerHTML = ""; + + const selectedSportID = sportDropdown.value; + const gendersList = await Data.getGenders(selectedSportID); + + if(selectedSportID) { + gendersList.forEach(gender => { + const option = document.createElement('option'); + option.text = (gender.name == "female") ? "Female" : (gender.name == "male") ? "Male" : ""; + option.value = gender.name; + genderDropdown.appendChild(option); + }); + } + + listDivisions(); +} + +async function listDivisions() { + divisionDropdown.innerHTML = ""; + + const selectedSportID = sportDropdown.value; + const selectedGender = genderDropdown.value; + + if(selectedGender) { + const divisionsList = await Data.getDivisions(selectedSportID, selectedGender); + + divisionsList.forEach(division => { + const option = document.createElement('option'); + option.text = division.name; + option.value = division.id; + divisionDropdown.appendChild(option); + }); + } +} + +async function listTeams() { + teamDropdown.innerHTML = ""; + + const selectedSportID = sportDropdown.value; + + if(selectedSportID) { + const teamsList = await Data.getTeams(selectedSportID); + + teamsList.forEach(team => { + const option = document.createElement('option'); + option.text = team.name; + option.value = team.id; + teamDropdown.appendChild(option); + }); + } +} + + + + + +sportDropdown.onchange = (() => { + listGenders(); + listTeams(); +}); +genderDropdown.onchange = listDivisions; \ No newline at end of file diff --git a/public/stylesheets/form.css b/public/stylesheets/form.css new file mode 100644 index 0000000..85b34bc --- /dev/null +++ b/public/stylesheets/form.css @@ -0,0 +1,34 @@ +form { + display: flex; + flex-direction: column; + } + + span { + display: flex; + flex-direction: column; + } + + + .form-main-dropdown { + width: 100%; + } + + .form-section { + margin-bottom: 0.75em; + } + + .form-section-input { + flex-direction: row; + } + + input { + width: 100%; + } + + .form-score-input{ + width: 35%; + } + + #submit-button { + margin-top: 1.5em; + } \ No newline at end of file diff --git a/public/stylesheets/index.css b/public/stylesheets/index.css new file mode 100644 index 0000000..e4d4d83 --- /dev/null +++ b/public/stylesheets/index.css @@ -0,0 +1,3 @@ +h1 { + text-align: left; +} \ No newline at end of file diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css index 6a727df..b6a5c49 100644 --- a/public/stylesheets/style.css +++ b/public/stylesheets/style.css @@ -7,3 +7,11 @@ a { color: #00B7FF; } +#mobile-view { + max-width: 20em; + margin-left: auto; + margin-right: auto; + display: flex; + flex-direction: column; +} + diff --git a/public/stylesheets/submit.css b/public/stylesheets/submit.css index 80a8146..af4e239 100644 --- a/public/stylesheets/submit.css +++ b/public/stylesheets/submit.css @@ -1,41 +1,3 @@ h1 { text-align: center; -} - -form { - display: flex; - flex-direction: column; - max-width: 20em; - margin-left: auto; - margin-right: auto; -} - -span { - display: flex; - flex-direction: column; -} - - -.main-dropdown { - width: 100%; -} - -.form-section { - margin-bottom: 0.75em; -} - -.form-section-input { - flex-direction: row; -} - -input { - width: 100%; -} - -.score-input{ - width: 35%; -} - -button { - margin-top: 1.5em; } \ No newline at end of file diff --git a/routes/index.js b/routes/index.js index c806b1c..4cb7f20 100644 --- a/routes/index.js +++ b/routes/index.js @@ -4,7 +4,7 @@ var database = require('../database/database'); /* GET home page. */ router.get('/', function(req, res, next) { - res.render('index', { title: 'Submit Score' }); + res.render('index', { title: 'View Scores' }); }); module.exports = router; diff --git a/views/index.pug b/views/index.pug index e69de29..8a93bd0 100644 --- a/views/index.pug +++ b/views/index.pug @@ -0,0 +1,36 @@ +extends layout + +block stylesheets + link(rel='stylesheet', href='/stylesheets/index.css') + link(rel='stylesheet', href='/stylesheets/form.css') + +block content + div#mobile-view + h1 Score Tracker + div + span(class='form-section') + label Year + span(class='form-section-input') + select#year-dropdown(name="year" class="form-main-dropdown") + span(class='form-section') + label Sport + span(class='form-section-input') + select#sport-dropdown(name="sport" class="form-main-dropdown") + select#gender-dropdown(name="gender") + select#division-dropdown(name="division") + span(class='form-section') + label Team + span(class='form-section-input') + select#team-dropdown(name="team" class="form-main-dropdown") + span(class='form-section') + div + table + tr + th Win? + th Opponent + th Score + th Date + + +block scripts + script(src='/scripts/index.js' type="module") \ No newline at end of file diff --git a/views/submit.pug b/views/submit.pug index 97d7b37..85f352f 100644 --- a/views/submit.pug +++ b/views/submit.pug @@ -2,36 +2,38 @@ extends layout block stylesheets link(rel='stylesheet', href='/stylesheets/submit.css') + link(rel='stylesheet', href='/stylesheets/form.css') block content - h1 Submit Score - form(action='/submit', method='POST') - span(class='form-section') - label Year - span(class='form-section-input') - select#year-dropdown(name="year" class="main-dropdown") - span(class='form-section') - label Sport - span(class='form-section-input') - select#sport-dropdown(name="sport" class="main-dropdown") - select#gender-dropdown(name="gender") - select#division-dropdown(name="division") - span(class='form-section') - label Date of match - span(class='form-section-input') - input(type="date", name="date", value=date) - span(class='form-section') - label Your team - span(class='form-section-input') - select#team1-dropdown(name="team1" class="main-dropdown") - input(class="score-input", type="number", name="team1-score", value="0") - span(class='form-section') - label Opponent - span(class='form-section-input') - select#team2-dropdown(name="team2" class="main-dropdown") - input(class="score-input", type="number", name="team2-score", value="0") - span(class='form-section') - button(type="submit") Submit + div#mobile-view + h1 Submit Score + form(action='/submit', method='POST') + span(class='form-section') + label Year + span(class='form-section-input') + select#year-dropdown(name="year" class="form-main-dropdown") + span(class='form-section') + label Sport + span(class='form-section-input') + select#sport-dropdown(name="sport" class="form-main-dropdown") + select#gender-dropdown(name="gender") + select#division-dropdown(name="division") + span(class='form-section') + label Date of match + span(class='form-section-input') + input(type="date", name="date", value=date) + span(class='form-section') + label Your team + span(class='form-section-input') + select#team1-dropdown(name="team1" class="form-main-dropdown") + input(class="form-score-input", type="number", name="team1-score", value="0") + span(class='form-section') + label Opponent + span(class='form-section-input') + select#team2-dropdown(name="team2" class="form-main-dropdown") + input(class="form-score-input", type="number", name="team2-score", value="0") + span(class='form-section') + button#submit-button(type="submit") Submit block scripts script(src='/scripts/submit.js' type="module") \ No newline at end of file From 1abffb43bde5c2a19a48c37e037adb91b4ebdb19 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sun, 21 Nov 2021 19:33:10 -0700 Subject: [PATCH 072/255] Add function to get team name from an ID --- database/scores/teams.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/database/scores/teams.js b/database/scores/teams.js index 89f2c4c..7aa3d32 100644 --- a/database/scores/teams.js +++ b/database/scores/teams.js @@ -51,6 +51,15 @@ async function retrieveBySport(sportID) { return teamsList; } +async function getFromID(id) { + const query = `SELECT team_name + FROM scores.teams + WHERE team_id = $1;`; + const name = await database.executeQuery(query, [teamID])[0][0]; + + return new Team(id, name); +} + @@ -58,4 +67,5 @@ async function retrieveBySport(sportID) { exports.add = add; exports.rename = rename; exports.remove = remove; -exports.retrieveBySport = retrieveBySport; \ No newline at end of file +exports.retrieveBySport = retrieveBySport; +exports.getFromID = getFromID; \ No newline at end of file From 6de2f13b327c951344b74639149c7036dfc850b2 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sun, 21 Nov 2021 20:53:16 -0700 Subject: [PATCH 073/255] Add moment.js to client scripts --- public/scripts/moment.js | 5670 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 5670 insertions(+) create mode 100644 public/scripts/moment.js diff --git a/public/scripts/moment.js b/public/scripts/moment.js new file mode 100644 index 0000000..1484d6c --- /dev/null +++ b/public/scripts/moment.js @@ -0,0 +1,5670 @@ +//! moment.js +//! version : 2.29.1 +//! authors : Tim Wood, Iskren Chernev, Moment.js contributors +//! license : MIT +//! momentjs.com + +;(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + global.moment = factory() +}(this, (function () { 'use strict'; + + var hookCallback; + + function hooks() { + return hookCallback.apply(null, arguments); + } + + // This is done to register the method called with moment() + // without creating circular dependencies. + function setHookCallback(callback) { + hookCallback = callback; + } + + function isArray(input) { + return ( + input instanceof Array || + Object.prototype.toString.call(input) === '[object Array]' + ); + } + + function isObject(input) { + // IE8 will treat undefined and null as object if it wasn't for + // input != null + return ( + input != null && + Object.prototype.toString.call(input) === '[object Object]' + ); + } + + function hasOwnProp(a, b) { + return Object.prototype.hasOwnProperty.call(a, b); + } + + function isObjectEmpty(obj) { + if (Object.getOwnPropertyNames) { + return Object.getOwnPropertyNames(obj).length === 0; + } else { + var k; + for (k in obj) { + if (hasOwnProp(obj, k)) { + return false; + } + } + return true; + } + } + + function isUndefined(input) { + return input === void 0; + } + + function isNumber(input) { + return ( + typeof input === 'number' || + Object.prototype.toString.call(input) === '[object Number]' + ); + } + + function isDate(input) { + return ( + input instanceof Date || + Object.prototype.toString.call(input) === '[object Date]' + ); + } + + function map(arr, fn) { + var res = [], + i; + for (i = 0; i < arr.length; ++i) { + res.push(fn(arr[i], i)); + } + return res; + } + + function extend(a, b) { + for (var i in b) { + if (hasOwnProp(b, i)) { + a[i] = b[i]; + } + } + + if (hasOwnProp(b, 'toString')) { + a.toString = b.toString; + } + + if (hasOwnProp(b, 'valueOf')) { + a.valueOf = b.valueOf; + } + + return a; + } + + function createUTC(input, format, locale, strict) { + return createLocalOrUTC(input, format, locale, strict, true).utc(); + } + + function defaultParsingFlags() { + // We need to deep clone this object. + return { + empty: false, + unusedTokens: [], + unusedInput: [], + overflow: -2, + charsLeftOver: 0, + nullInput: false, + invalidEra: null, + invalidMonth: null, + invalidFormat: false, + userInvalidated: false, + iso: false, + parsedDateParts: [], + era: null, + meridiem: null, + rfc2822: false, + weekdayMismatch: false, + }; + } + + function getParsingFlags(m) { + if (m._pf == null) { + m._pf = defaultParsingFlags(); + } + return m._pf; + } + + var some; + if (Array.prototype.some) { + some = Array.prototype.some; + } else { + some = function (fun) { + var t = Object(this), + len = t.length >>> 0, + i; + + for (i = 0; i < len; i++) { + if (i in t && fun.call(this, t[i], i, t)) { + return true; + } + } + + return false; + }; + } + + function isValid(m) { + if (m._isValid == null) { + var flags = getParsingFlags(m), + parsedParts = some.call(flags.parsedDateParts, function (i) { + return i != null; + }), + isNowValid = + !isNaN(m._d.getTime()) && + flags.overflow < 0 && + !flags.empty && + !flags.invalidEra && + !flags.invalidMonth && + !flags.invalidWeekday && + !flags.weekdayMismatch && + !flags.nullInput && + !flags.invalidFormat && + !flags.userInvalidated && + (!flags.meridiem || (flags.meridiem && parsedParts)); + + if (m._strict) { + isNowValid = + isNowValid && + flags.charsLeftOver === 0 && + flags.unusedTokens.length === 0 && + flags.bigHour === undefined; + } + + if (Object.isFrozen == null || !Object.isFrozen(m)) { + m._isValid = isNowValid; + } else { + return isNowValid; + } + } + return m._isValid; + } + + function createInvalid(flags) { + var m = createUTC(NaN); + if (flags != null) { + extend(getParsingFlags(m), flags); + } else { + getParsingFlags(m).userInvalidated = true; + } + + return m; + } + + // Plugins that add properties should also add the key here (null value), + // so we can properly clone ourselves. + var momentProperties = (hooks.momentProperties = []), + updateInProgress = false; + + function copyConfig(to, from) { + var i, prop, val; + + if (!isUndefined(from._isAMomentObject)) { + to._isAMomentObject = from._isAMomentObject; + } + if (!isUndefined(from._i)) { + to._i = from._i; + } + if (!isUndefined(from._f)) { + to._f = from._f; + } + if (!isUndefined(from._l)) { + to._l = from._l; + } + if (!isUndefined(from._strict)) { + to._strict = from._strict; + } + if (!isUndefined(from._tzm)) { + to._tzm = from._tzm; + } + if (!isUndefined(from._isUTC)) { + to._isUTC = from._isUTC; + } + if (!isUndefined(from._offset)) { + to._offset = from._offset; + } + if (!isUndefined(from._pf)) { + to._pf = getParsingFlags(from); + } + if (!isUndefined(from._locale)) { + to._locale = from._locale; + } + + if (momentProperties.length > 0) { + for (i = 0; i < momentProperties.length; i++) { + prop = momentProperties[i]; + val = from[prop]; + if (!isUndefined(val)) { + to[prop] = val; + } + } + } + + return to; + } + + // Moment prototype object + function Moment(config) { + copyConfig(this, config); + this._d = new Date(config._d != null ? config._d.getTime() : NaN); + if (!this.isValid()) { + this._d = new Date(NaN); + } + // Prevent infinite loop in case updateOffset creates new moment + // objects. + if (updateInProgress === false) { + updateInProgress = true; + hooks.updateOffset(this); + updateInProgress = false; + } + } + + function isMoment(obj) { + return ( + obj instanceof Moment || (obj != null && obj._isAMomentObject != null) + ); + } + + function warn(msg) { + if ( + hooks.suppressDeprecationWarnings === false && + typeof console !== 'undefined' && + console.warn + ) { + console.warn('Deprecation warning: ' + msg); + } + } + + function deprecate(msg, fn) { + var firstTime = true; + + return extend(function () { + if (hooks.deprecationHandler != null) { + hooks.deprecationHandler(null, msg); + } + if (firstTime) { + var args = [], + arg, + i, + key; + for (i = 0; i < arguments.length; i++) { + arg = ''; + if (typeof arguments[i] === 'object') { + arg += '\n[' + i + '] '; + for (key in arguments[0]) { + if (hasOwnProp(arguments[0], key)) { + arg += key + ': ' + arguments[0][key] + ', '; + } + } + arg = arg.slice(0, -2); // Remove trailing comma and space + } else { + arg = arguments[i]; + } + args.push(arg); + } + warn( + msg + + '\nArguments: ' + + Array.prototype.slice.call(args).join('') + + '\n' + + new Error().stack + ); + firstTime = false; + } + return fn.apply(this, arguments); + }, fn); + } + + var deprecations = {}; + + function deprecateSimple(name, msg) { + if (hooks.deprecationHandler != null) { + hooks.deprecationHandler(name, msg); + } + if (!deprecations[name]) { + warn(msg); + deprecations[name] = true; + } + } + + hooks.suppressDeprecationWarnings = false; + hooks.deprecationHandler = null; + + function isFunction(input) { + return ( + (typeof Function !== 'undefined' && input instanceof Function) || + Object.prototype.toString.call(input) === '[object Function]' + ); + } + + function set(config) { + var prop, i; + for (i in config) { + if (hasOwnProp(config, i)) { + prop = config[i]; + if (isFunction(prop)) { + this[i] = prop; + } else { + this['_' + i] = prop; + } + } + } + this._config = config; + // Lenient ordinal parsing accepts just a number in addition to + // number + (possibly) stuff coming from _dayOfMonthOrdinalParse. + // TODO: Remove "ordinalParse" fallback in next major release. + this._dayOfMonthOrdinalParseLenient = new RegExp( + (this._dayOfMonthOrdinalParse.source || this._ordinalParse.source) + + '|' + + /\d{1,2}/.source + ); + } + + function mergeConfigs(parentConfig, childConfig) { + var res = extend({}, parentConfig), + prop; + for (prop in childConfig) { + if (hasOwnProp(childConfig, prop)) { + if (isObject(parentConfig[prop]) && isObject(childConfig[prop])) { + res[prop] = {}; + extend(res[prop], parentConfig[prop]); + extend(res[prop], childConfig[prop]); + } else if (childConfig[prop] != null) { + res[prop] = childConfig[prop]; + } else { + delete res[prop]; + } + } + } + for (prop in parentConfig) { + if ( + hasOwnProp(parentConfig, prop) && + !hasOwnProp(childConfig, prop) && + isObject(parentConfig[prop]) + ) { + // make sure changes to properties don't modify parent config + res[prop] = extend({}, res[prop]); + } + } + return res; + } + + function Locale(config) { + if (config != null) { + this.set(config); + } + } + + var keys; + + if (Object.keys) { + keys = Object.keys; + } else { + keys = function (obj) { + var i, + res = []; + for (i in obj) { + if (hasOwnProp(obj, i)) { + res.push(i); + } + } + return res; + }; + } + + var defaultCalendar = { + sameDay: '[Today at] LT', + nextDay: '[Tomorrow at] LT', + nextWeek: 'dddd [at] LT', + lastDay: '[Yesterday at] LT', + lastWeek: '[Last] dddd [at] LT', + sameElse: 'L', + }; + + function calendar(key, mom, now) { + var output = this._calendar[key] || this._calendar['sameElse']; + return isFunction(output) ? output.call(mom, now) : output; + } + + function zeroFill(number, targetLength, forceSign) { + var absNumber = '' + Math.abs(number), + zerosToFill = targetLength - absNumber.length, + sign = number >= 0; + return ( + (sign ? (forceSign ? '+' : '') : '-') + + Math.pow(10, Math.max(0, zerosToFill)).toString().substr(1) + + absNumber + ); + } + + var formattingTokens = /(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|N{1,5}|YYYYYY|YYYYY|YYYY|YY|y{2,4}|yo?|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g, + localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g, + formatFunctions = {}, + formatTokenFunctions = {}; + + // token: 'M' + // padded: ['MM', 2] + // ordinal: 'Mo' + // callback: function () { this.month() + 1 } + function addFormatToken(token, padded, ordinal, callback) { + var func = callback; + if (typeof callback === 'string') { + func = function () { + return this[callback](); + }; + } + if (token) { + formatTokenFunctions[token] = func; + } + if (padded) { + formatTokenFunctions[padded[0]] = function () { + return zeroFill(func.apply(this, arguments), padded[1], padded[2]); + }; + } + if (ordinal) { + formatTokenFunctions[ordinal] = function () { + return this.localeData().ordinal( + func.apply(this, arguments), + token + ); + }; + } + } + + function removeFormattingTokens(input) { + if (input.match(/\[[\s\S]/)) { + return input.replace(/^\[|\]$/g, ''); + } + return input.replace(/\\/g, ''); + } + + function makeFormatFunction(format) { + var array = format.match(formattingTokens), + i, + length; + + for (i = 0, length = array.length; i < length; i++) { + if (formatTokenFunctions[array[i]]) { + array[i] = formatTokenFunctions[array[i]]; + } else { + array[i] = removeFormattingTokens(array[i]); + } + } + + return function (mom) { + var output = '', + i; + for (i = 0; i < length; i++) { + output += isFunction(array[i]) + ? array[i].call(mom, format) + : array[i]; + } + return output; + }; + } + + // format date using native date object + function formatMoment(m, format) { + if (!m.isValid()) { + return m.localeData().invalidDate(); + } + + format = expandFormat(format, m.localeData()); + formatFunctions[format] = + formatFunctions[format] || makeFormatFunction(format); + + return formatFunctions[format](m); + } + + function expandFormat(format, locale) { + var i = 5; + + function replaceLongDateFormatTokens(input) { + return locale.longDateFormat(input) || input; + } + + localFormattingTokens.lastIndex = 0; + while (i >= 0 && localFormattingTokens.test(format)) { + format = format.replace( + localFormattingTokens, + replaceLongDateFormatTokens + ); + localFormattingTokens.lastIndex = 0; + i -= 1; + } + + return format; + } + + var defaultLongDateFormat = { + LTS: 'h:mm:ss A', + LT: 'h:mm A', + L: 'MM/DD/YYYY', + LL: 'MMMM D, YYYY', + LLL: 'MMMM D, YYYY h:mm A', + LLLL: 'dddd, MMMM D, YYYY h:mm A', + }; + + function longDateFormat(key) { + var format = this._longDateFormat[key], + formatUpper = this._longDateFormat[key.toUpperCase()]; + + if (format || !formatUpper) { + return format; + } + + this._longDateFormat[key] = formatUpper + .match(formattingTokens) + .map(function (tok) { + if ( + tok === 'MMMM' || + tok === 'MM' || + tok === 'DD' || + tok === 'dddd' + ) { + return tok.slice(1); + } + return tok; + }) + .join(''); + + return this._longDateFormat[key]; + } + + var defaultInvalidDate = 'Invalid date'; + + function invalidDate() { + return this._invalidDate; + } + + var defaultOrdinal = '%d', + defaultDayOfMonthOrdinalParse = /\d{1,2}/; + + function ordinal(number) { + return this._ordinal.replace('%d', number); + } + + var defaultRelativeTime = { + future: 'in %s', + past: '%s ago', + s: 'a few seconds', + ss: '%d seconds', + m: 'a minute', + mm: '%d minutes', + h: 'an hour', + hh: '%d hours', + d: 'a day', + dd: '%d days', + w: 'a week', + ww: '%d weeks', + M: 'a month', + MM: '%d months', + y: 'a year', + yy: '%d years', + }; + + function relativeTime(number, withoutSuffix, string, isFuture) { + var output = this._relativeTime[string]; + return isFunction(output) + ? output(number, withoutSuffix, string, isFuture) + : output.replace(/%d/i, number); + } + + function pastFuture(diff, output) { + var format = this._relativeTime[diff > 0 ? 'future' : 'past']; + return isFunction(format) ? format(output) : format.replace(/%s/i, output); + } + + var aliases = {}; + + function addUnitAlias(unit, shorthand) { + var lowerCase = unit.toLowerCase(); + aliases[lowerCase] = aliases[lowerCase + 's'] = aliases[shorthand] = unit; + } + + function normalizeUnits(units) { + return typeof units === 'string' + ? aliases[units] || aliases[units.toLowerCase()] + : undefined; + } + + function normalizeObjectUnits(inputObject) { + var normalizedInput = {}, + normalizedProp, + prop; + + for (prop in inputObject) { + if (hasOwnProp(inputObject, prop)) { + normalizedProp = normalizeUnits(prop); + if (normalizedProp) { + normalizedInput[normalizedProp] = inputObject[prop]; + } + } + } + + return normalizedInput; + } + + var priorities = {}; + + function addUnitPriority(unit, priority) { + priorities[unit] = priority; + } + + function getPrioritizedUnits(unitsObj) { + var units = [], + u; + for (u in unitsObj) { + if (hasOwnProp(unitsObj, u)) { + units.push({ unit: u, priority: priorities[u] }); + } + } + units.sort(function (a, b) { + return a.priority - b.priority; + }); + return units; + } + + function isLeapYear(year) { + return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; + } + + function absFloor(number) { + if (number < 0) { + // -0 -> 0 + return Math.ceil(number) || 0; + } else { + return Math.floor(number); + } + } + + function toInt(argumentForCoercion) { + var coercedNumber = +argumentForCoercion, + value = 0; + + if (coercedNumber !== 0 && isFinite(coercedNumber)) { + value = absFloor(coercedNumber); + } + + return value; + } + + function makeGetSet(unit, keepTime) { + return function (value) { + if (value != null) { + set$1(this, unit, value); + hooks.updateOffset(this, keepTime); + return this; + } else { + return get(this, unit); + } + }; + } + + function get(mom, unit) { + return mom.isValid() + ? mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit]() + : NaN; + } + + function set$1(mom, unit, value) { + if (mom.isValid() && !isNaN(value)) { + if ( + unit === 'FullYear' && + isLeapYear(mom.year()) && + mom.month() === 1 && + mom.date() === 29 + ) { + value = toInt(value); + mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit]( + value, + mom.month(), + daysInMonth(value, mom.month()) + ); + } else { + mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value); + } + } + } + + // MOMENTS + + function stringGet(units) { + units = normalizeUnits(units); + if (isFunction(this[units])) { + return this[units](); + } + return this; + } + + function stringSet(units, value) { + if (typeof units === 'object') { + units = normalizeObjectUnits(units); + var prioritized = getPrioritizedUnits(units), + i; + for (i = 0; i < prioritized.length; i++) { + this[prioritized[i].unit](units[prioritized[i].unit]); + } + } else { + units = normalizeUnits(units); + if (isFunction(this[units])) { + return this[units](value); + } + } + return this; + } + + var match1 = /\d/, // 0 - 9 + match2 = /\d\d/, // 00 - 99 + match3 = /\d{3}/, // 000 - 999 + match4 = /\d{4}/, // 0000 - 9999 + match6 = /[+-]?\d{6}/, // -999999 - 999999 + match1to2 = /\d\d?/, // 0 - 99 + match3to4 = /\d\d\d\d?/, // 999 - 9999 + match5to6 = /\d\d\d\d\d\d?/, // 99999 - 999999 + match1to3 = /\d{1,3}/, // 0 - 999 + match1to4 = /\d{1,4}/, // 0 - 9999 + match1to6 = /[+-]?\d{1,6}/, // -999999 - 999999 + matchUnsigned = /\d+/, // 0 - inf + matchSigned = /[+-]?\d+/, // -inf - inf + matchOffset = /Z|[+-]\d\d:?\d\d/gi, // +00:00 -00:00 +0000 -0000 or Z + matchShortOffset = /Z|[+-]\d\d(?::?\d\d)?/gi, // +00 -00 +00:00 -00:00 +0000 -0000 or Z + matchTimestamp = /[+-]?\d+(\.\d{1,3})?/, // 123456789 123456789.123 + // any word (or two) characters or numbers including two/three word month in arabic. + // includes scottish gaelic two word and hyphenated months + matchWord = /[0-9]{0,256}['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFF07\uFF10-\uFFEF]{1,256}|[\u0600-\u06FF\/]{1,256}(\s*?[\u0600-\u06FF]{1,256}){1,2}/i, + regexes; + + regexes = {}; + + function addRegexToken(token, regex, strictRegex) { + regexes[token] = isFunction(regex) + ? regex + : function (isStrict, localeData) { + return isStrict && strictRegex ? strictRegex : regex; + }; + } + + function getParseRegexForToken(token, config) { + if (!hasOwnProp(regexes, token)) { + return new RegExp(unescapeFormat(token)); + } + + return regexes[token](config._strict, config._locale); + } + + // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript + function unescapeFormat(s) { + return regexEscape( + s + .replace('\\', '') + .replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function ( + matched, + p1, + p2, + p3, + p4 + ) { + return p1 || p2 || p3 || p4; + }) + ); + } + + function regexEscape(s) { + return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); + } + + var tokens = {}; + + function addParseToken(token, callback) { + var i, + func = callback; + if (typeof token === 'string') { + token = [token]; + } + if (isNumber(callback)) { + func = function (input, array) { + array[callback] = toInt(input); + }; + } + for (i = 0; i < token.length; i++) { + tokens[token[i]] = func; + } + } + + function addWeekParseToken(token, callback) { + addParseToken(token, function (input, array, config, token) { + config._w = config._w || {}; + callback(input, config._w, config, token); + }); + } + + function addTimeToArrayFromToken(token, input, config) { + if (input != null && hasOwnProp(tokens, token)) { + tokens[token](input, config._a, config, token); + } + } + + var YEAR = 0, + MONTH = 1, + DATE = 2, + HOUR = 3, + MINUTE = 4, + SECOND = 5, + MILLISECOND = 6, + WEEK = 7, + WEEKDAY = 8; + + function mod(n, x) { + return ((n % x) + x) % x; + } + + var indexOf; + + if (Array.prototype.indexOf) { + indexOf = Array.prototype.indexOf; + } else { + indexOf = function (o) { + // I know + var i; + for (i = 0; i < this.length; ++i) { + if (this[i] === o) { + return i; + } + } + return -1; + }; + } + + function daysInMonth(year, month) { + if (isNaN(year) || isNaN(month)) { + return NaN; + } + var modMonth = mod(month, 12); + year += (month - modMonth) / 12; + return modMonth === 1 + ? isLeapYear(year) + ? 29 + : 28 + : 31 - ((modMonth % 7) % 2); + } + + // FORMATTING + + addFormatToken('M', ['MM', 2], 'Mo', function () { + return this.month() + 1; + }); + + addFormatToken('MMM', 0, 0, function (format) { + return this.localeData().monthsShort(this, format); + }); + + addFormatToken('MMMM', 0, 0, function (format) { + return this.localeData().months(this, format); + }); + + // ALIASES + + addUnitAlias('month', 'M'); + + // PRIORITY + + addUnitPriority('month', 8); + + // PARSING + + addRegexToken('M', match1to2); + addRegexToken('MM', match1to2, match2); + addRegexToken('MMM', function (isStrict, locale) { + return locale.monthsShortRegex(isStrict); + }); + addRegexToken('MMMM', function (isStrict, locale) { + return locale.monthsRegex(isStrict); + }); + + addParseToken(['M', 'MM'], function (input, array) { + array[MONTH] = toInt(input) - 1; + }); + + addParseToken(['MMM', 'MMMM'], function (input, array, config, token) { + var month = config._locale.monthsParse(input, token, config._strict); + // if we didn't find a month name, mark the date as invalid. + if (month != null) { + array[MONTH] = month; + } else { + getParsingFlags(config).invalidMonth = input; + } + }); + + // LOCALES + + var defaultLocaleMonths = 'January_February_March_April_May_June_July_August_September_October_November_December'.split( + '_' + ), + defaultLocaleMonthsShort = 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split( + '_' + ), + MONTHS_IN_FORMAT = /D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/, + defaultMonthsShortRegex = matchWord, + defaultMonthsRegex = matchWord; + + function localeMonths(m, format) { + if (!m) { + return isArray(this._months) + ? this._months + : this._months['standalone']; + } + return isArray(this._months) + ? this._months[m.month()] + : this._months[ + (this._months.isFormat || MONTHS_IN_FORMAT).test(format) + ? 'format' + : 'standalone' + ][m.month()]; + } + + function localeMonthsShort(m, format) { + if (!m) { + return isArray(this._monthsShort) + ? this._monthsShort + : this._monthsShort['standalone']; + } + return isArray(this._monthsShort) + ? this._monthsShort[m.month()] + : this._monthsShort[ + MONTHS_IN_FORMAT.test(format) ? 'format' : 'standalone' + ][m.month()]; + } + + function handleStrictParse(monthName, format, strict) { + var i, + ii, + mom, + llc = monthName.toLocaleLowerCase(); + if (!this._monthsParse) { + // this is not used + this._monthsParse = []; + this._longMonthsParse = []; + this._shortMonthsParse = []; + for (i = 0; i < 12; ++i) { + mom = createUTC([2000, i]); + this._shortMonthsParse[i] = this.monthsShort( + mom, + '' + ).toLocaleLowerCase(); + this._longMonthsParse[i] = this.months(mom, '').toLocaleLowerCase(); + } + } + + if (strict) { + if (format === 'MMM') { + ii = indexOf.call(this._shortMonthsParse, llc); + return ii !== -1 ? ii : null; + } else { + ii = indexOf.call(this._longMonthsParse, llc); + return ii !== -1 ? ii : null; + } + } else { + if (format === 'MMM') { + ii = indexOf.call(this._shortMonthsParse, llc); + if (ii !== -1) { + return ii; + } + ii = indexOf.call(this._longMonthsParse, llc); + return ii !== -1 ? ii : null; + } else { + ii = indexOf.call(this._longMonthsParse, llc); + if (ii !== -1) { + return ii; + } + ii = indexOf.call(this._shortMonthsParse, llc); + return ii !== -1 ? ii : null; + } + } + } + + function localeMonthsParse(monthName, format, strict) { + var i, mom, regex; + + if (this._monthsParseExact) { + return handleStrictParse.call(this, monthName, format, strict); + } + + if (!this._monthsParse) { + this._monthsParse = []; + this._longMonthsParse = []; + this._shortMonthsParse = []; + } + + // TODO: add sorting + // Sorting makes sure if one month (or abbr) is a prefix of another + // see sorting in computeMonthsParse + for (i = 0; i < 12; i++) { + // make the regex if we don't have it already + mom = createUTC([2000, i]); + if (strict && !this._longMonthsParse[i]) { + this._longMonthsParse[i] = new RegExp( + '^' + this.months(mom, '').replace('.', '') + '$', + 'i' + ); + this._shortMonthsParse[i] = new RegExp( + '^' + this.monthsShort(mom, '').replace('.', '') + '$', + 'i' + ); + } + if (!strict && !this._monthsParse[i]) { + regex = + '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, ''); + this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i'); + } + // test the regex + if ( + strict && + format === 'MMMM' && + this._longMonthsParse[i].test(monthName) + ) { + return i; + } else if ( + strict && + format === 'MMM' && + this._shortMonthsParse[i].test(monthName) + ) { + return i; + } else if (!strict && this._monthsParse[i].test(monthName)) { + return i; + } + } + } + + // MOMENTS + + function setMonth(mom, value) { + var dayOfMonth; + + if (!mom.isValid()) { + // No op + return mom; + } + + if (typeof value === 'string') { + if (/^\d+$/.test(value)) { + value = toInt(value); + } else { + value = mom.localeData().monthsParse(value); + // TODO: Another silent failure? + if (!isNumber(value)) { + return mom; + } + } + } + + dayOfMonth = Math.min(mom.date(), daysInMonth(mom.year(), value)); + mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth); + return mom; + } + + function getSetMonth(value) { + if (value != null) { + setMonth(this, value); + hooks.updateOffset(this, true); + return this; + } else { + return get(this, 'Month'); + } + } + + function getDaysInMonth() { + return daysInMonth(this.year(), this.month()); + } + + function monthsShortRegex(isStrict) { + if (this._monthsParseExact) { + if (!hasOwnProp(this, '_monthsRegex')) { + computeMonthsParse.call(this); + } + if (isStrict) { + return this._monthsShortStrictRegex; + } else { + return this._monthsShortRegex; + } + } else { + if (!hasOwnProp(this, '_monthsShortRegex')) { + this._monthsShortRegex = defaultMonthsShortRegex; + } + return this._monthsShortStrictRegex && isStrict + ? this._monthsShortStrictRegex + : this._monthsShortRegex; + } + } + + function monthsRegex(isStrict) { + if (this._monthsParseExact) { + if (!hasOwnProp(this, '_monthsRegex')) { + computeMonthsParse.call(this); + } + if (isStrict) { + return this._monthsStrictRegex; + } else { + return this._monthsRegex; + } + } else { + if (!hasOwnProp(this, '_monthsRegex')) { + this._monthsRegex = defaultMonthsRegex; + } + return this._monthsStrictRegex && isStrict + ? this._monthsStrictRegex + : this._monthsRegex; + } + } + + function computeMonthsParse() { + function cmpLenRev(a, b) { + return b.length - a.length; + } + + var shortPieces = [], + longPieces = [], + mixedPieces = [], + i, + mom; + for (i = 0; i < 12; i++) { + // make the regex if we don't have it already + mom = createUTC([2000, i]); + shortPieces.push(this.monthsShort(mom, '')); + longPieces.push(this.months(mom, '')); + mixedPieces.push(this.months(mom, '')); + mixedPieces.push(this.monthsShort(mom, '')); + } + // Sorting makes sure if one month (or abbr) is a prefix of another it + // will match the longer piece. + shortPieces.sort(cmpLenRev); + longPieces.sort(cmpLenRev); + mixedPieces.sort(cmpLenRev); + for (i = 0; i < 12; i++) { + shortPieces[i] = regexEscape(shortPieces[i]); + longPieces[i] = regexEscape(longPieces[i]); + } + for (i = 0; i < 24; i++) { + mixedPieces[i] = regexEscape(mixedPieces[i]); + } + + this._monthsRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i'); + this._monthsShortRegex = this._monthsRegex; + this._monthsStrictRegex = new RegExp( + '^(' + longPieces.join('|') + ')', + 'i' + ); + this._monthsShortStrictRegex = new RegExp( + '^(' + shortPieces.join('|') + ')', + 'i' + ); + } + + // FORMATTING + + addFormatToken('Y', 0, 0, function () { + var y = this.year(); + return y <= 9999 ? zeroFill(y, 4) : '+' + y; + }); + + addFormatToken(0, ['YY', 2], 0, function () { + return this.year() % 100; + }); + + addFormatToken(0, ['YYYY', 4], 0, 'year'); + addFormatToken(0, ['YYYYY', 5], 0, 'year'); + addFormatToken(0, ['YYYYYY', 6, true], 0, 'year'); + + // ALIASES + + addUnitAlias('year', 'y'); + + // PRIORITIES + + addUnitPriority('year', 1); + + // PARSING + + addRegexToken('Y', matchSigned); + addRegexToken('YY', match1to2, match2); + addRegexToken('YYYY', match1to4, match4); + addRegexToken('YYYYY', match1to6, match6); + addRegexToken('YYYYYY', match1to6, match6); + + addParseToken(['YYYYY', 'YYYYYY'], YEAR); + addParseToken('YYYY', function (input, array) { + array[YEAR] = + input.length === 2 ? hooks.parseTwoDigitYear(input) : toInt(input); + }); + addParseToken('YY', function (input, array) { + array[YEAR] = hooks.parseTwoDigitYear(input); + }); + addParseToken('Y', function (input, array) { + array[YEAR] = parseInt(input, 10); + }); + + // HELPERS + + function daysInYear(year) { + return isLeapYear(year) ? 366 : 365; + } + + // HOOKS + + hooks.parseTwoDigitYear = function (input) { + return toInt(input) + (toInt(input) > 68 ? 1900 : 2000); + }; + + // MOMENTS + + var getSetYear = makeGetSet('FullYear', true); + + function getIsLeapYear() { + return isLeapYear(this.year()); + } + + function createDate(y, m, d, h, M, s, ms) { + // can't just apply() to create a date: + // https://stackoverflow.com/q/181348 + var date; + // the date constructor remaps years 0-99 to 1900-1999 + if (y < 100 && y >= 0) { + // preserve leap years using a full 400 year cycle, then reset + date = new Date(y + 400, m, d, h, M, s, ms); + if (isFinite(date.getFullYear())) { + date.setFullYear(y); + } + } else { + date = new Date(y, m, d, h, M, s, ms); + } + + return date; + } + + function createUTCDate(y) { + var date, args; + // the Date.UTC function remaps years 0-99 to 1900-1999 + if (y < 100 && y >= 0) { + args = Array.prototype.slice.call(arguments); + // preserve leap years using a full 400 year cycle, then reset + args[0] = y + 400; + date = new Date(Date.UTC.apply(null, args)); + if (isFinite(date.getUTCFullYear())) { + date.setUTCFullYear(y); + } + } else { + date = new Date(Date.UTC.apply(null, arguments)); + } + + return date; + } + + // start-of-first-week - start-of-year + function firstWeekOffset(year, dow, doy) { + var // first-week day -- which january is always in the first week (4 for iso, 1 for other) + fwd = 7 + dow - doy, + // first-week day local weekday -- which local weekday is fwd + fwdlw = (7 + createUTCDate(year, 0, fwd).getUTCDay() - dow) % 7; + + return -fwdlw + fwd - 1; + } + + // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday + function dayOfYearFromWeeks(year, week, weekday, dow, doy) { + var localWeekday = (7 + weekday - dow) % 7, + weekOffset = firstWeekOffset(year, dow, doy), + dayOfYear = 1 + 7 * (week - 1) + localWeekday + weekOffset, + resYear, + resDayOfYear; + + if (dayOfYear <= 0) { + resYear = year - 1; + resDayOfYear = daysInYear(resYear) + dayOfYear; + } else if (dayOfYear > daysInYear(year)) { + resYear = year + 1; + resDayOfYear = dayOfYear - daysInYear(year); + } else { + resYear = year; + resDayOfYear = dayOfYear; + } + + return { + year: resYear, + dayOfYear: resDayOfYear, + }; + } + + function weekOfYear(mom, dow, doy) { + var weekOffset = firstWeekOffset(mom.year(), dow, doy), + week = Math.floor((mom.dayOfYear() - weekOffset - 1) / 7) + 1, + resWeek, + resYear; + + if (week < 1) { + resYear = mom.year() - 1; + resWeek = week + weeksInYear(resYear, dow, doy); + } else if (week > weeksInYear(mom.year(), dow, doy)) { + resWeek = week - weeksInYear(mom.year(), dow, doy); + resYear = mom.year() + 1; + } else { + resYear = mom.year(); + resWeek = week; + } + + return { + week: resWeek, + year: resYear, + }; + } + + function weeksInYear(year, dow, doy) { + var weekOffset = firstWeekOffset(year, dow, doy), + weekOffsetNext = firstWeekOffset(year + 1, dow, doy); + return (daysInYear(year) - weekOffset + weekOffsetNext) / 7; + } + + // FORMATTING + + addFormatToken('w', ['ww', 2], 'wo', 'week'); + addFormatToken('W', ['WW', 2], 'Wo', 'isoWeek'); + + // ALIASES + + addUnitAlias('week', 'w'); + addUnitAlias('isoWeek', 'W'); + + // PRIORITIES + + addUnitPriority('week', 5); + addUnitPriority('isoWeek', 5); + + // PARSING + + addRegexToken('w', match1to2); + addRegexToken('ww', match1to2, match2); + addRegexToken('W', match1to2); + addRegexToken('WW', match1to2, match2); + + addWeekParseToken(['w', 'ww', 'W', 'WW'], function ( + input, + week, + config, + token + ) { + week[token.substr(0, 1)] = toInt(input); + }); + + // HELPERS + + // LOCALES + + function localeWeek(mom) { + return weekOfYear(mom, this._week.dow, this._week.doy).week; + } + + var defaultLocaleWeek = { + dow: 0, // Sunday is the first day of the week. + doy: 6, // The week that contains Jan 6th is the first week of the year. + }; + + function localeFirstDayOfWeek() { + return this._week.dow; + } + + function localeFirstDayOfYear() { + return this._week.doy; + } + + // MOMENTS + + function getSetWeek(input) { + var week = this.localeData().week(this); + return input == null ? week : this.add((input - week) * 7, 'd'); + } + + function getSetISOWeek(input) { + var week = weekOfYear(this, 1, 4).week; + return input == null ? week : this.add((input - week) * 7, 'd'); + } + + // FORMATTING + + addFormatToken('d', 0, 'do', 'day'); + + addFormatToken('dd', 0, 0, function (format) { + return this.localeData().weekdaysMin(this, format); + }); + + addFormatToken('ddd', 0, 0, function (format) { + return this.localeData().weekdaysShort(this, format); + }); + + addFormatToken('dddd', 0, 0, function (format) { + return this.localeData().weekdays(this, format); + }); + + addFormatToken('e', 0, 0, 'weekday'); + addFormatToken('E', 0, 0, 'isoWeekday'); + + // ALIASES + + addUnitAlias('day', 'd'); + addUnitAlias('weekday', 'e'); + addUnitAlias('isoWeekday', 'E'); + + // PRIORITY + addUnitPriority('day', 11); + addUnitPriority('weekday', 11); + addUnitPriority('isoWeekday', 11); + + // PARSING + + addRegexToken('d', match1to2); + addRegexToken('e', match1to2); + addRegexToken('E', match1to2); + addRegexToken('dd', function (isStrict, locale) { + return locale.weekdaysMinRegex(isStrict); + }); + addRegexToken('ddd', function (isStrict, locale) { + return locale.weekdaysShortRegex(isStrict); + }); + addRegexToken('dddd', function (isStrict, locale) { + return locale.weekdaysRegex(isStrict); + }); + + addWeekParseToken(['dd', 'ddd', 'dddd'], function (input, week, config, token) { + var weekday = config._locale.weekdaysParse(input, token, config._strict); + // if we didn't get a weekday name, mark the date as invalid + if (weekday != null) { + week.d = weekday; + } else { + getParsingFlags(config).invalidWeekday = input; + } + }); + + addWeekParseToken(['d', 'e', 'E'], function (input, week, config, token) { + week[token] = toInt(input); + }); + + // HELPERS + + function parseWeekday(input, locale) { + if (typeof input !== 'string') { + return input; + } + + if (!isNaN(input)) { + return parseInt(input, 10); + } + + input = locale.weekdaysParse(input); + if (typeof input === 'number') { + return input; + } + + return null; + } + + function parseIsoWeekday(input, locale) { + if (typeof input === 'string') { + return locale.weekdaysParse(input) % 7 || 7; + } + return isNaN(input) ? null : input; + } + + // LOCALES + function shiftWeekdays(ws, n) { + return ws.slice(n, 7).concat(ws.slice(0, n)); + } + + var defaultLocaleWeekdays = 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split( + '_' + ), + defaultLocaleWeekdaysShort = 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'), + defaultLocaleWeekdaysMin = 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'), + defaultWeekdaysRegex = matchWord, + defaultWeekdaysShortRegex = matchWord, + defaultWeekdaysMinRegex = matchWord; + + function localeWeekdays(m, format) { + var weekdays = isArray(this._weekdays) + ? this._weekdays + : this._weekdays[ + m && m !== true && this._weekdays.isFormat.test(format) + ? 'format' + : 'standalone' + ]; + return m === true + ? shiftWeekdays(weekdays, this._week.dow) + : m + ? weekdays[m.day()] + : weekdays; + } + + function localeWeekdaysShort(m) { + return m === true + ? shiftWeekdays(this._weekdaysShort, this._week.dow) + : m + ? this._weekdaysShort[m.day()] + : this._weekdaysShort; + } + + function localeWeekdaysMin(m) { + return m === true + ? shiftWeekdays(this._weekdaysMin, this._week.dow) + : m + ? this._weekdaysMin[m.day()] + : this._weekdaysMin; + } + + function handleStrictParse$1(weekdayName, format, strict) { + var i, + ii, + mom, + llc = weekdayName.toLocaleLowerCase(); + if (!this._weekdaysParse) { + this._weekdaysParse = []; + this._shortWeekdaysParse = []; + this._minWeekdaysParse = []; + + for (i = 0; i < 7; ++i) { + mom = createUTC([2000, 1]).day(i); + this._minWeekdaysParse[i] = this.weekdaysMin( + mom, + '' + ).toLocaleLowerCase(); + this._shortWeekdaysParse[i] = this.weekdaysShort( + mom, + '' + ).toLocaleLowerCase(); + this._weekdaysParse[i] = this.weekdays(mom, '').toLocaleLowerCase(); + } + } + + if (strict) { + if (format === 'dddd') { + ii = indexOf.call(this._weekdaysParse, llc); + return ii !== -1 ? ii : null; + } else if (format === 'ddd') { + ii = indexOf.call(this._shortWeekdaysParse, llc); + return ii !== -1 ? ii : null; + } else { + ii = indexOf.call(this._minWeekdaysParse, llc); + return ii !== -1 ? ii : null; + } + } else { + if (format === 'dddd') { + ii = indexOf.call(this._weekdaysParse, llc); + if (ii !== -1) { + return ii; + } + ii = indexOf.call(this._shortWeekdaysParse, llc); + if (ii !== -1) { + return ii; + } + ii = indexOf.call(this._minWeekdaysParse, llc); + return ii !== -1 ? ii : null; + } else if (format === 'ddd') { + ii = indexOf.call(this._shortWeekdaysParse, llc); + if (ii !== -1) { + return ii; + } + ii = indexOf.call(this._weekdaysParse, llc); + if (ii !== -1) { + return ii; + } + ii = indexOf.call(this._minWeekdaysParse, llc); + return ii !== -1 ? ii : null; + } else { + ii = indexOf.call(this._minWeekdaysParse, llc); + if (ii !== -1) { + return ii; + } + ii = indexOf.call(this._weekdaysParse, llc); + if (ii !== -1) { + return ii; + } + ii = indexOf.call(this._shortWeekdaysParse, llc); + return ii !== -1 ? ii : null; + } + } + } + + function localeWeekdaysParse(weekdayName, format, strict) { + var i, mom, regex; + + if (this._weekdaysParseExact) { + return handleStrictParse$1.call(this, weekdayName, format, strict); + } + + if (!this._weekdaysParse) { + this._weekdaysParse = []; + this._minWeekdaysParse = []; + this._shortWeekdaysParse = []; + this._fullWeekdaysParse = []; + } + + for (i = 0; i < 7; i++) { + // make the regex if we don't have it already + + mom = createUTC([2000, 1]).day(i); + if (strict && !this._fullWeekdaysParse[i]) { + this._fullWeekdaysParse[i] = new RegExp( + '^' + this.weekdays(mom, '').replace('.', '\\.?') + '$', + 'i' + ); + this._shortWeekdaysParse[i] = new RegExp( + '^' + this.weekdaysShort(mom, '').replace('.', '\\.?') + '$', + 'i' + ); + this._minWeekdaysParse[i] = new RegExp( + '^' + this.weekdaysMin(mom, '').replace('.', '\\.?') + '$', + 'i' + ); + } + if (!this._weekdaysParse[i]) { + regex = + '^' + + this.weekdays(mom, '') + + '|^' + + this.weekdaysShort(mom, '') + + '|^' + + this.weekdaysMin(mom, ''); + this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i'); + } + // test the regex + if ( + strict && + format === 'dddd' && + this._fullWeekdaysParse[i].test(weekdayName) + ) { + return i; + } else if ( + strict && + format === 'ddd' && + this._shortWeekdaysParse[i].test(weekdayName) + ) { + return i; + } else if ( + strict && + format === 'dd' && + this._minWeekdaysParse[i].test(weekdayName) + ) { + return i; + } else if (!strict && this._weekdaysParse[i].test(weekdayName)) { + return i; + } + } + } + + // MOMENTS + + function getSetDayOfWeek(input) { + if (!this.isValid()) { + return input != null ? this : NaN; + } + var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay(); + if (input != null) { + input = parseWeekday(input, this.localeData()); + return this.add(input - day, 'd'); + } else { + return day; + } + } + + function getSetLocaleDayOfWeek(input) { + if (!this.isValid()) { + return input != null ? this : NaN; + } + var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7; + return input == null ? weekday : this.add(input - weekday, 'd'); + } + + function getSetISODayOfWeek(input) { + if (!this.isValid()) { + return input != null ? this : NaN; + } + + // behaves the same as moment#day except + // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6) + // as a setter, sunday should belong to the previous week. + + if (input != null) { + var weekday = parseIsoWeekday(input, this.localeData()); + return this.day(this.day() % 7 ? weekday : weekday - 7); + } else { + return this.day() || 7; + } + } + + function weekdaysRegex(isStrict) { + if (this._weekdaysParseExact) { + if (!hasOwnProp(this, '_weekdaysRegex')) { + computeWeekdaysParse.call(this); + } + if (isStrict) { + return this._weekdaysStrictRegex; + } else { + return this._weekdaysRegex; + } + } else { + if (!hasOwnProp(this, '_weekdaysRegex')) { + this._weekdaysRegex = defaultWeekdaysRegex; + } + return this._weekdaysStrictRegex && isStrict + ? this._weekdaysStrictRegex + : this._weekdaysRegex; + } + } + + function weekdaysShortRegex(isStrict) { + if (this._weekdaysParseExact) { + if (!hasOwnProp(this, '_weekdaysRegex')) { + computeWeekdaysParse.call(this); + } + if (isStrict) { + return this._weekdaysShortStrictRegex; + } else { + return this._weekdaysShortRegex; + } + } else { + if (!hasOwnProp(this, '_weekdaysShortRegex')) { + this._weekdaysShortRegex = defaultWeekdaysShortRegex; + } + return this._weekdaysShortStrictRegex && isStrict + ? this._weekdaysShortStrictRegex + : this._weekdaysShortRegex; + } + } + + function weekdaysMinRegex(isStrict) { + if (this._weekdaysParseExact) { + if (!hasOwnProp(this, '_weekdaysRegex')) { + computeWeekdaysParse.call(this); + } + if (isStrict) { + return this._weekdaysMinStrictRegex; + } else { + return this._weekdaysMinRegex; + } + } else { + if (!hasOwnProp(this, '_weekdaysMinRegex')) { + this._weekdaysMinRegex = defaultWeekdaysMinRegex; + } + return this._weekdaysMinStrictRegex && isStrict + ? this._weekdaysMinStrictRegex + : this._weekdaysMinRegex; + } + } + + function computeWeekdaysParse() { + function cmpLenRev(a, b) { + return b.length - a.length; + } + + var minPieces = [], + shortPieces = [], + longPieces = [], + mixedPieces = [], + i, + mom, + minp, + shortp, + longp; + for (i = 0; i < 7; i++) { + // make the regex if we don't have it already + mom = createUTC([2000, 1]).day(i); + minp = regexEscape(this.weekdaysMin(mom, '')); + shortp = regexEscape(this.weekdaysShort(mom, '')); + longp = regexEscape(this.weekdays(mom, '')); + minPieces.push(minp); + shortPieces.push(shortp); + longPieces.push(longp); + mixedPieces.push(minp); + mixedPieces.push(shortp); + mixedPieces.push(longp); + } + // Sorting makes sure if one weekday (or abbr) is a prefix of another it + // will match the longer piece. + minPieces.sort(cmpLenRev); + shortPieces.sort(cmpLenRev); + longPieces.sort(cmpLenRev); + mixedPieces.sort(cmpLenRev); + + this._weekdaysRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i'); + this._weekdaysShortRegex = this._weekdaysRegex; + this._weekdaysMinRegex = this._weekdaysRegex; + + this._weekdaysStrictRegex = new RegExp( + '^(' + longPieces.join('|') + ')', + 'i' + ); + this._weekdaysShortStrictRegex = new RegExp( + '^(' + shortPieces.join('|') + ')', + 'i' + ); + this._weekdaysMinStrictRegex = new RegExp( + '^(' + minPieces.join('|') + ')', + 'i' + ); + } + + // FORMATTING + + function hFormat() { + return this.hours() % 12 || 12; + } + + function kFormat() { + return this.hours() || 24; + } + + addFormatToken('H', ['HH', 2], 0, 'hour'); + addFormatToken('h', ['hh', 2], 0, hFormat); + addFormatToken('k', ['kk', 2], 0, kFormat); + + addFormatToken('hmm', 0, 0, function () { + return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2); + }); + + addFormatToken('hmmss', 0, 0, function () { + return ( + '' + + hFormat.apply(this) + + zeroFill(this.minutes(), 2) + + zeroFill(this.seconds(), 2) + ); + }); + + addFormatToken('Hmm', 0, 0, function () { + return '' + this.hours() + zeroFill(this.minutes(), 2); + }); + + addFormatToken('Hmmss', 0, 0, function () { + return ( + '' + + this.hours() + + zeroFill(this.minutes(), 2) + + zeroFill(this.seconds(), 2) + ); + }); + + function meridiem(token, lowercase) { + addFormatToken(token, 0, 0, function () { + return this.localeData().meridiem( + this.hours(), + this.minutes(), + lowercase + ); + }); + } + + meridiem('a', true); + meridiem('A', false); + + // ALIASES + + addUnitAlias('hour', 'h'); + + // PRIORITY + addUnitPriority('hour', 13); + + // PARSING + + function matchMeridiem(isStrict, locale) { + return locale._meridiemParse; + } + + addRegexToken('a', matchMeridiem); + addRegexToken('A', matchMeridiem); + addRegexToken('H', match1to2); + addRegexToken('h', match1to2); + addRegexToken('k', match1to2); + addRegexToken('HH', match1to2, match2); + addRegexToken('hh', match1to2, match2); + addRegexToken('kk', match1to2, match2); + + addRegexToken('hmm', match3to4); + addRegexToken('hmmss', match5to6); + addRegexToken('Hmm', match3to4); + addRegexToken('Hmmss', match5to6); + + addParseToken(['H', 'HH'], HOUR); + addParseToken(['k', 'kk'], function (input, array, config) { + var kInput = toInt(input); + array[HOUR] = kInput === 24 ? 0 : kInput; + }); + addParseToken(['a', 'A'], function (input, array, config) { + config._isPm = config._locale.isPM(input); + config._meridiem = input; + }); + addParseToken(['h', 'hh'], function (input, array, config) { + array[HOUR] = toInt(input); + getParsingFlags(config).bigHour = true; + }); + addParseToken('hmm', function (input, array, config) { + var pos = input.length - 2; + array[HOUR] = toInt(input.substr(0, pos)); + array[MINUTE] = toInt(input.substr(pos)); + getParsingFlags(config).bigHour = true; + }); + addParseToken('hmmss', function (input, array, config) { + var pos1 = input.length - 4, + pos2 = input.length - 2; + array[HOUR] = toInt(input.substr(0, pos1)); + array[MINUTE] = toInt(input.substr(pos1, 2)); + array[SECOND] = toInt(input.substr(pos2)); + getParsingFlags(config).bigHour = true; + }); + addParseToken('Hmm', function (input, array, config) { + var pos = input.length - 2; + array[HOUR] = toInt(input.substr(0, pos)); + array[MINUTE] = toInt(input.substr(pos)); + }); + addParseToken('Hmmss', function (input, array, config) { + var pos1 = input.length - 4, + pos2 = input.length - 2; + array[HOUR] = toInt(input.substr(0, pos1)); + array[MINUTE] = toInt(input.substr(pos1, 2)); + array[SECOND] = toInt(input.substr(pos2)); + }); + + // LOCALES + + function localeIsPM(input) { + // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays + // Using charAt should be more compatible. + return (input + '').toLowerCase().charAt(0) === 'p'; + } + + var defaultLocaleMeridiemParse = /[ap]\.?m?\.?/i, + // Setting the hour should keep the time, because the user explicitly + // specified which hour they want. So trying to maintain the same hour (in + // a new timezone) makes sense. Adding/subtracting hours does not follow + // this rule. + getSetHour = makeGetSet('Hours', true); + + function localeMeridiem(hours, minutes, isLower) { + if (hours > 11) { + return isLower ? 'pm' : 'PM'; + } else { + return isLower ? 'am' : 'AM'; + } + } + + var baseConfig = { + calendar: defaultCalendar, + longDateFormat: defaultLongDateFormat, + invalidDate: defaultInvalidDate, + ordinal: defaultOrdinal, + dayOfMonthOrdinalParse: defaultDayOfMonthOrdinalParse, + relativeTime: defaultRelativeTime, + + months: defaultLocaleMonths, + monthsShort: defaultLocaleMonthsShort, + + week: defaultLocaleWeek, + + weekdays: defaultLocaleWeekdays, + weekdaysMin: defaultLocaleWeekdaysMin, + weekdaysShort: defaultLocaleWeekdaysShort, + + meridiemParse: defaultLocaleMeridiemParse, + }; + + // internal storage for locale config files + var locales = {}, + localeFamilies = {}, + globalLocale; + + function commonPrefix(arr1, arr2) { + var i, + minl = Math.min(arr1.length, arr2.length); + for (i = 0; i < minl; i += 1) { + if (arr1[i] !== arr2[i]) { + return i; + } + } + return minl; + } + + function normalizeLocale(key) { + return key ? key.toLowerCase().replace('_', '-') : key; + } + + // pick the locale from the array + // try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each + // substring from most specific to least, but move to the next array item if it's a more specific variant than the current root + function chooseLocale(names) { + var i = 0, + j, + next, + locale, + split; + + while (i < names.length) { + split = normalizeLocale(names[i]).split('-'); + j = split.length; + next = normalizeLocale(names[i + 1]); + next = next ? next.split('-') : null; + while (j > 0) { + locale = loadLocale(split.slice(0, j).join('-')); + if (locale) { + return locale; + } + if ( + next && + next.length >= j && + commonPrefix(split, next) >= j - 1 + ) { + //the next array item is better than a shallower substring of this one + break; + } + j--; + } + i++; + } + return globalLocale; + } + + function loadLocale(name) { + var oldLocale = null, + aliasedRequire; + // TODO: Find a better way to register and load all the locales in Node + if ( + locales[name] === undefined && + typeof module !== 'undefined' && + module && + module.exports + ) { + try { + oldLocale = globalLocale._abbr; + aliasedRequire = require; + aliasedRequire('./locale/' + name); + getSetGlobalLocale(oldLocale); + } catch (e) { + // mark as not found to avoid repeating expensive file require call causing high CPU + // when trying to find en-US, en_US, en-us for every format call + locales[name] = null; // null means not found + } + } + return locales[name]; + } + + // This function will load locale and then set the global locale. If + // no arguments are passed in, it will simply return the current global + // locale key. + function getSetGlobalLocale(key, values) { + var data; + if (key) { + if (isUndefined(values)) { + data = getLocale(key); + } else { + data = defineLocale(key, values); + } + + if (data) { + // moment.duration._locale = moment._locale = data; + globalLocale = data; + } else { + if (typeof console !== 'undefined' && console.warn) { + //warn user if arguments are passed but the locale could not be set + console.warn( + 'Locale ' + key + ' not found. Did you forget to load it?' + ); + } + } + } + + return globalLocale._abbr; + } + + function defineLocale(name, config) { + if (config !== null) { + var locale, + parentConfig = baseConfig; + config.abbr = name; + if (locales[name] != null) { + deprecateSimple( + 'defineLocaleOverride', + 'use moment.updateLocale(localeName, config) to change ' + + 'an existing locale. moment.defineLocale(localeName, ' + + 'config) should only be used for creating a new locale ' + + 'See http://momentjs.com/guides/#/warnings/define-locale/ for more info.' + ); + parentConfig = locales[name]._config; + } else if (config.parentLocale != null) { + if (locales[config.parentLocale] != null) { + parentConfig = locales[config.parentLocale]._config; + } else { + locale = loadLocale(config.parentLocale); + if (locale != null) { + parentConfig = locale._config; + } else { + if (!localeFamilies[config.parentLocale]) { + localeFamilies[config.parentLocale] = []; + } + localeFamilies[config.parentLocale].push({ + name: name, + config: config, + }); + return null; + } + } + } + locales[name] = new Locale(mergeConfigs(parentConfig, config)); + + if (localeFamilies[name]) { + localeFamilies[name].forEach(function (x) { + defineLocale(x.name, x.config); + }); + } + + // backwards compat for now: also set the locale + // make sure we set the locale AFTER all child locales have been + // created, so we won't end up with the child locale set. + getSetGlobalLocale(name); + + return locales[name]; + } else { + // useful for testing + delete locales[name]; + return null; + } + } + + function updateLocale(name, config) { + if (config != null) { + var locale, + tmpLocale, + parentConfig = baseConfig; + + if (locales[name] != null && locales[name].parentLocale != null) { + // Update existing child locale in-place to avoid memory-leaks + locales[name].set(mergeConfigs(locales[name]._config, config)); + } else { + // MERGE + tmpLocale = loadLocale(name); + if (tmpLocale != null) { + parentConfig = tmpLocale._config; + } + config = mergeConfigs(parentConfig, config); + if (tmpLocale == null) { + // updateLocale is called for creating a new locale + // Set abbr so it will have a name (getters return + // undefined otherwise). + config.abbr = name; + } + locale = new Locale(config); + locale.parentLocale = locales[name]; + locales[name] = locale; + } + + // backwards compat for now: also set the locale + getSetGlobalLocale(name); + } else { + // pass null for config to unupdate, useful for tests + if (locales[name] != null) { + if (locales[name].parentLocale != null) { + locales[name] = locales[name].parentLocale; + if (name === getSetGlobalLocale()) { + getSetGlobalLocale(name); + } + } else if (locales[name] != null) { + delete locales[name]; + } + } + } + return locales[name]; + } + + // returns locale data + function getLocale(key) { + var locale; + + if (key && key._locale && key._locale._abbr) { + key = key._locale._abbr; + } + + if (!key) { + return globalLocale; + } + + if (!isArray(key)) { + //short-circuit everything else + locale = loadLocale(key); + if (locale) { + return locale; + } + key = [key]; + } + + return chooseLocale(key); + } + + function listLocales() { + return keys(locales); + } + + function checkOverflow(m) { + var overflow, + a = m._a; + + if (a && getParsingFlags(m).overflow === -2) { + overflow = + a[MONTH] < 0 || a[MONTH] > 11 + ? MONTH + : a[DATE] < 1 || a[DATE] > daysInMonth(a[YEAR], a[MONTH]) + ? DATE + : a[HOUR] < 0 || + a[HOUR] > 24 || + (a[HOUR] === 24 && + (a[MINUTE] !== 0 || + a[SECOND] !== 0 || + a[MILLISECOND] !== 0)) + ? HOUR + : a[MINUTE] < 0 || a[MINUTE] > 59 + ? MINUTE + : a[SECOND] < 0 || a[SECOND] > 59 + ? SECOND + : a[MILLISECOND] < 0 || a[MILLISECOND] > 999 + ? MILLISECOND + : -1; + + if ( + getParsingFlags(m)._overflowDayOfYear && + (overflow < YEAR || overflow > DATE) + ) { + overflow = DATE; + } + if (getParsingFlags(m)._overflowWeeks && overflow === -1) { + overflow = WEEK; + } + if (getParsingFlags(m)._overflowWeekday && overflow === -1) { + overflow = WEEKDAY; + } + + getParsingFlags(m).overflow = overflow; + } + + return m; + } + + // iso 8601 regex + // 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00) + var extendedIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([+-]\d\d(?::?\d\d)?|\s*Z)?)?$/, + basicIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d|))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([+-]\d\d(?::?\d\d)?|\s*Z)?)?$/, + tzRegex = /Z|[+-]\d\d(?::?\d\d)?/, + isoDates = [ + ['YYYYYY-MM-DD', /[+-]\d{6}-\d\d-\d\d/], + ['YYYY-MM-DD', /\d{4}-\d\d-\d\d/], + ['GGGG-[W]WW-E', /\d{4}-W\d\d-\d/], + ['GGGG-[W]WW', /\d{4}-W\d\d/, false], + ['YYYY-DDD', /\d{4}-\d{3}/], + ['YYYY-MM', /\d{4}-\d\d/, false], + ['YYYYYYMMDD', /[+-]\d{10}/], + ['YYYYMMDD', /\d{8}/], + ['GGGG[W]WWE', /\d{4}W\d{3}/], + ['GGGG[W]WW', /\d{4}W\d{2}/, false], + ['YYYYDDD', /\d{7}/], + ['YYYYMM', /\d{6}/, false], + ['YYYY', /\d{4}/, false], + ], + // iso time formats and regexes + isoTimes = [ + ['HH:mm:ss.SSSS', /\d\d:\d\d:\d\d\.\d+/], + ['HH:mm:ss,SSSS', /\d\d:\d\d:\d\d,\d+/], + ['HH:mm:ss', /\d\d:\d\d:\d\d/], + ['HH:mm', /\d\d:\d\d/], + ['HHmmss.SSSS', /\d\d\d\d\d\d\.\d+/], + ['HHmmss,SSSS', /\d\d\d\d\d\d,\d+/], + ['HHmmss', /\d\d\d\d\d\d/], + ['HHmm', /\d\d\d\d/], + ['HH', /\d\d/], + ], + aspNetJsonRegex = /^\/?Date\((-?\d+)/i, + // RFC 2822 regex: For details see https://tools.ietf.org/html/rfc2822#section-3.3 + rfc2822 = /^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\d{4}))$/, + obsOffsets = { + UT: 0, + GMT: 0, + EDT: -4 * 60, + EST: -5 * 60, + CDT: -5 * 60, + CST: -6 * 60, + MDT: -6 * 60, + MST: -7 * 60, + PDT: -7 * 60, + PST: -8 * 60, + }; + + // date from iso format + function configFromISO(config) { + var i, + l, + string = config._i, + match = extendedIsoRegex.exec(string) || basicIsoRegex.exec(string), + allowTime, + dateFormat, + timeFormat, + tzFormat; + + if (match) { + getParsingFlags(config).iso = true; + + for (i = 0, l = isoDates.length; i < l; i++) { + if (isoDates[i][1].exec(match[1])) { + dateFormat = isoDates[i][0]; + allowTime = isoDates[i][2] !== false; + break; + } + } + if (dateFormat == null) { + config._isValid = false; + return; + } + if (match[3]) { + for (i = 0, l = isoTimes.length; i < l; i++) { + if (isoTimes[i][1].exec(match[3])) { + // match[2] should be 'T' or space + timeFormat = (match[2] || ' ') + isoTimes[i][0]; + break; + } + } + if (timeFormat == null) { + config._isValid = false; + return; + } + } + if (!allowTime && timeFormat != null) { + config._isValid = false; + return; + } + if (match[4]) { + if (tzRegex.exec(match[4])) { + tzFormat = 'Z'; + } else { + config._isValid = false; + return; + } + } + config._f = dateFormat + (timeFormat || '') + (tzFormat || ''); + configFromStringAndFormat(config); + } else { + config._isValid = false; + } + } + + function extractFromRFC2822Strings( + yearStr, + monthStr, + dayStr, + hourStr, + minuteStr, + secondStr + ) { + var result = [ + untruncateYear(yearStr), + defaultLocaleMonthsShort.indexOf(monthStr), + parseInt(dayStr, 10), + parseInt(hourStr, 10), + parseInt(minuteStr, 10), + ]; + + if (secondStr) { + result.push(parseInt(secondStr, 10)); + } + + return result; + } + + function untruncateYear(yearStr) { + var year = parseInt(yearStr, 10); + if (year <= 49) { + return 2000 + year; + } else if (year <= 999) { + return 1900 + year; + } + return year; + } + + function preprocessRFC2822(s) { + // Remove comments and folding whitespace and replace multiple-spaces with a single space + return s + .replace(/\([^)]*\)|[\n\t]/g, ' ') + .replace(/(\s\s+)/g, ' ') + .replace(/^\s\s*/, '') + .replace(/\s\s*$/, ''); + } + + function checkWeekday(weekdayStr, parsedInput, config) { + if (weekdayStr) { + // TODO: Replace the vanilla JS Date object with an independent day-of-week check. + var weekdayProvided = defaultLocaleWeekdaysShort.indexOf(weekdayStr), + weekdayActual = new Date( + parsedInput[0], + parsedInput[1], + parsedInput[2] + ).getDay(); + if (weekdayProvided !== weekdayActual) { + getParsingFlags(config).weekdayMismatch = true; + config._isValid = false; + return false; + } + } + return true; + } + + function calculateOffset(obsOffset, militaryOffset, numOffset) { + if (obsOffset) { + return obsOffsets[obsOffset]; + } else if (militaryOffset) { + // the only allowed military tz is Z + return 0; + } else { + var hm = parseInt(numOffset, 10), + m = hm % 100, + h = (hm - m) / 100; + return h * 60 + m; + } + } + + // date and time from ref 2822 format + function configFromRFC2822(config) { + var match = rfc2822.exec(preprocessRFC2822(config._i)), + parsedArray; + if (match) { + parsedArray = extractFromRFC2822Strings( + match[4], + match[3], + match[2], + match[5], + match[6], + match[7] + ); + if (!checkWeekday(match[1], parsedArray, config)) { + return; + } + + config._a = parsedArray; + config._tzm = calculateOffset(match[8], match[9], match[10]); + + config._d = createUTCDate.apply(null, config._a); + config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); + + getParsingFlags(config).rfc2822 = true; + } else { + config._isValid = false; + } + } + + // date from 1) ASP.NET, 2) ISO, 3) RFC 2822 formats, or 4) optional fallback if parsing isn't strict + function configFromString(config) { + var matched = aspNetJsonRegex.exec(config._i); + if (matched !== null) { + config._d = new Date(+matched[1]); + return; + } + + configFromISO(config); + if (config._isValid === false) { + delete config._isValid; + } else { + return; + } + + configFromRFC2822(config); + if (config._isValid === false) { + delete config._isValid; + } else { + return; + } + + if (config._strict) { + config._isValid = false; + } else { + // Final attempt, use Input Fallback + hooks.createFromInputFallback(config); + } + } + + hooks.createFromInputFallback = deprecate( + 'value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), ' + + 'which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are ' + + 'discouraged. Please refer to http://momentjs.com/guides/#/warnings/js-date/ for more info.', + function (config) { + config._d = new Date(config._i + (config._useUTC ? ' UTC' : '')); + } + ); + + // Pick the first defined of two or three arguments. + function defaults(a, b, c) { + if (a != null) { + return a; + } + if (b != null) { + return b; + } + return c; + } + + function currentDateArray(config) { + // hooks is actually the exported moment object + var nowValue = new Date(hooks.now()); + if (config._useUTC) { + return [ + nowValue.getUTCFullYear(), + nowValue.getUTCMonth(), + nowValue.getUTCDate(), + ]; + } + return [nowValue.getFullYear(), nowValue.getMonth(), nowValue.getDate()]; + } + + // convert an array to a date. + // the array should mirror the parameters below + // note: all values past the year are optional and will default to the lowest possible value. + // [year, month, day , hour, minute, second, millisecond] + function configFromArray(config) { + var i, + date, + input = [], + currentDate, + expectedWeekday, + yearToUse; + + if (config._d) { + return; + } + + currentDate = currentDateArray(config); + + //compute day of the year from weeks and weekdays + if (config._w && config._a[DATE] == null && config._a[MONTH] == null) { + dayOfYearFromWeekInfo(config); + } + + //if the day of the year is set, figure out what it is + if (config._dayOfYear != null) { + yearToUse = defaults(config._a[YEAR], currentDate[YEAR]); + + if ( + config._dayOfYear > daysInYear(yearToUse) || + config._dayOfYear === 0 + ) { + getParsingFlags(config)._overflowDayOfYear = true; + } + + date = createUTCDate(yearToUse, 0, config._dayOfYear); + config._a[MONTH] = date.getUTCMonth(); + config._a[DATE] = date.getUTCDate(); + } + + // Default to current date. + // * if no year, month, day of month are given, default to today + // * if day of month is given, default month and year + // * if month is given, default only year + // * if year is given, don't default anything + for (i = 0; i < 3 && config._a[i] == null; ++i) { + config._a[i] = input[i] = currentDate[i]; + } + + // Zero out whatever was not defaulted, including time + for (; i < 7; i++) { + config._a[i] = input[i] = + config._a[i] == null ? (i === 2 ? 1 : 0) : config._a[i]; + } + + // Check for 24:00:00.000 + if ( + config._a[HOUR] === 24 && + config._a[MINUTE] === 0 && + config._a[SECOND] === 0 && + config._a[MILLISECOND] === 0 + ) { + config._nextDay = true; + config._a[HOUR] = 0; + } + + config._d = (config._useUTC ? createUTCDate : createDate).apply( + null, + input + ); + expectedWeekday = config._useUTC + ? config._d.getUTCDay() + : config._d.getDay(); + + // Apply timezone offset from input. The actual utcOffset can be changed + // with parseZone. + if (config._tzm != null) { + config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); + } + + if (config._nextDay) { + config._a[HOUR] = 24; + } + + // check for mismatching day of week + if ( + config._w && + typeof config._w.d !== 'undefined' && + config._w.d !== expectedWeekday + ) { + getParsingFlags(config).weekdayMismatch = true; + } + } + + function dayOfYearFromWeekInfo(config) { + var w, weekYear, week, weekday, dow, doy, temp, weekdayOverflow, curWeek; + + w = config._w; + if (w.GG != null || w.W != null || w.E != null) { + dow = 1; + doy = 4; + + // TODO: We need to take the current isoWeekYear, but that depends on + // how we interpret now (local, utc, fixed offset). So create + // a now version of current config (take local/utc/offset flags, and + // create now). + weekYear = defaults( + w.GG, + config._a[YEAR], + weekOfYear(createLocal(), 1, 4).year + ); + week = defaults(w.W, 1); + weekday = defaults(w.E, 1); + if (weekday < 1 || weekday > 7) { + weekdayOverflow = true; + } + } else { + dow = config._locale._week.dow; + doy = config._locale._week.doy; + + curWeek = weekOfYear(createLocal(), dow, doy); + + weekYear = defaults(w.gg, config._a[YEAR], curWeek.year); + + // Default to current week. + week = defaults(w.w, curWeek.week); + + if (w.d != null) { + // weekday -- low day numbers are considered next week + weekday = w.d; + if (weekday < 0 || weekday > 6) { + weekdayOverflow = true; + } + } else if (w.e != null) { + // local weekday -- counting starts from beginning of week + weekday = w.e + dow; + if (w.e < 0 || w.e > 6) { + weekdayOverflow = true; + } + } else { + // default to beginning of week + weekday = dow; + } + } + if (week < 1 || week > weeksInYear(weekYear, dow, doy)) { + getParsingFlags(config)._overflowWeeks = true; + } else if (weekdayOverflow != null) { + getParsingFlags(config)._overflowWeekday = true; + } else { + temp = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy); + config._a[YEAR] = temp.year; + config._dayOfYear = temp.dayOfYear; + } + } + + // constant that refers to the ISO standard + hooks.ISO_8601 = function () {}; + + // constant that refers to the RFC 2822 form + hooks.RFC_2822 = function () {}; + + // date from string and format string + function configFromStringAndFormat(config) { + // TODO: Move this to another part of the creation flow to prevent circular deps + if (config._f === hooks.ISO_8601) { + configFromISO(config); + return; + } + if (config._f === hooks.RFC_2822) { + configFromRFC2822(config); + return; + } + config._a = []; + getParsingFlags(config).empty = true; + + // This array is used to make a Date, either with `new Date` or `Date.UTC` + var string = '' + config._i, + i, + parsedInput, + tokens, + token, + skipped, + stringLength = string.length, + totalParsedInputLength = 0, + era; + + tokens = + expandFormat(config._f, config._locale).match(formattingTokens) || []; + + for (i = 0; i < tokens.length; i++) { + token = tokens[i]; + parsedInput = (string.match(getParseRegexForToken(token, config)) || + [])[0]; + if (parsedInput) { + skipped = string.substr(0, string.indexOf(parsedInput)); + if (skipped.length > 0) { + getParsingFlags(config).unusedInput.push(skipped); + } + string = string.slice( + string.indexOf(parsedInput) + parsedInput.length + ); + totalParsedInputLength += parsedInput.length; + } + // don't parse if it's not a known token + if (formatTokenFunctions[token]) { + if (parsedInput) { + getParsingFlags(config).empty = false; + } else { + getParsingFlags(config).unusedTokens.push(token); + } + addTimeToArrayFromToken(token, parsedInput, config); + } else if (config._strict && !parsedInput) { + getParsingFlags(config).unusedTokens.push(token); + } + } + + // add remaining unparsed input length to the string + getParsingFlags(config).charsLeftOver = + stringLength - totalParsedInputLength; + if (string.length > 0) { + getParsingFlags(config).unusedInput.push(string); + } + + // clear _12h flag if hour is <= 12 + if ( + config._a[HOUR] <= 12 && + getParsingFlags(config).bigHour === true && + config._a[HOUR] > 0 + ) { + getParsingFlags(config).bigHour = undefined; + } + + getParsingFlags(config).parsedDateParts = config._a.slice(0); + getParsingFlags(config).meridiem = config._meridiem; + // handle meridiem + config._a[HOUR] = meridiemFixWrap( + config._locale, + config._a[HOUR], + config._meridiem + ); + + // handle era + era = getParsingFlags(config).era; + if (era !== null) { + config._a[YEAR] = config._locale.erasConvertYear(era, config._a[YEAR]); + } + + configFromArray(config); + checkOverflow(config); + } + + function meridiemFixWrap(locale, hour, meridiem) { + var isPm; + + if (meridiem == null) { + // nothing to do + return hour; + } + if (locale.meridiemHour != null) { + return locale.meridiemHour(hour, meridiem); + } else if (locale.isPM != null) { + // Fallback + isPm = locale.isPM(meridiem); + if (isPm && hour < 12) { + hour += 12; + } + if (!isPm && hour === 12) { + hour = 0; + } + return hour; + } else { + // this is not supposed to happen + return hour; + } + } + + // date from string and array of format strings + function configFromStringAndArray(config) { + var tempConfig, + bestMoment, + scoreToBeat, + i, + currentScore, + validFormatFound, + bestFormatIsValid = false; + + if (config._f.length === 0) { + getParsingFlags(config).invalidFormat = true; + config._d = new Date(NaN); + return; + } + + for (i = 0; i < config._f.length; i++) { + currentScore = 0; + validFormatFound = false; + tempConfig = copyConfig({}, config); + if (config._useUTC != null) { + tempConfig._useUTC = config._useUTC; + } + tempConfig._f = config._f[i]; + configFromStringAndFormat(tempConfig); + + if (isValid(tempConfig)) { + validFormatFound = true; + } + + // if there is any input that was not parsed add a penalty for that format + currentScore += getParsingFlags(tempConfig).charsLeftOver; + + //or tokens + currentScore += getParsingFlags(tempConfig).unusedTokens.length * 10; + + getParsingFlags(tempConfig).score = currentScore; + + if (!bestFormatIsValid) { + if ( + scoreToBeat == null || + currentScore < scoreToBeat || + validFormatFound + ) { + scoreToBeat = currentScore; + bestMoment = tempConfig; + if (validFormatFound) { + bestFormatIsValid = true; + } + } + } else { + if (currentScore < scoreToBeat) { + scoreToBeat = currentScore; + bestMoment = tempConfig; + } + } + } + + extend(config, bestMoment || tempConfig); + } + + function configFromObject(config) { + if (config._d) { + return; + } + + var i = normalizeObjectUnits(config._i), + dayOrDate = i.day === undefined ? i.date : i.day; + config._a = map( + [i.year, i.month, dayOrDate, i.hour, i.minute, i.second, i.millisecond], + function (obj) { + return obj && parseInt(obj, 10); + } + ); + + configFromArray(config); + } + + function createFromConfig(config) { + var res = new Moment(checkOverflow(prepareConfig(config))); + if (res._nextDay) { + // Adding is smart enough around DST + res.add(1, 'd'); + res._nextDay = undefined; + } + + return res; + } + + function prepareConfig(config) { + var input = config._i, + format = config._f; + + config._locale = config._locale || getLocale(config._l); + + if (input === null || (format === undefined && input === '')) { + return createInvalid({ nullInput: true }); + } + + if (typeof input === 'string') { + config._i = input = config._locale.preparse(input); + } + + if (isMoment(input)) { + return new Moment(checkOverflow(input)); + } else if (isDate(input)) { + config._d = input; + } else if (isArray(format)) { + configFromStringAndArray(config); + } else if (format) { + configFromStringAndFormat(config); + } else { + configFromInput(config); + } + + if (!isValid(config)) { + config._d = null; + } + + return config; + } + + function configFromInput(config) { + var input = config._i; + if (isUndefined(input)) { + config._d = new Date(hooks.now()); + } else if (isDate(input)) { + config._d = new Date(input.valueOf()); + } else if (typeof input === 'string') { + configFromString(config); + } else if (isArray(input)) { + config._a = map(input.slice(0), function (obj) { + return parseInt(obj, 10); + }); + configFromArray(config); + } else if (isObject(input)) { + configFromObject(config); + } else if (isNumber(input)) { + // from milliseconds + config._d = new Date(input); + } else { + hooks.createFromInputFallback(config); + } + } + + function createLocalOrUTC(input, format, locale, strict, isUTC) { + var c = {}; + + if (format === true || format === false) { + strict = format; + format = undefined; + } + + if (locale === true || locale === false) { + strict = locale; + locale = undefined; + } + + if ( + (isObject(input) && isObjectEmpty(input)) || + (isArray(input) && input.length === 0) + ) { + input = undefined; + } + // object construction must be done this way. + // https://github.com/moment/moment/issues/1423 + c._isAMomentObject = true; + c._useUTC = c._isUTC = isUTC; + c._l = locale; + c._i = input; + c._f = format; + c._strict = strict; + + return createFromConfig(c); + } + + function createLocal(input, format, locale, strict) { + return createLocalOrUTC(input, format, locale, strict, false); + } + + var prototypeMin = deprecate( + 'moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/', + function () { + var other = createLocal.apply(null, arguments); + if (this.isValid() && other.isValid()) { + return other < this ? this : other; + } else { + return createInvalid(); + } + } + ), + prototypeMax = deprecate( + 'moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/', + function () { + var other = createLocal.apply(null, arguments); + if (this.isValid() && other.isValid()) { + return other > this ? this : other; + } else { + return createInvalid(); + } + } + ); + + // Pick a moment m from moments so that m[fn](other) is true for all + // other. This relies on the function fn to be transitive. + // + // moments should either be an array of moment objects or an array, whose + // first element is an array of moment objects. + function pickBy(fn, moments) { + var res, i; + if (moments.length === 1 && isArray(moments[0])) { + moments = moments[0]; + } + if (!moments.length) { + return createLocal(); + } + res = moments[0]; + for (i = 1; i < moments.length; ++i) { + if (!moments[i].isValid() || moments[i][fn](res)) { + res = moments[i]; + } + } + return res; + } + + // TODO: Use [].sort instead? + function min() { + var args = [].slice.call(arguments, 0); + + return pickBy('isBefore', args); + } + + function max() { + var args = [].slice.call(arguments, 0); + + return pickBy('isAfter', args); + } + + var now = function () { + return Date.now ? Date.now() : +new Date(); + }; + + var ordering = [ + 'year', + 'quarter', + 'month', + 'week', + 'day', + 'hour', + 'minute', + 'second', + 'millisecond', + ]; + + function isDurationValid(m) { + var key, + unitHasDecimal = false, + i; + for (key in m) { + if ( + hasOwnProp(m, key) && + !( + indexOf.call(ordering, key) !== -1 && + (m[key] == null || !isNaN(m[key])) + ) + ) { + return false; + } + } + + for (i = 0; i < ordering.length; ++i) { + if (m[ordering[i]]) { + if (unitHasDecimal) { + return false; // only allow non-integers for smallest unit + } + if (parseFloat(m[ordering[i]]) !== toInt(m[ordering[i]])) { + unitHasDecimal = true; + } + } + } + + return true; + } + + function isValid$1() { + return this._isValid; + } + + function createInvalid$1() { + return createDuration(NaN); + } + + function Duration(duration) { + var normalizedInput = normalizeObjectUnits(duration), + years = normalizedInput.year || 0, + quarters = normalizedInput.quarter || 0, + months = normalizedInput.month || 0, + weeks = normalizedInput.week || normalizedInput.isoWeek || 0, + days = normalizedInput.day || 0, + hours = normalizedInput.hour || 0, + minutes = normalizedInput.minute || 0, + seconds = normalizedInput.second || 0, + milliseconds = normalizedInput.millisecond || 0; + + this._isValid = isDurationValid(normalizedInput); + + // representation for dateAddRemove + this._milliseconds = + +milliseconds + + seconds * 1e3 + // 1000 + minutes * 6e4 + // 1000 * 60 + hours * 1000 * 60 * 60; //using 1000 * 60 * 60 instead of 36e5 to avoid floating point rounding errors https://github.com/moment/moment/issues/2978 + // Because of dateAddRemove treats 24 hours as different from a + // day when working around DST, we need to store them separately + this._days = +days + weeks * 7; + // It is impossible to translate months into days without knowing + // which months you are are talking about, so we have to store + // it separately. + this._months = +months + quarters * 3 + years * 12; + + this._data = {}; + + this._locale = getLocale(); + + this._bubble(); + } + + function isDuration(obj) { + return obj instanceof Duration; + } + + function absRound(number) { + if (number < 0) { + return Math.round(-1 * number) * -1; + } else { + return Math.round(number); + } + } + + // compare two arrays, return the number of differences + function compareArrays(array1, array2, dontConvert) { + var len = Math.min(array1.length, array2.length), + lengthDiff = Math.abs(array1.length - array2.length), + diffs = 0, + i; + for (i = 0; i < len; i++) { + if ( + (dontConvert && array1[i] !== array2[i]) || + (!dontConvert && toInt(array1[i]) !== toInt(array2[i])) + ) { + diffs++; + } + } + return diffs + lengthDiff; + } + + // FORMATTING + + function offset(token, separator) { + addFormatToken(token, 0, 0, function () { + var offset = this.utcOffset(), + sign = '+'; + if (offset < 0) { + offset = -offset; + sign = '-'; + } + return ( + sign + + zeroFill(~~(offset / 60), 2) + + separator + + zeroFill(~~offset % 60, 2) + ); + }); + } + + offset('Z', ':'); + offset('ZZ', ''); + + // PARSING + + addRegexToken('Z', matchShortOffset); + addRegexToken('ZZ', matchShortOffset); + addParseToken(['Z', 'ZZ'], function (input, array, config) { + config._useUTC = true; + config._tzm = offsetFromString(matchShortOffset, input); + }); + + // HELPERS + + // timezone chunker + // '+10:00' > ['10', '00'] + // '-1530' > ['-15', '30'] + var chunkOffset = /([\+\-]|\d\d)/gi; + + function offsetFromString(matcher, string) { + var matches = (string || '').match(matcher), + chunk, + parts, + minutes; + + if (matches === null) { + return null; + } + + chunk = matches[matches.length - 1] || []; + parts = (chunk + '').match(chunkOffset) || ['-', 0, 0]; + minutes = +(parts[1] * 60) + toInt(parts[2]); + + return minutes === 0 ? 0 : parts[0] === '+' ? minutes : -minutes; + } + + // Return a moment from input, that is local/utc/zone equivalent to model. + function cloneWithOffset(input, model) { + var res, diff; + if (model._isUTC) { + res = model.clone(); + diff = + (isMoment(input) || isDate(input) + ? input.valueOf() + : createLocal(input).valueOf()) - res.valueOf(); + // Use low-level api, because this fn is low-level api. + res._d.setTime(res._d.valueOf() + diff); + hooks.updateOffset(res, false); + return res; + } else { + return createLocal(input).local(); + } + } + + function getDateOffset(m) { + // On Firefox.24 Date#getTimezoneOffset returns a floating point. + // https://github.com/moment/moment/pull/1871 + return -Math.round(m._d.getTimezoneOffset()); + } + + // HOOKS + + // This function will be called whenever a moment is mutated. + // It is intended to keep the offset in sync with the timezone. + hooks.updateOffset = function () {}; + + // MOMENTS + + // keepLocalTime = true means only change the timezone, without + // affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]--> + // 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset + // +0200, so we adjust the time as needed, to be valid. + // + // Keeping the time actually adds/subtracts (one hour) + // from the actual represented time. That is why we call updateOffset + // a second time. In case it wants us to change the offset again + // _changeInProgress == true case, then we have to adjust, because + // there is no such time in the given timezone. + function getSetOffset(input, keepLocalTime, keepMinutes) { + var offset = this._offset || 0, + localAdjust; + if (!this.isValid()) { + return input != null ? this : NaN; + } + if (input != null) { + if (typeof input === 'string') { + input = offsetFromString(matchShortOffset, input); + if (input === null) { + return this; + } + } else if (Math.abs(input) < 16 && !keepMinutes) { + input = input * 60; + } + if (!this._isUTC && keepLocalTime) { + localAdjust = getDateOffset(this); + } + this._offset = input; + this._isUTC = true; + if (localAdjust != null) { + this.add(localAdjust, 'm'); + } + if (offset !== input) { + if (!keepLocalTime || this._changeInProgress) { + addSubtract( + this, + createDuration(input - offset, 'm'), + 1, + false + ); + } else if (!this._changeInProgress) { + this._changeInProgress = true; + hooks.updateOffset(this, true); + this._changeInProgress = null; + } + } + return this; + } else { + return this._isUTC ? offset : getDateOffset(this); + } + } + + function getSetZone(input, keepLocalTime) { + if (input != null) { + if (typeof input !== 'string') { + input = -input; + } + + this.utcOffset(input, keepLocalTime); + + return this; + } else { + return -this.utcOffset(); + } + } + + function setOffsetToUTC(keepLocalTime) { + return this.utcOffset(0, keepLocalTime); + } + + function setOffsetToLocal(keepLocalTime) { + if (this._isUTC) { + this.utcOffset(0, keepLocalTime); + this._isUTC = false; + + if (keepLocalTime) { + this.subtract(getDateOffset(this), 'm'); + } + } + return this; + } + + function setOffsetToParsedOffset() { + if (this._tzm != null) { + this.utcOffset(this._tzm, false, true); + } else if (typeof this._i === 'string') { + var tZone = offsetFromString(matchOffset, this._i); + if (tZone != null) { + this.utcOffset(tZone); + } else { + this.utcOffset(0, true); + } + } + return this; + } + + function hasAlignedHourOffset(input) { + if (!this.isValid()) { + return false; + } + input = input ? createLocal(input).utcOffset() : 0; + + return (this.utcOffset() - input) % 60 === 0; + } + + function isDaylightSavingTime() { + return ( + this.utcOffset() > this.clone().month(0).utcOffset() || + this.utcOffset() > this.clone().month(5).utcOffset() + ); + } + + function isDaylightSavingTimeShifted() { + if (!isUndefined(this._isDSTShifted)) { + return this._isDSTShifted; + } + + var c = {}, + other; + + copyConfig(c, this); + c = prepareConfig(c); + + if (c._a) { + other = c._isUTC ? createUTC(c._a) : createLocal(c._a); + this._isDSTShifted = + this.isValid() && compareArrays(c._a, other.toArray()) > 0; + } else { + this._isDSTShifted = false; + } + + return this._isDSTShifted; + } + + function isLocal() { + return this.isValid() ? !this._isUTC : false; + } + + function isUtcOffset() { + return this.isValid() ? this._isUTC : false; + } + + function isUtc() { + return this.isValid() ? this._isUTC && this._offset === 0 : false; + } + + // ASP.NET json date format regex + var aspNetRegex = /^(-|\+)?(?:(\d*)[. ])?(\d+):(\d+)(?::(\d+)(\.\d*)?)?$/, + // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html + // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere + // and further modified to allow for strings containing both week and day + isoRegex = /^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/; + + function createDuration(input, key) { + var duration = input, + // matching against regexp is expensive, do it on demand + match = null, + sign, + ret, + diffRes; + + if (isDuration(input)) { + duration = { + ms: input._milliseconds, + d: input._days, + M: input._months, + }; + } else if (isNumber(input) || !isNaN(+input)) { + duration = {}; + if (key) { + duration[key] = +input; + } else { + duration.milliseconds = +input; + } + } else if ((match = aspNetRegex.exec(input))) { + sign = match[1] === '-' ? -1 : 1; + duration = { + y: 0, + d: toInt(match[DATE]) * sign, + h: toInt(match[HOUR]) * sign, + m: toInt(match[MINUTE]) * sign, + s: toInt(match[SECOND]) * sign, + ms: toInt(absRound(match[MILLISECOND] * 1000)) * sign, // the millisecond decimal point is included in the match + }; + } else if ((match = isoRegex.exec(input))) { + sign = match[1] === '-' ? -1 : 1; + duration = { + y: parseIso(match[2], sign), + M: parseIso(match[3], sign), + w: parseIso(match[4], sign), + d: parseIso(match[5], sign), + h: parseIso(match[6], sign), + m: parseIso(match[7], sign), + s: parseIso(match[8], sign), + }; + } else if (duration == null) { + // checks for null or undefined + duration = {}; + } else if ( + typeof duration === 'object' && + ('from' in duration || 'to' in duration) + ) { + diffRes = momentsDifference( + createLocal(duration.from), + createLocal(duration.to) + ); + + duration = {}; + duration.ms = diffRes.milliseconds; + duration.M = diffRes.months; + } + + ret = new Duration(duration); + + if (isDuration(input) && hasOwnProp(input, '_locale')) { + ret._locale = input._locale; + } + + if (isDuration(input) && hasOwnProp(input, '_isValid')) { + ret._isValid = input._isValid; + } + + return ret; + } + + createDuration.fn = Duration.prototype; + createDuration.invalid = createInvalid$1; + + function parseIso(inp, sign) { + // We'd normally use ~~inp for this, but unfortunately it also + // converts floats to ints. + // inp may be undefined, so careful calling replace on it. + var res = inp && parseFloat(inp.replace(',', '.')); + // apply sign while we're at it + return (isNaN(res) ? 0 : res) * sign; + } + + function positiveMomentsDifference(base, other) { + var res = {}; + + res.months = + other.month() - base.month() + (other.year() - base.year()) * 12; + if (base.clone().add(res.months, 'M').isAfter(other)) { + --res.months; + } + + res.milliseconds = +other - +base.clone().add(res.months, 'M'); + + return res; + } + + function momentsDifference(base, other) { + var res; + if (!(base.isValid() && other.isValid())) { + return { milliseconds: 0, months: 0 }; + } + + other = cloneWithOffset(other, base); + if (base.isBefore(other)) { + res = positiveMomentsDifference(base, other); + } else { + res = positiveMomentsDifference(other, base); + res.milliseconds = -res.milliseconds; + res.months = -res.months; + } + + return res; + } + + // TODO: remove 'name' arg after deprecation is removed + function createAdder(direction, name) { + return function (val, period) { + var dur, tmp; + //invert the arguments, but complain about it + if (period !== null && !isNaN(+period)) { + deprecateSimple( + name, + 'moment().' + + name + + '(period, number) is deprecated. Please use moment().' + + name + + '(number, period). ' + + 'See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info.' + ); + tmp = val; + val = period; + period = tmp; + } + + dur = createDuration(val, period); + addSubtract(this, dur, direction); + return this; + }; + } + + function addSubtract(mom, duration, isAdding, updateOffset) { + var milliseconds = duration._milliseconds, + days = absRound(duration._days), + months = absRound(duration._months); + + if (!mom.isValid()) { + // No op + return; + } + + updateOffset = updateOffset == null ? true : updateOffset; + + if (months) { + setMonth(mom, get(mom, 'Month') + months * isAdding); + } + if (days) { + set$1(mom, 'Date', get(mom, 'Date') + days * isAdding); + } + if (milliseconds) { + mom._d.setTime(mom._d.valueOf() + milliseconds * isAdding); + } + if (updateOffset) { + hooks.updateOffset(mom, days || months); + } + } + + var add = createAdder(1, 'add'), + subtract = createAdder(-1, 'subtract'); + + function isString(input) { + return typeof input === 'string' || input instanceof String; + } + + // type MomentInput = Moment | Date | string | number | (number | string)[] | MomentInputObject | void; // null | undefined + function isMomentInput(input) { + return ( + isMoment(input) || + isDate(input) || + isString(input) || + isNumber(input) || + isNumberOrStringArray(input) || + isMomentInputObject(input) || + input === null || + input === undefined + ); + } + + function isMomentInputObject(input) { + var objectTest = isObject(input) && !isObjectEmpty(input), + propertyTest = false, + properties = [ + 'years', + 'year', + 'y', + 'months', + 'month', + 'M', + 'days', + 'day', + 'd', + 'dates', + 'date', + 'D', + 'hours', + 'hour', + 'h', + 'minutes', + 'minute', + 'm', + 'seconds', + 'second', + 's', + 'milliseconds', + 'millisecond', + 'ms', + ], + i, + property; + + for (i = 0; i < properties.length; i += 1) { + property = properties[i]; + propertyTest = propertyTest || hasOwnProp(input, property); + } + + return objectTest && propertyTest; + } + + function isNumberOrStringArray(input) { + var arrayTest = isArray(input), + dataTypeTest = false; + if (arrayTest) { + dataTypeTest = + input.filter(function (item) { + return !isNumber(item) && isString(input); + }).length === 0; + } + return arrayTest && dataTypeTest; + } + + function isCalendarSpec(input) { + var objectTest = isObject(input) && !isObjectEmpty(input), + propertyTest = false, + properties = [ + 'sameDay', + 'nextDay', + 'lastDay', + 'nextWeek', + 'lastWeek', + 'sameElse', + ], + i, + property; + + for (i = 0; i < properties.length; i += 1) { + property = properties[i]; + propertyTest = propertyTest || hasOwnProp(input, property); + } + + return objectTest && propertyTest; + } + + function getCalendarFormat(myMoment, now) { + var diff = myMoment.diff(now, 'days', true); + return diff < -6 + ? 'sameElse' + : diff < -1 + ? 'lastWeek' + : diff < 0 + ? 'lastDay' + : diff < 1 + ? 'sameDay' + : diff < 2 + ? 'nextDay' + : diff < 7 + ? 'nextWeek' + : 'sameElse'; + } + + function calendar$1(time, formats) { + // Support for single parameter, formats only overload to the calendar function + if (arguments.length === 1) { + if (!arguments[0]) { + time = undefined; + formats = undefined; + } else if (isMomentInput(arguments[0])) { + time = arguments[0]; + formats = undefined; + } else if (isCalendarSpec(arguments[0])) { + formats = arguments[0]; + time = undefined; + } + } + // We want to compare the start of today, vs this. + // Getting start-of-today depends on whether we're local/utc/offset or not. + var now = time || createLocal(), + sod = cloneWithOffset(now, this).startOf('day'), + format = hooks.calendarFormat(this, sod) || 'sameElse', + output = + formats && + (isFunction(formats[format]) + ? formats[format].call(this, now) + : formats[format]); + + return this.format( + output || this.localeData().calendar(format, this, createLocal(now)) + ); + } + + function clone() { + return new Moment(this); + } + + function isAfter(input, units) { + var localInput = isMoment(input) ? input : createLocal(input); + if (!(this.isValid() && localInput.isValid())) { + return false; + } + units = normalizeUnits(units) || 'millisecond'; + if (units === 'millisecond') { + return this.valueOf() > localInput.valueOf(); + } else { + return localInput.valueOf() < this.clone().startOf(units).valueOf(); + } + } + + function isBefore(input, units) { + var localInput = isMoment(input) ? input : createLocal(input); + if (!(this.isValid() && localInput.isValid())) { + return false; + } + units = normalizeUnits(units) || 'millisecond'; + if (units === 'millisecond') { + return this.valueOf() < localInput.valueOf(); + } else { + return this.clone().endOf(units).valueOf() < localInput.valueOf(); + } + } + + function isBetween(from, to, units, inclusivity) { + var localFrom = isMoment(from) ? from : createLocal(from), + localTo = isMoment(to) ? to : createLocal(to); + if (!(this.isValid() && localFrom.isValid() && localTo.isValid())) { + return false; + } + inclusivity = inclusivity || '()'; + return ( + (inclusivity[0] === '(' + ? this.isAfter(localFrom, units) + : !this.isBefore(localFrom, units)) && + (inclusivity[1] === ')' + ? this.isBefore(localTo, units) + : !this.isAfter(localTo, units)) + ); + } + + function isSame(input, units) { + var localInput = isMoment(input) ? input : createLocal(input), + inputMs; + if (!(this.isValid() && localInput.isValid())) { + return false; + } + units = normalizeUnits(units) || 'millisecond'; + if (units === 'millisecond') { + return this.valueOf() === localInput.valueOf(); + } else { + inputMs = localInput.valueOf(); + return ( + this.clone().startOf(units).valueOf() <= inputMs && + inputMs <= this.clone().endOf(units).valueOf() + ); + } + } + + function isSameOrAfter(input, units) { + return this.isSame(input, units) || this.isAfter(input, units); + } + + function isSameOrBefore(input, units) { + return this.isSame(input, units) || this.isBefore(input, units); + } + + function diff(input, units, asFloat) { + var that, zoneDelta, output; + + if (!this.isValid()) { + return NaN; + } + + that = cloneWithOffset(input, this); + + if (!that.isValid()) { + return NaN; + } + + zoneDelta = (that.utcOffset() - this.utcOffset()) * 6e4; + + units = normalizeUnits(units); + + switch (units) { + case 'year': + output = monthDiff(this, that) / 12; + break; + case 'month': + output = monthDiff(this, that); + break; + case 'quarter': + output = monthDiff(this, that) / 3; + break; + case 'second': + output = (this - that) / 1e3; + break; // 1000 + case 'minute': + output = (this - that) / 6e4; + break; // 1000 * 60 + case 'hour': + output = (this - that) / 36e5; + break; // 1000 * 60 * 60 + case 'day': + output = (this - that - zoneDelta) / 864e5; + break; // 1000 * 60 * 60 * 24, negate dst + case 'week': + output = (this - that - zoneDelta) / 6048e5; + break; // 1000 * 60 * 60 * 24 * 7, negate dst + default: + output = this - that; + } + + return asFloat ? output : absFloor(output); + } + + function monthDiff(a, b) { + if (a.date() < b.date()) { + // end-of-month calculations work correct when the start month has more + // days than the end month. + return -monthDiff(b, a); + } + // difference in months + var wholeMonthDiff = (b.year() - a.year()) * 12 + (b.month() - a.month()), + // b is in (anchor - 1 month, anchor + 1 month) + anchor = a.clone().add(wholeMonthDiff, 'months'), + anchor2, + adjust; + + if (b - anchor < 0) { + anchor2 = a.clone().add(wholeMonthDiff - 1, 'months'); + // linear across the month + adjust = (b - anchor) / (anchor - anchor2); + } else { + anchor2 = a.clone().add(wholeMonthDiff + 1, 'months'); + // linear across the month + adjust = (b - anchor) / (anchor2 - anchor); + } + + //check for negative zero, return zero if negative zero + return -(wholeMonthDiff + adjust) || 0; + } + + hooks.defaultFormat = 'YYYY-MM-DDTHH:mm:ssZ'; + hooks.defaultFormatUtc = 'YYYY-MM-DDTHH:mm:ss[Z]'; + + function toString() { + return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ'); + } + + function toISOString(keepOffset) { + if (!this.isValid()) { + return null; + } + var utc = keepOffset !== true, + m = utc ? this.clone().utc() : this; + if (m.year() < 0 || m.year() > 9999) { + return formatMoment( + m, + utc + ? 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]' + : 'YYYYYY-MM-DD[T]HH:mm:ss.SSSZ' + ); + } + if (isFunction(Date.prototype.toISOString)) { + // native implementation is ~50x faster, use it when we can + if (utc) { + return this.toDate().toISOString(); + } else { + return new Date(this.valueOf() + this.utcOffset() * 60 * 1000) + .toISOString() + .replace('Z', formatMoment(m, 'Z')); + } + } + return formatMoment( + m, + utc ? 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]' : 'YYYY-MM-DD[T]HH:mm:ss.SSSZ' + ); + } + + /** + * Return a human readable representation of a moment that can + * also be evaluated to get a new moment which is the same + * + * @link https://nodejs.org/dist/latest/docs/api/util.html#util_custom_inspect_function_on_objects + */ + function inspect() { + if (!this.isValid()) { + return 'moment.invalid(/* ' + this._i + ' */)'; + } + var func = 'moment', + zone = '', + prefix, + year, + datetime, + suffix; + if (!this.isLocal()) { + func = this.utcOffset() === 0 ? 'moment.utc' : 'moment.parseZone'; + zone = 'Z'; + } + prefix = '[' + func + '("]'; + year = 0 <= this.year() && this.year() <= 9999 ? 'YYYY' : 'YYYYYY'; + datetime = '-MM-DD[T]HH:mm:ss.SSS'; + suffix = zone + '[")]'; + + return this.format(prefix + year + datetime + suffix); + } + + function format(inputString) { + if (!inputString) { + inputString = this.isUtc() + ? hooks.defaultFormatUtc + : hooks.defaultFormat; + } + var output = formatMoment(this, inputString); + return this.localeData().postformat(output); + } + + function from(time, withoutSuffix) { + if ( + this.isValid() && + ((isMoment(time) && time.isValid()) || createLocal(time).isValid()) + ) { + return createDuration({ to: this, from: time }) + .locale(this.locale()) + .humanize(!withoutSuffix); + } else { + return this.localeData().invalidDate(); + } + } + + function fromNow(withoutSuffix) { + return this.from(createLocal(), withoutSuffix); + } + + function to(time, withoutSuffix) { + if ( + this.isValid() && + ((isMoment(time) && time.isValid()) || createLocal(time).isValid()) + ) { + return createDuration({ from: this, to: time }) + .locale(this.locale()) + .humanize(!withoutSuffix); + } else { + return this.localeData().invalidDate(); + } + } + + function toNow(withoutSuffix) { + return this.to(createLocal(), withoutSuffix); + } + + // If passed a locale key, it will set the locale for this + // instance. Otherwise, it will return the locale configuration + // variables for this instance. + function locale(key) { + var newLocaleData; + + if (key === undefined) { + return this._locale._abbr; + } else { + newLocaleData = getLocale(key); + if (newLocaleData != null) { + this._locale = newLocaleData; + } + return this; + } + } + + var lang = deprecate( + 'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.', + function (key) { + if (key === undefined) { + return this.localeData(); + } else { + return this.locale(key); + } + } + ); + + function localeData() { + return this._locale; + } + + var MS_PER_SECOND = 1000, + MS_PER_MINUTE = 60 * MS_PER_SECOND, + MS_PER_HOUR = 60 * MS_PER_MINUTE, + MS_PER_400_YEARS = (365 * 400 + 97) * 24 * MS_PER_HOUR; + + // actual modulo - handles negative numbers (for dates before 1970): + function mod$1(dividend, divisor) { + return ((dividend % divisor) + divisor) % divisor; + } + + function localStartOfDate(y, m, d) { + // the date constructor remaps years 0-99 to 1900-1999 + if (y < 100 && y >= 0) { + // preserve leap years using a full 400 year cycle, then reset + return new Date(y + 400, m, d) - MS_PER_400_YEARS; + } else { + return new Date(y, m, d).valueOf(); + } + } + + function utcStartOfDate(y, m, d) { + // Date.UTC remaps years 0-99 to 1900-1999 + if (y < 100 && y >= 0) { + // preserve leap years using a full 400 year cycle, then reset + return Date.UTC(y + 400, m, d) - MS_PER_400_YEARS; + } else { + return Date.UTC(y, m, d); + } + } + + function startOf(units) { + var time, startOfDate; + units = normalizeUnits(units); + if (units === undefined || units === 'millisecond' || !this.isValid()) { + return this; + } + + startOfDate = this._isUTC ? utcStartOfDate : localStartOfDate; + + switch (units) { + case 'year': + time = startOfDate(this.year(), 0, 1); + break; + case 'quarter': + time = startOfDate( + this.year(), + this.month() - (this.month() % 3), + 1 + ); + break; + case 'month': + time = startOfDate(this.year(), this.month(), 1); + break; + case 'week': + time = startOfDate( + this.year(), + this.month(), + this.date() - this.weekday() + ); + break; + case 'isoWeek': + time = startOfDate( + this.year(), + this.month(), + this.date() - (this.isoWeekday() - 1) + ); + break; + case 'day': + case 'date': + time = startOfDate(this.year(), this.month(), this.date()); + break; + case 'hour': + time = this._d.valueOf(); + time -= mod$1( + time + (this._isUTC ? 0 : this.utcOffset() * MS_PER_MINUTE), + MS_PER_HOUR + ); + break; + case 'minute': + time = this._d.valueOf(); + time -= mod$1(time, MS_PER_MINUTE); + break; + case 'second': + time = this._d.valueOf(); + time -= mod$1(time, MS_PER_SECOND); + break; + } + + this._d.setTime(time); + hooks.updateOffset(this, true); + return this; + } + + function endOf(units) { + var time, startOfDate; + units = normalizeUnits(units); + if (units === undefined || units === 'millisecond' || !this.isValid()) { + return this; + } + + startOfDate = this._isUTC ? utcStartOfDate : localStartOfDate; + + switch (units) { + case 'year': + time = startOfDate(this.year() + 1, 0, 1) - 1; + break; + case 'quarter': + time = + startOfDate( + this.year(), + this.month() - (this.month() % 3) + 3, + 1 + ) - 1; + break; + case 'month': + time = startOfDate(this.year(), this.month() + 1, 1) - 1; + break; + case 'week': + time = + startOfDate( + this.year(), + this.month(), + this.date() - this.weekday() + 7 + ) - 1; + break; + case 'isoWeek': + time = + startOfDate( + this.year(), + this.month(), + this.date() - (this.isoWeekday() - 1) + 7 + ) - 1; + break; + case 'day': + case 'date': + time = startOfDate(this.year(), this.month(), this.date() + 1) - 1; + break; + case 'hour': + time = this._d.valueOf(); + time += + MS_PER_HOUR - + mod$1( + time + (this._isUTC ? 0 : this.utcOffset() * MS_PER_MINUTE), + MS_PER_HOUR + ) - + 1; + break; + case 'minute': + time = this._d.valueOf(); + time += MS_PER_MINUTE - mod$1(time, MS_PER_MINUTE) - 1; + break; + case 'second': + time = this._d.valueOf(); + time += MS_PER_SECOND - mod$1(time, MS_PER_SECOND) - 1; + break; + } + + this._d.setTime(time); + hooks.updateOffset(this, true); + return this; + } + + function valueOf() { + return this._d.valueOf() - (this._offset || 0) * 60000; + } + + function unix() { + return Math.floor(this.valueOf() / 1000); + } + + function toDate() { + return new Date(this.valueOf()); + } + + function toArray() { + var m = this; + return [ + m.year(), + m.month(), + m.date(), + m.hour(), + m.minute(), + m.second(), + m.millisecond(), + ]; + } + + function toObject() { + var m = this; + return { + years: m.year(), + months: m.month(), + date: m.date(), + hours: m.hours(), + minutes: m.minutes(), + seconds: m.seconds(), + milliseconds: m.milliseconds(), + }; + } + + function toJSON() { + // new Date(NaN).toJSON() === null + return this.isValid() ? this.toISOString() : null; + } + + function isValid$2() { + return isValid(this); + } + + function parsingFlags() { + return extend({}, getParsingFlags(this)); + } + + function invalidAt() { + return getParsingFlags(this).overflow; + } + + function creationData() { + return { + input: this._i, + format: this._f, + locale: this._locale, + isUTC: this._isUTC, + strict: this._strict, + }; + } + + addFormatToken('N', 0, 0, 'eraAbbr'); + addFormatToken('NN', 0, 0, 'eraAbbr'); + addFormatToken('NNN', 0, 0, 'eraAbbr'); + addFormatToken('NNNN', 0, 0, 'eraName'); + addFormatToken('NNNNN', 0, 0, 'eraNarrow'); + + addFormatToken('y', ['y', 1], 'yo', 'eraYear'); + addFormatToken('y', ['yy', 2], 0, 'eraYear'); + addFormatToken('y', ['yyy', 3], 0, 'eraYear'); + addFormatToken('y', ['yyyy', 4], 0, 'eraYear'); + + addRegexToken('N', matchEraAbbr); + addRegexToken('NN', matchEraAbbr); + addRegexToken('NNN', matchEraAbbr); + addRegexToken('NNNN', matchEraName); + addRegexToken('NNNNN', matchEraNarrow); + + addParseToken(['N', 'NN', 'NNN', 'NNNN', 'NNNNN'], function ( + input, + array, + config, + token + ) { + var era = config._locale.erasParse(input, token, config._strict); + if (era) { + getParsingFlags(config).era = era; + } else { + getParsingFlags(config).invalidEra = input; + } + }); + + addRegexToken('y', matchUnsigned); + addRegexToken('yy', matchUnsigned); + addRegexToken('yyy', matchUnsigned); + addRegexToken('yyyy', matchUnsigned); + addRegexToken('yo', matchEraYearOrdinal); + + addParseToken(['y', 'yy', 'yyy', 'yyyy'], YEAR); + addParseToken(['yo'], function (input, array, config, token) { + var match; + if (config._locale._eraYearOrdinalRegex) { + match = input.match(config._locale._eraYearOrdinalRegex); + } + + if (config._locale.eraYearOrdinalParse) { + array[YEAR] = config._locale.eraYearOrdinalParse(input, match); + } else { + array[YEAR] = parseInt(input, 10); + } + }); + + function localeEras(m, format) { + var i, + l, + date, + eras = this._eras || getLocale('en')._eras; + for (i = 0, l = eras.length; i < l; ++i) { + switch (typeof eras[i].since) { + case 'string': + // truncate time + date = hooks(eras[i].since).startOf('day'); + eras[i].since = date.valueOf(); + break; + } + + switch (typeof eras[i].until) { + case 'undefined': + eras[i].until = +Infinity; + break; + case 'string': + // truncate time + date = hooks(eras[i].until).startOf('day').valueOf(); + eras[i].until = date.valueOf(); + break; + } + } + return eras; + } + + function localeErasParse(eraName, format, strict) { + var i, + l, + eras = this.eras(), + name, + abbr, + narrow; + eraName = eraName.toUpperCase(); + + for (i = 0, l = eras.length; i < l; ++i) { + name = eras[i].name.toUpperCase(); + abbr = eras[i].abbr.toUpperCase(); + narrow = eras[i].narrow.toUpperCase(); + + if (strict) { + switch (format) { + case 'N': + case 'NN': + case 'NNN': + if (abbr === eraName) { + return eras[i]; + } + break; + + case 'NNNN': + if (name === eraName) { + return eras[i]; + } + break; + + case 'NNNNN': + if (narrow === eraName) { + return eras[i]; + } + break; + } + } else if ([name, abbr, narrow].indexOf(eraName) >= 0) { + return eras[i]; + } + } + } + + function localeErasConvertYear(era, year) { + var dir = era.since <= era.until ? +1 : -1; + if (year === undefined) { + return hooks(era.since).year(); + } else { + return hooks(era.since).year() + (year - era.offset) * dir; + } + } + + function getEraName() { + var i, + l, + val, + eras = this.localeData().eras(); + for (i = 0, l = eras.length; i < l; ++i) { + // truncate time + val = this.clone().startOf('day').valueOf(); + + if (eras[i].since <= val && val <= eras[i].until) { + return eras[i].name; + } + if (eras[i].until <= val && val <= eras[i].since) { + return eras[i].name; + } + } + + return ''; + } + + function getEraNarrow() { + var i, + l, + val, + eras = this.localeData().eras(); + for (i = 0, l = eras.length; i < l; ++i) { + // truncate time + val = this.clone().startOf('day').valueOf(); + + if (eras[i].since <= val && val <= eras[i].until) { + return eras[i].narrow; + } + if (eras[i].until <= val && val <= eras[i].since) { + return eras[i].narrow; + } + } + + return ''; + } + + function getEraAbbr() { + var i, + l, + val, + eras = this.localeData().eras(); + for (i = 0, l = eras.length; i < l; ++i) { + // truncate time + val = this.clone().startOf('day').valueOf(); + + if (eras[i].since <= val && val <= eras[i].until) { + return eras[i].abbr; + } + if (eras[i].until <= val && val <= eras[i].since) { + return eras[i].abbr; + } + } + + return ''; + } + + function getEraYear() { + var i, + l, + dir, + val, + eras = this.localeData().eras(); + for (i = 0, l = eras.length; i < l; ++i) { + dir = eras[i].since <= eras[i].until ? +1 : -1; + + // truncate time + val = this.clone().startOf('day').valueOf(); + + if ( + (eras[i].since <= val && val <= eras[i].until) || + (eras[i].until <= val && val <= eras[i].since) + ) { + return ( + (this.year() - hooks(eras[i].since).year()) * dir + + eras[i].offset + ); + } + } + + return this.year(); + } + + function erasNameRegex(isStrict) { + if (!hasOwnProp(this, '_erasNameRegex')) { + computeErasParse.call(this); + } + return isStrict ? this._erasNameRegex : this._erasRegex; + } + + function erasAbbrRegex(isStrict) { + if (!hasOwnProp(this, '_erasAbbrRegex')) { + computeErasParse.call(this); + } + return isStrict ? this._erasAbbrRegex : this._erasRegex; + } + + function erasNarrowRegex(isStrict) { + if (!hasOwnProp(this, '_erasNarrowRegex')) { + computeErasParse.call(this); + } + return isStrict ? this._erasNarrowRegex : this._erasRegex; + } + + function matchEraAbbr(isStrict, locale) { + return locale.erasAbbrRegex(isStrict); + } + + function matchEraName(isStrict, locale) { + return locale.erasNameRegex(isStrict); + } + + function matchEraNarrow(isStrict, locale) { + return locale.erasNarrowRegex(isStrict); + } + + function matchEraYearOrdinal(isStrict, locale) { + return locale._eraYearOrdinalRegex || matchUnsigned; + } + + function computeErasParse() { + var abbrPieces = [], + namePieces = [], + narrowPieces = [], + mixedPieces = [], + i, + l, + eras = this.eras(); + + for (i = 0, l = eras.length; i < l; ++i) { + namePieces.push(regexEscape(eras[i].name)); + abbrPieces.push(regexEscape(eras[i].abbr)); + narrowPieces.push(regexEscape(eras[i].narrow)); + + mixedPieces.push(regexEscape(eras[i].name)); + mixedPieces.push(regexEscape(eras[i].abbr)); + mixedPieces.push(regexEscape(eras[i].narrow)); + } + + this._erasRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i'); + this._erasNameRegex = new RegExp('^(' + namePieces.join('|') + ')', 'i'); + this._erasAbbrRegex = new RegExp('^(' + abbrPieces.join('|') + ')', 'i'); + this._erasNarrowRegex = new RegExp( + '^(' + narrowPieces.join('|') + ')', + 'i' + ); + } + + // FORMATTING + + addFormatToken(0, ['gg', 2], 0, function () { + return this.weekYear() % 100; + }); + + addFormatToken(0, ['GG', 2], 0, function () { + return this.isoWeekYear() % 100; + }); + + function addWeekYearFormatToken(token, getter) { + addFormatToken(0, [token, token.length], 0, getter); + } + + addWeekYearFormatToken('gggg', 'weekYear'); + addWeekYearFormatToken('ggggg', 'weekYear'); + addWeekYearFormatToken('GGGG', 'isoWeekYear'); + addWeekYearFormatToken('GGGGG', 'isoWeekYear'); + + // ALIASES + + addUnitAlias('weekYear', 'gg'); + addUnitAlias('isoWeekYear', 'GG'); + + // PRIORITY + + addUnitPriority('weekYear', 1); + addUnitPriority('isoWeekYear', 1); + + // PARSING + + addRegexToken('G', matchSigned); + addRegexToken('g', matchSigned); + addRegexToken('GG', match1to2, match2); + addRegexToken('gg', match1to2, match2); + addRegexToken('GGGG', match1to4, match4); + addRegexToken('gggg', match1to4, match4); + addRegexToken('GGGGG', match1to6, match6); + addRegexToken('ggggg', match1to6, match6); + + addWeekParseToken(['gggg', 'ggggg', 'GGGG', 'GGGGG'], function ( + input, + week, + config, + token + ) { + week[token.substr(0, 2)] = toInt(input); + }); + + addWeekParseToken(['gg', 'GG'], function (input, week, config, token) { + week[token] = hooks.parseTwoDigitYear(input); + }); + + // MOMENTS + + function getSetWeekYear(input) { + return getSetWeekYearHelper.call( + this, + input, + this.week(), + this.weekday(), + this.localeData()._week.dow, + this.localeData()._week.doy + ); + } + + function getSetISOWeekYear(input) { + return getSetWeekYearHelper.call( + this, + input, + this.isoWeek(), + this.isoWeekday(), + 1, + 4 + ); + } + + function getISOWeeksInYear() { + return weeksInYear(this.year(), 1, 4); + } + + function getISOWeeksInISOWeekYear() { + return weeksInYear(this.isoWeekYear(), 1, 4); + } + + function getWeeksInYear() { + var weekInfo = this.localeData()._week; + return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy); + } + + function getWeeksInWeekYear() { + var weekInfo = this.localeData()._week; + return weeksInYear(this.weekYear(), weekInfo.dow, weekInfo.doy); + } + + function getSetWeekYearHelper(input, week, weekday, dow, doy) { + var weeksTarget; + if (input == null) { + return weekOfYear(this, dow, doy).year; + } else { + weeksTarget = weeksInYear(input, dow, doy); + if (week > weeksTarget) { + week = weeksTarget; + } + return setWeekAll.call(this, input, week, weekday, dow, doy); + } + } + + function setWeekAll(weekYear, week, weekday, dow, doy) { + var dayOfYearData = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy), + date = createUTCDate(dayOfYearData.year, 0, dayOfYearData.dayOfYear); + + this.year(date.getUTCFullYear()); + this.month(date.getUTCMonth()); + this.date(date.getUTCDate()); + return this; + } + + // FORMATTING + + addFormatToken('Q', 0, 'Qo', 'quarter'); + + // ALIASES + + addUnitAlias('quarter', 'Q'); + + // PRIORITY + + addUnitPriority('quarter', 7); + + // PARSING + + addRegexToken('Q', match1); + addParseToken('Q', function (input, array) { + array[MONTH] = (toInt(input) - 1) * 3; + }); + + // MOMENTS + + function getSetQuarter(input) { + return input == null + ? Math.ceil((this.month() + 1) / 3) + : this.month((input - 1) * 3 + (this.month() % 3)); + } + + // FORMATTING + + addFormatToken('D', ['DD', 2], 'Do', 'date'); + + // ALIASES + + addUnitAlias('date', 'D'); + + // PRIORITY + addUnitPriority('date', 9); + + // PARSING + + addRegexToken('D', match1to2); + addRegexToken('DD', match1to2, match2); + addRegexToken('Do', function (isStrict, locale) { + // TODO: Remove "ordinalParse" fallback in next major release. + return isStrict + ? locale._dayOfMonthOrdinalParse || locale._ordinalParse + : locale._dayOfMonthOrdinalParseLenient; + }); + + addParseToken(['D', 'DD'], DATE); + addParseToken('Do', function (input, array) { + array[DATE] = toInt(input.match(match1to2)[0]); + }); + + // MOMENTS + + var getSetDayOfMonth = makeGetSet('Date', true); + + // FORMATTING + + addFormatToken('DDD', ['DDDD', 3], 'DDDo', 'dayOfYear'); + + // ALIASES + + addUnitAlias('dayOfYear', 'DDD'); + + // PRIORITY + addUnitPriority('dayOfYear', 4); + + // PARSING + + addRegexToken('DDD', match1to3); + addRegexToken('DDDD', match3); + addParseToken(['DDD', 'DDDD'], function (input, array, config) { + config._dayOfYear = toInt(input); + }); + + // HELPERS + + // MOMENTS + + function getSetDayOfYear(input) { + var dayOfYear = + Math.round( + (this.clone().startOf('day') - this.clone().startOf('year')) / 864e5 + ) + 1; + return input == null ? dayOfYear : this.add(input - dayOfYear, 'd'); + } + + // FORMATTING + + addFormatToken('m', ['mm', 2], 0, 'minute'); + + // ALIASES + + addUnitAlias('minute', 'm'); + + // PRIORITY + + addUnitPriority('minute', 14); + + // PARSING + + addRegexToken('m', match1to2); + addRegexToken('mm', match1to2, match2); + addParseToken(['m', 'mm'], MINUTE); + + // MOMENTS + + var getSetMinute = makeGetSet('Minutes', false); + + // FORMATTING + + addFormatToken('s', ['ss', 2], 0, 'second'); + + // ALIASES + + addUnitAlias('second', 's'); + + // PRIORITY + + addUnitPriority('second', 15); + + // PARSING + + addRegexToken('s', match1to2); + addRegexToken('ss', match1to2, match2); + addParseToken(['s', 'ss'], SECOND); + + // MOMENTS + + var getSetSecond = makeGetSet('Seconds', false); + + // FORMATTING + + addFormatToken('S', 0, 0, function () { + return ~~(this.millisecond() / 100); + }); + + addFormatToken(0, ['SS', 2], 0, function () { + return ~~(this.millisecond() / 10); + }); + + addFormatToken(0, ['SSS', 3], 0, 'millisecond'); + addFormatToken(0, ['SSSS', 4], 0, function () { + return this.millisecond() * 10; + }); + addFormatToken(0, ['SSSSS', 5], 0, function () { + return this.millisecond() * 100; + }); + addFormatToken(0, ['SSSSSS', 6], 0, function () { + return this.millisecond() * 1000; + }); + addFormatToken(0, ['SSSSSSS', 7], 0, function () { + return this.millisecond() * 10000; + }); + addFormatToken(0, ['SSSSSSSS', 8], 0, function () { + return this.millisecond() * 100000; + }); + addFormatToken(0, ['SSSSSSSSS', 9], 0, function () { + return this.millisecond() * 1000000; + }); + + // ALIASES + + addUnitAlias('millisecond', 'ms'); + + // PRIORITY + + addUnitPriority('millisecond', 16); + + // PARSING + + addRegexToken('S', match1to3, match1); + addRegexToken('SS', match1to3, match2); + addRegexToken('SSS', match1to3, match3); + + var token, getSetMillisecond; + for (token = 'SSSS'; token.length <= 9; token += 'S') { + addRegexToken(token, matchUnsigned); + } + + function parseMs(input, array) { + array[MILLISECOND] = toInt(('0.' + input) * 1000); + } + + for (token = 'S'; token.length <= 9; token += 'S') { + addParseToken(token, parseMs); + } + + getSetMillisecond = makeGetSet('Milliseconds', false); + + // FORMATTING + + addFormatToken('z', 0, 0, 'zoneAbbr'); + addFormatToken('zz', 0, 0, 'zoneName'); + + // MOMENTS + + function getZoneAbbr() { + return this._isUTC ? 'UTC' : ''; + } + + function getZoneName() { + return this._isUTC ? 'Coordinated Universal Time' : ''; + } + + var proto = Moment.prototype; + + proto.add = add; + proto.calendar = calendar$1; + proto.clone = clone; + proto.diff = diff; + proto.endOf = endOf; + proto.format = format; + proto.from = from; + proto.fromNow = fromNow; + proto.to = to; + proto.toNow = toNow; + proto.get = stringGet; + proto.invalidAt = invalidAt; + proto.isAfter = isAfter; + proto.isBefore = isBefore; + proto.isBetween = isBetween; + proto.isSame = isSame; + proto.isSameOrAfter = isSameOrAfter; + proto.isSameOrBefore = isSameOrBefore; + proto.isValid = isValid$2; + proto.lang = lang; + proto.locale = locale; + proto.localeData = localeData; + proto.max = prototypeMax; + proto.min = prototypeMin; + proto.parsingFlags = parsingFlags; + proto.set = stringSet; + proto.startOf = startOf; + proto.subtract = subtract; + proto.toArray = toArray; + proto.toObject = toObject; + proto.toDate = toDate; + proto.toISOString = toISOString; + proto.inspect = inspect; + if (typeof Symbol !== 'undefined' && Symbol.for != null) { + proto[Symbol.for('nodejs.util.inspect.custom')] = function () { + return 'Moment<' + this.format() + '>'; + }; + } + proto.toJSON = toJSON; + proto.toString = toString; + proto.unix = unix; + proto.valueOf = valueOf; + proto.creationData = creationData; + proto.eraName = getEraName; + proto.eraNarrow = getEraNarrow; + proto.eraAbbr = getEraAbbr; + proto.eraYear = getEraYear; + proto.year = getSetYear; + proto.isLeapYear = getIsLeapYear; + proto.weekYear = getSetWeekYear; + proto.isoWeekYear = getSetISOWeekYear; + proto.quarter = proto.quarters = getSetQuarter; + proto.month = getSetMonth; + proto.daysInMonth = getDaysInMonth; + proto.week = proto.weeks = getSetWeek; + proto.isoWeek = proto.isoWeeks = getSetISOWeek; + proto.weeksInYear = getWeeksInYear; + proto.weeksInWeekYear = getWeeksInWeekYear; + proto.isoWeeksInYear = getISOWeeksInYear; + proto.isoWeeksInISOWeekYear = getISOWeeksInISOWeekYear; + proto.date = getSetDayOfMonth; + proto.day = proto.days = getSetDayOfWeek; + proto.weekday = getSetLocaleDayOfWeek; + proto.isoWeekday = getSetISODayOfWeek; + proto.dayOfYear = getSetDayOfYear; + proto.hour = proto.hours = getSetHour; + proto.minute = proto.minutes = getSetMinute; + proto.second = proto.seconds = getSetSecond; + proto.millisecond = proto.milliseconds = getSetMillisecond; + proto.utcOffset = getSetOffset; + proto.utc = setOffsetToUTC; + proto.local = setOffsetToLocal; + proto.parseZone = setOffsetToParsedOffset; + proto.hasAlignedHourOffset = hasAlignedHourOffset; + proto.isDST = isDaylightSavingTime; + proto.isLocal = isLocal; + proto.isUtcOffset = isUtcOffset; + proto.isUtc = isUtc; + proto.isUTC = isUtc; + proto.zoneAbbr = getZoneAbbr; + proto.zoneName = getZoneName; + proto.dates = deprecate( + 'dates accessor is deprecated. Use date instead.', + getSetDayOfMonth + ); + proto.months = deprecate( + 'months accessor is deprecated. Use month instead', + getSetMonth + ); + proto.years = deprecate( + 'years accessor is deprecated. Use year instead', + getSetYear + ); + proto.zone = deprecate( + 'moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/', + getSetZone + ); + proto.isDSTShifted = deprecate( + 'isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information', + isDaylightSavingTimeShifted + ); + + function createUnix(input) { + return createLocal(input * 1000); + } + + function createInZone() { + return createLocal.apply(null, arguments).parseZone(); + } + + function preParsePostFormat(string) { + return string; + } + + var proto$1 = Locale.prototype; + + proto$1.calendar = calendar; + proto$1.longDateFormat = longDateFormat; + proto$1.invalidDate = invalidDate; + proto$1.ordinal = ordinal; + proto$1.preparse = preParsePostFormat; + proto$1.postformat = preParsePostFormat; + proto$1.relativeTime = relativeTime; + proto$1.pastFuture = pastFuture; + proto$1.set = set; + proto$1.eras = localeEras; + proto$1.erasParse = localeErasParse; + proto$1.erasConvertYear = localeErasConvertYear; + proto$1.erasAbbrRegex = erasAbbrRegex; + proto$1.erasNameRegex = erasNameRegex; + proto$1.erasNarrowRegex = erasNarrowRegex; + + proto$1.months = localeMonths; + proto$1.monthsShort = localeMonthsShort; + proto$1.monthsParse = localeMonthsParse; + proto$1.monthsRegex = monthsRegex; + proto$1.monthsShortRegex = monthsShortRegex; + proto$1.week = localeWeek; + proto$1.firstDayOfYear = localeFirstDayOfYear; + proto$1.firstDayOfWeek = localeFirstDayOfWeek; + + proto$1.weekdays = localeWeekdays; + proto$1.weekdaysMin = localeWeekdaysMin; + proto$1.weekdaysShort = localeWeekdaysShort; + proto$1.weekdaysParse = localeWeekdaysParse; + + proto$1.weekdaysRegex = weekdaysRegex; + proto$1.weekdaysShortRegex = weekdaysShortRegex; + proto$1.weekdaysMinRegex = weekdaysMinRegex; + + proto$1.isPM = localeIsPM; + proto$1.meridiem = localeMeridiem; + + function get$1(format, index, field, setter) { + var locale = getLocale(), + utc = createUTC().set(setter, index); + return locale[field](utc, format); + } + + function listMonthsImpl(format, index, field) { + if (isNumber(format)) { + index = format; + format = undefined; + } + + format = format || ''; + + if (index != null) { + return get$1(format, index, field, 'month'); + } + + var i, + out = []; + for (i = 0; i < 12; i++) { + out[i] = get$1(format, i, field, 'month'); + } + return out; + } + + // () + // (5) + // (fmt, 5) + // (fmt) + // (true) + // (true, 5) + // (true, fmt, 5) + // (true, fmt) + function listWeekdaysImpl(localeSorted, format, index, field) { + if (typeof localeSorted === 'boolean') { + if (isNumber(format)) { + index = format; + format = undefined; + } + + format = format || ''; + } else { + format = localeSorted; + index = format; + localeSorted = false; + + if (isNumber(format)) { + index = format; + format = undefined; + } + + format = format || ''; + } + + var locale = getLocale(), + shift = localeSorted ? locale._week.dow : 0, + i, + out = []; + + if (index != null) { + return get$1(format, (index + shift) % 7, field, 'day'); + } + + for (i = 0; i < 7; i++) { + out[i] = get$1(format, (i + shift) % 7, field, 'day'); + } + return out; + } + + function listMonths(format, index) { + return listMonthsImpl(format, index, 'months'); + } + + function listMonthsShort(format, index) { + return listMonthsImpl(format, index, 'monthsShort'); + } + + function listWeekdays(localeSorted, format, index) { + return listWeekdaysImpl(localeSorted, format, index, 'weekdays'); + } + + function listWeekdaysShort(localeSorted, format, index) { + return listWeekdaysImpl(localeSorted, format, index, 'weekdaysShort'); + } + + function listWeekdaysMin(localeSorted, format, index) { + return listWeekdaysImpl(localeSorted, format, index, 'weekdaysMin'); + } + + getSetGlobalLocale('en', { + eras: [ + { + since: '0001-01-01', + until: +Infinity, + offset: 1, + name: 'Anno Domini', + narrow: 'AD', + abbr: 'AD', + }, + { + since: '0000-12-31', + until: -Infinity, + offset: 1, + name: 'Before Christ', + narrow: 'BC', + abbr: 'BC', + }, + ], + dayOfMonthOrdinalParse: /\d{1,2}(th|st|nd|rd)/, + ordinal: function (number) { + var b = number % 10, + output = + toInt((number % 100) / 10) === 1 + ? 'th' + : b === 1 + ? 'st' + : b === 2 + ? 'nd' + : b === 3 + ? 'rd' + : 'th'; + return number + output; + }, + }); + + // Side effect imports + + hooks.lang = deprecate( + 'moment.lang is deprecated. Use moment.locale instead.', + getSetGlobalLocale + ); + hooks.langData = deprecate( + 'moment.langData is deprecated. Use moment.localeData instead.', + getLocale + ); + + var mathAbs = Math.abs; + + function abs() { + var data = this._data; + + this._milliseconds = mathAbs(this._milliseconds); + this._days = mathAbs(this._days); + this._months = mathAbs(this._months); + + data.milliseconds = mathAbs(data.milliseconds); + data.seconds = mathAbs(data.seconds); + data.minutes = mathAbs(data.minutes); + data.hours = mathAbs(data.hours); + data.months = mathAbs(data.months); + data.years = mathAbs(data.years); + + return this; + } + + function addSubtract$1(duration, input, value, direction) { + var other = createDuration(input, value); + + duration._milliseconds += direction * other._milliseconds; + duration._days += direction * other._days; + duration._months += direction * other._months; + + return duration._bubble(); + } + + // supports only 2.0-style add(1, 's') or add(duration) + function add$1(input, value) { + return addSubtract$1(this, input, value, 1); + } + + // supports only 2.0-style subtract(1, 's') or subtract(duration) + function subtract$1(input, value) { + return addSubtract$1(this, input, value, -1); + } + + function absCeil(number) { + if (number < 0) { + return Math.floor(number); + } else { + return Math.ceil(number); + } + } + + function bubble() { + var milliseconds = this._milliseconds, + days = this._days, + months = this._months, + data = this._data, + seconds, + minutes, + hours, + years, + monthsFromDays; + + // if we have a mix of positive and negative values, bubble down first + // check: https://github.com/moment/moment/issues/2166 + if ( + !( + (milliseconds >= 0 && days >= 0 && months >= 0) || + (milliseconds <= 0 && days <= 0 && months <= 0) + ) + ) { + milliseconds += absCeil(monthsToDays(months) + days) * 864e5; + days = 0; + months = 0; + } + + // The following code bubbles up values, see the tests for + // examples of what that means. + data.milliseconds = milliseconds % 1000; + + seconds = absFloor(milliseconds / 1000); + data.seconds = seconds % 60; + + minutes = absFloor(seconds / 60); + data.minutes = minutes % 60; + + hours = absFloor(minutes / 60); + data.hours = hours % 24; + + days += absFloor(hours / 24); + + // convert days to months + monthsFromDays = absFloor(daysToMonths(days)); + months += monthsFromDays; + days -= absCeil(monthsToDays(monthsFromDays)); + + // 12 months -> 1 year + years = absFloor(months / 12); + months %= 12; + + data.days = days; + data.months = months; + data.years = years; + + return this; + } + + function daysToMonths(days) { + // 400 years have 146097 days (taking into account leap year rules) + // 400 years have 12 months === 4800 + return (days * 4800) / 146097; + } + + function monthsToDays(months) { + // the reverse of daysToMonths + return (months * 146097) / 4800; + } + + function as(units) { + if (!this.isValid()) { + return NaN; + } + var days, + months, + milliseconds = this._milliseconds; + + units = normalizeUnits(units); + + if (units === 'month' || units === 'quarter' || units === 'year') { + days = this._days + milliseconds / 864e5; + months = this._months + daysToMonths(days); + switch (units) { + case 'month': + return months; + case 'quarter': + return months / 3; + case 'year': + return months / 12; + } + } else { + // handle milliseconds separately because of floating point math errors (issue #1867) + days = this._days + Math.round(monthsToDays(this._months)); + switch (units) { + case 'week': + return days / 7 + milliseconds / 6048e5; + case 'day': + return days + milliseconds / 864e5; + case 'hour': + return days * 24 + milliseconds / 36e5; + case 'minute': + return days * 1440 + milliseconds / 6e4; + case 'second': + return days * 86400 + milliseconds / 1000; + // Math.floor prevents floating point math errors here + case 'millisecond': + return Math.floor(days * 864e5) + milliseconds; + default: + throw new Error('Unknown unit ' + units); + } + } + } + + // TODO: Use this.as('ms')? + function valueOf$1() { + if (!this.isValid()) { + return NaN; + } + return ( + this._milliseconds + + this._days * 864e5 + + (this._months % 12) * 2592e6 + + toInt(this._months / 12) * 31536e6 + ); + } + + function makeAs(alias) { + return function () { + return this.as(alias); + }; + } + + var asMilliseconds = makeAs('ms'), + asSeconds = makeAs('s'), + asMinutes = makeAs('m'), + asHours = makeAs('h'), + asDays = makeAs('d'), + asWeeks = makeAs('w'), + asMonths = makeAs('M'), + asQuarters = makeAs('Q'), + asYears = makeAs('y'); + + function clone$1() { + return createDuration(this); + } + + function get$2(units) { + units = normalizeUnits(units); + return this.isValid() ? this[units + 's']() : NaN; + } + + function makeGetter(name) { + return function () { + return this.isValid() ? this._data[name] : NaN; + }; + } + + var milliseconds = makeGetter('milliseconds'), + seconds = makeGetter('seconds'), + minutes = makeGetter('minutes'), + hours = makeGetter('hours'), + days = makeGetter('days'), + months = makeGetter('months'), + years = makeGetter('years'); + + function weeks() { + return absFloor(this.days() / 7); + } + + var round = Math.round, + thresholds = { + ss: 44, // a few seconds to seconds + s: 45, // seconds to minute + m: 45, // minutes to hour + h: 22, // hours to day + d: 26, // days to month/week + w: null, // weeks to month + M: 11, // months to year + }; + + // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize + function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) { + return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture); + } + + function relativeTime$1(posNegDuration, withoutSuffix, thresholds, locale) { + var duration = createDuration(posNegDuration).abs(), + seconds = round(duration.as('s')), + minutes = round(duration.as('m')), + hours = round(duration.as('h')), + days = round(duration.as('d')), + months = round(duration.as('M')), + weeks = round(duration.as('w')), + years = round(duration.as('y')), + a = + (seconds <= thresholds.ss && ['s', seconds]) || + (seconds < thresholds.s && ['ss', seconds]) || + (minutes <= 1 && ['m']) || + (minutes < thresholds.m && ['mm', minutes]) || + (hours <= 1 && ['h']) || + (hours < thresholds.h && ['hh', hours]) || + (days <= 1 && ['d']) || + (days < thresholds.d && ['dd', days]); + + if (thresholds.w != null) { + a = + a || + (weeks <= 1 && ['w']) || + (weeks < thresholds.w && ['ww', weeks]); + } + a = a || + (months <= 1 && ['M']) || + (months < thresholds.M && ['MM', months]) || + (years <= 1 && ['y']) || ['yy', years]; + + a[2] = withoutSuffix; + a[3] = +posNegDuration > 0; + a[4] = locale; + return substituteTimeAgo.apply(null, a); + } + + // This function allows you to set the rounding function for relative time strings + function getSetRelativeTimeRounding(roundingFunction) { + if (roundingFunction === undefined) { + return round; + } + if (typeof roundingFunction === 'function') { + round = roundingFunction; + return true; + } + return false; + } + + // This function allows you to set a threshold for relative time strings + function getSetRelativeTimeThreshold(threshold, limit) { + if (thresholds[threshold] === undefined) { + return false; + } + if (limit === undefined) { + return thresholds[threshold]; + } + thresholds[threshold] = limit; + if (threshold === 's') { + thresholds.ss = limit - 1; + } + return true; + } + + function humanize(argWithSuffix, argThresholds) { + if (!this.isValid()) { + return this.localeData().invalidDate(); + } + + var withSuffix = false, + th = thresholds, + locale, + output; + + if (typeof argWithSuffix === 'object') { + argThresholds = argWithSuffix; + argWithSuffix = false; + } + if (typeof argWithSuffix === 'boolean') { + withSuffix = argWithSuffix; + } + if (typeof argThresholds === 'object') { + th = Object.assign({}, thresholds, argThresholds); + if (argThresholds.s != null && argThresholds.ss == null) { + th.ss = argThresholds.s - 1; + } + } + + locale = this.localeData(); + output = relativeTime$1(this, !withSuffix, th, locale); + + if (withSuffix) { + output = locale.pastFuture(+this, output); + } + + return locale.postformat(output); + } + + var abs$1 = Math.abs; + + function sign(x) { + return (x > 0) - (x < 0) || +x; + } + + function toISOString$1() { + // for ISO strings we do not use the normal bubbling rules: + // * milliseconds bubble up until they become hours + // * days do not bubble at all + // * months bubble up until they become years + // This is because there is no context-free conversion between hours and days + // (think of clock changes) + // and also not between days and months (28-31 days per month) + if (!this.isValid()) { + return this.localeData().invalidDate(); + } + + var seconds = abs$1(this._milliseconds) / 1000, + days = abs$1(this._days), + months = abs$1(this._months), + minutes, + hours, + years, + s, + total = this.asSeconds(), + totalSign, + ymSign, + daysSign, + hmsSign; + + if (!total) { + // this is the same as C#'s (Noda) and python (isodate)... + // but not other JS (goog.date) + return 'P0D'; + } + + // 3600 seconds -> 60 minutes -> 1 hour + minutes = absFloor(seconds / 60); + hours = absFloor(minutes / 60); + seconds %= 60; + minutes %= 60; + + // 12 months -> 1 year + years = absFloor(months / 12); + months %= 12; + + // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js + s = seconds ? seconds.toFixed(3).replace(/\.?0+$/, '') : ''; + + totalSign = total < 0 ? '-' : ''; + ymSign = sign(this._months) !== sign(total) ? '-' : ''; + daysSign = sign(this._days) !== sign(total) ? '-' : ''; + hmsSign = sign(this._milliseconds) !== sign(total) ? '-' : ''; + + return ( + totalSign + + 'P' + + (years ? ymSign + years + 'Y' : '') + + (months ? ymSign + months + 'M' : '') + + (days ? daysSign + days + 'D' : '') + + (hours || minutes || seconds ? 'T' : '') + + (hours ? hmsSign + hours + 'H' : '') + + (minutes ? hmsSign + minutes + 'M' : '') + + (seconds ? hmsSign + s + 'S' : '') + ); + } + + var proto$2 = Duration.prototype; + + proto$2.isValid = isValid$1; + proto$2.abs = abs; + proto$2.add = add$1; + proto$2.subtract = subtract$1; + proto$2.as = as; + proto$2.asMilliseconds = asMilliseconds; + proto$2.asSeconds = asSeconds; + proto$2.asMinutes = asMinutes; + proto$2.asHours = asHours; + proto$2.asDays = asDays; + proto$2.asWeeks = asWeeks; + proto$2.asMonths = asMonths; + proto$2.asQuarters = asQuarters; + proto$2.asYears = asYears; + proto$2.valueOf = valueOf$1; + proto$2._bubble = bubble; + proto$2.clone = clone$1; + proto$2.get = get$2; + proto$2.milliseconds = milliseconds; + proto$2.seconds = seconds; + proto$2.minutes = minutes; + proto$2.hours = hours; + proto$2.days = days; + proto$2.weeks = weeks; + proto$2.months = months; + proto$2.years = years; + proto$2.humanize = humanize; + proto$2.toISOString = toISOString$1; + proto$2.toString = toISOString$1; + proto$2.toJSON = toISOString$1; + proto$2.locale = locale; + proto$2.localeData = localeData; + + proto$2.toIsoString = deprecate( + 'toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)', + toISOString$1 + ); + proto$2.lang = lang; + + // FORMATTING + + addFormatToken('X', 0, 0, 'unix'); + addFormatToken('x', 0, 0, 'valueOf'); + + // PARSING + + addRegexToken('x', matchSigned); + addRegexToken('X', matchTimestamp); + addParseToken('X', function (input, array, config) { + config._d = new Date(parseFloat(input) * 1000); + }); + addParseToken('x', function (input, array, config) { + config._d = new Date(toInt(input)); + }); + + //! moment.js + + hooks.version = '2.29.1'; + + setHookCallback(createLocal); + + hooks.fn = proto; + hooks.min = min; + hooks.max = max; + hooks.now = now; + hooks.utc = createUTC; + hooks.unix = createUnix; + hooks.months = listMonths; + hooks.isDate = isDate; + hooks.locale = getSetGlobalLocale; + hooks.invalid = createInvalid; + hooks.duration = createDuration; + hooks.isMoment = isMoment; + hooks.weekdays = listWeekdays; + hooks.parseZone = createInZone; + hooks.localeData = getLocale; + hooks.isDuration = isDuration; + hooks.monthsShort = listMonthsShort; + hooks.weekdaysMin = listWeekdaysMin; + hooks.defineLocale = defineLocale; + hooks.updateLocale = updateLocale; + hooks.locales = listLocales; + hooks.weekdaysShort = listWeekdaysShort; + hooks.normalizeUnits = normalizeUnits; + hooks.relativeTimeRounding = getSetRelativeTimeRounding; + hooks.relativeTimeThreshold = getSetRelativeTimeThreshold; + hooks.calendarFormat = getCalendarFormat; + hooks.prototype = proto; + + // currently HTML5 input type only supports 24-hour formats + hooks.HTML5_FMT = { + DATETIME_LOCAL: 'YYYY-MM-DDTHH:mm', // + DATETIME_LOCAL_SECONDS: 'YYYY-MM-DDTHH:mm:ss', // + DATETIME_LOCAL_MS: 'YYYY-MM-DDTHH:mm:ss.SSS', // + DATE: 'YYYY-MM-DD', // + TIME: 'HH:mm', // + TIME_SECONDS: 'HH:mm:ss', // + TIME_MS: 'HH:mm:ss.SSS', // + WEEK: 'GGGG-[W]WW', // + MONTH: 'YYYY-MM', // + }; + + return hooks; + +}))); From c55b9c241facb38343082d7c9097b6d5bba58875 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sun, 21 Nov 2021 21:09:31 -0700 Subject: [PATCH 074/255] Remove moment.js in favor of Temporal --- package-lock.json | 14 - package.json | 1 - public/scripts/moment.js | 5670 -------------------------------------- 3 files changed, 5685 deletions(-) delete mode 100644 public/scripts/moment.js diff --git a/package-lock.json b/package-lock.json index 821a54c..225db7b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,6 @@ "dotenv": "^10.0.0", "express": "~4.16.0", "http-errors": "~1.6.2", - "moment": "^2.29.1", "morgan": "~1.9.0", "nodemailer": "^6.6.5", "passport": "^0.5.0", @@ -910,14 +909,6 @@ "ms": "2.0.0" } }, - "node_modules/moment": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", - "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==", - "engines": { - "node": "*" - } - }, "node_modules/morgan": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.0.tgz", @@ -2409,11 +2400,6 @@ } } }, - "moment": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", - "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" - }, "morgan": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.0.tgz", diff --git a/package.json b/package.json index 2e3323a..c94c979 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,6 @@ "dotenv": "^10.0.0", "express": "~4.16.0", "http-errors": "~1.6.2", - "moment": "^2.29.1", "morgan": "~1.9.0", "nodemailer": "^6.6.5", "passport": "^0.5.0", diff --git a/public/scripts/moment.js b/public/scripts/moment.js deleted file mode 100644 index 1484d6c..0000000 --- a/public/scripts/moment.js +++ /dev/null @@ -1,5670 +0,0 @@ -//! moment.js -//! version : 2.29.1 -//! authors : Tim Wood, Iskren Chernev, Moment.js contributors -//! license : MIT -//! momentjs.com - -;(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : - typeof define === 'function' && define.amd ? define(factory) : - global.moment = factory() -}(this, (function () { 'use strict'; - - var hookCallback; - - function hooks() { - return hookCallback.apply(null, arguments); - } - - // This is done to register the method called with moment() - // without creating circular dependencies. - function setHookCallback(callback) { - hookCallback = callback; - } - - function isArray(input) { - return ( - input instanceof Array || - Object.prototype.toString.call(input) === '[object Array]' - ); - } - - function isObject(input) { - // IE8 will treat undefined and null as object if it wasn't for - // input != null - return ( - input != null && - Object.prototype.toString.call(input) === '[object Object]' - ); - } - - function hasOwnProp(a, b) { - return Object.prototype.hasOwnProperty.call(a, b); - } - - function isObjectEmpty(obj) { - if (Object.getOwnPropertyNames) { - return Object.getOwnPropertyNames(obj).length === 0; - } else { - var k; - for (k in obj) { - if (hasOwnProp(obj, k)) { - return false; - } - } - return true; - } - } - - function isUndefined(input) { - return input === void 0; - } - - function isNumber(input) { - return ( - typeof input === 'number' || - Object.prototype.toString.call(input) === '[object Number]' - ); - } - - function isDate(input) { - return ( - input instanceof Date || - Object.prototype.toString.call(input) === '[object Date]' - ); - } - - function map(arr, fn) { - var res = [], - i; - for (i = 0; i < arr.length; ++i) { - res.push(fn(arr[i], i)); - } - return res; - } - - function extend(a, b) { - for (var i in b) { - if (hasOwnProp(b, i)) { - a[i] = b[i]; - } - } - - if (hasOwnProp(b, 'toString')) { - a.toString = b.toString; - } - - if (hasOwnProp(b, 'valueOf')) { - a.valueOf = b.valueOf; - } - - return a; - } - - function createUTC(input, format, locale, strict) { - return createLocalOrUTC(input, format, locale, strict, true).utc(); - } - - function defaultParsingFlags() { - // We need to deep clone this object. - return { - empty: false, - unusedTokens: [], - unusedInput: [], - overflow: -2, - charsLeftOver: 0, - nullInput: false, - invalidEra: null, - invalidMonth: null, - invalidFormat: false, - userInvalidated: false, - iso: false, - parsedDateParts: [], - era: null, - meridiem: null, - rfc2822: false, - weekdayMismatch: false, - }; - } - - function getParsingFlags(m) { - if (m._pf == null) { - m._pf = defaultParsingFlags(); - } - return m._pf; - } - - var some; - if (Array.prototype.some) { - some = Array.prototype.some; - } else { - some = function (fun) { - var t = Object(this), - len = t.length >>> 0, - i; - - for (i = 0; i < len; i++) { - if (i in t && fun.call(this, t[i], i, t)) { - return true; - } - } - - return false; - }; - } - - function isValid(m) { - if (m._isValid == null) { - var flags = getParsingFlags(m), - parsedParts = some.call(flags.parsedDateParts, function (i) { - return i != null; - }), - isNowValid = - !isNaN(m._d.getTime()) && - flags.overflow < 0 && - !flags.empty && - !flags.invalidEra && - !flags.invalidMonth && - !flags.invalidWeekday && - !flags.weekdayMismatch && - !flags.nullInput && - !flags.invalidFormat && - !flags.userInvalidated && - (!flags.meridiem || (flags.meridiem && parsedParts)); - - if (m._strict) { - isNowValid = - isNowValid && - flags.charsLeftOver === 0 && - flags.unusedTokens.length === 0 && - flags.bigHour === undefined; - } - - if (Object.isFrozen == null || !Object.isFrozen(m)) { - m._isValid = isNowValid; - } else { - return isNowValid; - } - } - return m._isValid; - } - - function createInvalid(flags) { - var m = createUTC(NaN); - if (flags != null) { - extend(getParsingFlags(m), flags); - } else { - getParsingFlags(m).userInvalidated = true; - } - - return m; - } - - // Plugins that add properties should also add the key here (null value), - // so we can properly clone ourselves. - var momentProperties = (hooks.momentProperties = []), - updateInProgress = false; - - function copyConfig(to, from) { - var i, prop, val; - - if (!isUndefined(from._isAMomentObject)) { - to._isAMomentObject = from._isAMomentObject; - } - if (!isUndefined(from._i)) { - to._i = from._i; - } - if (!isUndefined(from._f)) { - to._f = from._f; - } - if (!isUndefined(from._l)) { - to._l = from._l; - } - if (!isUndefined(from._strict)) { - to._strict = from._strict; - } - if (!isUndefined(from._tzm)) { - to._tzm = from._tzm; - } - if (!isUndefined(from._isUTC)) { - to._isUTC = from._isUTC; - } - if (!isUndefined(from._offset)) { - to._offset = from._offset; - } - if (!isUndefined(from._pf)) { - to._pf = getParsingFlags(from); - } - if (!isUndefined(from._locale)) { - to._locale = from._locale; - } - - if (momentProperties.length > 0) { - for (i = 0; i < momentProperties.length; i++) { - prop = momentProperties[i]; - val = from[prop]; - if (!isUndefined(val)) { - to[prop] = val; - } - } - } - - return to; - } - - // Moment prototype object - function Moment(config) { - copyConfig(this, config); - this._d = new Date(config._d != null ? config._d.getTime() : NaN); - if (!this.isValid()) { - this._d = new Date(NaN); - } - // Prevent infinite loop in case updateOffset creates new moment - // objects. - if (updateInProgress === false) { - updateInProgress = true; - hooks.updateOffset(this); - updateInProgress = false; - } - } - - function isMoment(obj) { - return ( - obj instanceof Moment || (obj != null && obj._isAMomentObject != null) - ); - } - - function warn(msg) { - if ( - hooks.suppressDeprecationWarnings === false && - typeof console !== 'undefined' && - console.warn - ) { - console.warn('Deprecation warning: ' + msg); - } - } - - function deprecate(msg, fn) { - var firstTime = true; - - return extend(function () { - if (hooks.deprecationHandler != null) { - hooks.deprecationHandler(null, msg); - } - if (firstTime) { - var args = [], - arg, - i, - key; - for (i = 0; i < arguments.length; i++) { - arg = ''; - if (typeof arguments[i] === 'object') { - arg += '\n[' + i + '] '; - for (key in arguments[0]) { - if (hasOwnProp(arguments[0], key)) { - arg += key + ': ' + arguments[0][key] + ', '; - } - } - arg = arg.slice(0, -2); // Remove trailing comma and space - } else { - arg = arguments[i]; - } - args.push(arg); - } - warn( - msg + - '\nArguments: ' + - Array.prototype.slice.call(args).join('') + - '\n' + - new Error().stack - ); - firstTime = false; - } - return fn.apply(this, arguments); - }, fn); - } - - var deprecations = {}; - - function deprecateSimple(name, msg) { - if (hooks.deprecationHandler != null) { - hooks.deprecationHandler(name, msg); - } - if (!deprecations[name]) { - warn(msg); - deprecations[name] = true; - } - } - - hooks.suppressDeprecationWarnings = false; - hooks.deprecationHandler = null; - - function isFunction(input) { - return ( - (typeof Function !== 'undefined' && input instanceof Function) || - Object.prototype.toString.call(input) === '[object Function]' - ); - } - - function set(config) { - var prop, i; - for (i in config) { - if (hasOwnProp(config, i)) { - prop = config[i]; - if (isFunction(prop)) { - this[i] = prop; - } else { - this['_' + i] = prop; - } - } - } - this._config = config; - // Lenient ordinal parsing accepts just a number in addition to - // number + (possibly) stuff coming from _dayOfMonthOrdinalParse. - // TODO: Remove "ordinalParse" fallback in next major release. - this._dayOfMonthOrdinalParseLenient = new RegExp( - (this._dayOfMonthOrdinalParse.source || this._ordinalParse.source) + - '|' + - /\d{1,2}/.source - ); - } - - function mergeConfigs(parentConfig, childConfig) { - var res = extend({}, parentConfig), - prop; - for (prop in childConfig) { - if (hasOwnProp(childConfig, prop)) { - if (isObject(parentConfig[prop]) && isObject(childConfig[prop])) { - res[prop] = {}; - extend(res[prop], parentConfig[prop]); - extend(res[prop], childConfig[prop]); - } else if (childConfig[prop] != null) { - res[prop] = childConfig[prop]; - } else { - delete res[prop]; - } - } - } - for (prop in parentConfig) { - if ( - hasOwnProp(parentConfig, prop) && - !hasOwnProp(childConfig, prop) && - isObject(parentConfig[prop]) - ) { - // make sure changes to properties don't modify parent config - res[prop] = extend({}, res[prop]); - } - } - return res; - } - - function Locale(config) { - if (config != null) { - this.set(config); - } - } - - var keys; - - if (Object.keys) { - keys = Object.keys; - } else { - keys = function (obj) { - var i, - res = []; - for (i in obj) { - if (hasOwnProp(obj, i)) { - res.push(i); - } - } - return res; - }; - } - - var defaultCalendar = { - sameDay: '[Today at] LT', - nextDay: '[Tomorrow at] LT', - nextWeek: 'dddd [at] LT', - lastDay: '[Yesterday at] LT', - lastWeek: '[Last] dddd [at] LT', - sameElse: 'L', - }; - - function calendar(key, mom, now) { - var output = this._calendar[key] || this._calendar['sameElse']; - return isFunction(output) ? output.call(mom, now) : output; - } - - function zeroFill(number, targetLength, forceSign) { - var absNumber = '' + Math.abs(number), - zerosToFill = targetLength - absNumber.length, - sign = number >= 0; - return ( - (sign ? (forceSign ? '+' : '') : '-') + - Math.pow(10, Math.max(0, zerosToFill)).toString().substr(1) + - absNumber - ); - } - - var formattingTokens = /(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|N{1,5}|YYYYYY|YYYYY|YYYY|YY|y{2,4}|yo?|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g, - localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g, - formatFunctions = {}, - formatTokenFunctions = {}; - - // token: 'M' - // padded: ['MM', 2] - // ordinal: 'Mo' - // callback: function () { this.month() + 1 } - function addFormatToken(token, padded, ordinal, callback) { - var func = callback; - if (typeof callback === 'string') { - func = function () { - return this[callback](); - }; - } - if (token) { - formatTokenFunctions[token] = func; - } - if (padded) { - formatTokenFunctions[padded[0]] = function () { - return zeroFill(func.apply(this, arguments), padded[1], padded[2]); - }; - } - if (ordinal) { - formatTokenFunctions[ordinal] = function () { - return this.localeData().ordinal( - func.apply(this, arguments), - token - ); - }; - } - } - - function removeFormattingTokens(input) { - if (input.match(/\[[\s\S]/)) { - return input.replace(/^\[|\]$/g, ''); - } - return input.replace(/\\/g, ''); - } - - function makeFormatFunction(format) { - var array = format.match(formattingTokens), - i, - length; - - for (i = 0, length = array.length; i < length; i++) { - if (formatTokenFunctions[array[i]]) { - array[i] = formatTokenFunctions[array[i]]; - } else { - array[i] = removeFormattingTokens(array[i]); - } - } - - return function (mom) { - var output = '', - i; - for (i = 0; i < length; i++) { - output += isFunction(array[i]) - ? array[i].call(mom, format) - : array[i]; - } - return output; - }; - } - - // format date using native date object - function formatMoment(m, format) { - if (!m.isValid()) { - return m.localeData().invalidDate(); - } - - format = expandFormat(format, m.localeData()); - formatFunctions[format] = - formatFunctions[format] || makeFormatFunction(format); - - return formatFunctions[format](m); - } - - function expandFormat(format, locale) { - var i = 5; - - function replaceLongDateFormatTokens(input) { - return locale.longDateFormat(input) || input; - } - - localFormattingTokens.lastIndex = 0; - while (i >= 0 && localFormattingTokens.test(format)) { - format = format.replace( - localFormattingTokens, - replaceLongDateFormatTokens - ); - localFormattingTokens.lastIndex = 0; - i -= 1; - } - - return format; - } - - var defaultLongDateFormat = { - LTS: 'h:mm:ss A', - LT: 'h:mm A', - L: 'MM/DD/YYYY', - LL: 'MMMM D, YYYY', - LLL: 'MMMM D, YYYY h:mm A', - LLLL: 'dddd, MMMM D, YYYY h:mm A', - }; - - function longDateFormat(key) { - var format = this._longDateFormat[key], - formatUpper = this._longDateFormat[key.toUpperCase()]; - - if (format || !formatUpper) { - return format; - } - - this._longDateFormat[key] = formatUpper - .match(formattingTokens) - .map(function (tok) { - if ( - tok === 'MMMM' || - tok === 'MM' || - tok === 'DD' || - tok === 'dddd' - ) { - return tok.slice(1); - } - return tok; - }) - .join(''); - - return this._longDateFormat[key]; - } - - var defaultInvalidDate = 'Invalid date'; - - function invalidDate() { - return this._invalidDate; - } - - var defaultOrdinal = '%d', - defaultDayOfMonthOrdinalParse = /\d{1,2}/; - - function ordinal(number) { - return this._ordinal.replace('%d', number); - } - - var defaultRelativeTime = { - future: 'in %s', - past: '%s ago', - s: 'a few seconds', - ss: '%d seconds', - m: 'a minute', - mm: '%d minutes', - h: 'an hour', - hh: '%d hours', - d: 'a day', - dd: '%d days', - w: 'a week', - ww: '%d weeks', - M: 'a month', - MM: '%d months', - y: 'a year', - yy: '%d years', - }; - - function relativeTime(number, withoutSuffix, string, isFuture) { - var output = this._relativeTime[string]; - return isFunction(output) - ? output(number, withoutSuffix, string, isFuture) - : output.replace(/%d/i, number); - } - - function pastFuture(diff, output) { - var format = this._relativeTime[diff > 0 ? 'future' : 'past']; - return isFunction(format) ? format(output) : format.replace(/%s/i, output); - } - - var aliases = {}; - - function addUnitAlias(unit, shorthand) { - var lowerCase = unit.toLowerCase(); - aliases[lowerCase] = aliases[lowerCase + 's'] = aliases[shorthand] = unit; - } - - function normalizeUnits(units) { - return typeof units === 'string' - ? aliases[units] || aliases[units.toLowerCase()] - : undefined; - } - - function normalizeObjectUnits(inputObject) { - var normalizedInput = {}, - normalizedProp, - prop; - - for (prop in inputObject) { - if (hasOwnProp(inputObject, prop)) { - normalizedProp = normalizeUnits(prop); - if (normalizedProp) { - normalizedInput[normalizedProp] = inputObject[prop]; - } - } - } - - return normalizedInput; - } - - var priorities = {}; - - function addUnitPriority(unit, priority) { - priorities[unit] = priority; - } - - function getPrioritizedUnits(unitsObj) { - var units = [], - u; - for (u in unitsObj) { - if (hasOwnProp(unitsObj, u)) { - units.push({ unit: u, priority: priorities[u] }); - } - } - units.sort(function (a, b) { - return a.priority - b.priority; - }); - return units; - } - - function isLeapYear(year) { - return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; - } - - function absFloor(number) { - if (number < 0) { - // -0 -> 0 - return Math.ceil(number) || 0; - } else { - return Math.floor(number); - } - } - - function toInt(argumentForCoercion) { - var coercedNumber = +argumentForCoercion, - value = 0; - - if (coercedNumber !== 0 && isFinite(coercedNumber)) { - value = absFloor(coercedNumber); - } - - return value; - } - - function makeGetSet(unit, keepTime) { - return function (value) { - if (value != null) { - set$1(this, unit, value); - hooks.updateOffset(this, keepTime); - return this; - } else { - return get(this, unit); - } - }; - } - - function get(mom, unit) { - return mom.isValid() - ? mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit]() - : NaN; - } - - function set$1(mom, unit, value) { - if (mom.isValid() && !isNaN(value)) { - if ( - unit === 'FullYear' && - isLeapYear(mom.year()) && - mom.month() === 1 && - mom.date() === 29 - ) { - value = toInt(value); - mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit]( - value, - mom.month(), - daysInMonth(value, mom.month()) - ); - } else { - mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value); - } - } - } - - // MOMENTS - - function stringGet(units) { - units = normalizeUnits(units); - if (isFunction(this[units])) { - return this[units](); - } - return this; - } - - function stringSet(units, value) { - if (typeof units === 'object') { - units = normalizeObjectUnits(units); - var prioritized = getPrioritizedUnits(units), - i; - for (i = 0; i < prioritized.length; i++) { - this[prioritized[i].unit](units[prioritized[i].unit]); - } - } else { - units = normalizeUnits(units); - if (isFunction(this[units])) { - return this[units](value); - } - } - return this; - } - - var match1 = /\d/, // 0 - 9 - match2 = /\d\d/, // 00 - 99 - match3 = /\d{3}/, // 000 - 999 - match4 = /\d{4}/, // 0000 - 9999 - match6 = /[+-]?\d{6}/, // -999999 - 999999 - match1to2 = /\d\d?/, // 0 - 99 - match3to4 = /\d\d\d\d?/, // 999 - 9999 - match5to6 = /\d\d\d\d\d\d?/, // 99999 - 999999 - match1to3 = /\d{1,3}/, // 0 - 999 - match1to4 = /\d{1,4}/, // 0 - 9999 - match1to6 = /[+-]?\d{1,6}/, // -999999 - 999999 - matchUnsigned = /\d+/, // 0 - inf - matchSigned = /[+-]?\d+/, // -inf - inf - matchOffset = /Z|[+-]\d\d:?\d\d/gi, // +00:00 -00:00 +0000 -0000 or Z - matchShortOffset = /Z|[+-]\d\d(?::?\d\d)?/gi, // +00 -00 +00:00 -00:00 +0000 -0000 or Z - matchTimestamp = /[+-]?\d+(\.\d{1,3})?/, // 123456789 123456789.123 - // any word (or two) characters or numbers including two/three word month in arabic. - // includes scottish gaelic two word and hyphenated months - matchWord = /[0-9]{0,256}['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFF07\uFF10-\uFFEF]{1,256}|[\u0600-\u06FF\/]{1,256}(\s*?[\u0600-\u06FF]{1,256}){1,2}/i, - regexes; - - regexes = {}; - - function addRegexToken(token, regex, strictRegex) { - regexes[token] = isFunction(regex) - ? regex - : function (isStrict, localeData) { - return isStrict && strictRegex ? strictRegex : regex; - }; - } - - function getParseRegexForToken(token, config) { - if (!hasOwnProp(regexes, token)) { - return new RegExp(unescapeFormat(token)); - } - - return regexes[token](config._strict, config._locale); - } - - // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript - function unescapeFormat(s) { - return regexEscape( - s - .replace('\\', '') - .replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function ( - matched, - p1, - p2, - p3, - p4 - ) { - return p1 || p2 || p3 || p4; - }) - ); - } - - function regexEscape(s) { - return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); - } - - var tokens = {}; - - function addParseToken(token, callback) { - var i, - func = callback; - if (typeof token === 'string') { - token = [token]; - } - if (isNumber(callback)) { - func = function (input, array) { - array[callback] = toInt(input); - }; - } - for (i = 0; i < token.length; i++) { - tokens[token[i]] = func; - } - } - - function addWeekParseToken(token, callback) { - addParseToken(token, function (input, array, config, token) { - config._w = config._w || {}; - callback(input, config._w, config, token); - }); - } - - function addTimeToArrayFromToken(token, input, config) { - if (input != null && hasOwnProp(tokens, token)) { - tokens[token](input, config._a, config, token); - } - } - - var YEAR = 0, - MONTH = 1, - DATE = 2, - HOUR = 3, - MINUTE = 4, - SECOND = 5, - MILLISECOND = 6, - WEEK = 7, - WEEKDAY = 8; - - function mod(n, x) { - return ((n % x) + x) % x; - } - - var indexOf; - - if (Array.prototype.indexOf) { - indexOf = Array.prototype.indexOf; - } else { - indexOf = function (o) { - // I know - var i; - for (i = 0; i < this.length; ++i) { - if (this[i] === o) { - return i; - } - } - return -1; - }; - } - - function daysInMonth(year, month) { - if (isNaN(year) || isNaN(month)) { - return NaN; - } - var modMonth = mod(month, 12); - year += (month - modMonth) / 12; - return modMonth === 1 - ? isLeapYear(year) - ? 29 - : 28 - : 31 - ((modMonth % 7) % 2); - } - - // FORMATTING - - addFormatToken('M', ['MM', 2], 'Mo', function () { - return this.month() + 1; - }); - - addFormatToken('MMM', 0, 0, function (format) { - return this.localeData().monthsShort(this, format); - }); - - addFormatToken('MMMM', 0, 0, function (format) { - return this.localeData().months(this, format); - }); - - // ALIASES - - addUnitAlias('month', 'M'); - - // PRIORITY - - addUnitPriority('month', 8); - - // PARSING - - addRegexToken('M', match1to2); - addRegexToken('MM', match1to2, match2); - addRegexToken('MMM', function (isStrict, locale) { - return locale.monthsShortRegex(isStrict); - }); - addRegexToken('MMMM', function (isStrict, locale) { - return locale.monthsRegex(isStrict); - }); - - addParseToken(['M', 'MM'], function (input, array) { - array[MONTH] = toInt(input) - 1; - }); - - addParseToken(['MMM', 'MMMM'], function (input, array, config, token) { - var month = config._locale.monthsParse(input, token, config._strict); - // if we didn't find a month name, mark the date as invalid. - if (month != null) { - array[MONTH] = month; - } else { - getParsingFlags(config).invalidMonth = input; - } - }); - - // LOCALES - - var defaultLocaleMonths = 'January_February_March_April_May_June_July_August_September_October_November_December'.split( - '_' - ), - defaultLocaleMonthsShort = 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split( - '_' - ), - MONTHS_IN_FORMAT = /D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/, - defaultMonthsShortRegex = matchWord, - defaultMonthsRegex = matchWord; - - function localeMonths(m, format) { - if (!m) { - return isArray(this._months) - ? this._months - : this._months['standalone']; - } - return isArray(this._months) - ? this._months[m.month()] - : this._months[ - (this._months.isFormat || MONTHS_IN_FORMAT).test(format) - ? 'format' - : 'standalone' - ][m.month()]; - } - - function localeMonthsShort(m, format) { - if (!m) { - return isArray(this._monthsShort) - ? this._monthsShort - : this._monthsShort['standalone']; - } - return isArray(this._monthsShort) - ? this._monthsShort[m.month()] - : this._monthsShort[ - MONTHS_IN_FORMAT.test(format) ? 'format' : 'standalone' - ][m.month()]; - } - - function handleStrictParse(monthName, format, strict) { - var i, - ii, - mom, - llc = monthName.toLocaleLowerCase(); - if (!this._monthsParse) { - // this is not used - this._monthsParse = []; - this._longMonthsParse = []; - this._shortMonthsParse = []; - for (i = 0; i < 12; ++i) { - mom = createUTC([2000, i]); - this._shortMonthsParse[i] = this.monthsShort( - mom, - '' - ).toLocaleLowerCase(); - this._longMonthsParse[i] = this.months(mom, '').toLocaleLowerCase(); - } - } - - if (strict) { - if (format === 'MMM') { - ii = indexOf.call(this._shortMonthsParse, llc); - return ii !== -1 ? ii : null; - } else { - ii = indexOf.call(this._longMonthsParse, llc); - return ii !== -1 ? ii : null; - } - } else { - if (format === 'MMM') { - ii = indexOf.call(this._shortMonthsParse, llc); - if (ii !== -1) { - return ii; - } - ii = indexOf.call(this._longMonthsParse, llc); - return ii !== -1 ? ii : null; - } else { - ii = indexOf.call(this._longMonthsParse, llc); - if (ii !== -1) { - return ii; - } - ii = indexOf.call(this._shortMonthsParse, llc); - return ii !== -1 ? ii : null; - } - } - } - - function localeMonthsParse(monthName, format, strict) { - var i, mom, regex; - - if (this._monthsParseExact) { - return handleStrictParse.call(this, monthName, format, strict); - } - - if (!this._monthsParse) { - this._monthsParse = []; - this._longMonthsParse = []; - this._shortMonthsParse = []; - } - - // TODO: add sorting - // Sorting makes sure if one month (or abbr) is a prefix of another - // see sorting in computeMonthsParse - for (i = 0; i < 12; i++) { - // make the regex if we don't have it already - mom = createUTC([2000, i]); - if (strict && !this._longMonthsParse[i]) { - this._longMonthsParse[i] = new RegExp( - '^' + this.months(mom, '').replace('.', '') + '$', - 'i' - ); - this._shortMonthsParse[i] = new RegExp( - '^' + this.monthsShort(mom, '').replace('.', '') + '$', - 'i' - ); - } - if (!strict && !this._monthsParse[i]) { - regex = - '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, ''); - this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i'); - } - // test the regex - if ( - strict && - format === 'MMMM' && - this._longMonthsParse[i].test(monthName) - ) { - return i; - } else if ( - strict && - format === 'MMM' && - this._shortMonthsParse[i].test(monthName) - ) { - return i; - } else if (!strict && this._monthsParse[i].test(monthName)) { - return i; - } - } - } - - // MOMENTS - - function setMonth(mom, value) { - var dayOfMonth; - - if (!mom.isValid()) { - // No op - return mom; - } - - if (typeof value === 'string') { - if (/^\d+$/.test(value)) { - value = toInt(value); - } else { - value = mom.localeData().monthsParse(value); - // TODO: Another silent failure? - if (!isNumber(value)) { - return mom; - } - } - } - - dayOfMonth = Math.min(mom.date(), daysInMonth(mom.year(), value)); - mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth); - return mom; - } - - function getSetMonth(value) { - if (value != null) { - setMonth(this, value); - hooks.updateOffset(this, true); - return this; - } else { - return get(this, 'Month'); - } - } - - function getDaysInMonth() { - return daysInMonth(this.year(), this.month()); - } - - function monthsShortRegex(isStrict) { - if (this._monthsParseExact) { - if (!hasOwnProp(this, '_monthsRegex')) { - computeMonthsParse.call(this); - } - if (isStrict) { - return this._monthsShortStrictRegex; - } else { - return this._monthsShortRegex; - } - } else { - if (!hasOwnProp(this, '_monthsShortRegex')) { - this._monthsShortRegex = defaultMonthsShortRegex; - } - return this._monthsShortStrictRegex && isStrict - ? this._monthsShortStrictRegex - : this._monthsShortRegex; - } - } - - function monthsRegex(isStrict) { - if (this._monthsParseExact) { - if (!hasOwnProp(this, '_monthsRegex')) { - computeMonthsParse.call(this); - } - if (isStrict) { - return this._monthsStrictRegex; - } else { - return this._monthsRegex; - } - } else { - if (!hasOwnProp(this, '_monthsRegex')) { - this._monthsRegex = defaultMonthsRegex; - } - return this._monthsStrictRegex && isStrict - ? this._monthsStrictRegex - : this._monthsRegex; - } - } - - function computeMonthsParse() { - function cmpLenRev(a, b) { - return b.length - a.length; - } - - var shortPieces = [], - longPieces = [], - mixedPieces = [], - i, - mom; - for (i = 0; i < 12; i++) { - // make the regex if we don't have it already - mom = createUTC([2000, i]); - shortPieces.push(this.monthsShort(mom, '')); - longPieces.push(this.months(mom, '')); - mixedPieces.push(this.months(mom, '')); - mixedPieces.push(this.monthsShort(mom, '')); - } - // Sorting makes sure if one month (or abbr) is a prefix of another it - // will match the longer piece. - shortPieces.sort(cmpLenRev); - longPieces.sort(cmpLenRev); - mixedPieces.sort(cmpLenRev); - for (i = 0; i < 12; i++) { - shortPieces[i] = regexEscape(shortPieces[i]); - longPieces[i] = regexEscape(longPieces[i]); - } - for (i = 0; i < 24; i++) { - mixedPieces[i] = regexEscape(mixedPieces[i]); - } - - this._monthsRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i'); - this._monthsShortRegex = this._monthsRegex; - this._monthsStrictRegex = new RegExp( - '^(' + longPieces.join('|') + ')', - 'i' - ); - this._monthsShortStrictRegex = new RegExp( - '^(' + shortPieces.join('|') + ')', - 'i' - ); - } - - // FORMATTING - - addFormatToken('Y', 0, 0, function () { - var y = this.year(); - return y <= 9999 ? zeroFill(y, 4) : '+' + y; - }); - - addFormatToken(0, ['YY', 2], 0, function () { - return this.year() % 100; - }); - - addFormatToken(0, ['YYYY', 4], 0, 'year'); - addFormatToken(0, ['YYYYY', 5], 0, 'year'); - addFormatToken(0, ['YYYYYY', 6, true], 0, 'year'); - - // ALIASES - - addUnitAlias('year', 'y'); - - // PRIORITIES - - addUnitPriority('year', 1); - - // PARSING - - addRegexToken('Y', matchSigned); - addRegexToken('YY', match1to2, match2); - addRegexToken('YYYY', match1to4, match4); - addRegexToken('YYYYY', match1to6, match6); - addRegexToken('YYYYYY', match1to6, match6); - - addParseToken(['YYYYY', 'YYYYYY'], YEAR); - addParseToken('YYYY', function (input, array) { - array[YEAR] = - input.length === 2 ? hooks.parseTwoDigitYear(input) : toInt(input); - }); - addParseToken('YY', function (input, array) { - array[YEAR] = hooks.parseTwoDigitYear(input); - }); - addParseToken('Y', function (input, array) { - array[YEAR] = parseInt(input, 10); - }); - - // HELPERS - - function daysInYear(year) { - return isLeapYear(year) ? 366 : 365; - } - - // HOOKS - - hooks.parseTwoDigitYear = function (input) { - return toInt(input) + (toInt(input) > 68 ? 1900 : 2000); - }; - - // MOMENTS - - var getSetYear = makeGetSet('FullYear', true); - - function getIsLeapYear() { - return isLeapYear(this.year()); - } - - function createDate(y, m, d, h, M, s, ms) { - // can't just apply() to create a date: - // https://stackoverflow.com/q/181348 - var date; - // the date constructor remaps years 0-99 to 1900-1999 - if (y < 100 && y >= 0) { - // preserve leap years using a full 400 year cycle, then reset - date = new Date(y + 400, m, d, h, M, s, ms); - if (isFinite(date.getFullYear())) { - date.setFullYear(y); - } - } else { - date = new Date(y, m, d, h, M, s, ms); - } - - return date; - } - - function createUTCDate(y) { - var date, args; - // the Date.UTC function remaps years 0-99 to 1900-1999 - if (y < 100 && y >= 0) { - args = Array.prototype.slice.call(arguments); - // preserve leap years using a full 400 year cycle, then reset - args[0] = y + 400; - date = new Date(Date.UTC.apply(null, args)); - if (isFinite(date.getUTCFullYear())) { - date.setUTCFullYear(y); - } - } else { - date = new Date(Date.UTC.apply(null, arguments)); - } - - return date; - } - - // start-of-first-week - start-of-year - function firstWeekOffset(year, dow, doy) { - var // first-week day -- which january is always in the first week (4 for iso, 1 for other) - fwd = 7 + dow - doy, - // first-week day local weekday -- which local weekday is fwd - fwdlw = (7 + createUTCDate(year, 0, fwd).getUTCDay() - dow) % 7; - - return -fwdlw + fwd - 1; - } - - // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday - function dayOfYearFromWeeks(year, week, weekday, dow, doy) { - var localWeekday = (7 + weekday - dow) % 7, - weekOffset = firstWeekOffset(year, dow, doy), - dayOfYear = 1 + 7 * (week - 1) + localWeekday + weekOffset, - resYear, - resDayOfYear; - - if (dayOfYear <= 0) { - resYear = year - 1; - resDayOfYear = daysInYear(resYear) + dayOfYear; - } else if (dayOfYear > daysInYear(year)) { - resYear = year + 1; - resDayOfYear = dayOfYear - daysInYear(year); - } else { - resYear = year; - resDayOfYear = dayOfYear; - } - - return { - year: resYear, - dayOfYear: resDayOfYear, - }; - } - - function weekOfYear(mom, dow, doy) { - var weekOffset = firstWeekOffset(mom.year(), dow, doy), - week = Math.floor((mom.dayOfYear() - weekOffset - 1) / 7) + 1, - resWeek, - resYear; - - if (week < 1) { - resYear = mom.year() - 1; - resWeek = week + weeksInYear(resYear, dow, doy); - } else if (week > weeksInYear(mom.year(), dow, doy)) { - resWeek = week - weeksInYear(mom.year(), dow, doy); - resYear = mom.year() + 1; - } else { - resYear = mom.year(); - resWeek = week; - } - - return { - week: resWeek, - year: resYear, - }; - } - - function weeksInYear(year, dow, doy) { - var weekOffset = firstWeekOffset(year, dow, doy), - weekOffsetNext = firstWeekOffset(year + 1, dow, doy); - return (daysInYear(year) - weekOffset + weekOffsetNext) / 7; - } - - // FORMATTING - - addFormatToken('w', ['ww', 2], 'wo', 'week'); - addFormatToken('W', ['WW', 2], 'Wo', 'isoWeek'); - - // ALIASES - - addUnitAlias('week', 'w'); - addUnitAlias('isoWeek', 'W'); - - // PRIORITIES - - addUnitPriority('week', 5); - addUnitPriority('isoWeek', 5); - - // PARSING - - addRegexToken('w', match1to2); - addRegexToken('ww', match1to2, match2); - addRegexToken('W', match1to2); - addRegexToken('WW', match1to2, match2); - - addWeekParseToken(['w', 'ww', 'W', 'WW'], function ( - input, - week, - config, - token - ) { - week[token.substr(0, 1)] = toInt(input); - }); - - // HELPERS - - // LOCALES - - function localeWeek(mom) { - return weekOfYear(mom, this._week.dow, this._week.doy).week; - } - - var defaultLocaleWeek = { - dow: 0, // Sunday is the first day of the week. - doy: 6, // The week that contains Jan 6th is the first week of the year. - }; - - function localeFirstDayOfWeek() { - return this._week.dow; - } - - function localeFirstDayOfYear() { - return this._week.doy; - } - - // MOMENTS - - function getSetWeek(input) { - var week = this.localeData().week(this); - return input == null ? week : this.add((input - week) * 7, 'd'); - } - - function getSetISOWeek(input) { - var week = weekOfYear(this, 1, 4).week; - return input == null ? week : this.add((input - week) * 7, 'd'); - } - - // FORMATTING - - addFormatToken('d', 0, 'do', 'day'); - - addFormatToken('dd', 0, 0, function (format) { - return this.localeData().weekdaysMin(this, format); - }); - - addFormatToken('ddd', 0, 0, function (format) { - return this.localeData().weekdaysShort(this, format); - }); - - addFormatToken('dddd', 0, 0, function (format) { - return this.localeData().weekdays(this, format); - }); - - addFormatToken('e', 0, 0, 'weekday'); - addFormatToken('E', 0, 0, 'isoWeekday'); - - // ALIASES - - addUnitAlias('day', 'd'); - addUnitAlias('weekday', 'e'); - addUnitAlias('isoWeekday', 'E'); - - // PRIORITY - addUnitPriority('day', 11); - addUnitPriority('weekday', 11); - addUnitPriority('isoWeekday', 11); - - // PARSING - - addRegexToken('d', match1to2); - addRegexToken('e', match1to2); - addRegexToken('E', match1to2); - addRegexToken('dd', function (isStrict, locale) { - return locale.weekdaysMinRegex(isStrict); - }); - addRegexToken('ddd', function (isStrict, locale) { - return locale.weekdaysShortRegex(isStrict); - }); - addRegexToken('dddd', function (isStrict, locale) { - return locale.weekdaysRegex(isStrict); - }); - - addWeekParseToken(['dd', 'ddd', 'dddd'], function (input, week, config, token) { - var weekday = config._locale.weekdaysParse(input, token, config._strict); - // if we didn't get a weekday name, mark the date as invalid - if (weekday != null) { - week.d = weekday; - } else { - getParsingFlags(config).invalidWeekday = input; - } - }); - - addWeekParseToken(['d', 'e', 'E'], function (input, week, config, token) { - week[token] = toInt(input); - }); - - // HELPERS - - function parseWeekday(input, locale) { - if (typeof input !== 'string') { - return input; - } - - if (!isNaN(input)) { - return parseInt(input, 10); - } - - input = locale.weekdaysParse(input); - if (typeof input === 'number') { - return input; - } - - return null; - } - - function parseIsoWeekday(input, locale) { - if (typeof input === 'string') { - return locale.weekdaysParse(input) % 7 || 7; - } - return isNaN(input) ? null : input; - } - - // LOCALES - function shiftWeekdays(ws, n) { - return ws.slice(n, 7).concat(ws.slice(0, n)); - } - - var defaultLocaleWeekdays = 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split( - '_' - ), - defaultLocaleWeekdaysShort = 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'), - defaultLocaleWeekdaysMin = 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'), - defaultWeekdaysRegex = matchWord, - defaultWeekdaysShortRegex = matchWord, - defaultWeekdaysMinRegex = matchWord; - - function localeWeekdays(m, format) { - var weekdays = isArray(this._weekdays) - ? this._weekdays - : this._weekdays[ - m && m !== true && this._weekdays.isFormat.test(format) - ? 'format' - : 'standalone' - ]; - return m === true - ? shiftWeekdays(weekdays, this._week.dow) - : m - ? weekdays[m.day()] - : weekdays; - } - - function localeWeekdaysShort(m) { - return m === true - ? shiftWeekdays(this._weekdaysShort, this._week.dow) - : m - ? this._weekdaysShort[m.day()] - : this._weekdaysShort; - } - - function localeWeekdaysMin(m) { - return m === true - ? shiftWeekdays(this._weekdaysMin, this._week.dow) - : m - ? this._weekdaysMin[m.day()] - : this._weekdaysMin; - } - - function handleStrictParse$1(weekdayName, format, strict) { - var i, - ii, - mom, - llc = weekdayName.toLocaleLowerCase(); - if (!this._weekdaysParse) { - this._weekdaysParse = []; - this._shortWeekdaysParse = []; - this._minWeekdaysParse = []; - - for (i = 0; i < 7; ++i) { - mom = createUTC([2000, 1]).day(i); - this._minWeekdaysParse[i] = this.weekdaysMin( - mom, - '' - ).toLocaleLowerCase(); - this._shortWeekdaysParse[i] = this.weekdaysShort( - mom, - '' - ).toLocaleLowerCase(); - this._weekdaysParse[i] = this.weekdays(mom, '').toLocaleLowerCase(); - } - } - - if (strict) { - if (format === 'dddd') { - ii = indexOf.call(this._weekdaysParse, llc); - return ii !== -1 ? ii : null; - } else if (format === 'ddd') { - ii = indexOf.call(this._shortWeekdaysParse, llc); - return ii !== -1 ? ii : null; - } else { - ii = indexOf.call(this._minWeekdaysParse, llc); - return ii !== -1 ? ii : null; - } - } else { - if (format === 'dddd') { - ii = indexOf.call(this._weekdaysParse, llc); - if (ii !== -1) { - return ii; - } - ii = indexOf.call(this._shortWeekdaysParse, llc); - if (ii !== -1) { - return ii; - } - ii = indexOf.call(this._minWeekdaysParse, llc); - return ii !== -1 ? ii : null; - } else if (format === 'ddd') { - ii = indexOf.call(this._shortWeekdaysParse, llc); - if (ii !== -1) { - return ii; - } - ii = indexOf.call(this._weekdaysParse, llc); - if (ii !== -1) { - return ii; - } - ii = indexOf.call(this._minWeekdaysParse, llc); - return ii !== -1 ? ii : null; - } else { - ii = indexOf.call(this._minWeekdaysParse, llc); - if (ii !== -1) { - return ii; - } - ii = indexOf.call(this._weekdaysParse, llc); - if (ii !== -1) { - return ii; - } - ii = indexOf.call(this._shortWeekdaysParse, llc); - return ii !== -1 ? ii : null; - } - } - } - - function localeWeekdaysParse(weekdayName, format, strict) { - var i, mom, regex; - - if (this._weekdaysParseExact) { - return handleStrictParse$1.call(this, weekdayName, format, strict); - } - - if (!this._weekdaysParse) { - this._weekdaysParse = []; - this._minWeekdaysParse = []; - this._shortWeekdaysParse = []; - this._fullWeekdaysParse = []; - } - - for (i = 0; i < 7; i++) { - // make the regex if we don't have it already - - mom = createUTC([2000, 1]).day(i); - if (strict && !this._fullWeekdaysParse[i]) { - this._fullWeekdaysParse[i] = new RegExp( - '^' + this.weekdays(mom, '').replace('.', '\\.?') + '$', - 'i' - ); - this._shortWeekdaysParse[i] = new RegExp( - '^' + this.weekdaysShort(mom, '').replace('.', '\\.?') + '$', - 'i' - ); - this._minWeekdaysParse[i] = new RegExp( - '^' + this.weekdaysMin(mom, '').replace('.', '\\.?') + '$', - 'i' - ); - } - if (!this._weekdaysParse[i]) { - regex = - '^' + - this.weekdays(mom, '') + - '|^' + - this.weekdaysShort(mom, '') + - '|^' + - this.weekdaysMin(mom, ''); - this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i'); - } - // test the regex - if ( - strict && - format === 'dddd' && - this._fullWeekdaysParse[i].test(weekdayName) - ) { - return i; - } else if ( - strict && - format === 'ddd' && - this._shortWeekdaysParse[i].test(weekdayName) - ) { - return i; - } else if ( - strict && - format === 'dd' && - this._minWeekdaysParse[i].test(weekdayName) - ) { - return i; - } else if (!strict && this._weekdaysParse[i].test(weekdayName)) { - return i; - } - } - } - - // MOMENTS - - function getSetDayOfWeek(input) { - if (!this.isValid()) { - return input != null ? this : NaN; - } - var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay(); - if (input != null) { - input = parseWeekday(input, this.localeData()); - return this.add(input - day, 'd'); - } else { - return day; - } - } - - function getSetLocaleDayOfWeek(input) { - if (!this.isValid()) { - return input != null ? this : NaN; - } - var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7; - return input == null ? weekday : this.add(input - weekday, 'd'); - } - - function getSetISODayOfWeek(input) { - if (!this.isValid()) { - return input != null ? this : NaN; - } - - // behaves the same as moment#day except - // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6) - // as a setter, sunday should belong to the previous week. - - if (input != null) { - var weekday = parseIsoWeekday(input, this.localeData()); - return this.day(this.day() % 7 ? weekday : weekday - 7); - } else { - return this.day() || 7; - } - } - - function weekdaysRegex(isStrict) { - if (this._weekdaysParseExact) { - if (!hasOwnProp(this, '_weekdaysRegex')) { - computeWeekdaysParse.call(this); - } - if (isStrict) { - return this._weekdaysStrictRegex; - } else { - return this._weekdaysRegex; - } - } else { - if (!hasOwnProp(this, '_weekdaysRegex')) { - this._weekdaysRegex = defaultWeekdaysRegex; - } - return this._weekdaysStrictRegex && isStrict - ? this._weekdaysStrictRegex - : this._weekdaysRegex; - } - } - - function weekdaysShortRegex(isStrict) { - if (this._weekdaysParseExact) { - if (!hasOwnProp(this, '_weekdaysRegex')) { - computeWeekdaysParse.call(this); - } - if (isStrict) { - return this._weekdaysShortStrictRegex; - } else { - return this._weekdaysShortRegex; - } - } else { - if (!hasOwnProp(this, '_weekdaysShortRegex')) { - this._weekdaysShortRegex = defaultWeekdaysShortRegex; - } - return this._weekdaysShortStrictRegex && isStrict - ? this._weekdaysShortStrictRegex - : this._weekdaysShortRegex; - } - } - - function weekdaysMinRegex(isStrict) { - if (this._weekdaysParseExact) { - if (!hasOwnProp(this, '_weekdaysRegex')) { - computeWeekdaysParse.call(this); - } - if (isStrict) { - return this._weekdaysMinStrictRegex; - } else { - return this._weekdaysMinRegex; - } - } else { - if (!hasOwnProp(this, '_weekdaysMinRegex')) { - this._weekdaysMinRegex = defaultWeekdaysMinRegex; - } - return this._weekdaysMinStrictRegex && isStrict - ? this._weekdaysMinStrictRegex - : this._weekdaysMinRegex; - } - } - - function computeWeekdaysParse() { - function cmpLenRev(a, b) { - return b.length - a.length; - } - - var minPieces = [], - shortPieces = [], - longPieces = [], - mixedPieces = [], - i, - mom, - minp, - shortp, - longp; - for (i = 0; i < 7; i++) { - // make the regex if we don't have it already - mom = createUTC([2000, 1]).day(i); - minp = regexEscape(this.weekdaysMin(mom, '')); - shortp = regexEscape(this.weekdaysShort(mom, '')); - longp = regexEscape(this.weekdays(mom, '')); - minPieces.push(minp); - shortPieces.push(shortp); - longPieces.push(longp); - mixedPieces.push(minp); - mixedPieces.push(shortp); - mixedPieces.push(longp); - } - // Sorting makes sure if one weekday (or abbr) is a prefix of another it - // will match the longer piece. - minPieces.sort(cmpLenRev); - shortPieces.sort(cmpLenRev); - longPieces.sort(cmpLenRev); - mixedPieces.sort(cmpLenRev); - - this._weekdaysRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i'); - this._weekdaysShortRegex = this._weekdaysRegex; - this._weekdaysMinRegex = this._weekdaysRegex; - - this._weekdaysStrictRegex = new RegExp( - '^(' + longPieces.join('|') + ')', - 'i' - ); - this._weekdaysShortStrictRegex = new RegExp( - '^(' + shortPieces.join('|') + ')', - 'i' - ); - this._weekdaysMinStrictRegex = new RegExp( - '^(' + minPieces.join('|') + ')', - 'i' - ); - } - - // FORMATTING - - function hFormat() { - return this.hours() % 12 || 12; - } - - function kFormat() { - return this.hours() || 24; - } - - addFormatToken('H', ['HH', 2], 0, 'hour'); - addFormatToken('h', ['hh', 2], 0, hFormat); - addFormatToken('k', ['kk', 2], 0, kFormat); - - addFormatToken('hmm', 0, 0, function () { - return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2); - }); - - addFormatToken('hmmss', 0, 0, function () { - return ( - '' + - hFormat.apply(this) + - zeroFill(this.minutes(), 2) + - zeroFill(this.seconds(), 2) - ); - }); - - addFormatToken('Hmm', 0, 0, function () { - return '' + this.hours() + zeroFill(this.minutes(), 2); - }); - - addFormatToken('Hmmss', 0, 0, function () { - return ( - '' + - this.hours() + - zeroFill(this.minutes(), 2) + - zeroFill(this.seconds(), 2) - ); - }); - - function meridiem(token, lowercase) { - addFormatToken(token, 0, 0, function () { - return this.localeData().meridiem( - this.hours(), - this.minutes(), - lowercase - ); - }); - } - - meridiem('a', true); - meridiem('A', false); - - // ALIASES - - addUnitAlias('hour', 'h'); - - // PRIORITY - addUnitPriority('hour', 13); - - // PARSING - - function matchMeridiem(isStrict, locale) { - return locale._meridiemParse; - } - - addRegexToken('a', matchMeridiem); - addRegexToken('A', matchMeridiem); - addRegexToken('H', match1to2); - addRegexToken('h', match1to2); - addRegexToken('k', match1to2); - addRegexToken('HH', match1to2, match2); - addRegexToken('hh', match1to2, match2); - addRegexToken('kk', match1to2, match2); - - addRegexToken('hmm', match3to4); - addRegexToken('hmmss', match5to6); - addRegexToken('Hmm', match3to4); - addRegexToken('Hmmss', match5to6); - - addParseToken(['H', 'HH'], HOUR); - addParseToken(['k', 'kk'], function (input, array, config) { - var kInput = toInt(input); - array[HOUR] = kInput === 24 ? 0 : kInput; - }); - addParseToken(['a', 'A'], function (input, array, config) { - config._isPm = config._locale.isPM(input); - config._meridiem = input; - }); - addParseToken(['h', 'hh'], function (input, array, config) { - array[HOUR] = toInt(input); - getParsingFlags(config).bigHour = true; - }); - addParseToken('hmm', function (input, array, config) { - var pos = input.length - 2; - array[HOUR] = toInt(input.substr(0, pos)); - array[MINUTE] = toInt(input.substr(pos)); - getParsingFlags(config).bigHour = true; - }); - addParseToken('hmmss', function (input, array, config) { - var pos1 = input.length - 4, - pos2 = input.length - 2; - array[HOUR] = toInt(input.substr(0, pos1)); - array[MINUTE] = toInt(input.substr(pos1, 2)); - array[SECOND] = toInt(input.substr(pos2)); - getParsingFlags(config).bigHour = true; - }); - addParseToken('Hmm', function (input, array, config) { - var pos = input.length - 2; - array[HOUR] = toInt(input.substr(0, pos)); - array[MINUTE] = toInt(input.substr(pos)); - }); - addParseToken('Hmmss', function (input, array, config) { - var pos1 = input.length - 4, - pos2 = input.length - 2; - array[HOUR] = toInt(input.substr(0, pos1)); - array[MINUTE] = toInt(input.substr(pos1, 2)); - array[SECOND] = toInt(input.substr(pos2)); - }); - - // LOCALES - - function localeIsPM(input) { - // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays - // Using charAt should be more compatible. - return (input + '').toLowerCase().charAt(0) === 'p'; - } - - var defaultLocaleMeridiemParse = /[ap]\.?m?\.?/i, - // Setting the hour should keep the time, because the user explicitly - // specified which hour they want. So trying to maintain the same hour (in - // a new timezone) makes sense. Adding/subtracting hours does not follow - // this rule. - getSetHour = makeGetSet('Hours', true); - - function localeMeridiem(hours, minutes, isLower) { - if (hours > 11) { - return isLower ? 'pm' : 'PM'; - } else { - return isLower ? 'am' : 'AM'; - } - } - - var baseConfig = { - calendar: defaultCalendar, - longDateFormat: defaultLongDateFormat, - invalidDate: defaultInvalidDate, - ordinal: defaultOrdinal, - dayOfMonthOrdinalParse: defaultDayOfMonthOrdinalParse, - relativeTime: defaultRelativeTime, - - months: defaultLocaleMonths, - monthsShort: defaultLocaleMonthsShort, - - week: defaultLocaleWeek, - - weekdays: defaultLocaleWeekdays, - weekdaysMin: defaultLocaleWeekdaysMin, - weekdaysShort: defaultLocaleWeekdaysShort, - - meridiemParse: defaultLocaleMeridiemParse, - }; - - // internal storage for locale config files - var locales = {}, - localeFamilies = {}, - globalLocale; - - function commonPrefix(arr1, arr2) { - var i, - minl = Math.min(arr1.length, arr2.length); - for (i = 0; i < minl; i += 1) { - if (arr1[i] !== arr2[i]) { - return i; - } - } - return minl; - } - - function normalizeLocale(key) { - return key ? key.toLowerCase().replace('_', '-') : key; - } - - // pick the locale from the array - // try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each - // substring from most specific to least, but move to the next array item if it's a more specific variant than the current root - function chooseLocale(names) { - var i = 0, - j, - next, - locale, - split; - - while (i < names.length) { - split = normalizeLocale(names[i]).split('-'); - j = split.length; - next = normalizeLocale(names[i + 1]); - next = next ? next.split('-') : null; - while (j > 0) { - locale = loadLocale(split.slice(0, j).join('-')); - if (locale) { - return locale; - } - if ( - next && - next.length >= j && - commonPrefix(split, next) >= j - 1 - ) { - //the next array item is better than a shallower substring of this one - break; - } - j--; - } - i++; - } - return globalLocale; - } - - function loadLocale(name) { - var oldLocale = null, - aliasedRequire; - // TODO: Find a better way to register and load all the locales in Node - if ( - locales[name] === undefined && - typeof module !== 'undefined' && - module && - module.exports - ) { - try { - oldLocale = globalLocale._abbr; - aliasedRequire = require; - aliasedRequire('./locale/' + name); - getSetGlobalLocale(oldLocale); - } catch (e) { - // mark as not found to avoid repeating expensive file require call causing high CPU - // when trying to find en-US, en_US, en-us for every format call - locales[name] = null; // null means not found - } - } - return locales[name]; - } - - // This function will load locale and then set the global locale. If - // no arguments are passed in, it will simply return the current global - // locale key. - function getSetGlobalLocale(key, values) { - var data; - if (key) { - if (isUndefined(values)) { - data = getLocale(key); - } else { - data = defineLocale(key, values); - } - - if (data) { - // moment.duration._locale = moment._locale = data; - globalLocale = data; - } else { - if (typeof console !== 'undefined' && console.warn) { - //warn user if arguments are passed but the locale could not be set - console.warn( - 'Locale ' + key + ' not found. Did you forget to load it?' - ); - } - } - } - - return globalLocale._abbr; - } - - function defineLocale(name, config) { - if (config !== null) { - var locale, - parentConfig = baseConfig; - config.abbr = name; - if (locales[name] != null) { - deprecateSimple( - 'defineLocaleOverride', - 'use moment.updateLocale(localeName, config) to change ' + - 'an existing locale. moment.defineLocale(localeName, ' + - 'config) should only be used for creating a new locale ' + - 'See http://momentjs.com/guides/#/warnings/define-locale/ for more info.' - ); - parentConfig = locales[name]._config; - } else if (config.parentLocale != null) { - if (locales[config.parentLocale] != null) { - parentConfig = locales[config.parentLocale]._config; - } else { - locale = loadLocale(config.parentLocale); - if (locale != null) { - parentConfig = locale._config; - } else { - if (!localeFamilies[config.parentLocale]) { - localeFamilies[config.parentLocale] = []; - } - localeFamilies[config.parentLocale].push({ - name: name, - config: config, - }); - return null; - } - } - } - locales[name] = new Locale(mergeConfigs(parentConfig, config)); - - if (localeFamilies[name]) { - localeFamilies[name].forEach(function (x) { - defineLocale(x.name, x.config); - }); - } - - // backwards compat for now: also set the locale - // make sure we set the locale AFTER all child locales have been - // created, so we won't end up with the child locale set. - getSetGlobalLocale(name); - - return locales[name]; - } else { - // useful for testing - delete locales[name]; - return null; - } - } - - function updateLocale(name, config) { - if (config != null) { - var locale, - tmpLocale, - parentConfig = baseConfig; - - if (locales[name] != null && locales[name].parentLocale != null) { - // Update existing child locale in-place to avoid memory-leaks - locales[name].set(mergeConfigs(locales[name]._config, config)); - } else { - // MERGE - tmpLocale = loadLocale(name); - if (tmpLocale != null) { - parentConfig = tmpLocale._config; - } - config = mergeConfigs(parentConfig, config); - if (tmpLocale == null) { - // updateLocale is called for creating a new locale - // Set abbr so it will have a name (getters return - // undefined otherwise). - config.abbr = name; - } - locale = new Locale(config); - locale.parentLocale = locales[name]; - locales[name] = locale; - } - - // backwards compat for now: also set the locale - getSetGlobalLocale(name); - } else { - // pass null for config to unupdate, useful for tests - if (locales[name] != null) { - if (locales[name].parentLocale != null) { - locales[name] = locales[name].parentLocale; - if (name === getSetGlobalLocale()) { - getSetGlobalLocale(name); - } - } else if (locales[name] != null) { - delete locales[name]; - } - } - } - return locales[name]; - } - - // returns locale data - function getLocale(key) { - var locale; - - if (key && key._locale && key._locale._abbr) { - key = key._locale._abbr; - } - - if (!key) { - return globalLocale; - } - - if (!isArray(key)) { - //short-circuit everything else - locale = loadLocale(key); - if (locale) { - return locale; - } - key = [key]; - } - - return chooseLocale(key); - } - - function listLocales() { - return keys(locales); - } - - function checkOverflow(m) { - var overflow, - a = m._a; - - if (a && getParsingFlags(m).overflow === -2) { - overflow = - a[MONTH] < 0 || a[MONTH] > 11 - ? MONTH - : a[DATE] < 1 || a[DATE] > daysInMonth(a[YEAR], a[MONTH]) - ? DATE - : a[HOUR] < 0 || - a[HOUR] > 24 || - (a[HOUR] === 24 && - (a[MINUTE] !== 0 || - a[SECOND] !== 0 || - a[MILLISECOND] !== 0)) - ? HOUR - : a[MINUTE] < 0 || a[MINUTE] > 59 - ? MINUTE - : a[SECOND] < 0 || a[SECOND] > 59 - ? SECOND - : a[MILLISECOND] < 0 || a[MILLISECOND] > 999 - ? MILLISECOND - : -1; - - if ( - getParsingFlags(m)._overflowDayOfYear && - (overflow < YEAR || overflow > DATE) - ) { - overflow = DATE; - } - if (getParsingFlags(m)._overflowWeeks && overflow === -1) { - overflow = WEEK; - } - if (getParsingFlags(m)._overflowWeekday && overflow === -1) { - overflow = WEEKDAY; - } - - getParsingFlags(m).overflow = overflow; - } - - return m; - } - - // iso 8601 regex - // 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00) - var extendedIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([+-]\d\d(?::?\d\d)?|\s*Z)?)?$/, - basicIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d|))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([+-]\d\d(?::?\d\d)?|\s*Z)?)?$/, - tzRegex = /Z|[+-]\d\d(?::?\d\d)?/, - isoDates = [ - ['YYYYYY-MM-DD', /[+-]\d{6}-\d\d-\d\d/], - ['YYYY-MM-DD', /\d{4}-\d\d-\d\d/], - ['GGGG-[W]WW-E', /\d{4}-W\d\d-\d/], - ['GGGG-[W]WW', /\d{4}-W\d\d/, false], - ['YYYY-DDD', /\d{4}-\d{3}/], - ['YYYY-MM', /\d{4}-\d\d/, false], - ['YYYYYYMMDD', /[+-]\d{10}/], - ['YYYYMMDD', /\d{8}/], - ['GGGG[W]WWE', /\d{4}W\d{3}/], - ['GGGG[W]WW', /\d{4}W\d{2}/, false], - ['YYYYDDD', /\d{7}/], - ['YYYYMM', /\d{6}/, false], - ['YYYY', /\d{4}/, false], - ], - // iso time formats and regexes - isoTimes = [ - ['HH:mm:ss.SSSS', /\d\d:\d\d:\d\d\.\d+/], - ['HH:mm:ss,SSSS', /\d\d:\d\d:\d\d,\d+/], - ['HH:mm:ss', /\d\d:\d\d:\d\d/], - ['HH:mm', /\d\d:\d\d/], - ['HHmmss.SSSS', /\d\d\d\d\d\d\.\d+/], - ['HHmmss,SSSS', /\d\d\d\d\d\d,\d+/], - ['HHmmss', /\d\d\d\d\d\d/], - ['HHmm', /\d\d\d\d/], - ['HH', /\d\d/], - ], - aspNetJsonRegex = /^\/?Date\((-?\d+)/i, - // RFC 2822 regex: For details see https://tools.ietf.org/html/rfc2822#section-3.3 - rfc2822 = /^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\d{4}))$/, - obsOffsets = { - UT: 0, - GMT: 0, - EDT: -4 * 60, - EST: -5 * 60, - CDT: -5 * 60, - CST: -6 * 60, - MDT: -6 * 60, - MST: -7 * 60, - PDT: -7 * 60, - PST: -8 * 60, - }; - - // date from iso format - function configFromISO(config) { - var i, - l, - string = config._i, - match = extendedIsoRegex.exec(string) || basicIsoRegex.exec(string), - allowTime, - dateFormat, - timeFormat, - tzFormat; - - if (match) { - getParsingFlags(config).iso = true; - - for (i = 0, l = isoDates.length; i < l; i++) { - if (isoDates[i][1].exec(match[1])) { - dateFormat = isoDates[i][0]; - allowTime = isoDates[i][2] !== false; - break; - } - } - if (dateFormat == null) { - config._isValid = false; - return; - } - if (match[3]) { - for (i = 0, l = isoTimes.length; i < l; i++) { - if (isoTimes[i][1].exec(match[3])) { - // match[2] should be 'T' or space - timeFormat = (match[2] || ' ') + isoTimes[i][0]; - break; - } - } - if (timeFormat == null) { - config._isValid = false; - return; - } - } - if (!allowTime && timeFormat != null) { - config._isValid = false; - return; - } - if (match[4]) { - if (tzRegex.exec(match[4])) { - tzFormat = 'Z'; - } else { - config._isValid = false; - return; - } - } - config._f = dateFormat + (timeFormat || '') + (tzFormat || ''); - configFromStringAndFormat(config); - } else { - config._isValid = false; - } - } - - function extractFromRFC2822Strings( - yearStr, - monthStr, - dayStr, - hourStr, - minuteStr, - secondStr - ) { - var result = [ - untruncateYear(yearStr), - defaultLocaleMonthsShort.indexOf(monthStr), - parseInt(dayStr, 10), - parseInt(hourStr, 10), - parseInt(minuteStr, 10), - ]; - - if (secondStr) { - result.push(parseInt(secondStr, 10)); - } - - return result; - } - - function untruncateYear(yearStr) { - var year = parseInt(yearStr, 10); - if (year <= 49) { - return 2000 + year; - } else if (year <= 999) { - return 1900 + year; - } - return year; - } - - function preprocessRFC2822(s) { - // Remove comments and folding whitespace and replace multiple-spaces with a single space - return s - .replace(/\([^)]*\)|[\n\t]/g, ' ') - .replace(/(\s\s+)/g, ' ') - .replace(/^\s\s*/, '') - .replace(/\s\s*$/, ''); - } - - function checkWeekday(weekdayStr, parsedInput, config) { - if (weekdayStr) { - // TODO: Replace the vanilla JS Date object with an independent day-of-week check. - var weekdayProvided = defaultLocaleWeekdaysShort.indexOf(weekdayStr), - weekdayActual = new Date( - parsedInput[0], - parsedInput[1], - parsedInput[2] - ).getDay(); - if (weekdayProvided !== weekdayActual) { - getParsingFlags(config).weekdayMismatch = true; - config._isValid = false; - return false; - } - } - return true; - } - - function calculateOffset(obsOffset, militaryOffset, numOffset) { - if (obsOffset) { - return obsOffsets[obsOffset]; - } else if (militaryOffset) { - // the only allowed military tz is Z - return 0; - } else { - var hm = parseInt(numOffset, 10), - m = hm % 100, - h = (hm - m) / 100; - return h * 60 + m; - } - } - - // date and time from ref 2822 format - function configFromRFC2822(config) { - var match = rfc2822.exec(preprocessRFC2822(config._i)), - parsedArray; - if (match) { - parsedArray = extractFromRFC2822Strings( - match[4], - match[3], - match[2], - match[5], - match[6], - match[7] - ); - if (!checkWeekday(match[1], parsedArray, config)) { - return; - } - - config._a = parsedArray; - config._tzm = calculateOffset(match[8], match[9], match[10]); - - config._d = createUTCDate.apply(null, config._a); - config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); - - getParsingFlags(config).rfc2822 = true; - } else { - config._isValid = false; - } - } - - // date from 1) ASP.NET, 2) ISO, 3) RFC 2822 formats, or 4) optional fallback if parsing isn't strict - function configFromString(config) { - var matched = aspNetJsonRegex.exec(config._i); - if (matched !== null) { - config._d = new Date(+matched[1]); - return; - } - - configFromISO(config); - if (config._isValid === false) { - delete config._isValid; - } else { - return; - } - - configFromRFC2822(config); - if (config._isValid === false) { - delete config._isValid; - } else { - return; - } - - if (config._strict) { - config._isValid = false; - } else { - // Final attempt, use Input Fallback - hooks.createFromInputFallback(config); - } - } - - hooks.createFromInputFallback = deprecate( - 'value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), ' + - 'which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are ' + - 'discouraged. Please refer to http://momentjs.com/guides/#/warnings/js-date/ for more info.', - function (config) { - config._d = new Date(config._i + (config._useUTC ? ' UTC' : '')); - } - ); - - // Pick the first defined of two or three arguments. - function defaults(a, b, c) { - if (a != null) { - return a; - } - if (b != null) { - return b; - } - return c; - } - - function currentDateArray(config) { - // hooks is actually the exported moment object - var nowValue = new Date(hooks.now()); - if (config._useUTC) { - return [ - nowValue.getUTCFullYear(), - nowValue.getUTCMonth(), - nowValue.getUTCDate(), - ]; - } - return [nowValue.getFullYear(), nowValue.getMonth(), nowValue.getDate()]; - } - - // convert an array to a date. - // the array should mirror the parameters below - // note: all values past the year are optional and will default to the lowest possible value. - // [year, month, day , hour, minute, second, millisecond] - function configFromArray(config) { - var i, - date, - input = [], - currentDate, - expectedWeekday, - yearToUse; - - if (config._d) { - return; - } - - currentDate = currentDateArray(config); - - //compute day of the year from weeks and weekdays - if (config._w && config._a[DATE] == null && config._a[MONTH] == null) { - dayOfYearFromWeekInfo(config); - } - - //if the day of the year is set, figure out what it is - if (config._dayOfYear != null) { - yearToUse = defaults(config._a[YEAR], currentDate[YEAR]); - - if ( - config._dayOfYear > daysInYear(yearToUse) || - config._dayOfYear === 0 - ) { - getParsingFlags(config)._overflowDayOfYear = true; - } - - date = createUTCDate(yearToUse, 0, config._dayOfYear); - config._a[MONTH] = date.getUTCMonth(); - config._a[DATE] = date.getUTCDate(); - } - - // Default to current date. - // * if no year, month, day of month are given, default to today - // * if day of month is given, default month and year - // * if month is given, default only year - // * if year is given, don't default anything - for (i = 0; i < 3 && config._a[i] == null; ++i) { - config._a[i] = input[i] = currentDate[i]; - } - - // Zero out whatever was not defaulted, including time - for (; i < 7; i++) { - config._a[i] = input[i] = - config._a[i] == null ? (i === 2 ? 1 : 0) : config._a[i]; - } - - // Check for 24:00:00.000 - if ( - config._a[HOUR] === 24 && - config._a[MINUTE] === 0 && - config._a[SECOND] === 0 && - config._a[MILLISECOND] === 0 - ) { - config._nextDay = true; - config._a[HOUR] = 0; - } - - config._d = (config._useUTC ? createUTCDate : createDate).apply( - null, - input - ); - expectedWeekday = config._useUTC - ? config._d.getUTCDay() - : config._d.getDay(); - - // Apply timezone offset from input. The actual utcOffset can be changed - // with parseZone. - if (config._tzm != null) { - config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); - } - - if (config._nextDay) { - config._a[HOUR] = 24; - } - - // check for mismatching day of week - if ( - config._w && - typeof config._w.d !== 'undefined' && - config._w.d !== expectedWeekday - ) { - getParsingFlags(config).weekdayMismatch = true; - } - } - - function dayOfYearFromWeekInfo(config) { - var w, weekYear, week, weekday, dow, doy, temp, weekdayOverflow, curWeek; - - w = config._w; - if (w.GG != null || w.W != null || w.E != null) { - dow = 1; - doy = 4; - - // TODO: We need to take the current isoWeekYear, but that depends on - // how we interpret now (local, utc, fixed offset). So create - // a now version of current config (take local/utc/offset flags, and - // create now). - weekYear = defaults( - w.GG, - config._a[YEAR], - weekOfYear(createLocal(), 1, 4).year - ); - week = defaults(w.W, 1); - weekday = defaults(w.E, 1); - if (weekday < 1 || weekday > 7) { - weekdayOverflow = true; - } - } else { - dow = config._locale._week.dow; - doy = config._locale._week.doy; - - curWeek = weekOfYear(createLocal(), dow, doy); - - weekYear = defaults(w.gg, config._a[YEAR], curWeek.year); - - // Default to current week. - week = defaults(w.w, curWeek.week); - - if (w.d != null) { - // weekday -- low day numbers are considered next week - weekday = w.d; - if (weekday < 0 || weekday > 6) { - weekdayOverflow = true; - } - } else if (w.e != null) { - // local weekday -- counting starts from beginning of week - weekday = w.e + dow; - if (w.e < 0 || w.e > 6) { - weekdayOverflow = true; - } - } else { - // default to beginning of week - weekday = dow; - } - } - if (week < 1 || week > weeksInYear(weekYear, dow, doy)) { - getParsingFlags(config)._overflowWeeks = true; - } else if (weekdayOverflow != null) { - getParsingFlags(config)._overflowWeekday = true; - } else { - temp = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy); - config._a[YEAR] = temp.year; - config._dayOfYear = temp.dayOfYear; - } - } - - // constant that refers to the ISO standard - hooks.ISO_8601 = function () {}; - - // constant that refers to the RFC 2822 form - hooks.RFC_2822 = function () {}; - - // date from string and format string - function configFromStringAndFormat(config) { - // TODO: Move this to another part of the creation flow to prevent circular deps - if (config._f === hooks.ISO_8601) { - configFromISO(config); - return; - } - if (config._f === hooks.RFC_2822) { - configFromRFC2822(config); - return; - } - config._a = []; - getParsingFlags(config).empty = true; - - // This array is used to make a Date, either with `new Date` or `Date.UTC` - var string = '' + config._i, - i, - parsedInput, - tokens, - token, - skipped, - stringLength = string.length, - totalParsedInputLength = 0, - era; - - tokens = - expandFormat(config._f, config._locale).match(formattingTokens) || []; - - for (i = 0; i < tokens.length; i++) { - token = tokens[i]; - parsedInput = (string.match(getParseRegexForToken(token, config)) || - [])[0]; - if (parsedInput) { - skipped = string.substr(0, string.indexOf(parsedInput)); - if (skipped.length > 0) { - getParsingFlags(config).unusedInput.push(skipped); - } - string = string.slice( - string.indexOf(parsedInput) + parsedInput.length - ); - totalParsedInputLength += parsedInput.length; - } - // don't parse if it's not a known token - if (formatTokenFunctions[token]) { - if (parsedInput) { - getParsingFlags(config).empty = false; - } else { - getParsingFlags(config).unusedTokens.push(token); - } - addTimeToArrayFromToken(token, parsedInput, config); - } else if (config._strict && !parsedInput) { - getParsingFlags(config).unusedTokens.push(token); - } - } - - // add remaining unparsed input length to the string - getParsingFlags(config).charsLeftOver = - stringLength - totalParsedInputLength; - if (string.length > 0) { - getParsingFlags(config).unusedInput.push(string); - } - - // clear _12h flag if hour is <= 12 - if ( - config._a[HOUR] <= 12 && - getParsingFlags(config).bigHour === true && - config._a[HOUR] > 0 - ) { - getParsingFlags(config).bigHour = undefined; - } - - getParsingFlags(config).parsedDateParts = config._a.slice(0); - getParsingFlags(config).meridiem = config._meridiem; - // handle meridiem - config._a[HOUR] = meridiemFixWrap( - config._locale, - config._a[HOUR], - config._meridiem - ); - - // handle era - era = getParsingFlags(config).era; - if (era !== null) { - config._a[YEAR] = config._locale.erasConvertYear(era, config._a[YEAR]); - } - - configFromArray(config); - checkOverflow(config); - } - - function meridiemFixWrap(locale, hour, meridiem) { - var isPm; - - if (meridiem == null) { - // nothing to do - return hour; - } - if (locale.meridiemHour != null) { - return locale.meridiemHour(hour, meridiem); - } else if (locale.isPM != null) { - // Fallback - isPm = locale.isPM(meridiem); - if (isPm && hour < 12) { - hour += 12; - } - if (!isPm && hour === 12) { - hour = 0; - } - return hour; - } else { - // this is not supposed to happen - return hour; - } - } - - // date from string and array of format strings - function configFromStringAndArray(config) { - var tempConfig, - bestMoment, - scoreToBeat, - i, - currentScore, - validFormatFound, - bestFormatIsValid = false; - - if (config._f.length === 0) { - getParsingFlags(config).invalidFormat = true; - config._d = new Date(NaN); - return; - } - - for (i = 0; i < config._f.length; i++) { - currentScore = 0; - validFormatFound = false; - tempConfig = copyConfig({}, config); - if (config._useUTC != null) { - tempConfig._useUTC = config._useUTC; - } - tempConfig._f = config._f[i]; - configFromStringAndFormat(tempConfig); - - if (isValid(tempConfig)) { - validFormatFound = true; - } - - // if there is any input that was not parsed add a penalty for that format - currentScore += getParsingFlags(tempConfig).charsLeftOver; - - //or tokens - currentScore += getParsingFlags(tempConfig).unusedTokens.length * 10; - - getParsingFlags(tempConfig).score = currentScore; - - if (!bestFormatIsValid) { - if ( - scoreToBeat == null || - currentScore < scoreToBeat || - validFormatFound - ) { - scoreToBeat = currentScore; - bestMoment = tempConfig; - if (validFormatFound) { - bestFormatIsValid = true; - } - } - } else { - if (currentScore < scoreToBeat) { - scoreToBeat = currentScore; - bestMoment = tempConfig; - } - } - } - - extend(config, bestMoment || tempConfig); - } - - function configFromObject(config) { - if (config._d) { - return; - } - - var i = normalizeObjectUnits(config._i), - dayOrDate = i.day === undefined ? i.date : i.day; - config._a = map( - [i.year, i.month, dayOrDate, i.hour, i.minute, i.second, i.millisecond], - function (obj) { - return obj && parseInt(obj, 10); - } - ); - - configFromArray(config); - } - - function createFromConfig(config) { - var res = new Moment(checkOverflow(prepareConfig(config))); - if (res._nextDay) { - // Adding is smart enough around DST - res.add(1, 'd'); - res._nextDay = undefined; - } - - return res; - } - - function prepareConfig(config) { - var input = config._i, - format = config._f; - - config._locale = config._locale || getLocale(config._l); - - if (input === null || (format === undefined && input === '')) { - return createInvalid({ nullInput: true }); - } - - if (typeof input === 'string') { - config._i = input = config._locale.preparse(input); - } - - if (isMoment(input)) { - return new Moment(checkOverflow(input)); - } else if (isDate(input)) { - config._d = input; - } else if (isArray(format)) { - configFromStringAndArray(config); - } else if (format) { - configFromStringAndFormat(config); - } else { - configFromInput(config); - } - - if (!isValid(config)) { - config._d = null; - } - - return config; - } - - function configFromInput(config) { - var input = config._i; - if (isUndefined(input)) { - config._d = new Date(hooks.now()); - } else if (isDate(input)) { - config._d = new Date(input.valueOf()); - } else if (typeof input === 'string') { - configFromString(config); - } else if (isArray(input)) { - config._a = map(input.slice(0), function (obj) { - return parseInt(obj, 10); - }); - configFromArray(config); - } else if (isObject(input)) { - configFromObject(config); - } else if (isNumber(input)) { - // from milliseconds - config._d = new Date(input); - } else { - hooks.createFromInputFallback(config); - } - } - - function createLocalOrUTC(input, format, locale, strict, isUTC) { - var c = {}; - - if (format === true || format === false) { - strict = format; - format = undefined; - } - - if (locale === true || locale === false) { - strict = locale; - locale = undefined; - } - - if ( - (isObject(input) && isObjectEmpty(input)) || - (isArray(input) && input.length === 0) - ) { - input = undefined; - } - // object construction must be done this way. - // https://github.com/moment/moment/issues/1423 - c._isAMomentObject = true; - c._useUTC = c._isUTC = isUTC; - c._l = locale; - c._i = input; - c._f = format; - c._strict = strict; - - return createFromConfig(c); - } - - function createLocal(input, format, locale, strict) { - return createLocalOrUTC(input, format, locale, strict, false); - } - - var prototypeMin = deprecate( - 'moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/', - function () { - var other = createLocal.apply(null, arguments); - if (this.isValid() && other.isValid()) { - return other < this ? this : other; - } else { - return createInvalid(); - } - } - ), - prototypeMax = deprecate( - 'moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/', - function () { - var other = createLocal.apply(null, arguments); - if (this.isValid() && other.isValid()) { - return other > this ? this : other; - } else { - return createInvalid(); - } - } - ); - - // Pick a moment m from moments so that m[fn](other) is true for all - // other. This relies on the function fn to be transitive. - // - // moments should either be an array of moment objects or an array, whose - // first element is an array of moment objects. - function pickBy(fn, moments) { - var res, i; - if (moments.length === 1 && isArray(moments[0])) { - moments = moments[0]; - } - if (!moments.length) { - return createLocal(); - } - res = moments[0]; - for (i = 1; i < moments.length; ++i) { - if (!moments[i].isValid() || moments[i][fn](res)) { - res = moments[i]; - } - } - return res; - } - - // TODO: Use [].sort instead? - function min() { - var args = [].slice.call(arguments, 0); - - return pickBy('isBefore', args); - } - - function max() { - var args = [].slice.call(arguments, 0); - - return pickBy('isAfter', args); - } - - var now = function () { - return Date.now ? Date.now() : +new Date(); - }; - - var ordering = [ - 'year', - 'quarter', - 'month', - 'week', - 'day', - 'hour', - 'minute', - 'second', - 'millisecond', - ]; - - function isDurationValid(m) { - var key, - unitHasDecimal = false, - i; - for (key in m) { - if ( - hasOwnProp(m, key) && - !( - indexOf.call(ordering, key) !== -1 && - (m[key] == null || !isNaN(m[key])) - ) - ) { - return false; - } - } - - for (i = 0; i < ordering.length; ++i) { - if (m[ordering[i]]) { - if (unitHasDecimal) { - return false; // only allow non-integers for smallest unit - } - if (parseFloat(m[ordering[i]]) !== toInt(m[ordering[i]])) { - unitHasDecimal = true; - } - } - } - - return true; - } - - function isValid$1() { - return this._isValid; - } - - function createInvalid$1() { - return createDuration(NaN); - } - - function Duration(duration) { - var normalizedInput = normalizeObjectUnits(duration), - years = normalizedInput.year || 0, - quarters = normalizedInput.quarter || 0, - months = normalizedInput.month || 0, - weeks = normalizedInput.week || normalizedInput.isoWeek || 0, - days = normalizedInput.day || 0, - hours = normalizedInput.hour || 0, - minutes = normalizedInput.minute || 0, - seconds = normalizedInput.second || 0, - milliseconds = normalizedInput.millisecond || 0; - - this._isValid = isDurationValid(normalizedInput); - - // representation for dateAddRemove - this._milliseconds = - +milliseconds + - seconds * 1e3 + // 1000 - minutes * 6e4 + // 1000 * 60 - hours * 1000 * 60 * 60; //using 1000 * 60 * 60 instead of 36e5 to avoid floating point rounding errors https://github.com/moment/moment/issues/2978 - // Because of dateAddRemove treats 24 hours as different from a - // day when working around DST, we need to store them separately - this._days = +days + weeks * 7; - // It is impossible to translate months into days without knowing - // which months you are are talking about, so we have to store - // it separately. - this._months = +months + quarters * 3 + years * 12; - - this._data = {}; - - this._locale = getLocale(); - - this._bubble(); - } - - function isDuration(obj) { - return obj instanceof Duration; - } - - function absRound(number) { - if (number < 0) { - return Math.round(-1 * number) * -1; - } else { - return Math.round(number); - } - } - - // compare two arrays, return the number of differences - function compareArrays(array1, array2, dontConvert) { - var len = Math.min(array1.length, array2.length), - lengthDiff = Math.abs(array1.length - array2.length), - diffs = 0, - i; - for (i = 0; i < len; i++) { - if ( - (dontConvert && array1[i] !== array2[i]) || - (!dontConvert && toInt(array1[i]) !== toInt(array2[i])) - ) { - diffs++; - } - } - return diffs + lengthDiff; - } - - // FORMATTING - - function offset(token, separator) { - addFormatToken(token, 0, 0, function () { - var offset = this.utcOffset(), - sign = '+'; - if (offset < 0) { - offset = -offset; - sign = '-'; - } - return ( - sign + - zeroFill(~~(offset / 60), 2) + - separator + - zeroFill(~~offset % 60, 2) - ); - }); - } - - offset('Z', ':'); - offset('ZZ', ''); - - // PARSING - - addRegexToken('Z', matchShortOffset); - addRegexToken('ZZ', matchShortOffset); - addParseToken(['Z', 'ZZ'], function (input, array, config) { - config._useUTC = true; - config._tzm = offsetFromString(matchShortOffset, input); - }); - - // HELPERS - - // timezone chunker - // '+10:00' > ['10', '00'] - // '-1530' > ['-15', '30'] - var chunkOffset = /([\+\-]|\d\d)/gi; - - function offsetFromString(matcher, string) { - var matches = (string || '').match(matcher), - chunk, - parts, - minutes; - - if (matches === null) { - return null; - } - - chunk = matches[matches.length - 1] || []; - parts = (chunk + '').match(chunkOffset) || ['-', 0, 0]; - minutes = +(parts[1] * 60) + toInt(parts[2]); - - return minutes === 0 ? 0 : parts[0] === '+' ? minutes : -minutes; - } - - // Return a moment from input, that is local/utc/zone equivalent to model. - function cloneWithOffset(input, model) { - var res, diff; - if (model._isUTC) { - res = model.clone(); - diff = - (isMoment(input) || isDate(input) - ? input.valueOf() - : createLocal(input).valueOf()) - res.valueOf(); - // Use low-level api, because this fn is low-level api. - res._d.setTime(res._d.valueOf() + diff); - hooks.updateOffset(res, false); - return res; - } else { - return createLocal(input).local(); - } - } - - function getDateOffset(m) { - // On Firefox.24 Date#getTimezoneOffset returns a floating point. - // https://github.com/moment/moment/pull/1871 - return -Math.round(m._d.getTimezoneOffset()); - } - - // HOOKS - - // This function will be called whenever a moment is mutated. - // It is intended to keep the offset in sync with the timezone. - hooks.updateOffset = function () {}; - - // MOMENTS - - // keepLocalTime = true means only change the timezone, without - // affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]--> - // 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset - // +0200, so we adjust the time as needed, to be valid. - // - // Keeping the time actually adds/subtracts (one hour) - // from the actual represented time. That is why we call updateOffset - // a second time. In case it wants us to change the offset again - // _changeInProgress == true case, then we have to adjust, because - // there is no such time in the given timezone. - function getSetOffset(input, keepLocalTime, keepMinutes) { - var offset = this._offset || 0, - localAdjust; - if (!this.isValid()) { - return input != null ? this : NaN; - } - if (input != null) { - if (typeof input === 'string') { - input = offsetFromString(matchShortOffset, input); - if (input === null) { - return this; - } - } else if (Math.abs(input) < 16 && !keepMinutes) { - input = input * 60; - } - if (!this._isUTC && keepLocalTime) { - localAdjust = getDateOffset(this); - } - this._offset = input; - this._isUTC = true; - if (localAdjust != null) { - this.add(localAdjust, 'm'); - } - if (offset !== input) { - if (!keepLocalTime || this._changeInProgress) { - addSubtract( - this, - createDuration(input - offset, 'm'), - 1, - false - ); - } else if (!this._changeInProgress) { - this._changeInProgress = true; - hooks.updateOffset(this, true); - this._changeInProgress = null; - } - } - return this; - } else { - return this._isUTC ? offset : getDateOffset(this); - } - } - - function getSetZone(input, keepLocalTime) { - if (input != null) { - if (typeof input !== 'string') { - input = -input; - } - - this.utcOffset(input, keepLocalTime); - - return this; - } else { - return -this.utcOffset(); - } - } - - function setOffsetToUTC(keepLocalTime) { - return this.utcOffset(0, keepLocalTime); - } - - function setOffsetToLocal(keepLocalTime) { - if (this._isUTC) { - this.utcOffset(0, keepLocalTime); - this._isUTC = false; - - if (keepLocalTime) { - this.subtract(getDateOffset(this), 'm'); - } - } - return this; - } - - function setOffsetToParsedOffset() { - if (this._tzm != null) { - this.utcOffset(this._tzm, false, true); - } else if (typeof this._i === 'string') { - var tZone = offsetFromString(matchOffset, this._i); - if (tZone != null) { - this.utcOffset(tZone); - } else { - this.utcOffset(0, true); - } - } - return this; - } - - function hasAlignedHourOffset(input) { - if (!this.isValid()) { - return false; - } - input = input ? createLocal(input).utcOffset() : 0; - - return (this.utcOffset() - input) % 60 === 0; - } - - function isDaylightSavingTime() { - return ( - this.utcOffset() > this.clone().month(0).utcOffset() || - this.utcOffset() > this.clone().month(5).utcOffset() - ); - } - - function isDaylightSavingTimeShifted() { - if (!isUndefined(this._isDSTShifted)) { - return this._isDSTShifted; - } - - var c = {}, - other; - - copyConfig(c, this); - c = prepareConfig(c); - - if (c._a) { - other = c._isUTC ? createUTC(c._a) : createLocal(c._a); - this._isDSTShifted = - this.isValid() && compareArrays(c._a, other.toArray()) > 0; - } else { - this._isDSTShifted = false; - } - - return this._isDSTShifted; - } - - function isLocal() { - return this.isValid() ? !this._isUTC : false; - } - - function isUtcOffset() { - return this.isValid() ? this._isUTC : false; - } - - function isUtc() { - return this.isValid() ? this._isUTC && this._offset === 0 : false; - } - - // ASP.NET json date format regex - var aspNetRegex = /^(-|\+)?(?:(\d*)[. ])?(\d+):(\d+)(?::(\d+)(\.\d*)?)?$/, - // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html - // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere - // and further modified to allow for strings containing both week and day - isoRegex = /^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/; - - function createDuration(input, key) { - var duration = input, - // matching against regexp is expensive, do it on demand - match = null, - sign, - ret, - diffRes; - - if (isDuration(input)) { - duration = { - ms: input._milliseconds, - d: input._days, - M: input._months, - }; - } else if (isNumber(input) || !isNaN(+input)) { - duration = {}; - if (key) { - duration[key] = +input; - } else { - duration.milliseconds = +input; - } - } else if ((match = aspNetRegex.exec(input))) { - sign = match[1] === '-' ? -1 : 1; - duration = { - y: 0, - d: toInt(match[DATE]) * sign, - h: toInt(match[HOUR]) * sign, - m: toInt(match[MINUTE]) * sign, - s: toInt(match[SECOND]) * sign, - ms: toInt(absRound(match[MILLISECOND] * 1000)) * sign, // the millisecond decimal point is included in the match - }; - } else if ((match = isoRegex.exec(input))) { - sign = match[1] === '-' ? -1 : 1; - duration = { - y: parseIso(match[2], sign), - M: parseIso(match[3], sign), - w: parseIso(match[4], sign), - d: parseIso(match[5], sign), - h: parseIso(match[6], sign), - m: parseIso(match[7], sign), - s: parseIso(match[8], sign), - }; - } else if (duration == null) { - // checks for null or undefined - duration = {}; - } else if ( - typeof duration === 'object' && - ('from' in duration || 'to' in duration) - ) { - diffRes = momentsDifference( - createLocal(duration.from), - createLocal(duration.to) - ); - - duration = {}; - duration.ms = diffRes.milliseconds; - duration.M = diffRes.months; - } - - ret = new Duration(duration); - - if (isDuration(input) && hasOwnProp(input, '_locale')) { - ret._locale = input._locale; - } - - if (isDuration(input) && hasOwnProp(input, '_isValid')) { - ret._isValid = input._isValid; - } - - return ret; - } - - createDuration.fn = Duration.prototype; - createDuration.invalid = createInvalid$1; - - function parseIso(inp, sign) { - // We'd normally use ~~inp for this, but unfortunately it also - // converts floats to ints. - // inp may be undefined, so careful calling replace on it. - var res = inp && parseFloat(inp.replace(',', '.')); - // apply sign while we're at it - return (isNaN(res) ? 0 : res) * sign; - } - - function positiveMomentsDifference(base, other) { - var res = {}; - - res.months = - other.month() - base.month() + (other.year() - base.year()) * 12; - if (base.clone().add(res.months, 'M').isAfter(other)) { - --res.months; - } - - res.milliseconds = +other - +base.clone().add(res.months, 'M'); - - return res; - } - - function momentsDifference(base, other) { - var res; - if (!(base.isValid() && other.isValid())) { - return { milliseconds: 0, months: 0 }; - } - - other = cloneWithOffset(other, base); - if (base.isBefore(other)) { - res = positiveMomentsDifference(base, other); - } else { - res = positiveMomentsDifference(other, base); - res.milliseconds = -res.milliseconds; - res.months = -res.months; - } - - return res; - } - - // TODO: remove 'name' arg after deprecation is removed - function createAdder(direction, name) { - return function (val, period) { - var dur, tmp; - //invert the arguments, but complain about it - if (period !== null && !isNaN(+period)) { - deprecateSimple( - name, - 'moment().' + - name + - '(period, number) is deprecated. Please use moment().' + - name + - '(number, period). ' + - 'See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info.' - ); - tmp = val; - val = period; - period = tmp; - } - - dur = createDuration(val, period); - addSubtract(this, dur, direction); - return this; - }; - } - - function addSubtract(mom, duration, isAdding, updateOffset) { - var milliseconds = duration._milliseconds, - days = absRound(duration._days), - months = absRound(duration._months); - - if (!mom.isValid()) { - // No op - return; - } - - updateOffset = updateOffset == null ? true : updateOffset; - - if (months) { - setMonth(mom, get(mom, 'Month') + months * isAdding); - } - if (days) { - set$1(mom, 'Date', get(mom, 'Date') + days * isAdding); - } - if (milliseconds) { - mom._d.setTime(mom._d.valueOf() + milliseconds * isAdding); - } - if (updateOffset) { - hooks.updateOffset(mom, days || months); - } - } - - var add = createAdder(1, 'add'), - subtract = createAdder(-1, 'subtract'); - - function isString(input) { - return typeof input === 'string' || input instanceof String; - } - - // type MomentInput = Moment | Date | string | number | (number | string)[] | MomentInputObject | void; // null | undefined - function isMomentInput(input) { - return ( - isMoment(input) || - isDate(input) || - isString(input) || - isNumber(input) || - isNumberOrStringArray(input) || - isMomentInputObject(input) || - input === null || - input === undefined - ); - } - - function isMomentInputObject(input) { - var objectTest = isObject(input) && !isObjectEmpty(input), - propertyTest = false, - properties = [ - 'years', - 'year', - 'y', - 'months', - 'month', - 'M', - 'days', - 'day', - 'd', - 'dates', - 'date', - 'D', - 'hours', - 'hour', - 'h', - 'minutes', - 'minute', - 'm', - 'seconds', - 'second', - 's', - 'milliseconds', - 'millisecond', - 'ms', - ], - i, - property; - - for (i = 0; i < properties.length; i += 1) { - property = properties[i]; - propertyTest = propertyTest || hasOwnProp(input, property); - } - - return objectTest && propertyTest; - } - - function isNumberOrStringArray(input) { - var arrayTest = isArray(input), - dataTypeTest = false; - if (arrayTest) { - dataTypeTest = - input.filter(function (item) { - return !isNumber(item) && isString(input); - }).length === 0; - } - return arrayTest && dataTypeTest; - } - - function isCalendarSpec(input) { - var objectTest = isObject(input) && !isObjectEmpty(input), - propertyTest = false, - properties = [ - 'sameDay', - 'nextDay', - 'lastDay', - 'nextWeek', - 'lastWeek', - 'sameElse', - ], - i, - property; - - for (i = 0; i < properties.length; i += 1) { - property = properties[i]; - propertyTest = propertyTest || hasOwnProp(input, property); - } - - return objectTest && propertyTest; - } - - function getCalendarFormat(myMoment, now) { - var diff = myMoment.diff(now, 'days', true); - return diff < -6 - ? 'sameElse' - : diff < -1 - ? 'lastWeek' - : diff < 0 - ? 'lastDay' - : diff < 1 - ? 'sameDay' - : diff < 2 - ? 'nextDay' - : diff < 7 - ? 'nextWeek' - : 'sameElse'; - } - - function calendar$1(time, formats) { - // Support for single parameter, formats only overload to the calendar function - if (arguments.length === 1) { - if (!arguments[0]) { - time = undefined; - formats = undefined; - } else if (isMomentInput(arguments[0])) { - time = arguments[0]; - formats = undefined; - } else if (isCalendarSpec(arguments[0])) { - formats = arguments[0]; - time = undefined; - } - } - // We want to compare the start of today, vs this. - // Getting start-of-today depends on whether we're local/utc/offset or not. - var now = time || createLocal(), - sod = cloneWithOffset(now, this).startOf('day'), - format = hooks.calendarFormat(this, sod) || 'sameElse', - output = - formats && - (isFunction(formats[format]) - ? formats[format].call(this, now) - : formats[format]); - - return this.format( - output || this.localeData().calendar(format, this, createLocal(now)) - ); - } - - function clone() { - return new Moment(this); - } - - function isAfter(input, units) { - var localInput = isMoment(input) ? input : createLocal(input); - if (!(this.isValid() && localInput.isValid())) { - return false; - } - units = normalizeUnits(units) || 'millisecond'; - if (units === 'millisecond') { - return this.valueOf() > localInput.valueOf(); - } else { - return localInput.valueOf() < this.clone().startOf(units).valueOf(); - } - } - - function isBefore(input, units) { - var localInput = isMoment(input) ? input : createLocal(input); - if (!(this.isValid() && localInput.isValid())) { - return false; - } - units = normalizeUnits(units) || 'millisecond'; - if (units === 'millisecond') { - return this.valueOf() < localInput.valueOf(); - } else { - return this.clone().endOf(units).valueOf() < localInput.valueOf(); - } - } - - function isBetween(from, to, units, inclusivity) { - var localFrom = isMoment(from) ? from : createLocal(from), - localTo = isMoment(to) ? to : createLocal(to); - if (!(this.isValid() && localFrom.isValid() && localTo.isValid())) { - return false; - } - inclusivity = inclusivity || '()'; - return ( - (inclusivity[0] === '(' - ? this.isAfter(localFrom, units) - : !this.isBefore(localFrom, units)) && - (inclusivity[1] === ')' - ? this.isBefore(localTo, units) - : !this.isAfter(localTo, units)) - ); - } - - function isSame(input, units) { - var localInput = isMoment(input) ? input : createLocal(input), - inputMs; - if (!(this.isValid() && localInput.isValid())) { - return false; - } - units = normalizeUnits(units) || 'millisecond'; - if (units === 'millisecond') { - return this.valueOf() === localInput.valueOf(); - } else { - inputMs = localInput.valueOf(); - return ( - this.clone().startOf(units).valueOf() <= inputMs && - inputMs <= this.clone().endOf(units).valueOf() - ); - } - } - - function isSameOrAfter(input, units) { - return this.isSame(input, units) || this.isAfter(input, units); - } - - function isSameOrBefore(input, units) { - return this.isSame(input, units) || this.isBefore(input, units); - } - - function diff(input, units, asFloat) { - var that, zoneDelta, output; - - if (!this.isValid()) { - return NaN; - } - - that = cloneWithOffset(input, this); - - if (!that.isValid()) { - return NaN; - } - - zoneDelta = (that.utcOffset() - this.utcOffset()) * 6e4; - - units = normalizeUnits(units); - - switch (units) { - case 'year': - output = monthDiff(this, that) / 12; - break; - case 'month': - output = monthDiff(this, that); - break; - case 'quarter': - output = monthDiff(this, that) / 3; - break; - case 'second': - output = (this - that) / 1e3; - break; // 1000 - case 'minute': - output = (this - that) / 6e4; - break; // 1000 * 60 - case 'hour': - output = (this - that) / 36e5; - break; // 1000 * 60 * 60 - case 'day': - output = (this - that - zoneDelta) / 864e5; - break; // 1000 * 60 * 60 * 24, negate dst - case 'week': - output = (this - that - zoneDelta) / 6048e5; - break; // 1000 * 60 * 60 * 24 * 7, negate dst - default: - output = this - that; - } - - return asFloat ? output : absFloor(output); - } - - function monthDiff(a, b) { - if (a.date() < b.date()) { - // end-of-month calculations work correct when the start month has more - // days than the end month. - return -monthDiff(b, a); - } - // difference in months - var wholeMonthDiff = (b.year() - a.year()) * 12 + (b.month() - a.month()), - // b is in (anchor - 1 month, anchor + 1 month) - anchor = a.clone().add(wholeMonthDiff, 'months'), - anchor2, - adjust; - - if (b - anchor < 0) { - anchor2 = a.clone().add(wholeMonthDiff - 1, 'months'); - // linear across the month - adjust = (b - anchor) / (anchor - anchor2); - } else { - anchor2 = a.clone().add(wholeMonthDiff + 1, 'months'); - // linear across the month - adjust = (b - anchor) / (anchor2 - anchor); - } - - //check for negative zero, return zero if negative zero - return -(wholeMonthDiff + adjust) || 0; - } - - hooks.defaultFormat = 'YYYY-MM-DDTHH:mm:ssZ'; - hooks.defaultFormatUtc = 'YYYY-MM-DDTHH:mm:ss[Z]'; - - function toString() { - return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ'); - } - - function toISOString(keepOffset) { - if (!this.isValid()) { - return null; - } - var utc = keepOffset !== true, - m = utc ? this.clone().utc() : this; - if (m.year() < 0 || m.year() > 9999) { - return formatMoment( - m, - utc - ? 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]' - : 'YYYYYY-MM-DD[T]HH:mm:ss.SSSZ' - ); - } - if (isFunction(Date.prototype.toISOString)) { - // native implementation is ~50x faster, use it when we can - if (utc) { - return this.toDate().toISOString(); - } else { - return new Date(this.valueOf() + this.utcOffset() * 60 * 1000) - .toISOString() - .replace('Z', formatMoment(m, 'Z')); - } - } - return formatMoment( - m, - utc ? 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]' : 'YYYY-MM-DD[T]HH:mm:ss.SSSZ' - ); - } - - /** - * Return a human readable representation of a moment that can - * also be evaluated to get a new moment which is the same - * - * @link https://nodejs.org/dist/latest/docs/api/util.html#util_custom_inspect_function_on_objects - */ - function inspect() { - if (!this.isValid()) { - return 'moment.invalid(/* ' + this._i + ' */)'; - } - var func = 'moment', - zone = '', - prefix, - year, - datetime, - suffix; - if (!this.isLocal()) { - func = this.utcOffset() === 0 ? 'moment.utc' : 'moment.parseZone'; - zone = 'Z'; - } - prefix = '[' + func + '("]'; - year = 0 <= this.year() && this.year() <= 9999 ? 'YYYY' : 'YYYYYY'; - datetime = '-MM-DD[T]HH:mm:ss.SSS'; - suffix = zone + '[")]'; - - return this.format(prefix + year + datetime + suffix); - } - - function format(inputString) { - if (!inputString) { - inputString = this.isUtc() - ? hooks.defaultFormatUtc - : hooks.defaultFormat; - } - var output = formatMoment(this, inputString); - return this.localeData().postformat(output); - } - - function from(time, withoutSuffix) { - if ( - this.isValid() && - ((isMoment(time) && time.isValid()) || createLocal(time).isValid()) - ) { - return createDuration({ to: this, from: time }) - .locale(this.locale()) - .humanize(!withoutSuffix); - } else { - return this.localeData().invalidDate(); - } - } - - function fromNow(withoutSuffix) { - return this.from(createLocal(), withoutSuffix); - } - - function to(time, withoutSuffix) { - if ( - this.isValid() && - ((isMoment(time) && time.isValid()) || createLocal(time).isValid()) - ) { - return createDuration({ from: this, to: time }) - .locale(this.locale()) - .humanize(!withoutSuffix); - } else { - return this.localeData().invalidDate(); - } - } - - function toNow(withoutSuffix) { - return this.to(createLocal(), withoutSuffix); - } - - // If passed a locale key, it will set the locale for this - // instance. Otherwise, it will return the locale configuration - // variables for this instance. - function locale(key) { - var newLocaleData; - - if (key === undefined) { - return this._locale._abbr; - } else { - newLocaleData = getLocale(key); - if (newLocaleData != null) { - this._locale = newLocaleData; - } - return this; - } - } - - var lang = deprecate( - 'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.', - function (key) { - if (key === undefined) { - return this.localeData(); - } else { - return this.locale(key); - } - } - ); - - function localeData() { - return this._locale; - } - - var MS_PER_SECOND = 1000, - MS_PER_MINUTE = 60 * MS_PER_SECOND, - MS_PER_HOUR = 60 * MS_PER_MINUTE, - MS_PER_400_YEARS = (365 * 400 + 97) * 24 * MS_PER_HOUR; - - // actual modulo - handles negative numbers (for dates before 1970): - function mod$1(dividend, divisor) { - return ((dividend % divisor) + divisor) % divisor; - } - - function localStartOfDate(y, m, d) { - // the date constructor remaps years 0-99 to 1900-1999 - if (y < 100 && y >= 0) { - // preserve leap years using a full 400 year cycle, then reset - return new Date(y + 400, m, d) - MS_PER_400_YEARS; - } else { - return new Date(y, m, d).valueOf(); - } - } - - function utcStartOfDate(y, m, d) { - // Date.UTC remaps years 0-99 to 1900-1999 - if (y < 100 && y >= 0) { - // preserve leap years using a full 400 year cycle, then reset - return Date.UTC(y + 400, m, d) - MS_PER_400_YEARS; - } else { - return Date.UTC(y, m, d); - } - } - - function startOf(units) { - var time, startOfDate; - units = normalizeUnits(units); - if (units === undefined || units === 'millisecond' || !this.isValid()) { - return this; - } - - startOfDate = this._isUTC ? utcStartOfDate : localStartOfDate; - - switch (units) { - case 'year': - time = startOfDate(this.year(), 0, 1); - break; - case 'quarter': - time = startOfDate( - this.year(), - this.month() - (this.month() % 3), - 1 - ); - break; - case 'month': - time = startOfDate(this.year(), this.month(), 1); - break; - case 'week': - time = startOfDate( - this.year(), - this.month(), - this.date() - this.weekday() - ); - break; - case 'isoWeek': - time = startOfDate( - this.year(), - this.month(), - this.date() - (this.isoWeekday() - 1) - ); - break; - case 'day': - case 'date': - time = startOfDate(this.year(), this.month(), this.date()); - break; - case 'hour': - time = this._d.valueOf(); - time -= mod$1( - time + (this._isUTC ? 0 : this.utcOffset() * MS_PER_MINUTE), - MS_PER_HOUR - ); - break; - case 'minute': - time = this._d.valueOf(); - time -= mod$1(time, MS_PER_MINUTE); - break; - case 'second': - time = this._d.valueOf(); - time -= mod$1(time, MS_PER_SECOND); - break; - } - - this._d.setTime(time); - hooks.updateOffset(this, true); - return this; - } - - function endOf(units) { - var time, startOfDate; - units = normalizeUnits(units); - if (units === undefined || units === 'millisecond' || !this.isValid()) { - return this; - } - - startOfDate = this._isUTC ? utcStartOfDate : localStartOfDate; - - switch (units) { - case 'year': - time = startOfDate(this.year() + 1, 0, 1) - 1; - break; - case 'quarter': - time = - startOfDate( - this.year(), - this.month() - (this.month() % 3) + 3, - 1 - ) - 1; - break; - case 'month': - time = startOfDate(this.year(), this.month() + 1, 1) - 1; - break; - case 'week': - time = - startOfDate( - this.year(), - this.month(), - this.date() - this.weekday() + 7 - ) - 1; - break; - case 'isoWeek': - time = - startOfDate( - this.year(), - this.month(), - this.date() - (this.isoWeekday() - 1) + 7 - ) - 1; - break; - case 'day': - case 'date': - time = startOfDate(this.year(), this.month(), this.date() + 1) - 1; - break; - case 'hour': - time = this._d.valueOf(); - time += - MS_PER_HOUR - - mod$1( - time + (this._isUTC ? 0 : this.utcOffset() * MS_PER_MINUTE), - MS_PER_HOUR - ) - - 1; - break; - case 'minute': - time = this._d.valueOf(); - time += MS_PER_MINUTE - mod$1(time, MS_PER_MINUTE) - 1; - break; - case 'second': - time = this._d.valueOf(); - time += MS_PER_SECOND - mod$1(time, MS_PER_SECOND) - 1; - break; - } - - this._d.setTime(time); - hooks.updateOffset(this, true); - return this; - } - - function valueOf() { - return this._d.valueOf() - (this._offset || 0) * 60000; - } - - function unix() { - return Math.floor(this.valueOf() / 1000); - } - - function toDate() { - return new Date(this.valueOf()); - } - - function toArray() { - var m = this; - return [ - m.year(), - m.month(), - m.date(), - m.hour(), - m.minute(), - m.second(), - m.millisecond(), - ]; - } - - function toObject() { - var m = this; - return { - years: m.year(), - months: m.month(), - date: m.date(), - hours: m.hours(), - minutes: m.minutes(), - seconds: m.seconds(), - milliseconds: m.milliseconds(), - }; - } - - function toJSON() { - // new Date(NaN).toJSON() === null - return this.isValid() ? this.toISOString() : null; - } - - function isValid$2() { - return isValid(this); - } - - function parsingFlags() { - return extend({}, getParsingFlags(this)); - } - - function invalidAt() { - return getParsingFlags(this).overflow; - } - - function creationData() { - return { - input: this._i, - format: this._f, - locale: this._locale, - isUTC: this._isUTC, - strict: this._strict, - }; - } - - addFormatToken('N', 0, 0, 'eraAbbr'); - addFormatToken('NN', 0, 0, 'eraAbbr'); - addFormatToken('NNN', 0, 0, 'eraAbbr'); - addFormatToken('NNNN', 0, 0, 'eraName'); - addFormatToken('NNNNN', 0, 0, 'eraNarrow'); - - addFormatToken('y', ['y', 1], 'yo', 'eraYear'); - addFormatToken('y', ['yy', 2], 0, 'eraYear'); - addFormatToken('y', ['yyy', 3], 0, 'eraYear'); - addFormatToken('y', ['yyyy', 4], 0, 'eraYear'); - - addRegexToken('N', matchEraAbbr); - addRegexToken('NN', matchEraAbbr); - addRegexToken('NNN', matchEraAbbr); - addRegexToken('NNNN', matchEraName); - addRegexToken('NNNNN', matchEraNarrow); - - addParseToken(['N', 'NN', 'NNN', 'NNNN', 'NNNNN'], function ( - input, - array, - config, - token - ) { - var era = config._locale.erasParse(input, token, config._strict); - if (era) { - getParsingFlags(config).era = era; - } else { - getParsingFlags(config).invalidEra = input; - } - }); - - addRegexToken('y', matchUnsigned); - addRegexToken('yy', matchUnsigned); - addRegexToken('yyy', matchUnsigned); - addRegexToken('yyyy', matchUnsigned); - addRegexToken('yo', matchEraYearOrdinal); - - addParseToken(['y', 'yy', 'yyy', 'yyyy'], YEAR); - addParseToken(['yo'], function (input, array, config, token) { - var match; - if (config._locale._eraYearOrdinalRegex) { - match = input.match(config._locale._eraYearOrdinalRegex); - } - - if (config._locale.eraYearOrdinalParse) { - array[YEAR] = config._locale.eraYearOrdinalParse(input, match); - } else { - array[YEAR] = parseInt(input, 10); - } - }); - - function localeEras(m, format) { - var i, - l, - date, - eras = this._eras || getLocale('en')._eras; - for (i = 0, l = eras.length; i < l; ++i) { - switch (typeof eras[i].since) { - case 'string': - // truncate time - date = hooks(eras[i].since).startOf('day'); - eras[i].since = date.valueOf(); - break; - } - - switch (typeof eras[i].until) { - case 'undefined': - eras[i].until = +Infinity; - break; - case 'string': - // truncate time - date = hooks(eras[i].until).startOf('day').valueOf(); - eras[i].until = date.valueOf(); - break; - } - } - return eras; - } - - function localeErasParse(eraName, format, strict) { - var i, - l, - eras = this.eras(), - name, - abbr, - narrow; - eraName = eraName.toUpperCase(); - - for (i = 0, l = eras.length; i < l; ++i) { - name = eras[i].name.toUpperCase(); - abbr = eras[i].abbr.toUpperCase(); - narrow = eras[i].narrow.toUpperCase(); - - if (strict) { - switch (format) { - case 'N': - case 'NN': - case 'NNN': - if (abbr === eraName) { - return eras[i]; - } - break; - - case 'NNNN': - if (name === eraName) { - return eras[i]; - } - break; - - case 'NNNNN': - if (narrow === eraName) { - return eras[i]; - } - break; - } - } else if ([name, abbr, narrow].indexOf(eraName) >= 0) { - return eras[i]; - } - } - } - - function localeErasConvertYear(era, year) { - var dir = era.since <= era.until ? +1 : -1; - if (year === undefined) { - return hooks(era.since).year(); - } else { - return hooks(era.since).year() + (year - era.offset) * dir; - } - } - - function getEraName() { - var i, - l, - val, - eras = this.localeData().eras(); - for (i = 0, l = eras.length; i < l; ++i) { - // truncate time - val = this.clone().startOf('day').valueOf(); - - if (eras[i].since <= val && val <= eras[i].until) { - return eras[i].name; - } - if (eras[i].until <= val && val <= eras[i].since) { - return eras[i].name; - } - } - - return ''; - } - - function getEraNarrow() { - var i, - l, - val, - eras = this.localeData().eras(); - for (i = 0, l = eras.length; i < l; ++i) { - // truncate time - val = this.clone().startOf('day').valueOf(); - - if (eras[i].since <= val && val <= eras[i].until) { - return eras[i].narrow; - } - if (eras[i].until <= val && val <= eras[i].since) { - return eras[i].narrow; - } - } - - return ''; - } - - function getEraAbbr() { - var i, - l, - val, - eras = this.localeData().eras(); - for (i = 0, l = eras.length; i < l; ++i) { - // truncate time - val = this.clone().startOf('day').valueOf(); - - if (eras[i].since <= val && val <= eras[i].until) { - return eras[i].abbr; - } - if (eras[i].until <= val && val <= eras[i].since) { - return eras[i].abbr; - } - } - - return ''; - } - - function getEraYear() { - var i, - l, - dir, - val, - eras = this.localeData().eras(); - for (i = 0, l = eras.length; i < l; ++i) { - dir = eras[i].since <= eras[i].until ? +1 : -1; - - // truncate time - val = this.clone().startOf('day').valueOf(); - - if ( - (eras[i].since <= val && val <= eras[i].until) || - (eras[i].until <= val && val <= eras[i].since) - ) { - return ( - (this.year() - hooks(eras[i].since).year()) * dir + - eras[i].offset - ); - } - } - - return this.year(); - } - - function erasNameRegex(isStrict) { - if (!hasOwnProp(this, '_erasNameRegex')) { - computeErasParse.call(this); - } - return isStrict ? this._erasNameRegex : this._erasRegex; - } - - function erasAbbrRegex(isStrict) { - if (!hasOwnProp(this, '_erasAbbrRegex')) { - computeErasParse.call(this); - } - return isStrict ? this._erasAbbrRegex : this._erasRegex; - } - - function erasNarrowRegex(isStrict) { - if (!hasOwnProp(this, '_erasNarrowRegex')) { - computeErasParse.call(this); - } - return isStrict ? this._erasNarrowRegex : this._erasRegex; - } - - function matchEraAbbr(isStrict, locale) { - return locale.erasAbbrRegex(isStrict); - } - - function matchEraName(isStrict, locale) { - return locale.erasNameRegex(isStrict); - } - - function matchEraNarrow(isStrict, locale) { - return locale.erasNarrowRegex(isStrict); - } - - function matchEraYearOrdinal(isStrict, locale) { - return locale._eraYearOrdinalRegex || matchUnsigned; - } - - function computeErasParse() { - var abbrPieces = [], - namePieces = [], - narrowPieces = [], - mixedPieces = [], - i, - l, - eras = this.eras(); - - for (i = 0, l = eras.length; i < l; ++i) { - namePieces.push(regexEscape(eras[i].name)); - abbrPieces.push(regexEscape(eras[i].abbr)); - narrowPieces.push(regexEscape(eras[i].narrow)); - - mixedPieces.push(regexEscape(eras[i].name)); - mixedPieces.push(regexEscape(eras[i].abbr)); - mixedPieces.push(regexEscape(eras[i].narrow)); - } - - this._erasRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i'); - this._erasNameRegex = new RegExp('^(' + namePieces.join('|') + ')', 'i'); - this._erasAbbrRegex = new RegExp('^(' + abbrPieces.join('|') + ')', 'i'); - this._erasNarrowRegex = new RegExp( - '^(' + narrowPieces.join('|') + ')', - 'i' - ); - } - - // FORMATTING - - addFormatToken(0, ['gg', 2], 0, function () { - return this.weekYear() % 100; - }); - - addFormatToken(0, ['GG', 2], 0, function () { - return this.isoWeekYear() % 100; - }); - - function addWeekYearFormatToken(token, getter) { - addFormatToken(0, [token, token.length], 0, getter); - } - - addWeekYearFormatToken('gggg', 'weekYear'); - addWeekYearFormatToken('ggggg', 'weekYear'); - addWeekYearFormatToken('GGGG', 'isoWeekYear'); - addWeekYearFormatToken('GGGGG', 'isoWeekYear'); - - // ALIASES - - addUnitAlias('weekYear', 'gg'); - addUnitAlias('isoWeekYear', 'GG'); - - // PRIORITY - - addUnitPriority('weekYear', 1); - addUnitPriority('isoWeekYear', 1); - - // PARSING - - addRegexToken('G', matchSigned); - addRegexToken('g', matchSigned); - addRegexToken('GG', match1to2, match2); - addRegexToken('gg', match1to2, match2); - addRegexToken('GGGG', match1to4, match4); - addRegexToken('gggg', match1to4, match4); - addRegexToken('GGGGG', match1to6, match6); - addRegexToken('ggggg', match1to6, match6); - - addWeekParseToken(['gggg', 'ggggg', 'GGGG', 'GGGGG'], function ( - input, - week, - config, - token - ) { - week[token.substr(0, 2)] = toInt(input); - }); - - addWeekParseToken(['gg', 'GG'], function (input, week, config, token) { - week[token] = hooks.parseTwoDigitYear(input); - }); - - // MOMENTS - - function getSetWeekYear(input) { - return getSetWeekYearHelper.call( - this, - input, - this.week(), - this.weekday(), - this.localeData()._week.dow, - this.localeData()._week.doy - ); - } - - function getSetISOWeekYear(input) { - return getSetWeekYearHelper.call( - this, - input, - this.isoWeek(), - this.isoWeekday(), - 1, - 4 - ); - } - - function getISOWeeksInYear() { - return weeksInYear(this.year(), 1, 4); - } - - function getISOWeeksInISOWeekYear() { - return weeksInYear(this.isoWeekYear(), 1, 4); - } - - function getWeeksInYear() { - var weekInfo = this.localeData()._week; - return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy); - } - - function getWeeksInWeekYear() { - var weekInfo = this.localeData()._week; - return weeksInYear(this.weekYear(), weekInfo.dow, weekInfo.doy); - } - - function getSetWeekYearHelper(input, week, weekday, dow, doy) { - var weeksTarget; - if (input == null) { - return weekOfYear(this, dow, doy).year; - } else { - weeksTarget = weeksInYear(input, dow, doy); - if (week > weeksTarget) { - week = weeksTarget; - } - return setWeekAll.call(this, input, week, weekday, dow, doy); - } - } - - function setWeekAll(weekYear, week, weekday, dow, doy) { - var dayOfYearData = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy), - date = createUTCDate(dayOfYearData.year, 0, dayOfYearData.dayOfYear); - - this.year(date.getUTCFullYear()); - this.month(date.getUTCMonth()); - this.date(date.getUTCDate()); - return this; - } - - // FORMATTING - - addFormatToken('Q', 0, 'Qo', 'quarter'); - - // ALIASES - - addUnitAlias('quarter', 'Q'); - - // PRIORITY - - addUnitPriority('quarter', 7); - - // PARSING - - addRegexToken('Q', match1); - addParseToken('Q', function (input, array) { - array[MONTH] = (toInt(input) - 1) * 3; - }); - - // MOMENTS - - function getSetQuarter(input) { - return input == null - ? Math.ceil((this.month() + 1) / 3) - : this.month((input - 1) * 3 + (this.month() % 3)); - } - - // FORMATTING - - addFormatToken('D', ['DD', 2], 'Do', 'date'); - - // ALIASES - - addUnitAlias('date', 'D'); - - // PRIORITY - addUnitPriority('date', 9); - - // PARSING - - addRegexToken('D', match1to2); - addRegexToken('DD', match1to2, match2); - addRegexToken('Do', function (isStrict, locale) { - // TODO: Remove "ordinalParse" fallback in next major release. - return isStrict - ? locale._dayOfMonthOrdinalParse || locale._ordinalParse - : locale._dayOfMonthOrdinalParseLenient; - }); - - addParseToken(['D', 'DD'], DATE); - addParseToken('Do', function (input, array) { - array[DATE] = toInt(input.match(match1to2)[0]); - }); - - // MOMENTS - - var getSetDayOfMonth = makeGetSet('Date', true); - - // FORMATTING - - addFormatToken('DDD', ['DDDD', 3], 'DDDo', 'dayOfYear'); - - // ALIASES - - addUnitAlias('dayOfYear', 'DDD'); - - // PRIORITY - addUnitPriority('dayOfYear', 4); - - // PARSING - - addRegexToken('DDD', match1to3); - addRegexToken('DDDD', match3); - addParseToken(['DDD', 'DDDD'], function (input, array, config) { - config._dayOfYear = toInt(input); - }); - - // HELPERS - - // MOMENTS - - function getSetDayOfYear(input) { - var dayOfYear = - Math.round( - (this.clone().startOf('day') - this.clone().startOf('year')) / 864e5 - ) + 1; - return input == null ? dayOfYear : this.add(input - dayOfYear, 'd'); - } - - // FORMATTING - - addFormatToken('m', ['mm', 2], 0, 'minute'); - - // ALIASES - - addUnitAlias('minute', 'm'); - - // PRIORITY - - addUnitPriority('minute', 14); - - // PARSING - - addRegexToken('m', match1to2); - addRegexToken('mm', match1to2, match2); - addParseToken(['m', 'mm'], MINUTE); - - // MOMENTS - - var getSetMinute = makeGetSet('Minutes', false); - - // FORMATTING - - addFormatToken('s', ['ss', 2], 0, 'second'); - - // ALIASES - - addUnitAlias('second', 's'); - - // PRIORITY - - addUnitPriority('second', 15); - - // PARSING - - addRegexToken('s', match1to2); - addRegexToken('ss', match1to2, match2); - addParseToken(['s', 'ss'], SECOND); - - // MOMENTS - - var getSetSecond = makeGetSet('Seconds', false); - - // FORMATTING - - addFormatToken('S', 0, 0, function () { - return ~~(this.millisecond() / 100); - }); - - addFormatToken(0, ['SS', 2], 0, function () { - return ~~(this.millisecond() / 10); - }); - - addFormatToken(0, ['SSS', 3], 0, 'millisecond'); - addFormatToken(0, ['SSSS', 4], 0, function () { - return this.millisecond() * 10; - }); - addFormatToken(0, ['SSSSS', 5], 0, function () { - return this.millisecond() * 100; - }); - addFormatToken(0, ['SSSSSS', 6], 0, function () { - return this.millisecond() * 1000; - }); - addFormatToken(0, ['SSSSSSS', 7], 0, function () { - return this.millisecond() * 10000; - }); - addFormatToken(0, ['SSSSSSSS', 8], 0, function () { - return this.millisecond() * 100000; - }); - addFormatToken(0, ['SSSSSSSSS', 9], 0, function () { - return this.millisecond() * 1000000; - }); - - // ALIASES - - addUnitAlias('millisecond', 'ms'); - - // PRIORITY - - addUnitPriority('millisecond', 16); - - // PARSING - - addRegexToken('S', match1to3, match1); - addRegexToken('SS', match1to3, match2); - addRegexToken('SSS', match1to3, match3); - - var token, getSetMillisecond; - for (token = 'SSSS'; token.length <= 9; token += 'S') { - addRegexToken(token, matchUnsigned); - } - - function parseMs(input, array) { - array[MILLISECOND] = toInt(('0.' + input) * 1000); - } - - for (token = 'S'; token.length <= 9; token += 'S') { - addParseToken(token, parseMs); - } - - getSetMillisecond = makeGetSet('Milliseconds', false); - - // FORMATTING - - addFormatToken('z', 0, 0, 'zoneAbbr'); - addFormatToken('zz', 0, 0, 'zoneName'); - - // MOMENTS - - function getZoneAbbr() { - return this._isUTC ? 'UTC' : ''; - } - - function getZoneName() { - return this._isUTC ? 'Coordinated Universal Time' : ''; - } - - var proto = Moment.prototype; - - proto.add = add; - proto.calendar = calendar$1; - proto.clone = clone; - proto.diff = diff; - proto.endOf = endOf; - proto.format = format; - proto.from = from; - proto.fromNow = fromNow; - proto.to = to; - proto.toNow = toNow; - proto.get = stringGet; - proto.invalidAt = invalidAt; - proto.isAfter = isAfter; - proto.isBefore = isBefore; - proto.isBetween = isBetween; - proto.isSame = isSame; - proto.isSameOrAfter = isSameOrAfter; - proto.isSameOrBefore = isSameOrBefore; - proto.isValid = isValid$2; - proto.lang = lang; - proto.locale = locale; - proto.localeData = localeData; - proto.max = prototypeMax; - proto.min = prototypeMin; - proto.parsingFlags = parsingFlags; - proto.set = stringSet; - proto.startOf = startOf; - proto.subtract = subtract; - proto.toArray = toArray; - proto.toObject = toObject; - proto.toDate = toDate; - proto.toISOString = toISOString; - proto.inspect = inspect; - if (typeof Symbol !== 'undefined' && Symbol.for != null) { - proto[Symbol.for('nodejs.util.inspect.custom')] = function () { - return 'Moment<' + this.format() + '>'; - }; - } - proto.toJSON = toJSON; - proto.toString = toString; - proto.unix = unix; - proto.valueOf = valueOf; - proto.creationData = creationData; - proto.eraName = getEraName; - proto.eraNarrow = getEraNarrow; - proto.eraAbbr = getEraAbbr; - proto.eraYear = getEraYear; - proto.year = getSetYear; - proto.isLeapYear = getIsLeapYear; - proto.weekYear = getSetWeekYear; - proto.isoWeekYear = getSetISOWeekYear; - proto.quarter = proto.quarters = getSetQuarter; - proto.month = getSetMonth; - proto.daysInMonth = getDaysInMonth; - proto.week = proto.weeks = getSetWeek; - proto.isoWeek = proto.isoWeeks = getSetISOWeek; - proto.weeksInYear = getWeeksInYear; - proto.weeksInWeekYear = getWeeksInWeekYear; - proto.isoWeeksInYear = getISOWeeksInYear; - proto.isoWeeksInISOWeekYear = getISOWeeksInISOWeekYear; - proto.date = getSetDayOfMonth; - proto.day = proto.days = getSetDayOfWeek; - proto.weekday = getSetLocaleDayOfWeek; - proto.isoWeekday = getSetISODayOfWeek; - proto.dayOfYear = getSetDayOfYear; - proto.hour = proto.hours = getSetHour; - proto.minute = proto.minutes = getSetMinute; - proto.second = proto.seconds = getSetSecond; - proto.millisecond = proto.milliseconds = getSetMillisecond; - proto.utcOffset = getSetOffset; - proto.utc = setOffsetToUTC; - proto.local = setOffsetToLocal; - proto.parseZone = setOffsetToParsedOffset; - proto.hasAlignedHourOffset = hasAlignedHourOffset; - proto.isDST = isDaylightSavingTime; - proto.isLocal = isLocal; - proto.isUtcOffset = isUtcOffset; - proto.isUtc = isUtc; - proto.isUTC = isUtc; - proto.zoneAbbr = getZoneAbbr; - proto.zoneName = getZoneName; - proto.dates = deprecate( - 'dates accessor is deprecated. Use date instead.', - getSetDayOfMonth - ); - proto.months = deprecate( - 'months accessor is deprecated. Use month instead', - getSetMonth - ); - proto.years = deprecate( - 'years accessor is deprecated. Use year instead', - getSetYear - ); - proto.zone = deprecate( - 'moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/', - getSetZone - ); - proto.isDSTShifted = deprecate( - 'isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information', - isDaylightSavingTimeShifted - ); - - function createUnix(input) { - return createLocal(input * 1000); - } - - function createInZone() { - return createLocal.apply(null, arguments).parseZone(); - } - - function preParsePostFormat(string) { - return string; - } - - var proto$1 = Locale.prototype; - - proto$1.calendar = calendar; - proto$1.longDateFormat = longDateFormat; - proto$1.invalidDate = invalidDate; - proto$1.ordinal = ordinal; - proto$1.preparse = preParsePostFormat; - proto$1.postformat = preParsePostFormat; - proto$1.relativeTime = relativeTime; - proto$1.pastFuture = pastFuture; - proto$1.set = set; - proto$1.eras = localeEras; - proto$1.erasParse = localeErasParse; - proto$1.erasConvertYear = localeErasConvertYear; - proto$1.erasAbbrRegex = erasAbbrRegex; - proto$1.erasNameRegex = erasNameRegex; - proto$1.erasNarrowRegex = erasNarrowRegex; - - proto$1.months = localeMonths; - proto$1.monthsShort = localeMonthsShort; - proto$1.monthsParse = localeMonthsParse; - proto$1.monthsRegex = monthsRegex; - proto$1.monthsShortRegex = monthsShortRegex; - proto$1.week = localeWeek; - proto$1.firstDayOfYear = localeFirstDayOfYear; - proto$1.firstDayOfWeek = localeFirstDayOfWeek; - - proto$1.weekdays = localeWeekdays; - proto$1.weekdaysMin = localeWeekdaysMin; - proto$1.weekdaysShort = localeWeekdaysShort; - proto$1.weekdaysParse = localeWeekdaysParse; - - proto$1.weekdaysRegex = weekdaysRegex; - proto$1.weekdaysShortRegex = weekdaysShortRegex; - proto$1.weekdaysMinRegex = weekdaysMinRegex; - - proto$1.isPM = localeIsPM; - proto$1.meridiem = localeMeridiem; - - function get$1(format, index, field, setter) { - var locale = getLocale(), - utc = createUTC().set(setter, index); - return locale[field](utc, format); - } - - function listMonthsImpl(format, index, field) { - if (isNumber(format)) { - index = format; - format = undefined; - } - - format = format || ''; - - if (index != null) { - return get$1(format, index, field, 'month'); - } - - var i, - out = []; - for (i = 0; i < 12; i++) { - out[i] = get$1(format, i, field, 'month'); - } - return out; - } - - // () - // (5) - // (fmt, 5) - // (fmt) - // (true) - // (true, 5) - // (true, fmt, 5) - // (true, fmt) - function listWeekdaysImpl(localeSorted, format, index, field) { - if (typeof localeSorted === 'boolean') { - if (isNumber(format)) { - index = format; - format = undefined; - } - - format = format || ''; - } else { - format = localeSorted; - index = format; - localeSorted = false; - - if (isNumber(format)) { - index = format; - format = undefined; - } - - format = format || ''; - } - - var locale = getLocale(), - shift = localeSorted ? locale._week.dow : 0, - i, - out = []; - - if (index != null) { - return get$1(format, (index + shift) % 7, field, 'day'); - } - - for (i = 0; i < 7; i++) { - out[i] = get$1(format, (i + shift) % 7, field, 'day'); - } - return out; - } - - function listMonths(format, index) { - return listMonthsImpl(format, index, 'months'); - } - - function listMonthsShort(format, index) { - return listMonthsImpl(format, index, 'monthsShort'); - } - - function listWeekdays(localeSorted, format, index) { - return listWeekdaysImpl(localeSorted, format, index, 'weekdays'); - } - - function listWeekdaysShort(localeSorted, format, index) { - return listWeekdaysImpl(localeSorted, format, index, 'weekdaysShort'); - } - - function listWeekdaysMin(localeSorted, format, index) { - return listWeekdaysImpl(localeSorted, format, index, 'weekdaysMin'); - } - - getSetGlobalLocale('en', { - eras: [ - { - since: '0001-01-01', - until: +Infinity, - offset: 1, - name: 'Anno Domini', - narrow: 'AD', - abbr: 'AD', - }, - { - since: '0000-12-31', - until: -Infinity, - offset: 1, - name: 'Before Christ', - narrow: 'BC', - abbr: 'BC', - }, - ], - dayOfMonthOrdinalParse: /\d{1,2}(th|st|nd|rd)/, - ordinal: function (number) { - var b = number % 10, - output = - toInt((number % 100) / 10) === 1 - ? 'th' - : b === 1 - ? 'st' - : b === 2 - ? 'nd' - : b === 3 - ? 'rd' - : 'th'; - return number + output; - }, - }); - - // Side effect imports - - hooks.lang = deprecate( - 'moment.lang is deprecated. Use moment.locale instead.', - getSetGlobalLocale - ); - hooks.langData = deprecate( - 'moment.langData is deprecated. Use moment.localeData instead.', - getLocale - ); - - var mathAbs = Math.abs; - - function abs() { - var data = this._data; - - this._milliseconds = mathAbs(this._milliseconds); - this._days = mathAbs(this._days); - this._months = mathAbs(this._months); - - data.milliseconds = mathAbs(data.milliseconds); - data.seconds = mathAbs(data.seconds); - data.minutes = mathAbs(data.minutes); - data.hours = mathAbs(data.hours); - data.months = mathAbs(data.months); - data.years = mathAbs(data.years); - - return this; - } - - function addSubtract$1(duration, input, value, direction) { - var other = createDuration(input, value); - - duration._milliseconds += direction * other._milliseconds; - duration._days += direction * other._days; - duration._months += direction * other._months; - - return duration._bubble(); - } - - // supports only 2.0-style add(1, 's') or add(duration) - function add$1(input, value) { - return addSubtract$1(this, input, value, 1); - } - - // supports only 2.0-style subtract(1, 's') or subtract(duration) - function subtract$1(input, value) { - return addSubtract$1(this, input, value, -1); - } - - function absCeil(number) { - if (number < 0) { - return Math.floor(number); - } else { - return Math.ceil(number); - } - } - - function bubble() { - var milliseconds = this._milliseconds, - days = this._days, - months = this._months, - data = this._data, - seconds, - minutes, - hours, - years, - monthsFromDays; - - // if we have a mix of positive and negative values, bubble down first - // check: https://github.com/moment/moment/issues/2166 - if ( - !( - (milliseconds >= 0 && days >= 0 && months >= 0) || - (milliseconds <= 0 && days <= 0 && months <= 0) - ) - ) { - milliseconds += absCeil(monthsToDays(months) + days) * 864e5; - days = 0; - months = 0; - } - - // The following code bubbles up values, see the tests for - // examples of what that means. - data.milliseconds = milliseconds % 1000; - - seconds = absFloor(milliseconds / 1000); - data.seconds = seconds % 60; - - minutes = absFloor(seconds / 60); - data.minutes = minutes % 60; - - hours = absFloor(minutes / 60); - data.hours = hours % 24; - - days += absFloor(hours / 24); - - // convert days to months - monthsFromDays = absFloor(daysToMonths(days)); - months += monthsFromDays; - days -= absCeil(monthsToDays(monthsFromDays)); - - // 12 months -> 1 year - years = absFloor(months / 12); - months %= 12; - - data.days = days; - data.months = months; - data.years = years; - - return this; - } - - function daysToMonths(days) { - // 400 years have 146097 days (taking into account leap year rules) - // 400 years have 12 months === 4800 - return (days * 4800) / 146097; - } - - function monthsToDays(months) { - // the reverse of daysToMonths - return (months * 146097) / 4800; - } - - function as(units) { - if (!this.isValid()) { - return NaN; - } - var days, - months, - milliseconds = this._milliseconds; - - units = normalizeUnits(units); - - if (units === 'month' || units === 'quarter' || units === 'year') { - days = this._days + milliseconds / 864e5; - months = this._months + daysToMonths(days); - switch (units) { - case 'month': - return months; - case 'quarter': - return months / 3; - case 'year': - return months / 12; - } - } else { - // handle milliseconds separately because of floating point math errors (issue #1867) - days = this._days + Math.round(monthsToDays(this._months)); - switch (units) { - case 'week': - return days / 7 + milliseconds / 6048e5; - case 'day': - return days + milliseconds / 864e5; - case 'hour': - return days * 24 + milliseconds / 36e5; - case 'minute': - return days * 1440 + milliseconds / 6e4; - case 'second': - return days * 86400 + milliseconds / 1000; - // Math.floor prevents floating point math errors here - case 'millisecond': - return Math.floor(days * 864e5) + milliseconds; - default: - throw new Error('Unknown unit ' + units); - } - } - } - - // TODO: Use this.as('ms')? - function valueOf$1() { - if (!this.isValid()) { - return NaN; - } - return ( - this._milliseconds + - this._days * 864e5 + - (this._months % 12) * 2592e6 + - toInt(this._months / 12) * 31536e6 - ); - } - - function makeAs(alias) { - return function () { - return this.as(alias); - }; - } - - var asMilliseconds = makeAs('ms'), - asSeconds = makeAs('s'), - asMinutes = makeAs('m'), - asHours = makeAs('h'), - asDays = makeAs('d'), - asWeeks = makeAs('w'), - asMonths = makeAs('M'), - asQuarters = makeAs('Q'), - asYears = makeAs('y'); - - function clone$1() { - return createDuration(this); - } - - function get$2(units) { - units = normalizeUnits(units); - return this.isValid() ? this[units + 's']() : NaN; - } - - function makeGetter(name) { - return function () { - return this.isValid() ? this._data[name] : NaN; - }; - } - - var milliseconds = makeGetter('milliseconds'), - seconds = makeGetter('seconds'), - minutes = makeGetter('minutes'), - hours = makeGetter('hours'), - days = makeGetter('days'), - months = makeGetter('months'), - years = makeGetter('years'); - - function weeks() { - return absFloor(this.days() / 7); - } - - var round = Math.round, - thresholds = { - ss: 44, // a few seconds to seconds - s: 45, // seconds to minute - m: 45, // minutes to hour - h: 22, // hours to day - d: 26, // days to month/week - w: null, // weeks to month - M: 11, // months to year - }; - - // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize - function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) { - return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture); - } - - function relativeTime$1(posNegDuration, withoutSuffix, thresholds, locale) { - var duration = createDuration(posNegDuration).abs(), - seconds = round(duration.as('s')), - minutes = round(duration.as('m')), - hours = round(duration.as('h')), - days = round(duration.as('d')), - months = round(duration.as('M')), - weeks = round(duration.as('w')), - years = round(duration.as('y')), - a = - (seconds <= thresholds.ss && ['s', seconds]) || - (seconds < thresholds.s && ['ss', seconds]) || - (minutes <= 1 && ['m']) || - (minutes < thresholds.m && ['mm', minutes]) || - (hours <= 1 && ['h']) || - (hours < thresholds.h && ['hh', hours]) || - (days <= 1 && ['d']) || - (days < thresholds.d && ['dd', days]); - - if (thresholds.w != null) { - a = - a || - (weeks <= 1 && ['w']) || - (weeks < thresholds.w && ['ww', weeks]); - } - a = a || - (months <= 1 && ['M']) || - (months < thresholds.M && ['MM', months]) || - (years <= 1 && ['y']) || ['yy', years]; - - a[2] = withoutSuffix; - a[3] = +posNegDuration > 0; - a[4] = locale; - return substituteTimeAgo.apply(null, a); - } - - // This function allows you to set the rounding function for relative time strings - function getSetRelativeTimeRounding(roundingFunction) { - if (roundingFunction === undefined) { - return round; - } - if (typeof roundingFunction === 'function') { - round = roundingFunction; - return true; - } - return false; - } - - // This function allows you to set a threshold for relative time strings - function getSetRelativeTimeThreshold(threshold, limit) { - if (thresholds[threshold] === undefined) { - return false; - } - if (limit === undefined) { - return thresholds[threshold]; - } - thresholds[threshold] = limit; - if (threshold === 's') { - thresholds.ss = limit - 1; - } - return true; - } - - function humanize(argWithSuffix, argThresholds) { - if (!this.isValid()) { - return this.localeData().invalidDate(); - } - - var withSuffix = false, - th = thresholds, - locale, - output; - - if (typeof argWithSuffix === 'object') { - argThresholds = argWithSuffix; - argWithSuffix = false; - } - if (typeof argWithSuffix === 'boolean') { - withSuffix = argWithSuffix; - } - if (typeof argThresholds === 'object') { - th = Object.assign({}, thresholds, argThresholds); - if (argThresholds.s != null && argThresholds.ss == null) { - th.ss = argThresholds.s - 1; - } - } - - locale = this.localeData(); - output = relativeTime$1(this, !withSuffix, th, locale); - - if (withSuffix) { - output = locale.pastFuture(+this, output); - } - - return locale.postformat(output); - } - - var abs$1 = Math.abs; - - function sign(x) { - return (x > 0) - (x < 0) || +x; - } - - function toISOString$1() { - // for ISO strings we do not use the normal bubbling rules: - // * milliseconds bubble up until they become hours - // * days do not bubble at all - // * months bubble up until they become years - // This is because there is no context-free conversion between hours and days - // (think of clock changes) - // and also not between days and months (28-31 days per month) - if (!this.isValid()) { - return this.localeData().invalidDate(); - } - - var seconds = abs$1(this._milliseconds) / 1000, - days = abs$1(this._days), - months = abs$1(this._months), - minutes, - hours, - years, - s, - total = this.asSeconds(), - totalSign, - ymSign, - daysSign, - hmsSign; - - if (!total) { - // this is the same as C#'s (Noda) and python (isodate)... - // but not other JS (goog.date) - return 'P0D'; - } - - // 3600 seconds -> 60 minutes -> 1 hour - minutes = absFloor(seconds / 60); - hours = absFloor(minutes / 60); - seconds %= 60; - minutes %= 60; - - // 12 months -> 1 year - years = absFloor(months / 12); - months %= 12; - - // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js - s = seconds ? seconds.toFixed(3).replace(/\.?0+$/, '') : ''; - - totalSign = total < 0 ? '-' : ''; - ymSign = sign(this._months) !== sign(total) ? '-' : ''; - daysSign = sign(this._days) !== sign(total) ? '-' : ''; - hmsSign = sign(this._milliseconds) !== sign(total) ? '-' : ''; - - return ( - totalSign + - 'P' + - (years ? ymSign + years + 'Y' : '') + - (months ? ymSign + months + 'M' : '') + - (days ? daysSign + days + 'D' : '') + - (hours || minutes || seconds ? 'T' : '') + - (hours ? hmsSign + hours + 'H' : '') + - (minutes ? hmsSign + minutes + 'M' : '') + - (seconds ? hmsSign + s + 'S' : '') - ); - } - - var proto$2 = Duration.prototype; - - proto$2.isValid = isValid$1; - proto$2.abs = abs; - proto$2.add = add$1; - proto$2.subtract = subtract$1; - proto$2.as = as; - proto$2.asMilliseconds = asMilliseconds; - proto$2.asSeconds = asSeconds; - proto$2.asMinutes = asMinutes; - proto$2.asHours = asHours; - proto$2.asDays = asDays; - proto$2.asWeeks = asWeeks; - proto$2.asMonths = asMonths; - proto$2.asQuarters = asQuarters; - proto$2.asYears = asYears; - proto$2.valueOf = valueOf$1; - proto$2._bubble = bubble; - proto$2.clone = clone$1; - proto$2.get = get$2; - proto$2.milliseconds = milliseconds; - proto$2.seconds = seconds; - proto$2.minutes = minutes; - proto$2.hours = hours; - proto$2.days = days; - proto$2.weeks = weeks; - proto$2.months = months; - proto$2.years = years; - proto$2.humanize = humanize; - proto$2.toISOString = toISOString$1; - proto$2.toString = toISOString$1; - proto$2.toJSON = toISOString$1; - proto$2.locale = locale; - proto$2.localeData = localeData; - - proto$2.toIsoString = deprecate( - 'toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)', - toISOString$1 - ); - proto$2.lang = lang; - - // FORMATTING - - addFormatToken('X', 0, 0, 'unix'); - addFormatToken('x', 0, 0, 'valueOf'); - - // PARSING - - addRegexToken('x', matchSigned); - addRegexToken('X', matchTimestamp); - addParseToken('X', function (input, array, config) { - config._d = new Date(parseFloat(input) * 1000); - }); - addParseToken('x', function (input, array, config) { - config._d = new Date(toInt(input)); - }); - - //! moment.js - - hooks.version = '2.29.1'; - - setHookCallback(createLocal); - - hooks.fn = proto; - hooks.min = min; - hooks.max = max; - hooks.now = now; - hooks.utc = createUTC; - hooks.unix = createUnix; - hooks.months = listMonths; - hooks.isDate = isDate; - hooks.locale = getSetGlobalLocale; - hooks.invalid = createInvalid; - hooks.duration = createDuration; - hooks.isMoment = isMoment; - hooks.weekdays = listWeekdays; - hooks.parseZone = createInZone; - hooks.localeData = getLocale; - hooks.isDuration = isDuration; - hooks.monthsShort = listMonthsShort; - hooks.weekdaysMin = listWeekdaysMin; - hooks.defineLocale = defineLocale; - hooks.updateLocale = updateLocale; - hooks.locales = listLocales; - hooks.weekdaysShort = listWeekdaysShort; - hooks.normalizeUnits = normalizeUnits; - hooks.relativeTimeRounding = getSetRelativeTimeRounding; - hooks.relativeTimeThreshold = getSetRelativeTimeThreshold; - hooks.calendarFormat = getCalendarFormat; - hooks.prototype = proto; - - // currently HTML5 input type only supports 24-hour formats - hooks.HTML5_FMT = { - DATETIME_LOCAL: 'YYYY-MM-DDTHH:mm', // - DATETIME_LOCAL_SECONDS: 'YYYY-MM-DDTHH:mm:ss', // - DATETIME_LOCAL_MS: 'YYYY-MM-DDTHH:mm:ss.SSS', // - DATE: 'YYYY-MM-DD', // - TIME: 'HH:mm', // - TIME_SECONDS: 'HH:mm:ss', // - TIME_MS: 'HH:mm:ss.SSS', // - WEEK: 'GGGG-[W]WW', // - MONTH: 'YYYY-MM', // - }; - - return hooks; - -}))); From 8cd291ef2aa4876c45855ea592f845e2eadb501b Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sun, 21 Nov 2021 21:41:53 -0700 Subject: [PATCH 075/255] Store dates as strings --- database/scores/games.js | 4 ++-- routes/submit.js | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/database/scores/games.js b/database/scores/games.js index 96463ec..9af0b89 100644 --- a/database/scores/games.js +++ b/database/scores/games.js @@ -1,5 +1,4 @@ const database = require('./../database'); -const moment = require('moment'); @@ -51,8 +50,9 @@ async function retrieveByTeamDivisionAndSeason(teamID, divisionID, seasonID) { teamScore = opponentIsTeam2 ? row[6] : row[7]; opponentScore = opponentIsTeam2 ? row[7] : row[6]; - gamesList.push(new Game(row[0], moment(row[3]), teamID, opponentID, teamScore, opponentScore)); + gamesList.push(new Game(row[0], row[3].toISOString().slice(0,10), teamID, opponentID, teamScore, opponentScore)); }); + console.log(gamesList); return gamesList; } diff --git a/routes/submit.js b/routes/submit.js index 8510303..fcd660b 100644 --- a/routes/submit.js +++ b/routes/submit.js @@ -2,11 +2,10 @@ var express = require('express'); var router = express.Router(); var genders = require('../database/scores/genders'); var games = require('../database/scores/games'); -var moment = require('moment'); /* GET submit page. */ router.get('/', function(req, res, next) { - res.render('submit', { title: 'Submit Score', date: moment().format('YYYY-MM-DD') }); + res.render('submit', { title: 'Submit Score' }); }); /* POST submit page. */ From fc60e5186a0bd47aa1d28690b832e3876bf6cf26 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sun, 21 Nov 2021 21:42:03 -0700 Subject: [PATCH 076/255] Fix bug in teams.js --- database/scores/teams.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/database/scores/teams.js b/database/scores/teams.js index 7aa3d32..893cc34 100644 --- a/database/scores/teams.js +++ b/database/scores/teams.js @@ -55,8 +55,7 @@ async function getFromID(id) { const query = `SELECT team_name FROM scores.teams WHERE team_id = $1;`; - const name = await database.executeQuery(query, [teamID])[0][0]; - + const name = (await database.executeQuery(query, [id]))[0][0]; return new Team(id, name); } From 4f2b496cf2241c2acfeee3dcf3babc2cd65dd4f9 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sun, 21 Nov 2021 21:59:07 -0700 Subject: [PATCH 077/255] Add table of games to index page --- public/scripts/data.js | 12 +++++++ public/scripts/index.js | 68 ++++++++++++++++++++++++++++++++++-- public/stylesheets/index.css | 18 ++++++++++ routes/data.js | 13 ++++++- views/index.pug | 13 +++---- 5 files changed, 114 insertions(+), 10 deletions(-) diff --git a/public/scripts/data.js b/public/scripts/data.js index 7e21436..61f3923 100644 --- a/public/scripts/data.js +++ b/public/scripts/data.js @@ -26,4 +26,16 @@ export async function getTeams(sportID) { const response = await fetch(`/data/teams?sport=${+sportID}`); const teamsList = await response.json(); return teamsList; +} + +export async function getTeamName(teamID) { + const response = await fetch(`/data/team?team=${+teamID}`); + const team = await response.json(); + return team.name; +} + +export async function getGames(teamID, divisionID, seasonID) { + const response = await fetch(`/data/games?team=${+teamID}&division=${+divisionID}&season=${+seasonID}`); + const gamesList = await response.json(); + return gamesList; } \ No newline at end of file diff --git a/public/scripts/index.js b/public/scripts/index.js index 38627c0..4111353 100644 --- a/public/scripts/index.js +++ b/public/scripts/index.js @@ -1,11 +1,12 @@ import * as Data from "./data.js"; - const sportDropdown = document.getElementById('sport-dropdown'); const seasonDropdown = document.getElementById('year-dropdown'); const genderDropdown = document.getElementById('gender-dropdown'); const divisionDropdown = document.getElementById('division-dropdown'); const teamDropdown = document.getElementById('team-dropdown'); +const gamesTable = document.getElementById('games-table'); +const gamesTableHeader = document.getElementById('games-table-header'); @@ -38,7 +39,6 @@ async function listSports() { }); listGenders(); - listTeams(); } listSports(); @@ -76,6 +76,7 @@ async function listDivisions() { divisionDropdown.appendChild(option); }); } + listTeams(); } async function listTeams() { @@ -93,6 +94,66 @@ async function listTeams() { teamDropdown.appendChild(option); }); } + + listGames(); +} + +async function listGames() { + gamesTable.innerHTML = ""; + gamesTableHeader.textContent = ""; + + const selectedTeamID = teamDropdown.value; + const selectedDivisionID = divisionDropdown.value; + const selectedSeasonID = seasonDropdown.value; + + if(selectedTeamID && selectedDivisionID) { + await setupGamesTable(); + + const gamesList = await Data.getGames(selectedTeamID, selectedDivisionID, selectedSeasonID); + + gamesList.forEach((game) => { + const row = document.createElement('tr'); + + const scoreCell = document.createElement('td'); + const winLossLine = document.createElement('span'); + winLossLine.textContent = (game.team1Score > game.team2Score) ? "Win" : (game.team1Score < game.team2Score) ? "Loss" : "Tie"; + scoreCell.appendChild(winLossLine); + const scoreLine = document.createElement('span'); + scoreLine.textContent = game.team1Score + "-" + game.team2Score; + scoreCell.appendChild(scoreLine); + row.appendChild(scoreCell); + + const opponentCell = document.createElement('td'); + Data.getTeamName(game.team2ID) + .then(data => opponentCell.textContent = data); + row.appendChild(opponentCell); + + const dateCell = document.createElement('td'); + dateCell.textContent = game.date; + row.appendChild(dateCell); + + gamesTable.appendChild(row); + }); + } +} +async function setupGamesTable() { + gamesTableHeader.textContent = `Scores for ${teamDropdown.options[teamDropdown.selectedIndex].text}`; + + const row = document.createElement('tr'); + + const scoresHeader = document.createElement('th'); + scoresHeader.textContent = "Score" + row.appendChild(scoresHeader); + + const opponentHeader = document.createElement('th'); + opponentHeader.textContent = "Opponent"; + row.appendChild(opponentHeader); + + const dateHeader = document.createElement('th'); + dateHeader.textContent = "Date"; + row.appendChild(dateHeader); + + gamesTable.appendChild(row); } @@ -103,4 +164,5 @@ sportDropdown.onchange = (() => { listGenders(); listTeams(); }); -genderDropdown.onchange = listDivisions; \ No newline at end of file +genderDropdown.onchange = listDivisions; +teamDropdown.onchange = listGames; \ No newline at end of file diff --git a/public/stylesheets/index.css b/public/stylesheets/index.css index e4d4d83..2f8f252 100644 --- a/public/stylesheets/index.css +++ b/public/stylesheets/index.css @@ -1,3 +1,21 @@ h1 { text-align: left; +} + +th { + text-align: left; +} + +#score-column { + width: 20%; +} +#opponent-column{ + width: 60%; +} +#date-column { + width: 20%; +} + +tr { + height: 3em; } \ No newline at end of file diff --git a/routes/data.js b/routes/data.js index fc6c947..4344d2d 100644 --- a/routes/data.js +++ b/routes/data.js @@ -5,6 +5,7 @@ var seasons = require('../database/scores/seasons'); var genders = require('../database/scores/genders'); var divisions = require('../database/scores/divisions'); var teams = require('../database/scores/teams'); +var games = require('../database/scores/games'); router.get('/sports', function(req, res, next) { @@ -23,7 +24,7 @@ router.get('/genders', function(req, res, next) { }) router.get('/divisions', function(req, res, next) { - const gender = req.body.gender == 'female' ? genders.FEMALE : genders.MALE; + const gender = req.query.gender == 'female' ? genders.FEMALE : genders.MALE; divisions.retrieveBySportAndGender(req.query.sport, gender) .then(data => res.json(data)); @@ -34,4 +35,14 @@ router.get('/teams', function(req, res, next) { .then(data => res.json(data)); }) +router.get('/team', function(req, res, next) { + teams.getFromID(req.query.team) + .then(data => res.json(data)); +}) + +router.get('/games', function(req, res, next) { + games.retrieveByTeamDivisionAndSeason(req.query.team, req.query.division, req.query.season) + .then(data => res.json(data)); +}) + module.exports = router; \ No newline at end of file diff --git a/views/index.pug b/views/index.pug index 8a93bd0..6ec6e7c 100644 --- a/views/index.pug +++ b/views/index.pug @@ -24,12 +24,13 @@ block content select#team-dropdown(name="team" class="form-main-dropdown") span(class='form-section') div - table - tr - th Win? - th Opponent - th Score - th Date + h2#games-table-header + table + colgroup + col#score-column(span="1") + col#opponent-column(span="1") + col#date-column(span="1") + tbody#games-table block scripts From 8674781ba9cda6c08367c2dcae5cd75314594fbc Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sun, 21 Nov 2021 22:07:25 -0700 Subject: [PATCH 078/255] Tweak scores view page --- public/scripts/index.js | 62 +++++++++++++++++++++++------------------ views/index.pug | 1 + 2 files changed, 36 insertions(+), 27 deletions(-) diff --git a/public/scripts/index.js b/public/scripts/index.js index 4111353..4a4721c 100644 --- a/public/scripts/index.js +++ b/public/scripts/index.js @@ -7,6 +7,7 @@ const divisionDropdown = document.getElementById('division-dropdown'); const teamDropdown = document.getElementById('team-dropdown'); const gamesTable = document.getElementById('games-table'); const gamesTableHeader = document.getElementById('games-table-header'); +const noScoresMessage = document.getElementById('no-scores-message'); @@ -101,43 +102,49 @@ async function listTeams() { async function listGames() { gamesTable.innerHTML = ""; gamesTableHeader.textContent = ""; + noScoresMessage.textContent = ""; const selectedTeamID = teamDropdown.value; const selectedDivisionID = divisionDropdown.value; const selectedSeasonID = seasonDropdown.value; if(selectedTeamID && selectedDivisionID) { - await setupGamesTable(); + gamesTableHeader.textContent = `Scores for ${teamDropdown.options[teamDropdown.selectedIndex].text}`; const gamesList = await Data.getGames(selectedTeamID, selectedDivisionID, selectedSeasonID); + if(gamesList.length > 0) { + await setupGamesTableHeaders(); - gamesList.forEach((game) => { - const row = document.createElement('tr'); - - const scoreCell = document.createElement('td'); - const winLossLine = document.createElement('span'); - winLossLine.textContent = (game.team1Score > game.team2Score) ? "Win" : (game.team1Score < game.team2Score) ? "Loss" : "Tie"; - scoreCell.appendChild(winLossLine); - const scoreLine = document.createElement('span'); - scoreLine.textContent = game.team1Score + "-" + game.team2Score; - scoreCell.appendChild(scoreLine); - row.appendChild(scoreCell); - - const opponentCell = document.createElement('td'); - Data.getTeamName(game.team2ID) - .then(data => opponentCell.textContent = data); - row.appendChild(opponentCell); - - const dateCell = document.createElement('td'); - dateCell.textContent = game.date; - row.appendChild(dateCell); - - gamesTable.appendChild(row); - }); + gamesList.forEach((game) => { + const row = document.createElement('tr'); + + const scoreCell = document.createElement('td'); + const winLossLine = document.createElement('span'); + winLossLine.textContent = (game.team1Score > game.team2Score) ? "Win" : (game.team1Score < game.team2Score) ? "Loss" : "Tie"; + scoreCell.appendChild(winLossLine); + const scoreLine = document.createElement('span'); + scoreLine.textContent = game.team1Score + "-" + game.team2Score; + scoreCell.appendChild(scoreLine); + row.appendChild(scoreCell); + + const opponentCell = document.createElement('td'); + Data.getTeamName(game.team2ID) + .then(data => opponentCell.textContent = data); + row.appendChild(opponentCell); + + const dateCell = document.createElement('td'); + dateCell.textContent = game.date; + row.appendChild(dateCell); + + gamesTable.appendChild(row); + }); + } + else { + noScoresMessage.textContent = "No scores available"; + } } } -async function setupGamesTable() { - gamesTableHeader.textContent = `Scores for ${teamDropdown.options[teamDropdown.selectedIndex].text}`; +async function setupGamesTableHeaders() { const row = document.createElement('tr'); @@ -165,4 +172,5 @@ sportDropdown.onchange = (() => { listTeams(); }); genderDropdown.onchange = listDivisions; -teamDropdown.onchange = listGames; \ No newline at end of file +teamDropdown.onchange = listGames; +seasonDropdown.onchange = listGames; \ No newline at end of file diff --git a/views/index.pug b/views/index.pug index 6ec6e7c..1d3ffb2 100644 --- a/views/index.pug +++ b/views/index.pug @@ -25,6 +25,7 @@ block content span(class='form-section') div h2#games-table-header + span#no-scores-message table colgroup col#score-column(span="1") From 9db8ae51515f9c82fff957bb713217cbc192952e Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sun, 21 Nov 2021 23:16:29 -0700 Subject: [PATCH 079/255] remove require database from index.js --- routes/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/routes/index.js b/routes/index.js index 4cb7f20..3c536d2 100644 --- a/routes/index.js +++ b/routes/index.js @@ -1,6 +1,5 @@ var express = require('express'); var router = express.Router(); -var database = require('../database/database'); /* GET home page. */ router.get('/', function(req, res, next) { From dca907964843f2137e1f94e8e067af70eb48cca4 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sun, 21 Nov 2021 23:17:43 -0700 Subject: [PATCH 080/255] Create files for /manage page --- routes/manage.js | 9 +++++++++ views/manage.pug | 0 2 files changed, 9 insertions(+) create mode 100644 routes/manage.js create mode 100644 views/manage.pug diff --git a/routes/manage.js b/routes/manage.js new file mode 100644 index 0000000..796064b --- /dev/null +++ b/routes/manage.js @@ -0,0 +1,9 @@ +var express = require('express'); +var router = express.Router(); + +/* GET manage page. */ +router.get('/', function(req, res, next) { + res.render('manage', { title: 'Score Management' }); +}); + +module.exports = router; diff --git a/views/manage.pug b/views/manage.pug new file mode 100644 index 0000000..e69de29 From 41d9140863c5ea08abf7b88af456ecd6cab47914 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Sun, 21 Nov 2021 23:23:06 -0700 Subject: [PATCH 081/255] Remove console.log used for debugging --- database/scores/games.js | 1 - 1 file changed, 1 deletion(-) diff --git a/database/scores/games.js b/database/scores/games.js index 9af0b89..265dd56 100644 --- a/database/scores/games.js +++ b/database/scores/games.js @@ -52,7 +52,6 @@ async function retrieveByTeamDivisionAndSeason(teamID, divisionID, seasonID) { gamesList.push(new Game(row[0], row[3].toISOString().slice(0,10), teamID, opponentID, teamScore, opponentScore)); }); - console.log(gamesList); return gamesList; } From f7dfd24a68d96bef2d3ad71ce9b75dbacf9b1474 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 22 Nov 2021 13:44:19 -0700 Subject: [PATCH 082/255] Remove unnecessary div from index.pug --- views/index.pug | 1 - 1 file changed, 1 deletion(-) diff --git a/views/index.pug b/views/index.pug index 1d3ffb2..2e52919 100644 --- a/views/index.pug +++ b/views/index.pug @@ -22,7 +22,6 @@ block content label Team span(class='form-section-input') select#team-dropdown(name="team" class="form-main-dropdown") - span(class='form-section') div h2#games-table-header span#no-scores-message From a6e8e924e4459858eb2de01a9e5c7e0b6c7861b1 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 22 Nov 2021 13:56:57 -0700 Subject: [PATCH 083/255] Create basic layout for manage page --- views/manage.pug | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/views/manage.pug b/views/manage.pug index e69de29..79ac0b0 100644 --- a/views/manage.pug +++ b/views/manage.pug @@ -0,0 +1,31 @@ +extends layout + +block stylesheets + link(rel='stylesheet', href='/stylesheets/index.css') + link(rel='stylesheet', href='/stylesheets/form.css') + +block content + div#mobile-view + h1 Management Panel + div + span(class='form-section') + label Category + span(class='form-section-input') + select#category-dropdown(name="category" class="form-main-dropdown") + option(value="seasons") Seasons + option(value="sports") Sports + option(value="divisions") Divisions + option(value="teams") Teams + option(value="games") Games + div + h2#table-header + table + colgroup + col#score-column(span="1") + col#opponent-column(span="1") + col#date-column(span="1") + tbody#games-table + + +block scripts + script(src='/scripts/index.js' type="module") \ No newline at end of file From 7baa986a30093360ff704ddacf51fcdc1582a759 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 22 Nov 2021 13:57:13 -0700 Subject: [PATCH 084/255] Add manage route to app.js --- app.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app.js b/app.js index 29f7b4f..6bc76de 100644 --- a/app.js +++ b/app.js @@ -8,6 +8,7 @@ var indexRouter = require('./routes/index'); var usersRouter = require('./routes/users'); var submitRouter = require('./routes/submit'); var dataRouter = require('./routes/data'); +var manageRouter = require('./routes/manage'); var app = express(); @@ -25,6 +26,8 @@ app.use('/', indexRouter); app.use('/users', usersRouter); app.use('/submit', submitRouter); app.use('/data', dataRouter); +app.use('/manage', manageRouter); + // catch 404 and forward to error handler app.use(function(req, res, next) { From 77359ba3a7f99d1a58369a69e0619660dbe0960b Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 22 Nov 2021 14:07:06 -0700 Subject: [PATCH 085/255] Add formatting tweak to index.js --- public/scripts/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/scripts/index.js b/public/scripts/index.js index 4a4721c..41d9610 100644 --- a/public/scripts/index.js +++ b/public/scripts/index.js @@ -20,7 +20,7 @@ async function listSeasons() { seasonsList.forEach(season => { const option = document.createElement('option'); - option.text = season.year - 1 + "-" + season.year; + option.text = (season.year - 1) + "-" + season.year; option.value = season.id; seasonDropdown.appendChild(option); }); From 1ce1bdb27c4edc2921eeee241a138d12c3761158 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 22 Nov 2021 14:07:51 -0700 Subject: [PATCH 086/255] Add formatting tweak to submit.js --- public/scripts/submit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/scripts/submit.js b/public/scripts/submit.js index bd4d08a..8c3cf81 100644 --- a/public/scripts/submit.js +++ b/public/scripts/submit.js @@ -19,7 +19,7 @@ async function listSeasons() { seasonsList.forEach(season => { const option = document.createElement('option'); - option.text = season.year - 1 + "-" + season.year; + option.text = (season.year - 1) + "-" + season.year; option.value = season.id; seasonDropdown.appendChild(option); }); From 4aafccc5505fdd4d475854cf29a1cd8caf33b0e8 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 22 Nov 2021 14:50:32 -0700 Subject: [PATCH 087/255] Add functions to list seasons and sports in manage page --- public/scripts/manage.js | 116 +++++++++++++++++++++++++++++++++++++++ views/manage.pug | 13 ++--- 2 files changed, 120 insertions(+), 9 deletions(-) create mode 100644 public/scripts/manage.js diff --git a/public/scripts/manage.js b/public/scripts/manage.js new file mode 100644 index 0000000..19b700a --- /dev/null +++ b/public/scripts/manage.js @@ -0,0 +1,116 @@ +import * as Data from "./data.js"; + +const categoryDropdown = document.getElementById('category-dropdown'); +const itemsListTable = document.getElementById('items-list'); + +class Category { + constructor(name, getItems, listHeaders, listItem, addItem, submitItem, editItem) { + this.name = name; + this.getItems = getItems; + this.listHeaders = listHeaders; + this.listItem = listItem; + this.addItem = addItem; + this.submitItem = submitItem; + this.editItem = editItem; + } +} + +const CATEGORIES = []; + +CATEGORIES.push(new Category( + "seasons", + async function getSeasons() { + return await Data.getSeasons(); + }, + async function listSeasonHeaders() { + const headerRow = document.createElement('tr'); + + const yearsHeader = document.createElement('th'); + yearsHeader.textContent = "Years"; + + headerRow.appendChild(yearsHeader); + + itemsListTable.appendChild(headerRow); + }, + function listSeason(season, row) { + const yearCell = document.createElement('td'); + yearCell.textContent = (season.year - 1) + "-" + season.year; + row.appendChild(yearCell); + }, + async function addSeason() { + // + }, + async function submitSeason() { + // + }, + async function editSeason() { + + } +)); + +CATEGORIES.push(new Category( + "sports", + async function getSports() { + return await Data.getSports(); + }, + async function listSportHeaders() { + const headerRow = document.createElement('tr'); + + const yearsHeader = document.createElement('th'); + yearsHeader.textContent = "Name"; + headerRow.appendChild(yearsHeader); + + itemsListTable.appendChild(headerRow); + }, + function listSport(sport, row) { + const nameCell = document.createElement('td'); + nameCell.textContent = sport.name; + row.appendChild(nameCell); + }, + async function addSeason() { + // + }, + async function submitSeason() { + // + }, + async function editSeason() { + + } +)); + + + +async function listItems(category) { + itemsListTable.innerHTML = ""; + + const itemsList = await category.getItems(); + + await category.listHeaders(); + + itemsList.forEach(item => { + const row = document.createElement('tr'); + + category.listItem(item, row); + + const editCell = document.createElement('td'); + const editButton = document.createElement('button'); + editButton.textContent = "edit"; + editCell.appendChild(editButton); + row.appendChild(editCell); + + const removeCell = document.createElement('td'); + const removeButton = document.createElement('button'); + removeButton.textContent = "remove"; + removeCell.appendChild(removeButton); + row.appendChild(removeCell); + + itemsListTable.appendChild(row); + }); +} +listItems(CATEGORIES[0]); + + + +categoryDropdown.onchange = () => { + listItems(CATEGORIES[categoryDropdown.selectedIndex]); +}; \ No newline at end of file diff --git a/views/manage.pug b/views/manage.pug index 79ac0b0..7977824 100644 --- a/views/manage.pug +++ b/views/manage.pug @@ -1,7 +1,7 @@ extends layout block stylesheets - link(rel='stylesheet', href='/stylesheets/index.css') + link(rel='stylesheet', href='/stylesheets/manage.css') link(rel='stylesheet', href='/stylesheets/form.css') block content @@ -19,13 +19,8 @@ block content option(value="games") Games div h2#table-header - table - colgroup - col#score-column(span="1") - col#opponent-column(span="1") - col#date-column(span="1") - tbody#games-table - + table#items-list + button Add new... block scripts - script(src='/scripts/index.js' type="module") \ No newline at end of file + script(src='/scripts/manage.js' type="module") \ No newline at end of file From 75594a81a6497272941e897485797a9b9728ff74 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 22 Nov 2021 14:58:43 -0700 Subject: [PATCH 088/255] Add option to fetch all divisions --- public/scripts/data.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/public/scripts/data.js b/public/scripts/data.js index 61f3923..502bbb8 100644 --- a/public/scripts/data.js +++ b/public/scripts/data.js @@ -16,8 +16,12 @@ export async function getGenders(sportID) { return gendersList; } -export async function getDivisions(sportID, gender) { - const response = await fetch(`/data/divisions?sport=${+sportID}&gender=${gender}`); +export async function getDivisions(sportID = undefined, gender = undefined) { + let URL = '/data/divisions?'; + if(sportID) URL += `sport=${+sportID}&`; + if(gender) URL += `gender=${gender}&`; + + const response = await fetch(URL); const divisionsList = await response.json(); return divisionsList; } From 435748ae7663ea3af7219d76396ccfce24a22c4f Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 22 Nov 2021 15:20:59 -0700 Subject: [PATCH 089/255] Add functionality to return all divisions in database --- database/scores/divisions.js | 30 ++++++++++++++++++++++-------- routes/data.js | 5 +++-- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/database/scores/divisions.js b/database/scores/divisions.js index 38fa8be..80fe35e 100644 --- a/database/scores/divisions.js +++ b/database/scores/divisions.js @@ -6,9 +6,11 @@ const genders = require('./genders'); class Division { - constructor(id, name) { + constructor(id, name, gender, sportID) { this.id = id; this.name = name; + this.gender = gender; + this.sportID = sportID; } } @@ -29,7 +31,7 @@ async function add(name, gender, sportID) { return new Division(id, name); } -async function rename(id, division) { +async function rename(id, name) { const query = `UPDATE scores.divisions SET division_name = $2 WHERE division_id = $1;`; @@ -45,17 +47,29 @@ async function remove(id) { return new Division(id, name); } -async function retrieveBySportAndGender(sportID, gender) { - const query = `SELECT * +async function retrieve(sportID = undefined, gender = undefined) { + let table; + + if(sportID && gender) { + const query = `SELECT division_id, division_name, gender, sport_id FROM scores.divisions WHERE sport_id = $1 AND gender = $2 ORDER BY division_name;`; - const genderID = getGenderID(gender); - const table = await database.executeQuery(query, [sportID, genderID]); + const genderID = getGenderID(gender); + table = await database.executeQuery(query, [sportID, genderID]); + } + else { + const query = `SELECT division_id, division_name, gender, sport_id + FROM scores.divisions + ORDER BY division_name, + gender;`; + table = await database.executeQuery(query); + } const divisionsList = []; table.forEach((row) => { - divisionsList.push(new Division(row[0], row[1])); + let gender = (row[2] == "F") ? genders.FEMALE : genders.MALE; + divisionsList.push(new Division(row[0], row[1], gender, row[3])); }); return divisionsList; } @@ -67,4 +81,4 @@ async function retrieveBySportAndGender(sportID, gender) { exports.add = add; exports.rename = rename; exports.remove = remove; -exports.retrieveBySportAndGender = retrieveBySportAndGender; \ No newline at end of file +exports.retrieve = retrieve; \ No newline at end of file diff --git a/routes/data.js b/routes/data.js index 4344d2d..588ed2f 100644 --- a/routes/data.js +++ b/routes/data.js @@ -24,9 +24,10 @@ router.get('/genders', function(req, res, next) { }) router.get('/divisions', function(req, res, next) { - const gender = req.query.gender == 'female' ? genders.FEMALE : genders.MALE; + let gender; + if(req.query.gender) gender = (req.query.gender == 'female' ? genders.FEMALE : genders.MALE); - divisions.retrieveBySportAndGender(req.query.sport, gender) + divisions.retrieve(req.query.sport, gender) .then(data => res.json(data)); }) From b6f8b6bdaa78a57bdbba80ea08e0d0be3029a457 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 22 Nov 2021 15:30:02 -0700 Subject: [PATCH 090/255] Add functions to retrieve sport by ID --- database/scores/sports.js | 11 ++++++++++- public/scripts/data.js | 10 ++++++++-- routes/data.js | 5 +++++ 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/database/scores/sports.js b/database/scores/sports.js index 14a6e45..134009d 100644 --- a/database/scores/sports.js +++ b/database/scores/sports.js @@ -48,6 +48,14 @@ async function retrieveAll() { return sportsList; } +async function getFromID(id) { + const query = `SELECT sport_name + FROM scores.sports + WHERE sport_id = $1;`; + const name = (await database.executeQuery(query, [id]))[0][0]; + return new Sport(id, name); +} + @@ -55,4 +63,5 @@ async function retrieveAll() { exports.add = add; exports.rename = rename; exports.remove = remove; -exports.retrieveAll = retrieveAll; \ No newline at end of file +exports.retrieveAll = retrieveAll; +exports.getFromID = getFromID; \ No newline at end of file diff --git a/public/scripts/data.js b/public/scripts/data.js index 502bbb8..567c1ac 100644 --- a/public/scripts/data.js +++ b/public/scripts/data.js @@ -1,11 +1,17 @@ export async function getSports() { - const response = await fetch('/data/sports'); + const response = await fetch(`/data/sports`); const sportsList = await response.json(); return sportsList; } +export async function getSportName(sportID) { + const response = await fetch(`/data/sport?sport=${sportID}`); + const sport = await response.json(); + return sport.name; +} + export async function getSeasons() { - const response = await fetch('/data/seasons'); + const response = await fetch(`/data/seasons`); const seasonsList = await response.json(); return seasonsList; } diff --git a/routes/data.js b/routes/data.js index 588ed2f..fc61052 100644 --- a/routes/data.js +++ b/routes/data.js @@ -13,6 +13,11 @@ router.get('/sports', function(req, res, next) { .then(data => res.json(data)); }); +router.get('/sport', function(req, res, next) { + sports.getFromID(req.query.sport) + .then(data => res.json(data)); +}); + router.get('/seasons', function(req, res, next) { seasons.retrieveAll() .then(data => res.json(data)); From 18f840183fad1627fe59c2d1b621505c3b27c93a Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 22 Nov 2021 15:32:44 -0700 Subject: [PATCH 091/255] Add functionality to list divisions in management page --- public/scripts/manage.js | 53 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/public/scripts/manage.js b/public/scripts/manage.js index 19b700a..8958f1c 100644 --- a/public/scripts/manage.js +++ b/public/scripts/manage.js @@ -56,9 +56,9 @@ CATEGORIES.push(new Category( async function listSportHeaders() { const headerRow = document.createElement('tr'); - const yearsHeader = document.createElement('th'); - yearsHeader.textContent = "Name"; - headerRow.appendChild(yearsHeader); + const nameHeader = document.createElement('th'); + nameHeader.textContent = "Name"; + headerRow.appendChild(nameHeader); itemsListTable.appendChild(headerRow); }, @@ -67,6 +67,53 @@ CATEGORIES.push(new Category( nameCell.textContent = sport.name; row.appendChild(nameCell); }, + async function addSport() { + // + }, + async function submitSport() { + // + }, + async function editSport() { + + } +)); + +CATEGORIES.push(new Category( + "divisions", + async function getDivisions() { + return await Data.getDivisions(); + }, + async function listDivisionHeaders() { + const headerRow = document.createElement('tr'); + + const nameHeader = document.createElement('th'); + nameHeader.textContent = "Name"; + headerRow.appendChild(nameHeader); + + const genderHeader = document.createElement('th'); + headerRow.appendChild(genderHeader); + + const sportHeader = document.createElement('th'); + sportHeader.textContent = "Sport"; + headerRow.appendChild(sportHeader); + + itemsListTable.appendChild(headerRow); + }, + function listDivision(division, row) { + const nameCell = document.createElement('td'); + nameCell.textContent = division.name; + row.appendChild(nameCell); + + const genderCell = document.createElement('td'); + const gender = division.gender.name == "female" ? "F" : "M"; + genderCell.textContent = gender; + row.appendChild(genderCell); + + const sportCell = document.createElement('td'); + Data.getSportName(division.sportID) + .then(data => sportCell.textContent = data); + row.appendChild(sportCell); + }, async function addSeason() { // }, From 28b11b7266689063c9b112fa284a4c661119b3b6 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 22 Nov 2021 15:38:20 -0700 Subject: [PATCH 092/255] Group divisions by sport_id --- database/scores/divisions.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/database/scores/divisions.js b/database/scores/divisions.js index 80fe35e..a3cebd2 100644 --- a/database/scores/divisions.js +++ b/database/scores/divisions.js @@ -61,7 +61,8 @@ async function retrieve(sportID = undefined, gender = undefined) { else { const query = `SELECT division_id, division_name, gender, sport_id FROM scores.divisions - ORDER BY division_name, + ORDER BY sport_id, + division_name, gender;`; table = await database.executeQuery(query); } From 90ef696b717ec05e92ffd55b840b377ec44e2488 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 22 Nov 2021 15:45:19 -0700 Subject: [PATCH 093/255] Add retrieve all teams in database functionality --- database/scores/teams.js | 25 +++++++++++++++++++------ public/scripts/data.js | 7 +++++-- routes/data.js | 2 +- 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/database/scores/teams.js b/database/scores/teams.js index 893cc34..3717841 100644 --- a/database/scores/teams.js +++ b/database/scores/teams.js @@ -5,9 +5,10 @@ const database = require('./../database'); class Team { - constructor(id, name) { + constructor(id, name, sportID) { this.id = id; this.name = name; + this.sportID = sportID; } } @@ -37,16 +38,28 @@ async function remove(id) { return new Team(id, name); } -async function retrieveBySport(sportID) { - const query = `SELECT * +async function retrieve(sportID = undefined) { + let table; + + if(sportID) { + const query = `SELECT team_id, team_name, sport_id FROM scores.teams WHERE sport_id = $1 ORDER BY team_name;`; - const table = await database.executeQuery(query, [sportID]); + table = await database.executeQuery(query, [sportID]); + } + else { + const query = `SELECT team_id, team_name, sport_id + FROM scores.teams + ORDER BY + sport_id, + team_name;`; + table = await database.executeQuery(query); + } const teamsList = []; table.forEach((row) => { - teamsList.push(new Team(row[0], row[1])); + teamsList.push(new Team(row[0], row[1], row[2])); }); return teamsList; } @@ -66,5 +79,5 @@ async function getFromID(id) { exports.add = add; exports.rename = rename; exports.remove = remove; -exports.retrieveBySport = retrieveBySport; +exports.retrieve = retrieve; exports.getFromID = getFromID; \ No newline at end of file diff --git a/public/scripts/data.js b/public/scripts/data.js index 567c1ac..b83fdd0 100644 --- a/public/scripts/data.js +++ b/public/scripts/data.js @@ -32,8 +32,11 @@ export async function getDivisions(sportID = undefined, gender = undefined) { return divisionsList; } -export async function getTeams(sportID) { - const response = await fetch(`/data/teams?sport=${+sportID}`); +export async function getTeams(sportID = undefined) { + let URL = '/data/teams?'; + if(sportID) URL += `sport=${+sportID}&`; + + const response = await fetch(URL); const teamsList = await response.json(); return teamsList; } diff --git a/routes/data.js b/routes/data.js index fc61052..f9c6431 100644 --- a/routes/data.js +++ b/routes/data.js @@ -37,7 +37,7 @@ router.get('/divisions', function(req, res, next) { }) router.get('/teams', function(req, res, next) { - teams.retrieveBySport(req.query.sport) + teams.retrieve(req.query.sport) .then(data => res.json(data)); }) From 6fdbaedc791db514e5f61ab9318690c952fd0976 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 22 Nov 2021 15:48:30 -0700 Subject: [PATCH 094/255] Add functionality to list teams on manage page --- public/scripts/manage.js | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/public/scripts/manage.js b/public/scripts/manage.js index 8958f1c..f6bcda6 100644 --- a/public/scripts/manage.js +++ b/public/scripts/manage.js @@ -125,6 +125,45 @@ CATEGORIES.push(new Category( } )); +CATEGORIES.push(new Category( + "teams", + async function getTeams() { + return await Data.getTeams(); + }, + async function listTeamHeaders() { + const headerRow = document.createElement('tr'); + + const nameHeader = document.createElement('th'); + nameHeader.textContent = "Name"; + headerRow.appendChild(nameHeader); + + const sportHeader = document.createElement('th'); + sportHeader.textContent = "Sport"; + headerRow.appendChild(sportHeader); + + itemsListTable.appendChild(headerRow); + }, + function listTeam(team, row) { + const nameCell = document.createElement('td'); + nameCell.textContent = team.name; + row.appendChild(nameCell); + + const sportCell = document.createElement('td'); + Data.getSportName(team.sportID) + .then(data => sportCell.textContent = data); + row.appendChild(sportCell); + }, + async function addSeason() { + // + }, + async function submitSeason() { + // + }, + async function editSeason() { + + } +)); + async function listItems(category) { From 17f14573e94647befee47ca1190902f46103b484 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 22 Nov 2021 16:00:12 -0700 Subject: [PATCH 095/255] Add functionality to get all games from database --- database/scores/games.js | 25 +++++++++++++++++++------ public/scripts/data.js | 10 ++++++++-- routes/data.js | 2 +- 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/database/scores/games.js b/database/scores/games.js index 265dd56..9f27bab 100644 --- a/database/scores/games.js +++ b/database/scores/games.js @@ -5,13 +5,15 @@ const database = require('./../database'); class Game { - constructor(id, date, team1ID, team2ID, team1Score, team2Score) { + constructor(id, date, team1ID, team2ID, team1Score, team2Score, divisionID, seasonID) { this.id = id; this.date = date; this.team1ID = team1ID; this.team2ID = team2ID; this.team1Score = team1Score; this.team2Score = team2Score; + this.divisionID = divisionID; + this.seasonID = seasonID; } } @@ -36,12 +38,23 @@ async function remove(id) { return new Game(id, row[3], row[4], row[5], row[6], row[7]); } -async function retrieveByTeamDivisionAndSeason(teamID, divisionID, seasonID) { - const query = `SELECT * +async function retrieve(teamID, divisionID, seasonID) { + let table; + + if(teamID && divisionID && seasonID) { + const query = `SELECT game_id, division_id, season_id, game_date, team1_id, team2_id, team1_score, team2_score FROM scores.games WHERE (team1_id = $1 OR team2_id = $1) AND division_id = $2 AND season_id = $3 ORDER BY game_date DESC;`; - const table = (await database.executeQuery(query, [teamID,divisionID,seasonID])); + table = await database.executeQuery(query, [teamID,divisionID,seasonID]); + } + else { + const query = `SELECT game_id, division_id, season_id, game_date, team1_id, team2_id, team1_score, team2_score + FROM scores.games + ORDER BY game_date DESC;`; + table = await database.executeQuery(query); + } + const gamesList = []; table.forEach((row) => { @@ -50,7 +63,7 @@ async function retrieveByTeamDivisionAndSeason(teamID, divisionID, seasonID) { teamScore = opponentIsTeam2 ? row[6] : row[7]; opponentScore = opponentIsTeam2 ? row[7] : row[6]; - gamesList.push(new Game(row[0], row[3].toISOString().slice(0,10), teamID, opponentID, teamScore, opponentScore)); + gamesList.push(new Game(row[0], row[3].toISOString().slice(0,10), teamID, opponentID, teamScore, opponentScore, row[1], row[2])); }); return gamesList; } @@ -61,4 +74,4 @@ async function retrieveByTeamDivisionAndSeason(teamID, divisionID, seasonID) { exports.add = add; exports.remove = remove; -exports.retrieveByTeamDivisionAndSeason = retrieveByTeamDivisionAndSeason; \ No newline at end of file +exports.retrieve = retrieve; \ No newline at end of file diff --git a/public/scripts/data.js b/public/scripts/data.js index b83fdd0..9866321 100644 --- a/public/scripts/data.js +++ b/public/scripts/data.js @@ -47,8 +47,14 @@ export async function getTeamName(teamID) { return team.name; } -export async function getGames(teamID, divisionID, seasonID) { - const response = await fetch(`/data/games?team=${+teamID}&division=${+divisionID}&season=${+seasonID}`); +export async function getGames(teamID = undefined, divisionID = undefined, seasonID = undefined) { + let URL = '/data/games?'; + if(teamID) URL += `team=${+teamID}&`; + if(divisionID) URL += `division=${+divisionID}&`; + if(seasonID) URL += `season=${+seasonID}`; + + + const response = await fetch(URL); const gamesList = await response.json(); return gamesList; } \ No newline at end of file diff --git a/routes/data.js b/routes/data.js index f9c6431..76dd2ac 100644 --- a/routes/data.js +++ b/routes/data.js @@ -47,7 +47,7 @@ router.get('/team', function(req, res, next) { }) router.get('/games', function(req, res, next) { - games.retrieveByTeamDivisionAndSeason(req.query.team, req.query.division, req.query.season) + games.retrieve(req.query.team, req.query.division, req.query.season) .then(data => res.json(data)); }) From fe659184f41838ec53313d7d9b9ef84eee3f426b Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 22 Nov 2021 16:13:21 -0700 Subject: [PATCH 096/255] Fix bug in games.js --- database/scores/games.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/database/scores/games.js b/database/scores/games.js index 9f27bab..4055523 100644 --- a/database/scores/games.js +++ b/database/scores/games.js @@ -58,12 +58,17 @@ async function retrieve(teamID, divisionID, seasonID) { const gamesList = []; table.forEach((row) => { - opponentIsTeam2 = teamID != row[5]; - opponentID = opponentIsTeam2 ? row[5] : row[4]; - teamScore = opponentIsTeam2 ? row[6] : row[7]; - opponentScore = opponentIsTeam2 ? row[7] : row[6]; - - gamesList.push(new Game(row[0], row[3].toISOString().slice(0,10), teamID, opponentID, teamScore, opponentScore, row[1], row[2])); + if(teamID) { + const opponentIsTeam2 = teamID != row[5]; + const opponentID = opponentIsTeam2 ? row[5] : row[4]; + const teamScore = opponentIsTeam2 ? row[6] : row[7]; + const opponentScore = opponentIsTeam2 ? row[7] : row[6]; + + gamesList.push(new Game(row[0], row[3].toISOString().slice(0,10), teamID, opponentID, teamScore, opponentScore, row[1], row[2])); + } + else { + gamesList.push(new Game(row[0], row[3].toISOString().slice(0,10), row[4], row[5], row[6], row[7], row[1], row[2])); + } }); return gamesList; } From 1f55ba3ff9921942a17cf2d5f120e206ce6666ab Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 22 Nov 2021 16:27:50 -0700 Subject: [PATCH 097/255] Add functions to get division by ID --- database/scores/divisions.js | 20 +++++++++-- public/scripts/data.js | 6 ++++ public/scripts/manage.js | 68 ++++++++++++++++++++++++++++++++++++ routes/data.js | 5 +++ 4 files changed, 96 insertions(+), 3 deletions(-) diff --git a/database/scores/divisions.js b/database/scores/divisions.js index a3cebd2..7da86dd 100644 --- a/database/scores/divisions.js +++ b/database/scores/divisions.js @@ -20,6 +20,10 @@ function getGenderID(gender) { return (gender == genders.MALE) ? "M" : "F"; } +function getGenderFromID(genderID) { + return (genderID == "F") ? genders.FEMALE : genders.MALE; +} + async function add(name, gender, sportID) { @@ -69,12 +73,21 @@ async function retrieve(sportID = undefined, gender = undefined) { const divisionsList = []; table.forEach((row) => { - let gender = (row[2] == "F") ? genders.FEMALE : genders.MALE; - divisionsList.push(new Division(row[0], row[1], gender, row[3])); + divisionsList.push(new Division(row[0], row[1], getGenderFromID(row[2]), row[3])); }); return divisionsList; } +async function getFromID(id) { + const query = `SELECT division_id, division_name, gender, sport_id + FROM scores.divisions + WHERE division_id = $1;`; + const row = (await database.executeQuery(query, [id]))[0]; + + + return new Division(id, row[1], getGenderFromID(row[2]), row[3]); +} + @@ -82,4 +95,5 @@ async function retrieve(sportID = undefined, gender = undefined) { exports.add = add; exports.rename = rename; exports.remove = remove; -exports.retrieve = retrieve; \ No newline at end of file +exports.retrieve = retrieve; +exports.getFromID = getFromID; \ No newline at end of file diff --git a/public/scripts/data.js b/public/scripts/data.js index 9866321..8ef7479 100644 --- a/public/scripts/data.js +++ b/public/scripts/data.js @@ -32,6 +32,12 @@ export async function getDivisions(sportID = undefined, gender = undefined) { return divisionsList; } +export async function getDivision(divisionID) { + const response = await fetch(`/data/division?division=${divisionID}`); + const division = await response.json(); + return division; +} + export async function getTeams(sportID = undefined) { let URL = '/data/teams?'; if(sportID) URL += `sport=${+sportID}&`; diff --git a/public/scripts/manage.js b/public/scripts/manage.js index f6bcda6..83920f0 100644 --- a/public/scripts/manage.js +++ b/public/scripts/manage.js @@ -164,6 +164,74 @@ CATEGORIES.push(new Category( } )); +CATEGORIES.push(new Category( + "games", + async function getGames() { + return await Data.getGames(); + }, + async function listGameHeaders() { + const headerRow = document.createElement('tr'); + + const teamsHeader = document.createElement('th'); + teamsHeader.textContent = "Teams"; + headerRow.appendChild(teamsHeader); + + const scoreHeader = document.createElement('th'); + headerRow.appendChild(scoreHeader); + + const sportNameHeader = document.createElement('th'); + sportNameHeader.textContent = "Sport"; + headerRow.appendChild(sportNameHeader); + + const divisionNameHeader = document.createElement('th'); + headerRow.appendChild(divisionNameHeader); + + const divisionGenderHeader = document.createElement('th'); + headerRow.appendChild(divisionGenderHeader); + + const dateHeader = document.createElement('th'); + dateHeader.textContent = "Date"; + headerRow.appendChild(dateHeader); + + itemsListTable.appendChild(headerRow); + }, + function listGame(game, row) { + const teamsCell = document.createElement('td'); + const team1NameSpan = document.createElement('span'); + Data.getTeamName(game.team1ID) + .then(data => team1NameSpan.textContent = data); + teamsCell.appendChild(team1NameSpan); + const team2NameSpan = document.createElement('span'); + Data.getTeamName(game.team2ID) + .then(data => team2NameSpan.textContent = data); + teamsCell.appendChild(team2NameSpan); + row.appendChild(teamsCell); + + const scoresCell = document.createElement('td'); + const team1ScoreSpan = document.createElement('span'); + team1ScoreSpan.textContent = game.team1Score; + scoresCell.appendChild(team1ScoreSpan); + const team2ScoreSpan = document.createElement('span'); + team2ScoreSpan.textContent = game.team2Score; + scoresCell.appendChild(team2ScoreSpan); + row.appendChild(scoresCell); + + const sportCell = document.createElement('td'); + Data.getSportName(team.sportID) + .then(data => sportCell.textContent = data); + row.appendChild(sportCell); + }, + async function addSeason() { + // + }, + async function submitSeason() { + // + }, + async function editSeason() { + + } +)); + async function listItems(category) { diff --git a/routes/data.js b/routes/data.js index 76dd2ac..917b36b 100644 --- a/routes/data.js +++ b/routes/data.js @@ -36,6 +36,11 @@ router.get('/divisions', function(req, res, next) { .then(data => res.json(data)); }) +router.get('/division', function(req, res, next) { + divisions.getFromID(req.query.division) + .then(data => res.json(data)); +}) + router.get('/teams', function(req, res, next) { teams.retrieve(req.query.sport) .then(data => res.json(data)); From 54bef983121f6e467a7eaf0198da0338a873a5b7 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 22 Nov 2021 16:58:36 -0700 Subject: [PATCH 098/255] Add games list to manage page --- public/scripts/manage.js | 64 ++++++++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 19 deletions(-) diff --git a/public/scripts/manage.js b/public/scripts/manage.js index 83920f0..b609171 100644 --- a/public/scripts/manage.js +++ b/public/scripts/manage.js @@ -3,6 +3,11 @@ import * as Data from "./data.js"; const categoryDropdown = document.getElementById('category-dropdown'); const itemsListTable = document.getElementById('items-list'); + +function getGenderLetter(genderName) { + return genderName == "female" ? "F" : "M"; +} + class Category { constructor(name, getItems, listHeaders, listItem, addItem, submitItem, editItem) { this.name = name; @@ -105,7 +110,7 @@ CATEGORIES.push(new Category( row.appendChild(nameCell); const genderCell = document.createElement('td'); - const gender = division.gender.name == "female" ? "F" : "M"; + const gender = getGenderLetter(division.gender.name); genderCell.textContent = gender; row.appendChild(genderCell); @@ -183,12 +188,6 @@ CATEGORIES.push(new Category( sportNameHeader.textContent = "Sport"; headerRow.appendChild(sportNameHeader); - const divisionNameHeader = document.createElement('th'); - headerRow.appendChild(divisionNameHeader); - - const divisionGenderHeader = document.createElement('th'); - headerRow.appendChild(divisionGenderHeader); - const dateHeader = document.createElement('th'); dateHeader.textContent = "Date"; headerRow.appendChild(dateHeader); @@ -217,9 +216,32 @@ CATEGORIES.push(new Category( row.appendChild(scoresCell); const sportCell = document.createElement('td'); - Data.getSportName(team.sportID) - .then(data => sportCell.textContent = data); + const sportSpan = document.createElement('span'); + const divisionSpan = document.createElement('span'); + divisionSpan.classList.add('division-span'); + const divisionNameSpan = document.createElement('span'); + const divisionGenderSpan = document.createElement('span'); + divisionSpan.appendChild(divisionNameSpan); + divisionSpan.appendChild(divisionGenderSpan); + Data.getDivision(game.divisionID) + .then(data => { + Data.getSportName(data.sportID) + .then(data => sportSpan.textContent = data); + divisionNameSpan.textContent = data.name; + divisionGenderSpan.textContent = getGenderLetter(data.gender.name); + }); + sportCell.appendChild(sportSpan); + sportCell.appendChild(divisionSpan); row.appendChild(sportCell); + + const dateCell = document.createElement('td'); + const yearSpan = document.createElement('span'); + yearSpan.textContent = game.date.slice(0,4); + dateCell.appendChild(yearSpan); + const dateSpan = document.createElement('span'); + dateSpan.textContent = game.date.slice(5); + dateCell.appendChild(dateSpan); + row.appendChild(dateCell); }, async function addSeason() { // @@ -246,22 +268,26 @@ async function listItems(category) { category.listItem(item, row); - const editCell = document.createElement('td'); - const editButton = document.createElement('button'); - editButton.textContent = "edit"; - editCell.appendChild(editButton); - row.appendChild(editCell); + const manageCell = document.createElement('td'); - const removeCell = document.createElement('td'); + const editSpan = document.createElement('span'); + const editButton = document.createElement('button'); + editButton.textContent = "E"; + editSpan.appendChild(editButton); + manageCell.appendChild(editSpan); + + const removeSpan = document.createElement('remove'); const removeButton = document.createElement('button'); - removeButton.textContent = "remove"; - removeCell.appendChild(removeButton); - row.appendChild(removeCell); + removeButton.textContent = "D"; + removeSpan.appendChild(removeButton); + manageCell.appendChild(removeSpan); + + row.appendChild(manageCell); itemsListTable.appendChild(row); }); } -listItems(CATEGORIES[0]); +listItems(CATEGORIES[categoryDropdown.selectedIndex]); From 4d5669dab79a3b8b1be0320f762a0e93d978c0a0 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 22 Nov 2021 17:13:29 -0700 Subject: [PATCH 099/255] Tweak table layout --- public/scripts/manage.js | 37 ++++++++++++++++++++++++++++++++++- public/stylesheets/manage.css | 15 ++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 public/stylesheets/manage.css diff --git a/public/scripts/manage.js b/public/scripts/manage.js index b609171..80bc534 100644 --- a/public/scripts/manage.js +++ b/public/scripts/manage.js @@ -35,12 +35,19 @@ CATEGORIES.push(new Category( headerRow.appendChild(yearsHeader); + const spacerHeader = document.createElement('th'); + spacerHeader.classList.add('spacer-column'); + headerRow.appendChild(spacerHeader); + itemsListTable.appendChild(headerRow); }, function listSeason(season, row) { const yearCell = document.createElement('td'); yearCell.textContent = (season.year - 1) + "-" + season.year; row.appendChild(yearCell); + + const spacerCell = document.createElement('td'); + row.appendChild(spacerCell); }, async function addSeason() { // @@ -65,12 +72,19 @@ CATEGORIES.push(new Category( nameHeader.textContent = "Name"; headerRow.appendChild(nameHeader); + const spacerHeader = document.createElement('th'); + spacerHeader.classList.add('spacer-column'); + headerRow.appendChild(spacerHeader); + itemsListTable.appendChild(headerRow); }, function listSport(sport, row) { const nameCell = document.createElement('td'); nameCell.textContent = sport.name; row.appendChild(nameCell); + + const spacerCell = document.createElement('td'); + row.appendChild(spacerCell); }, async function addSport() { // @@ -97,6 +111,10 @@ CATEGORIES.push(new Category( const genderHeader = document.createElement('th'); headerRow.appendChild(genderHeader); + + const spacerHeader = document.createElement('th'); + spacerHeader.classList.add('spacer-column'); + headerRow.appendChild(spacerHeader); const sportHeader = document.createElement('th'); sportHeader.textContent = "Sport"; @@ -114,6 +132,9 @@ CATEGORIES.push(new Category( genderCell.textContent = gender; row.appendChild(genderCell); + const spacerCell = document.createElement('td'); + row.appendChild(spacerCell); + const sportCell = document.createElement('td'); Data.getSportName(division.sportID) .then(data => sportCell.textContent = data); @@ -141,6 +162,10 @@ CATEGORIES.push(new Category( const nameHeader = document.createElement('th'); nameHeader.textContent = "Name"; headerRow.appendChild(nameHeader); + + const spacerHeader = document.createElement('th'); + spacerHeader.classList.add('spacer-column'); + headerRow.appendChild(spacerHeader); const sportHeader = document.createElement('th'); sportHeader.textContent = "Sport"; @@ -153,6 +178,9 @@ CATEGORIES.push(new Category( nameCell.textContent = team.name; row.appendChild(nameCell); + const spacerCell = document.createElement('td'); + row.appendChild(spacerCell); + const sportCell = document.createElement('td'); Data.getSportName(team.sportID) .then(data => sportCell.textContent = data); @@ -184,6 +212,10 @@ CATEGORIES.push(new Category( const scoreHeader = document.createElement('th'); headerRow.appendChild(scoreHeader); + const spacerHeader = document.createElement('th'); + spacerHeader.classList.add('spacer-column'); + headerRow.appendChild(spacerHeader); + const sportNameHeader = document.createElement('th'); sportNameHeader.textContent = "Sport"; headerRow.appendChild(sportNameHeader); @@ -214,11 +246,14 @@ CATEGORIES.push(new Category( team2ScoreSpan.textContent = game.team2Score; scoresCell.appendChild(team2ScoreSpan); row.appendChild(scoresCell); + + const spacerCell = document.createElement('td'); + row.appendChild(spacerCell); const sportCell = document.createElement('td'); const sportSpan = document.createElement('span'); const divisionSpan = document.createElement('span'); - divisionSpan.classList.add('division-span'); + divisionSpan.classList.add('flat-content'); const divisionNameSpan = document.createElement('span'); const divisionGenderSpan = document.createElement('span'); divisionSpan.appendChild(divisionNameSpan); diff --git a/public/stylesheets/manage.css b/public/stylesheets/manage.css new file mode 100644 index 0000000..95ecca1 --- /dev/null +++ b/public/stylesheets/manage.css @@ -0,0 +1,15 @@ +.flat-content { + flex-direction: row; +} + +th { + text-align: left; +} + +td { + white-space: nowrap; +} + +.spacer-column { + width: 100%; +} \ No newline at end of file From 722bb4fce6933e9fc3a789a71c58f213bfc28980 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 22 Nov 2021 17:38:45 -0700 Subject: [PATCH 100/255] Edit css --- public/stylesheets/style.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css index b6a5c49..ec6953e 100644 --- a/public/stylesheets/style.css +++ b/public/stylesheets/style.css @@ -15,3 +15,6 @@ a { flex-direction: column; } +.flat-content { + flex-direction: row; +} \ No newline at end of file From 8587212af921870075817c9ee08e3e252b6842af Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 22 Nov 2021 17:45:58 -0700 Subject: [PATCH 101/255] Fix bug where games.js uses nonexistent moment.js module for formatting --- database/scores/games.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/database/scores/games.js b/database/scores/games.js index 4055523..922d600 100644 --- a/database/scores/games.js +++ b/database/scores/games.js @@ -23,10 +23,8 @@ async function add(divisionID, seasonID, date, team1ID, team2ID, team1Score, tea const query = `INSERT INTO scores.games(division_id, season_id, game_date, team1_id, team2_id, team1_score, team2_score) VALUES($1, $2, $3, $4, $5, $6, $7) RETURNING game_id;`; - - const dateISO = date.format('YYYY-MM-DD'); - const id = (await database.executeQuery(query, [divisionID, seasonID, dateISO, team1ID, team2ID, team1Score, team2Score]))[0][0]; + const id = (await database.executeQuery(query, [divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score]))[0][0]; return new Game(id, date, team1ID, team2ID, team1Score, team2Score); } From e257e9732087192151602008027a62c4ead3b297 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 22 Nov 2021 17:48:21 -0700 Subject: [PATCH 102/255] Edit CSS --- public/stylesheets/manage.css | 4 ---- 1 file changed, 4 deletions(-) diff --git a/public/stylesheets/manage.css b/public/stylesheets/manage.css index 95ecca1..6a5359f 100644 --- a/public/stylesheets/manage.css +++ b/public/stylesheets/manage.css @@ -1,7 +1,3 @@ -.flat-content { - flex-direction: row; -} - th { text-align: left; } From 9f2572336fc897e758bc6cdff00902936e72f837 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 22 Nov 2021 17:48:51 -0700 Subject: [PATCH 103/255] Move game submit page from /submit to /manage/addgame --- public/scripts/manage.js | 17 +-------------- routes/manage.js | 23 +++++++++++++++++++- routes/submit.js | 27 ------------------------ views/{submit.pug => manage/addgame.pug} | 2 +- views/manage/layout.pug | 10 +++++++++ 5 files changed, 34 insertions(+), 45 deletions(-) delete mode 100644 routes/submit.js rename views/{submit.pug => manage/addgame.pug} (93%) create mode 100644 views/manage/layout.pug diff --git a/public/scripts/manage.js b/public/scripts/manage.js index 80bc534..04656eb 100644 --- a/public/scripts/manage.js +++ b/public/scripts/manage.js @@ -52,9 +52,6 @@ CATEGORIES.push(new Category( async function addSeason() { // }, - async function submitSeason() { - // - }, async function editSeason() { } @@ -89,9 +86,6 @@ CATEGORIES.push(new Category( async function addSport() { // }, - async function submitSport() { - // - }, async function editSport() { } @@ -143,9 +137,6 @@ CATEGORIES.push(new Category( async function addSeason() { // }, - async function submitSeason() { - // - }, async function editSeason() { } @@ -189,9 +180,6 @@ CATEGORIES.push(new Category( async function addSeason() { // }, - async function submitSeason() { - // - }, async function editSeason() { } @@ -278,10 +266,7 @@ CATEGORIES.push(new Category( dateCell.appendChild(dateSpan); row.appendChild(dateCell); }, - async function addSeason() { - // - }, - async function submitSeason() { + async function addGame() { // }, async function editSeason() { diff --git a/routes/manage.js b/routes/manage.js index 796064b..454c3c9 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -1,9 +1,30 @@ var express = require('express'); var router = express.Router(); +var genders = require('../database/scores/genders'); +var games = require('../database/scores/games'); + -/* GET manage page. */ router.get('/', function(req, res, next) { res.render('manage', { title: 'Score Management' }); }); +router.get('/addgame', function(req, res, next) { + res.render('manage/addgame', { title: 'Submit Score' }); +}); + +router.post('/submitgame', function(req, res, next) { + const seasonID = req.body['year']; + const sportID = req.body['sport']; + const gender = (req.body['gender'] == "female") ? genders.FEMALE : genders.MALE; + const divisionID = req.body['division']; + const date = req.body['date']; + const team1ID = req.body['team1']; + const team1Score = req.body['team1-score']; + const team2ID = req.body['team2']; + const team2Score = req.body['team2-score']; + + games.add(divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score) + .then(res.send("SUCCESS")); +}); + module.exports = router; diff --git a/routes/submit.js b/routes/submit.js deleted file mode 100644 index fcd660b..0000000 --- a/routes/submit.js +++ /dev/null @@ -1,27 +0,0 @@ -var express = require('express'); -var router = express.Router(); -var genders = require('../database/scores/genders'); -var games = require('../database/scores/games'); - -/* GET submit page. */ -router.get('/', function(req, res, next) { - res.render('submit', { title: 'Submit Score' }); -}); - -/* POST submit page. */ -router.post('/', function(req, res, next) { - const seasonID = req.body['year']; - const sportID = req.body['sport']; - const gender = (req.body['gender'] == "female") ? genders.FEMALE : genders.MALE; - const divisionID = req.body['division']; - const date = moment(req.body['date']); - const team1ID = req.body['team1']; - const team1Score = req.body['team1-score']; - const team2ID = req.body['team2']; - const team2Score = req.body['team2-score']; - - games.add(divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score) - .then(res.send("SUCCESS")); -}); - -module.exports = router; diff --git a/views/submit.pug b/views/manage/addgame.pug similarity index 93% rename from views/submit.pug rename to views/manage/addgame.pug index 85f352f..04026f4 100644 --- a/views/submit.pug +++ b/views/manage/addgame.pug @@ -7,7 +7,7 @@ block stylesheets block content div#mobile-view h1 Submit Score - form(action='/submit', method='POST') + form(action='./submitgame', method='POST') span(class='form-section') label Year span(class='form-section-input') diff --git a/views/manage/layout.pug b/views/manage/layout.pug new file mode 100644 index 0000000..e4f85eb --- /dev/null +++ b/views/manage/layout.pug @@ -0,0 +1,10 @@ +doctype html +html + head + title= title + meta(name='viewport', content='width=device-width, initial-scale=1') + link(rel='stylesheet', href='/stylesheets/style.css') + block stylesheets + body + block content + block scripts From 7c5c059e09ca215048caaf71d53788032773eb01 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 22 Nov 2021 17:55:15 -0700 Subject: [PATCH 104/255] Remove submit router from app.js --- app.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/app.js b/app.js index 6bc76de..c773280 100644 --- a/app.js +++ b/app.js @@ -6,7 +6,6 @@ var logger = require('morgan'); var indexRouter = require('./routes/index'); var usersRouter = require('./routes/users'); -var submitRouter = require('./routes/submit'); var dataRouter = require('./routes/data'); var manageRouter = require('./routes/manage'); @@ -24,7 +23,6 @@ app.use(express.static(path.join(__dirname, 'public'))); app.use('/', indexRouter); app.use('/users', usersRouter); -app.use('/submit', submitRouter); app.use('/data', dataRouter); app.use('/manage', manageRouter); From 4133758bbafbb2c9085fd6d2eb7b2c3770796566 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 22 Nov 2021 18:04:36 -0700 Subject: [PATCH 105/255] Create page to add season --- public/scripts/manage/season.js | 0 routes/manage.js | 12 ++++++++++++ views/manage/addseason.pug | 19 +++++++++++++++++++ 3 files changed, 31 insertions(+) create mode 100644 public/scripts/manage/season.js create mode 100644 views/manage/addseason.pug diff --git a/public/scripts/manage/season.js b/public/scripts/manage/season.js new file mode 100644 index 0000000..e69de29 diff --git a/routes/manage.js b/routes/manage.js index 454c3c9..72d0784 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -2,6 +2,7 @@ var express = require('express'); var router = express.Router(); var genders = require('../database/scores/genders'); var games = require('../database/scores/games'); +var seasons = require('../database/scores/seasons'); router.get('/', function(req, res, next) { @@ -27,4 +28,15 @@ router.post('/submitgame', function(req, res, next) { .then(res.send("SUCCESS")); }); +router.get('/addseason', function(req, res, next) { + res.render('manage/addseason', { title: 'Add season', currentYear : (new Date()).getFullYear() }); +}); + +router.post('/submitseason', function(req, res, next) { + const year = req.body['year']; + + seasons.add(year) + .then(res.send("SUCCESS")); +}); + module.exports = router; diff --git a/views/manage/addseason.pug b/views/manage/addseason.pug new file mode 100644 index 0000000..e82937c --- /dev/null +++ b/views/manage/addseason.pug @@ -0,0 +1,19 @@ +extends layout + +block stylesheets + link(rel='stylesheet', href='/stylesheets/submit.css') + link(rel='stylesheet', href='/stylesheets/form.css') + +block content + div#mobile-view + h1 Add Season + form(action='./submitseason', method='POST') + span(class='form-section') + label Ending year + span(class='form-section-input') + input(type="number", name="year", value=currentYear) + span(class='form-section') + button#submit-button(type="submit") Submit + +block scripts + script(src='/scripts/season.js' type="module") \ No newline at end of file From 31575212ad0ef0e733ac8007b5a407282e3b62b6 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 22 Nov 2021 18:42:02 -0700 Subject: [PATCH 106/255] Allow "Add new..." button to redirect to webpages per category --- public/scripts/manage.js | 8 +++++--- views/manage.pug | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/public/scripts/manage.js b/public/scripts/manage.js index 04656eb..2f24fc4 100644 --- a/public/scripts/manage.js +++ b/public/scripts/manage.js @@ -2,6 +2,7 @@ import * as Data from "./data.js"; const categoryDropdown = document.getElementById('category-dropdown'); const itemsListTable = document.getElementById('items-list'); +const addNewButton = document.getElementById('add-new-button'); function getGenderLetter(genderName) { @@ -50,7 +51,7 @@ CATEGORIES.push(new Category( row.appendChild(spacerCell); }, async function addSeason() { - // + window.location.href = "manage/addseason"; }, async function editSeason() { @@ -267,7 +268,7 @@ CATEGORIES.push(new Category( row.appendChild(dateCell); }, async function addGame() { - // + window.location.href = "manage/addgame"; }, async function editSeason() { @@ -313,4 +314,5 @@ listItems(CATEGORIES[categoryDropdown.selectedIndex]); categoryDropdown.onchange = () => { listItems(CATEGORIES[categoryDropdown.selectedIndex]); -}; \ No newline at end of file +}; +addNewButton.addEventListener('click', () => CATEGORIES[categoryDropdown.selectedIndex].addItem()); \ No newline at end of file diff --git a/views/manage.pug b/views/manage.pug index 7977824..c43dcf9 100644 --- a/views/manage.pug +++ b/views/manage.pug @@ -20,7 +20,7 @@ block content div h2#table-header table#items-list - button Add new... + button#add-new-button Add new... block scripts script(src='/scripts/manage.js' type="module") \ No newline at end of file From f95015095d425d28147d16c4ff52bdbea0144ab2 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 22 Nov 2021 18:47:40 -0700 Subject: [PATCH 107/255] Add page for adding sports --- public/scripts/manage.js | 2 +- routes/manage.js | 12 ++++++++++++ views/manage/addsport.pug | 19 +++++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 views/manage/addsport.pug diff --git a/public/scripts/manage.js b/public/scripts/manage.js index 2f24fc4..2017341 100644 --- a/public/scripts/manage.js +++ b/public/scripts/manage.js @@ -85,7 +85,7 @@ CATEGORIES.push(new Category( row.appendChild(spacerCell); }, async function addSport() { - // + window.location.href = "manage/addsport"; }, async function editSport() { diff --git a/routes/manage.js b/routes/manage.js index 72d0784..4582727 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -3,6 +3,7 @@ var router = express.Router(); var genders = require('../database/scores/genders'); var games = require('../database/scores/games'); var seasons = require('../database/scores/seasons'); +var sports = require('../database/scores/sports'); router.get('/', function(req, res, next) { @@ -39,4 +40,15 @@ router.post('/submitseason', function(req, res, next) { .then(res.send("SUCCESS")); }); +router.get('/addsport', function(req, res, next) { + res.render('manage/addsport', { title: 'Add sport' }); +}); + +router.post('/submitsport', function(req, res, next) { + const name = req.body['name']; + + sports.add(name) + .then(res.send("SUCCESS")); +}); + module.exports = router; diff --git a/views/manage/addsport.pug b/views/manage/addsport.pug new file mode 100644 index 0000000..7f7e5ad --- /dev/null +++ b/views/manage/addsport.pug @@ -0,0 +1,19 @@ +extends layout + +block stylesheets + link(rel='stylesheet', href='/stylesheets/submit.css') + link(rel='stylesheet', href='/stylesheets/form.css') + +block content + div#mobile-view + h1 Add Sport + form(action='./submitsport', method='POST') + span(class='form-section') + label Sport name + span(class='form-section-input') + input(type="text", name="name") + span(class='form-section') + button#submit-button(type="submit") Submit + +block scripts + script(src='/scripts/sport.js' type="module") \ No newline at end of file From 1a3b013a0f93f9c12e8d4a170173636c5704e005 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 22 Nov 2021 19:11:16 -0700 Subject: [PATCH 108/255] Add page to add divisions --- public/scripts/manage.js | 6 +++--- public/scripts/manage/division.js | 18 ++++++++++++++++++ routes/manage.js | 23 +++++++++++++++++++++++ views/manage/adddivision.pug | 30 ++++++++++++++++++++++++++++++ 4 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 public/scripts/manage/division.js create mode 100644 views/manage/adddivision.pug diff --git a/public/scripts/manage.js b/public/scripts/manage.js index 2017341..770a517 100644 --- a/public/scripts/manage.js +++ b/public/scripts/manage.js @@ -135,10 +135,10 @@ CATEGORIES.push(new Category( .then(data => sportCell.textContent = data); row.appendChild(sportCell); }, - async function addSeason() { - // + async function addDivision() { + window.location.href = "manage/adddivision"; }, - async function editSeason() { + async function editDivision() { } )); diff --git a/public/scripts/manage/division.js b/public/scripts/manage/division.js new file mode 100644 index 0000000..6b37492 --- /dev/null +++ b/public/scripts/manage/division.js @@ -0,0 +1,18 @@ +import * as Data from "../data.js"; + + +const sportDropdown = document.getElementById('sport-dropdown'); + +async function listSports() { + sportDropdown.innerHTML = ""; + + const sportsList = await Data.getSports(); + + sportsList.forEach(sport => { + const option = document.createElement('option'); + option.text = sport.name; + option.value = sport.id; + sportDropdown.appendChild(option); + }); +} +listSports(); \ No newline at end of file diff --git a/routes/manage.js b/routes/manage.js index 4582727..72f17e8 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -4,6 +4,8 @@ var genders = require('../database/scores/genders'); var games = require('../database/scores/games'); var seasons = require('../database/scores/seasons'); var sports = require('../database/scores/sports'); +var divisions = require('../database/scores/divisions'); +var genders = require('../database/scores/genders'); router.get('/', function(req, res, next) { @@ -51,4 +53,25 @@ router.post('/submitsport', function(req, res, next) { .then(res.send("SUCCESS")); }); +router.get('/adddivision', function(req, res, next) { + res.render('manage/adddivision', { title: 'Add division' }); +}); + +router.post('/submitdivision', function(req, res, next) { + const name = req.body['name']; + const sport = req.body['sport']; + const genderName = req.body['gender']; + + if(genderName == "both") { + divisions.add(name, genders.FEMALE, sport) + .then(divisions.add(name, genders.MALE, sport) + .then(res.send("SUCCESS"))); + } + else { + const gender = (genderName == "female") ? genders.FEMALE : genders.MALE; + divisions.add(name, gender, sport) + .then(res.send("SUCCESS")); + } +}); + module.exports = router; diff --git a/views/manage/adddivision.pug b/views/manage/adddivision.pug new file mode 100644 index 0000000..4b8d7d1 --- /dev/null +++ b/views/manage/adddivision.pug @@ -0,0 +1,30 @@ +extends layout + +block stylesheets + link(rel='stylesheet', href='/stylesheets/submit.css') + link(rel='stylesheet', href='/stylesheets/form.css') + +block content + div#mobile-view + h1 Add Division + form(action='./submitdivision', method='POST') + span(class='form-section') + label Sport + span(class='form-section-input') + select#sport-dropdown(name="sport" class="form-main-dropdown") + span(class='form-section') + label Gender + span(class='form-section-input') + select#gender-dropdown(name="gender" class="form-main-dropdown") + option(value="both") Both + option(value="female") Female + option(value="male") Male + span(class='form-section') + label Division name + span(class='form-section-input') + input(type="text", name="name") + span(class='form-section') + button#submit-button(type="submit") Submit + +block scripts + script(src='/scripts/manage/division.js' type="module") \ No newline at end of file From aacff9cca1121b4afd4156fa7f5fa9d966b76620 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 22 Nov 2021 19:17:23 -0700 Subject: [PATCH 109/255] Add page to add teams --- public/scripts/manage.js | 6 +++--- public/scripts/manage/team.js | 18 ++++++++++++++++++ routes/manage.js | 13 +++++++++++++ views/manage/addteam.pug | 23 +++++++++++++++++++++++ 4 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 public/scripts/manage/team.js create mode 100644 views/manage/addteam.pug diff --git a/public/scripts/manage.js b/public/scripts/manage.js index 770a517..651047e 100644 --- a/public/scripts/manage.js +++ b/public/scripts/manage.js @@ -178,10 +178,10 @@ CATEGORIES.push(new Category( .then(data => sportCell.textContent = data); row.appendChild(sportCell); }, - async function addSeason() { - // + async function addTeam() { + window.location.href = "manage/addteam"; }, - async function editSeason() { + async function editTeam() { } )); diff --git a/public/scripts/manage/team.js b/public/scripts/manage/team.js new file mode 100644 index 0000000..6b37492 --- /dev/null +++ b/public/scripts/manage/team.js @@ -0,0 +1,18 @@ +import * as Data from "../data.js"; + + +const sportDropdown = document.getElementById('sport-dropdown'); + +async function listSports() { + sportDropdown.innerHTML = ""; + + const sportsList = await Data.getSports(); + + sportsList.forEach(sport => { + const option = document.createElement('option'); + option.text = sport.name; + option.value = sport.id; + sportDropdown.appendChild(option); + }); +} +listSports(); \ No newline at end of file diff --git a/routes/manage.js b/routes/manage.js index 72f17e8..04b2b9a 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -6,6 +6,7 @@ var seasons = require('../database/scores/seasons'); var sports = require('../database/scores/sports'); var divisions = require('../database/scores/divisions'); var genders = require('../database/scores/genders'); +var teams = require('../database/scores/teams'); router.get('/', function(req, res, next) { @@ -74,4 +75,16 @@ router.post('/submitdivision', function(req, res, next) { } }); +router.get('/addteam', function(req, res, next) { + res.render('manage/addteam', { title: 'Add team' }); +}); + +router.post('/submitteam', function(req, res, next) { + const name = req.body['name']; + const sport = req.body['sport']; + + teams.add(name, sport) + .then(res.send("SUCCESS")); +}); + module.exports = router; diff --git a/views/manage/addteam.pug b/views/manage/addteam.pug new file mode 100644 index 0000000..c3d422b --- /dev/null +++ b/views/manage/addteam.pug @@ -0,0 +1,23 @@ +extends layout + +block stylesheets + link(rel='stylesheet', href='/stylesheets/submit.css') + link(rel='stylesheet', href='/stylesheets/form.css') + +block content + div#mobile-view + h1 Add Team + form(action='./submitteam', method='POST') + span(class='form-section') + label Sport + span(class='form-section-input') + select#sport-dropdown(name="sport" class="form-main-dropdown") + span(class='form-section') + label Team name + span(class='form-section-input') + input(type="text", name="name") + span(class='form-section') + button#submit-button(type="submit") Submit + +block scripts + script(src='/scripts/manage/team.js' type="module") \ No newline at end of file From 5c7875382818d5725b9a7d2c928336e48f60ae78 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 22 Nov 2021 19:23:20 -0700 Subject: [PATCH 110/255] Fix bug regarding page redirection --- public/scripts/manage.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/public/scripts/manage.js b/public/scripts/manage.js index 651047e..5482569 100644 --- a/public/scripts/manage.js +++ b/public/scripts/manage.js @@ -51,7 +51,7 @@ CATEGORIES.push(new Category( row.appendChild(spacerCell); }, async function addSeason() { - window.location.href = "manage/addseason"; + window.location.href = "/manage/addseason"; }, async function editSeason() { @@ -85,7 +85,7 @@ CATEGORIES.push(new Category( row.appendChild(spacerCell); }, async function addSport() { - window.location.href = "manage/addsport"; + window.location.href = "/manage/addsport"; }, async function editSport() { @@ -136,7 +136,7 @@ CATEGORIES.push(new Category( row.appendChild(sportCell); }, async function addDivision() { - window.location.href = "manage/adddivision"; + window.location.href = "/manage/adddivision"; }, async function editDivision() { @@ -179,7 +179,7 @@ CATEGORIES.push(new Category( row.appendChild(sportCell); }, async function addTeam() { - window.location.href = "manage/addteam"; + window.location.href = "/manage/addteam"; }, async function editTeam() { @@ -268,7 +268,7 @@ CATEGORIES.push(new Category( row.appendChild(dateCell); }, async function addGame() { - window.location.href = "manage/addgame"; + window.location.href = "/manage/addgame"; }, async function editSeason() { From 6ef9096cb792e5bd4a0aaebf1391574ead25d267 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 22 Nov 2021 19:25:44 -0700 Subject: [PATCH 111/255] Remove delete button on manage page --- public/scripts/manage.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/public/scripts/manage.js b/public/scripts/manage.js index 5482569..1f4ff7e 100644 --- a/public/scripts/manage.js +++ b/public/scripts/manage.js @@ -297,12 +297,6 @@ async function listItems(category) { editSpan.appendChild(editButton); manageCell.appendChild(editSpan); - const removeSpan = document.createElement('remove'); - const removeButton = document.createElement('button'); - removeButton.textContent = "D"; - removeSpan.appendChild(removeButton); - manageCell.appendChild(removeSpan); - row.appendChild(manageCell); itemsListTable.appendChild(row); From 4fec3a45c800a07b999624ce7e7338d456ccfd61 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 22 Nov 2021 19:40:33 -0700 Subject: [PATCH 112/255] Change header in addseason.pug to match title --- views/manage/addseason.pug | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/manage/addseason.pug b/views/manage/addseason.pug index e82937c..96d4080 100644 --- a/views/manage/addseason.pug +++ b/views/manage/addseason.pug @@ -6,7 +6,7 @@ block stylesheets block content div#mobile-view - h1 Add Season + h1 #{title} form(action='./submitseason', method='POST') span(class='form-section') label Ending year From 35af76c90f7e4d4acf1979c1c8ce259881ea68e3 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 22 Nov 2021 22:03:02 -0700 Subject: [PATCH 113/255] Add ability to edit sports in manage page --- public/scripts/manage.js | 12 ++++++---- public/scripts/manage/sport.js | 43 ++++++++++++++++++++++++++++++++++ public/stylesheets/form.css | 4 ++++ routes/manage.js | 13 +++++----- views/manage/addsport.pug | 13 ++++++---- 5 files changed, 69 insertions(+), 16 deletions(-) create mode 100644 public/scripts/manage/sport.js diff --git a/public/scripts/manage.js b/public/scripts/manage.js index 1f4ff7e..8b0221b 100644 --- a/public/scripts/manage.js +++ b/public/scripts/manage.js @@ -10,13 +10,12 @@ function getGenderLetter(genderName) { } class Category { - constructor(name, getItems, listHeaders, listItem, addItem, submitItem, editItem) { + constructor(name, getItems, listHeaders, listItem, addItem, editItem) { this.name = name; this.getItems = getItems; this.listHeaders = listHeaders; this.listItem = listItem; this.addItem = addItem; - this.submitItem = submitItem; this.editItem = editItem; } } @@ -85,10 +84,10 @@ CATEGORIES.push(new Category( row.appendChild(spacerCell); }, async function addSport() { - window.location.href = "/manage/addsport"; + window.location.href = `/manage/sport`; }, - async function editSport() { - + async function editSport(id) { + window.location.href = `/manage/sport?sport=${id}` } )); @@ -294,6 +293,9 @@ async function listItems(category) { const editSpan = document.createElement('span'); const editButton = document.createElement('button'); editButton.textContent = "E"; + editButton.addEventListener('click', () => { + CATEGORIES[categoryDropdown.selectedIndex].editItem(item.id); + }); editSpan.appendChild(editButton); manageCell.appendChild(editSpan); diff --git a/public/scripts/manage/sport.js b/public/scripts/manage/sport.js new file mode 100644 index 0000000..2b12593 --- /dev/null +++ b/public/scripts/manage/sport.js @@ -0,0 +1,43 @@ +import * as Data from "../data.js"; + + +const mainHeader = document.getElementById('main-header'); +const nameTextbox = document.getElementById('name-textbox'); +const submitButton = document.getElementById('submit-button'); +const deleteButton = document.getElementById('delete-button'); +const submissionForm = document.getElementById('submission-form'); + + +async function initializeForm() { + let params = new URLSearchParams(location.search); + let sportID = params.get('sport') + if(sportID) { + mainHeader.textContent = "Edit Sport"; + + const sportName = await Data.getSportName(sportID); + + nameTextbox.value = sportName; + deleteButton.style.visibility = "visible"; + deleteButton.disabled = false; + + const sportIDInput = document.createElement('input'); + sportIDInput.setAttribute('name', 'sport'); + sportIDInput.setAttribute('value', sportID); + sportIDInput.setAttribute('type', 'hidden'); + submissionForm.appendChild(sportIDInput); + } + nameTextbox.disabled = false; + + nameTextbox.addEventListener('keyup', checkDataValidity); +} +initializeForm(); + +async function checkDataValidity() { + let dataIsValid = true; + + if(!nameTextbox.value) dataIsValid = false; + + + if(dataIsValid) submitButton.disabled = false; + else submitButton.disabled = true; +} \ No newline at end of file diff --git a/public/stylesheets/form.css b/public/stylesheets/form.css index 85b34bc..21abe10 100644 --- a/public/stylesheets/form.css +++ b/public/stylesheets/form.css @@ -31,4 +31,8 @@ form { #submit-button { margin-top: 1.5em; + } + + #delete-button { + visibility: hidden; } \ No newline at end of file diff --git a/routes/manage.js b/routes/manage.js index 04b2b9a..a42f734 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -33,7 +33,7 @@ router.post('/submitgame', function(req, res, next) { }); router.get('/addseason', function(req, res, next) { - res.render('manage/addseason', { title: 'Add season', currentYear : (new Date()).getFullYear() }); + res.render('manage/addseason', { title: 'Add Season', currentYear : (new Date()).getFullYear() }); }); router.post('/submitseason', function(req, res, next) { @@ -43,15 +43,16 @@ router.post('/submitseason', function(req, res, next) { .then(res.send("SUCCESS")); }); -router.get('/addsport', function(req, res, next) { - res.render('manage/addsport', { title: 'Add sport' }); +router.get('/sport', function(req, res, next) { + res.render('manage/addsport', { title: 'Add Sport' }); }); -router.post('/submitsport', function(req, res, next) { +router.post('/sport', function(req, res, next) { const name = req.body['name']; + const id = req.body['sport']; - sports.add(name) - .then(res.send("SUCCESS")); + if(id) sports.rename(id, name).then(res.redirect('/manage')); + else sports.add(name).then(res.redirect('/manage')); }); router.get('/adddivision', function(req, res, next) { diff --git a/views/manage/addsport.pug b/views/manage/addsport.pug index 7f7e5ad..5251b7e 100644 --- a/views/manage/addsport.pug +++ b/views/manage/addsport.pug @@ -6,14 +6,17 @@ block stylesheets block content div#mobile-view - h1 Add Sport - form(action='./submitsport', method='POST') + h1#main-header Add Sport + form#submission-form(action='./sport', method='POST') span(class='form-section') label Sport name span(class='form-section-input') - input(type="text", name="name") + input#name-textbox(type="text" name="name" disabled) span(class='form-section') - button#submit-button(type="submit") Submit + button#submit-button(type="submit" disabled) Submit + span(class='form-section') + button#delete-button(disabled) Delete + block scripts - script(src='/scripts/sport.js' type="module") \ No newline at end of file + script(src='/scripts/manage/sport.js' type="module") \ No newline at end of file From cf91548daca5c4b4b174adca65204ab9d57e18a4 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 22 Nov 2021 22:10:47 -0700 Subject: [PATCH 114/255] Add ability to remove sports --- public/scripts/manage/sport.js | 17 ++++++++++++++++- routes/manage.js | 4 +++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/public/scripts/manage/sport.js b/public/scripts/manage/sport.js index 2b12593..c30fbce 100644 --- a/public/scripts/manage/sport.js +++ b/public/scripts/manage/sport.js @@ -40,4 +40,19 @@ async function checkDataValidity() { if(dataIsValid) submitButton.disabled = false; else submitButton.disabled = true; -} \ No newline at end of file +} + +async function removeSport() { + const removeInput = document.createElement('input'); + removeInput.setAttribute('name', 'remove'); + removeInput.setAttribute('value', 1); + removeInput.setAttribute('type', 'hidden'); + submissionForm.appendChild(removeInput); + submissionForm.submit(); +} + +deleteButton.addEventListener('click', () => { + const verified = confirm("This sport will be removed."); + + if(verified) removeSport(); +}); \ No newline at end of file diff --git a/routes/manage.js b/routes/manage.js index a42f734..145dfa0 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -50,8 +50,10 @@ router.get('/sport', function(req, res, next) { router.post('/sport', function(req, res, next) { const name = req.body['name']; const id = req.body['sport']; + const remove = req.body['remove']; - if(id) sports.rename(id, name).then(res.redirect('/manage')); + if(remove) sports.remove(id).then(res.redirect('/manage')); + else if(id) sports.rename(id, name).then(res.redirect('/manage')); else sports.add(name).then(res.redirect('/manage')); }); From a2b9c5f50ec8e5e258a38b99e71a15dbaf130688 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 22 Nov 2021 23:23:57 -0700 Subject: [PATCH 115/255] Add ability to edit divisions --- public/scripts/form.js | 40 ++++++++++++++++++++ public/scripts/manage.js | 6 +-- public/scripts/manage/division.js | 61 +++++++++++++++++++++++++------ routes/manage.js | 32 ++++++++++------ views/manage/adddivision.pug | 14 ++++--- 5 files changed, 122 insertions(+), 31 deletions(-) create mode 100644 public/scripts/form.js diff --git a/public/scripts/form.js b/public/scripts/form.js new file mode 100644 index 0000000..9d4ee93 --- /dev/null +++ b/public/scripts/form.js @@ -0,0 +1,40 @@ +import * as Data from "./data.js"; + +export async function populateSports(sportDropdown, selectedSportID = undefined) { + sportDropdown.innerHTML = ""; + + const sportsList = await Data.getSports(); + + let currentIndex = 0; + let selectedSportIndex; + sportsList.forEach(sport => { + const option = document.createElement('option'); + option.text = sport.name; + option.value = sport.id; + sportDropdown.appendChild(option); + + if(sport.id == selectedSportID) selectedSportIndex = currentIndex; + currentIndex++; + }); + + if(selectedSportIndex) sportDropdown.selectedIndex = selectedSportIndex; +} + +export async function addHiddenValue(name, value, form) { + const valueInput = document.createElement('input'); + valueInput.setAttribute('name', name); + valueInput.setAttribute('value', value); + valueInput.setAttribute('type', 'hidden'); + form.appendChild(valueInput); +} + +export async function addRemoveFunction(removeButton, form, objectTitle) { + removeButton.addEventListener('click', async () => { + const verified = confirm(`This ${objectTitle} will be removed.`); + + if(verified) { + await addHiddenValue('remove', 1, form); + form.submit(); + } + }); +} \ No newline at end of file diff --git a/public/scripts/manage.js b/public/scripts/manage.js index 8b0221b..a30c179 100644 --- a/public/scripts/manage.js +++ b/public/scripts/manage.js @@ -135,10 +135,10 @@ CATEGORIES.push(new Category( row.appendChild(sportCell); }, async function addDivision() { - window.location.href = "/manage/adddivision"; + window.location.href = "/manage/division"; }, - async function editDivision() { - + async function editDivision(id) { + window.location.href = `/manage/division?division=${id}` } )); diff --git a/public/scripts/manage/division.js b/public/scripts/manage/division.js index 6b37492..fb623c5 100644 --- a/public/scripts/manage/division.js +++ b/public/scripts/manage/division.js @@ -1,18 +1,57 @@ import * as Data from "../data.js"; +import * as Form from "../form.js"; +const submissionForm = document.getElementById('submission-form'); const sportDropdown = document.getElementById('sport-dropdown'); +const genderDropdown = document.getElementById('gender-dropdown'); +const nameTextbox = document.getElementById('name-textbox'); +const submitButton = document.getElementById('submit-button'); +const deleteButton = document.getElementById('delete-button'); -async function listSports() { - sportDropdown.innerHTML = ""; - const sportsList = await Data.getSports(); - - sportsList.forEach(sport => { - const option = document.createElement('option'); - option.text = sport.name; - option.value = sport.id; - sportDropdown.appendChild(option); - }); +async function initializeForm() { + let params = new URLSearchParams(location.search); + let divisionID = params.get('division'); + if(divisionID) { + const division = await Data.getDivision(divisionID); + + nameTextbox.value = division.name; + + deleteButton.style.visibility = "visible"; + deleteButton.disabled = false; + + const gender = division.gender.name; + + if(gender == 'female') genderDropdown.selectedIndex = 1; + else genderDropdown.selectedIndex = 2; + + Form.populateSports(sportDropdown, division.sportID); + + Form.addHiddenValue('division', divisionID, submissionForm); + } + else { + Form.populateSports(sportDropdown); + + genderDropdown.disabled = false; + + sportDropdown.disabled = false; + } + + nameTextbox.disabled = false; + nameTextbox.addEventListener('keyup', checkDataValidity); } -listSports(); \ No newline at end of file +initializeForm(); + +async function checkDataValidity() { + let dataIsValid = true; + + if(!nameTextbox.value) dataIsValid = false; + + + if(dataIsValid) submitButton.disabled = false; + else submitButton.disabled = true; +} + +Form.addRemoveFunction(deleteButton, submissionForm, "division"); + diff --git a/routes/manage.js b/routes/manage.js index 145dfa0..4b6683f 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -57,24 +57,34 @@ router.post('/sport', function(req, res, next) { else sports.add(name).then(res.redirect('/manage')); }); -router.get('/adddivision', function(req, res, next) { - res.render('manage/adddivision', { title: 'Add division' }); +router.get('/division', function(req, res, next) { + let title = req.query.division ? 'Edit Division' : 'Add Division' + + res.render('manage/adddivision', { title }); }); -router.post('/submitdivision', function(req, res, next) { +router.post('/division', function(req, res, next) { const name = req.body['name']; const sport = req.body['sport']; const genderName = req.body['gender']; - if(genderName == "both") { - divisions.add(name, genders.FEMALE, sport) - .then(divisions.add(name, genders.MALE, sport) - .then(res.send("SUCCESS"))); - } + const id = req.body['division']; + const remove = req.body['remove']; + + + if(remove) divisions.remove(id).then(res.redirect('/manage')); + else if(id) divisions.rename(id, name).then(res.redirect('/manage')); else { - const gender = (genderName == "female") ? genders.FEMALE : genders.MALE; - divisions.add(name, gender, sport) - .then(res.send("SUCCESS")); + if(genderName == "both") { + divisions.add(name, genders.FEMALE, sport) + .then(divisions.add(name, genders.MALE, sport) + .then(res.redirect("/manage"))); + } + else { + const gender = (genderName == "female") ? genders.FEMALE : genders.MALE; + divisions.add(name, gender, sport) + .then(res.redirect("/manage")); + } } }); diff --git a/views/manage/adddivision.pug b/views/manage/adddivision.pug index 4b8d7d1..5db737c 100644 --- a/views/manage/adddivision.pug +++ b/views/manage/adddivision.pug @@ -6,25 +6,27 @@ block stylesheets block content div#mobile-view - h1 Add Division - form(action='./submitdivision', method='POST') + h1 #{title} + form#submission-form(action='./division', method='POST') span(class='form-section') label Sport span(class='form-section-input') - select#sport-dropdown(name="sport" class="form-main-dropdown") + select#sport-dropdown(name="sport" class="form-main-dropdown" disabled) span(class='form-section') label Gender span(class='form-section-input') - select#gender-dropdown(name="gender" class="form-main-dropdown") + select#gender-dropdown(name="gender" class="form-main-dropdown" disabled) option(value="both") Both option(value="female") Female option(value="male") Male span(class='form-section') label Division name span(class='form-section-input') - input(type="text", name="name") + input#name-textbox(type="text", name="name" disabled) span(class='form-section') - button#submit-button(type="submit") Submit + button#submit-button(type="submit" disabled) Submit + span(class='form-section') + button#delete-button(disabled) Delete block scripts script(src='/scripts/manage/division.js' type="module") \ No newline at end of file From 5d88f0ac4dd393ba9a2181ceda8127454d2af140 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 22 Nov 2021 23:45:24 -0700 Subject: [PATCH 116/255] Add ability to edit teams --- database/scores/teams.js | 7 +++-- public/scripts/data.js | 4 +-- public/scripts/index.js | 4 +-- public/scripts/manage.js | 14 +++++----- public/scripts/manage/team.js | 52 +++++++++++++++++++++++++++-------- routes/manage.js | 16 +++++++---- views/manage/addteam.pug | 12 ++++---- 7 files changed, 74 insertions(+), 35 deletions(-) diff --git a/database/scores/teams.js b/database/scores/teams.js index 3717841..592796f 100644 --- a/database/scores/teams.js +++ b/database/scores/teams.js @@ -65,11 +65,12 @@ async function retrieve(sportID = undefined) { } async function getFromID(id) { - const query = `SELECT team_name + const query = `SELECT team_name, sport_id FROM scores.teams WHERE team_id = $1;`; - const name = (await database.executeQuery(query, [id]))[0][0]; - return new Team(id, name); + const row = (await database.executeQuery(query, [id]))[0]; + console.log(row); + return new Team(id, row[0], row[1]); } diff --git a/public/scripts/data.js b/public/scripts/data.js index 8ef7479..f2eead9 100644 --- a/public/scripts/data.js +++ b/public/scripts/data.js @@ -47,10 +47,10 @@ export async function getTeams(sportID = undefined) { return teamsList; } -export async function getTeamName(teamID) { +export async function getTeam(teamID) { const response = await fetch(`/data/team?team=${+teamID}`); const team = await response.json(); - return team.name; + return team; } export async function getGames(teamID = undefined, divisionID = undefined, seasonID = undefined) { diff --git a/public/scripts/index.js b/public/scripts/index.js index 41d9610..d752ff1 100644 --- a/public/scripts/index.js +++ b/public/scripts/index.js @@ -128,8 +128,8 @@ async function listGames() { row.appendChild(scoreCell); const opponentCell = document.createElement('td'); - Data.getTeamName(game.team2ID) - .then(data => opponentCell.textContent = data); + Data.getTeam(game.team2ID) + .then(data => opponentCell.textContent = data.name); row.appendChild(opponentCell); const dateCell = document.createElement('td'); diff --git a/public/scripts/manage.js b/public/scripts/manage.js index a30c179..ca2f4b2 100644 --- a/public/scripts/manage.js +++ b/public/scripts/manage.js @@ -178,10 +178,10 @@ CATEGORIES.push(new Category( row.appendChild(sportCell); }, async function addTeam() { - window.location.href = "/manage/addteam"; + window.location.href = "/manage/team"; }, - async function editTeam() { - + async function editTeam(id) { + window.location.href = `/manage/team?team=${id}`; } )); @@ -217,12 +217,12 @@ CATEGORIES.push(new Category( function listGame(game, row) { const teamsCell = document.createElement('td'); const team1NameSpan = document.createElement('span'); - Data.getTeamName(game.team1ID) - .then(data => team1NameSpan.textContent = data); + Data.getTeam(game.team1ID) + .then(data => team1NameSpan.textContent = data.name); teamsCell.appendChild(team1NameSpan); const team2NameSpan = document.createElement('span'); - Data.getTeamName(game.team2ID) - .then(data => team2NameSpan.textContent = data); + Data.getTeam(game.team2ID) + .then(data => team2NameSpan.textContent = data.name); teamsCell.appendChild(team2NameSpan); row.appendChild(teamsCell); diff --git a/public/scripts/manage/team.js b/public/scripts/manage/team.js index 6b37492..d5d8291 100644 --- a/public/scripts/manage/team.js +++ b/public/scripts/manage/team.js @@ -1,18 +1,48 @@ import * as Data from "../data.js"; +import * as Form from "../form.js"; +const submissionForm = document.getElementById('submission-form'); const sportDropdown = document.getElementById('sport-dropdown'); +const nameTextbox = document.getElementById('name-textbox'); +const submitButton = document.getElementById('submit-button'); +const deleteButton = document.getElementById('delete-button'); -async function listSports() { - sportDropdown.innerHTML = ""; +async function initializeForm() { + let params = new URLSearchParams(location.search); + let teamID = params.get('team'); + if(teamID) { + const team = await Data.getTeam(teamID); - const sportsList = await Data.getSports(); - - sportsList.forEach(sport => { - const option = document.createElement('option'); - option.text = sport.name; - option.value = sport.id; - sportDropdown.appendChild(option); - }); + nameTextbox.value = team.name; + + deleteButton.style.visibility = "visible"; + deleteButton.disabled = false; + + Form.populateSports(sportDropdown, team.sportID); + + console.log(team.sportID); + Form.addHiddenValue('team', teamID, submissionForm); + } + else { + sportDropdown.disabled = false; + + Form.populateSports(sportDropdown); + } + + nameTextbox.disabled = false; + nameTextbox.addEventListener('keyup', checkDataValidity); } -listSports(); \ No newline at end of file +initializeForm(); + +async function checkDataValidity() { + let dataIsValid = true; + + if(!nameTextbox.value) dataIsValid = false; + + + if(dataIsValid) submitButton.disabled = false; + else submitButton.disabled = true; +} + +Form.addRemoveFunction(deleteButton, submissionForm, "team"); \ No newline at end of file diff --git a/routes/manage.js b/routes/manage.js index 4b6683f..4114790 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -88,16 +88,22 @@ router.post('/division', function(req, res, next) { } }); -router.get('/addteam', function(req, res, next) { - res.render('manage/addteam', { title: 'Add team' }); +router.get('/team', function(req, res, next) { + let title = req.query.team ? 'Edit Team' : 'Add Team' + + res.render('manage/addteam', { title }); }); -router.post('/submitteam', function(req, res, next) { +router.post('/team', function(req, res, next) { const name = req.body['name']; const sport = req.body['sport']; - teams.add(name, sport) - .then(res.send("SUCCESS")); + const id = req.body['team']; + const remove = req.body['remove']; + + if(remove) teams.remove(id).then(res.redirect('/manage')); + else if(id) teams.rename(id, name).then(res.redirect('/manage')); + else teams.add(name, sport).then(res.redirect("/manage")); }); module.exports = router; diff --git a/views/manage/addteam.pug b/views/manage/addteam.pug index c3d422b..3cb6ccb 100644 --- a/views/manage/addteam.pug +++ b/views/manage/addteam.pug @@ -6,18 +6,20 @@ block stylesheets block content div#mobile-view - h1 Add Team - form(action='./submitteam', method='POST') + h1 #{title} + form#submission-form(action='./team', method='POST') span(class='form-section') label Sport span(class='form-section-input') - select#sport-dropdown(name="sport" class="form-main-dropdown") + select#sport-dropdown(name="sport" class="form-main-dropdown" disabled) span(class='form-section') label Team name span(class='form-section-input') - input(type="text", name="name") + input#name-textbox(type="text", name="name" disabled) span(class='form-section') - button#submit-button(type="submit") Submit + button#submit-button(type="submit" disabled) Submit + span(class='form-section') + button#delete-button(disabled) Delete block scripts script(src='/scripts/manage/team.js' type="module") \ No newline at end of file From b5495b1f57c51fab126e4244af1420466d242236 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Tue, 23 Nov 2021 00:49:11 -0700 Subject: [PATCH 117/255] Add ability to edit games --- database/scores/games.js | 26 ++++++++++- public/scripts/data.js | 6 +++ public/scripts/form.js | 86 +++++++++++++++++++++++++++++++++++ public/scripts/manage.js | 6 +-- public/scripts/manage/game.js | 71 +++++++++++++++++++++++++++++ routes/data.js | 5 ++ routes/manage.js | 19 ++++++-- views/manage/addgame.pug | 28 ++++++------ views/manage/addseason.pug | 2 +- 9 files changed, 226 insertions(+), 23 deletions(-) create mode 100644 public/scripts/manage/game.js diff --git a/database/scores/games.js b/database/scores/games.js index 922d600..270941b 100644 --- a/database/scores/games.js +++ b/database/scores/games.js @@ -71,10 +71,34 @@ async function retrieve(teamID, divisionID, seasonID) { return gamesList; } +async function edit(gameID, divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score) { + const query = `UPDATE scores.games + SET division_id = $2, + season_id = $3, + game_date = $4, + team1_id = $5, + team2_id = $6, + team1_score = $7, + team2_score = $8 + WHERE game_id = $1;`; + await database.executeQuery(query, [gameID, divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score]); + return new Game(gameID, date, team1ID, team2ID, team1Score, team2Score, divisionID, seasonID); +} + +async function getFromID(gameID) { + const query = `SELECT game_id, division_id, season_id, game_date, team1_id, team2_id, team1_score, team2_score + FROM scores.games + WHERE game_id = $1;`; + const row = (await database.executeQuery(query, [gameID]))[0]; + return new Game(row[0], row[3].toISOString().slice(0,10), row[4], row[5], row[6], row[7], row[1], row[2]); +} + exports.add = add; exports.remove = remove; -exports.retrieve = retrieve; \ No newline at end of file +exports.retrieve = retrieve; +exports.edit = edit; +exports.getFromID = getFromID; \ No newline at end of file diff --git a/public/scripts/data.js b/public/scripts/data.js index f2eead9..873d4d6 100644 --- a/public/scripts/data.js +++ b/public/scripts/data.js @@ -63,4 +63,10 @@ export async function getGames(teamID = undefined, divisionID = undefined, seaso const response = await fetch(URL); const gamesList = await response.json(); return gamesList; +} + +export async function getGame(gameID) { + const response = await fetch(`/data/game?game=${gameID}`); + const game = await response.json(); + return game; } \ No newline at end of file diff --git a/public/scripts/form.js b/public/scripts/form.js index 9d4ee93..5132ea9 100644 --- a/public/scripts/form.js +++ b/public/scripts/form.js @@ -20,6 +20,92 @@ export async function populateSports(sportDropdown, selectedSportID = undefined) if(selectedSportIndex) sportDropdown.selectedIndex = selectedSportIndex; } +export async function populateSeasons(seasonDropdown, selectedSeasonID = undefined) { + seasonDropdown.innerHTML = ""; + + const seasonsList = await Data.getSeasons(); + + let currentIndex = 0; + let selectedSeasonIndex; + seasonsList.forEach(season => { + const option = document.createElement('option'); + option.text = (season.year - 1) + "-" + season.year; + option.value = season.id; + seasonDropdown.appendChild(option); + + if(season.id == selectedSeasonID) selectedSeasonIndex = currentIndex; + currentIndex++; + }); + + if(selectedSeasonIndex) seasonDropdown.selectedIndex = selectedSeasonIndex; +} + +export async function populateGenders(genderDropdown, selectedSportID, selectedGender = undefined) { + genderDropdown.innerHTML = ""; + + const gendersList = await Data.getGenders(selectedSportID); + + if(selectedSportID) { + let currentIndex = 0; + let selectedGenderIndex; + gendersList.forEach(gender => { + const option = document.createElement('option'); + option.text = (gender.name == "female") ? "Female" : (gender.name == "male") ? "Male" : ""; + option.value = gender.name; + genderDropdown.appendChild(option); + + if(gender.name == selectedGender) selectedGenderIndex = currentIndex; + currentIndex++; + }); + + if(selectedGenderIndex) genderDropdown.selectedIndex = selectedGenderIndex; + } +} + +export async function populateDivisions (divisionDropdown, selectedSportID, selectedGender, selectedDivisionID = undefined) { + divisionDropdown.innerHTML = ""; + + if(selectedSportID && selectedGender) { + const divisionsList = await Data.getDivisions(selectedSportID, selectedGender); + + let currentIndex = 0; + let selectedDivisionIndex; + divisionsList.forEach(division => { + const option = document.createElement('option'); + option.text = division.name; + option.value = division.id; + divisionDropdown.appendChild(option); + + if(division.id == selectedDivisionID) selectedDivisionIndex = currentIndex; + currentIndex++; + }); + + if(selectedDivisionIndex) divisionDropdown.selectedIndex = selectedDivisionIndex; + } +} + +export async function populateTeams(teamDropdown, selectedSportID, selectedTeamID) { + teamDropdown.innerHTML = ""; + + if(selectedSportID) { + const teamsList = await Data.getTeams(selectedSportID); + + let currentIndex = 0; + let selectedTeamIndex; + teamsList.forEach(team => { + const option = document.createElement('option'); + option.text = team.name; + option.value = team.id; + teamDropdown.appendChild(option); + + if(team.id == selectedTeamID) selectedTeamIndex = currentIndex; + currentIndex++; + }); + + if(selectedTeamIndex) teamDropdown.selectedIndex = selectedTeamIndex; + } +} + export async function addHiddenValue(name, value, form) { const valueInput = document.createElement('input'); valueInput.setAttribute('name', name); diff --git a/public/scripts/manage.js b/public/scripts/manage.js index ca2f4b2..27ad375 100644 --- a/public/scripts/manage.js +++ b/public/scripts/manage.js @@ -267,10 +267,10 @@ CATEGORIES.push(new Category( row.appendChild(dateCell); }, async function addGame() { - window.location.href = "/manage/addgame"; + window.location.href = "/manage/game"; }, - async function editSeason() { - + async function editGame(id) { + window.location.href = `/manage/game?game=${id}`; } )); diff --git a/public/scripts/manage/game.js b/public/scripts/manage/game.js new file mode 100644 index 0000000..0cc134b --- /dev/null +++ b/public/scripts/manage/game.js @@ -0,0 +1,71 @@ +import * as Data from "./../data.js"; +import * as Form from "./../form.js"; + + +const submissionForm = document.getElementById('submission-form'); +const sportDropdown = document.getElementById('sport-dropdown'); +const seasonDropdown = document.getElementById('year-dropdown'); +const genderDropdown = document.getElementById('gender-dropdown'); +const divisionDropdown = document.getElementById('division-dropdown'); +const dateInput = document.getElementById('date-input'); +const team1Dropdown = document.getElementById('team1-dropdown'); +const team2Dropdown = document.getElementById('team2-dropdown'); +const team1ScoreTextbox = document.getElementById('team1-score-textbox'); +const team2ScoreTextbox = document.getElementById('team2-score-textbox'); +const submitButton = document.getElementById('submit-button'); +const deleteButton = document.getElementById('delete-button'); + + +async function initializeForm() { + let params = new URLSearchParams(location.search); + let gameID = params.get('game'); + if(gameID) { + deleteButton.style.visibility = "visible"; + deleteButton.disabled = false; + + const game = await Data.getGame(gameID); + + Form.populateSeasons(seasonDropdown, game.seasonID); + Data.getDivision(game.divisionID) + .then(data => { + Form.populateSports(sportDropdown, data.sportID) + .then(() => { + Form.populateGenders(genderDropdown, sportDropdown.value, data.gender.name) + .then(() => { + Form.populateDivisions(divisionDropdown, sportDropdown.value, genderDropdown.value, game.divisionID); + }); + Form.populateTeams(team1Dropdown, sportDropdown.value, game.team1ID); + Form.populateTeams(team2Dropdown, sportDropdown.value, game.team2ID); + }); + }); + dateInput.value = game.date; + team1ScoreTextbox.value = game.team1Score; + team2ScoreTextbox.value = game.team2Score; + } + else { + Form.populateSeasons(seasonDropdown); + Form.populateSports(sportDropdown) + .then(() => { + Form.populateGenders(genderDropdown, sportDropdown.value) + .then(() => { + Form.populateDivisions(divisionDropdown, sportDropdown.value, genderDropdown.value); + }); + Form.populateTeams(team1Dropdown, sportDropdown.value); + Form.populateTeams(team2Dropdown, sportDropdown.value); + }); + dateInput.value = (new Date()).toISOString().slice(0,10); + } + seasonDropdown.disabled = false; + sportDropdown.disabled = false; + genderDropdown.disabled = false; + divisionDropdown.disabled = false; + dateInput.disabled = false; + team1Dropdown.disabled = false; + team2Dropdown.disabled = false; + team1ScoreTextbox.disabled = false; + team2ScoreTextbox.disabled = false; + submitButton.disabled = false; +} +initializeForm(); + +Form.addRemoveFunction(deleteButton, submissionForm, "game"); diff --git a/routes/data.js b/routes/data.js index 917b36b..6ad845a 100644 --- a/routes/data.js +++ b/routes/data.js @@ -56,4 +56,9 @@ router.get('/games', function(req, res, next) { .then(data => res.json(data)); }) +router.get('/game', function(req, res, next) { + games.getFromID(req.query.game) + .then(data => res.json(data)); +}) + module.exports = router; \ No newline at end of file diff --git a/routes/manage.js b/routes/manage.js index 4114790..e7c77ed 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -13,11 +13,13 @@ router.get('/', function(req, res, next) { res.render('manage', { title: 'Score Management' }); }); -router.get('/addgame', function(req, res, next) { - res.render('manage/addgame', { title: 'Submit Score' }); +router.get('/game', function(req, res, next) { + let title = req.query.game ? 'Edit Game' : 'Submit Score' + + res.render('manage/addgame', { title }); }); -router.post('/submitgame', function(req, res, next) { +router.post('/game', function(req, res, next) { const seasonID = req.body['year']; const sportID = req.body['sport']; const gender = (req.body['gender'] == "female") ? genders.FEMALE : genders.MALE; @@ -28,8 +30,15 @@ router.post('/submitgame', function(req, res, next) { const team2ID = req.body['team2']; const team2Score = req.body['team2-score']; - games.add(divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score) - .then(res.send("SUCCESS")); + const id = req.body['game']; + const remove = req.body['delete']; + + if(remove) games.remove(id) + .then(res.redirect("/manage")); + else if(id) games.edit(id, divisionId, seasonID, date, team1ID, team2ID, team1Score, team2Score) + .then(res.redirect('/manage')); + else games.add(divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score) + .then(res.redirect("/manage")); }); router.get('/addseason', function(req, res, next) { diff --git a/views/manage/addgame.pug b/views/manage/addgame.pug index 04026f4..039bfd1 100644 --- a/views/manage/addgame.pug +++ b/views/manage/addgame.pug @@ -6,34 +6,36 @@ block stylesheets block content div#mobile-view - h1 Submit Score - form(action='./submitgame', method='POST') + h1 #{title} + form#submission-form(action='./submitgame', method='POST') span(class='form-section') label Year span(class='form-section-input') - select#year-dropdown(name="year" class="form-main-dropdown") + select#year-dropdown(name="year" class="form-main-dropdown" disabled) span(class='form-section') label Sport span(class='form-section-input') - select#sport-dropdown(name="sport" class="form-main-dropdown") - select#gender-dropdown(name="gender") - select#division-dropdown(name="division") + select#sport-dropdown(name="sport" class="form-main-dropdown" disabled) + select#gender-dropdown(name="gender" disabled) + select#division-dropdown(name="division" disabled) span(class='form-section') label Date of match span(class='form-section-input') - input(type="date", name="date", value=date) + input#date-input(type="date", name="date" value=date disabled) span(class='form-section') label Your team span(class='form-section-input') - select#team1-dropdown(name="team1" class="form-main-dropdown") - input(class="form-score-input", type="number", name="team1-score", value="0") + select#team1-dropdown(name="team1" class="form-main-dropdown" disabled) + input#team1-score-textbox(class="form-score-input", type="number", name="team1-score", value="0" disabled) span(class='form-section') label Opponent span(class='form-section-input') - select#team2-dropdown(name="team2" class="form-main-dropdown") - input(class="form-score-input", type="number", name="team2-score", value="0") + select#team2-dropdown(name="team2" class="form-main-dropdown" disabled) + input#team2-score-textbox(class="form-score-input", type="number", name="team2-score", value="0" disabled) span(class='form-section') - button#submit-button(type="submit") Submit + button#submit-button(type="submit" disabled) Submit + span(class='form-section') + button#delete-button(disabled) Delete block scripts - script(src='/scripts/submit.js' type="module") \ No newline at end of file + script(src='/scripts/manage/game.js' type="module") \ No newline at end of file diff --git a/views/manage/addseason.pug b/views/manage/addseason.pug index 96d4080..6539a76 100644 --- a/views/manage/addseason.pug +++ b/views/manage/addseason.pug @@ -16,4 +16,4 @@ block content button#submit-button(type="submit") Submit block scripts - script(src='/scripts/season.js' type="module") \ No newline at end of file + script(src='/scripts/manage/season.js' type="module") \ No newline at end of file From bd0ae3bdfd0246a504477c6022094553e869bbd7 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Tue, 23 Nov 2021 14:48:19 -0700 Subject: [PATCH 118/255] Add ability to delete seasons --- public/scripts/manage.js | 24 ++++++++++++++++++++++-- routes/manage.js | 11 +++++++---- views/manage/addseason.pug | 2 +- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/public/scripts/manage.js b/public/scripts/manage.js index 27ad375..8c89bfd 100644 --- a/public/scripts/manage.js +++ b/public/scripts/manage.js @@ -50,10 +50,30 @@ CATEGORIES.push(new Category( row.appendChild(spacerCell); }, async function addSeason() { - window.location.href = "/manage/addseason"; + window.location.href = "/manage/season"; }, - async function editSeason() { + async function editSeason(id) { + const verified = confirm(`This season will be removed.`); + + if(verified) { + const form = document.createElement('form'); + form.action = "/manage/season"; + form.method = "POST"; + form.style.visibility = "hidden"; + itemsListTable.appendChild(form); + const remove = document.createElement('input'); + remove.setAttribute('name', 'remove'); + remove.setAttribute('value', 1); + form.appendChild(remove); + + const seasonID = document.createElement('input'); + seasonID.setAttribute('name', 'season'); + seasonID.setAttribute('value', id); + form.appendChild(seasonID); + + form.submit(); + } } )); diff --git a/routes/manage.js b/routes/manage.js index e7c77ed..9757e69 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -41,15 +41,18 @@ router.post('/game', function(req, res, next) { .then(res.redirect("/manage")); }); -router.get('/addseason', function(req, res, next) { +router.get('/season', function(req, res, next) { res.render('manage/addseason', { title: 'Add Season', currentYear : (new Date()).getFullYear() }); }); -router.post('/submitseason', function(req, res, next) { +router.post('/season', function(req, res, next) { const year = req.body['year']; - seasons.add(year) - .then(res.send("SUCCESS")); + const seasonID = req.body['season']; + const remove = req.body['remove']; + + if(remove) seasons.remove(seasonID).then(res.redirect('/manage')); + else seasons.add(year).then(res.redirect("/manage")); }); router.get('/sport', function(req, res, next) { diff --git a/views/manage/addseason.pug b/views/manage/addseason.pug index 6539a76..bdb13fd 100644 --- a/views/manage/addseason.pug +++ b/views/manage/addseason.pug @@ -7,7 +7,7 @@ block stylesheets block content div#mobile-view h1 #{title} - form(action='./submitseason', method='POST') + form(action='./season', method='POST') span(class='form-section') label Ending year span(class='form-section-input') From fb63ef6b416c148e3b1d37a5ed009fd44c99dfc8 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Tue, 23 Nov 2021 14:48:30 -0700 Subject: [PATCH 119/255] Add buttons to access add score and management pages --- public/scripts/index.js | 13 ++++++++++++- public/stylesheets/index.css | 11 +++++++++++ views/index.pug | 6 +++++- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/public/scripts/index.js b/public/scripts/index.js index d752ff1..843ed4b 100644 --- a/public/scripts/index.js +++ b/public/scripts/index.js @@ -8,6 +8,8 @@ const teamDropdown = document.getElementById('team-dropdown'); const gamesTable = document.getElementById('games-table'); const gamesTableHeader = document.getElementById('games-table-header'); const noScoresMessage = document.getElementById('no-scores-message'); +const addScoreButton = document.getElementById('add-score-button'); +const manageButton = document.getElementById('manage-button'); @@ -173,4 +175,13 @@ sportDropdown.onchange = (() => { }); genderDropdown.onchange = listDivisions; teamDropdown.onchange = listGames; -seasonDropdown.onchange = listGames; \ No newline at end of file +seasonDropdown.onchange = listGames; + + +addScoreButton.addEventListener('click', () => { + window.location.href = '/manage/game'; +}); + +manageButton.addEventListener('click', () => { + window.location.href = '/manage' +}); \ No newline at end of file diff --git a/public/stylesheets/index.css b/public/stylesheets/index.css index 2f8f252..7a60f19 100644 --- a/public/stylesheets/index.css +++ b/public/stylesheets/index.css @@ -18,4 +18,15 @@ th { tr { height: 3em; +} + +#header-div { + display: flex; + flex-direction: row; +} + +#actions-div { + display: flex; + flex-direction: column; + margin-left: auto; } \ No newline at end of file diff --git a/views/index.pug b/views/index.pug index 2e52919..674d874 100644 --- a/views/index.pug +++ b/views/index.pug @@ -6,7 +6,11 @@ block stylesheets block content div#mobile-view - h1 Score Tracker + div#header-div + h1 Score Tracker + div#actions-div + button#add-score-button + + button#manage-button Manage div span(class='form-section') label Year From 64026a791e9c67efffe81c050cb7b2ef11f55608 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 24 Nov 2021 12:46:21 -0700 Subject: [PATCH 120/255] Add packages for user authentication --- package-lock.json | 142 ++++++++++++++++++++++++++++++++++++++++++---- package.json | 1 + 2 files changed, 131 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index 225db7b..9ec6365 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "debug": "~2.6.9", "dotenv": "^10.0.0", "express": "~4.16.0", + "express-session": "^1.17.2", "http-errors": "~1.6.2", "morgan": "~1.9.0", "nodemailer": "^6.6.5", @@ -520,6 +521,59 @@ "node": ">= 0.10.0" } }, + "node_modules/express-session": { + "version": "1.17.2", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.2.tgz", + "integrity": "sha512-mPcYcLA0lvh7D4Oqr5aNJFMtBMKPLl++OKKxkHzZ0U0oDq1rpKBnkR5f5vCHR26VeArlTOEF9td4x5IjICksRQ==", + "dependencies": { + "cookie": "0.4.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-headers": "~1.0.2", + "parseurl": "~1.3.3", + "safe-buffer": "5.2.1", + "uid-safe": "~2.1.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/express-session/node_modules/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express-session/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express-session/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -965,9 +1019,9 @@ } }, "node_modules/on-headers": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz", - "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", "engines": { "node": ">= 0.8" } @@ -987,9 +1041,9 @@ "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" }, "node_modules/parseurl": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", - "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "engines": { "node": ">= 0.8" } @@ -1299,6 +1353,14 @@ "node": ">=0.6" } }, + "node_modules/random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/range-parser": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", @@ -1602,6 +1664,17 @@ "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", "optional": true }, + "node_modules/uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "dependencies": { + "random-bytes": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -2091,6 +2164,38 @@ "vary": "~1.1.2" } }, + "express-session": { + "version": "1.17.2", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.2.tgz", + "integrity": "sha512-mPcYcLA0lvh7D4Oqr5aNJFMtBMKPLl++OKKxkHzZ0U0oDq1rpKBnkR5f5vCHR26VeArlTOEF9td4x5IjICksRQ==", + "requires": { + "cookie": "0.4.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-headers": "~1.0.2", + "parseurl": "~1.3.3", + "safe-buffer": "5.2.1", + "uid-safe": "~2.1.5" + }, + "dependencies": { + "cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } + } + }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -2441,9 +2546,9 @@ } }, "on-headers": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz", - "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" }, "once": { "version": "1.4.0", @@ -2460,9 +2565,9 @@ "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" }, "parseurl": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", - "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" }, "passport": { "version": "0.5.0", @@ -2719,6 +2824,11 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" }, + "random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=" + }, "range-parser": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", @@ -2971,6 +3081,14 @@ "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", "optional": true }, + "uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "requires": { + "random-bytes": "~1.0.0" + } + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", diff --git a/package.json b/package.json index c94c979..eb6d387 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "debug": "~2.6.9", "dotenv": "^10.0.0", "express": "~4.16.0", + "express-session": "^1.17.2", "http-errors": "~1.6.2", "morgan": "~1.9.0", "nodemailer": "^6.6.5", From 9c9c106acd1ea730fb3877d49662fc14b456d0cb Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 24 Nov 2021 15:53:42 -0700 Subject: [PATCH 121/255] Database init script now creates accounts table --- database/init_database.sql | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/database/init_database.sql b/database/init_database.sql index ad4f0e7..ea8decf 100644 --- a/database/init_database.sql +++ b/database/init_database.sql @@ -22,10 +22,7 @@ scores: accounts: users: - *user_id* | email | salt | password_hash | role | approved - - sessions: - *session_id* | ~user_id~ | expiration_date + *user_id* | email | password | admin */ @@ -102,4 +99,13 @@ CREATE TABLE IF NOT EXISTS scores.games( ); +CREATE SCHEMA IF NOT EXISTS accounts; + +CREATE TABLE IF NOT EXISTS accounts.users( + user_id BIGINT GENERATED ALWAYS AS IDENTITY, + email TEXT UNIQUE NOT NULL, + password TEXT NOT NULL, + admin BOOLEAN NOT NULL DEFAULT FALSE + ); + COMMIT; \ No newline at end of file From fbfa500b8a513d198683f436ec02c384b70c448d Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 24 Nov 2021 15:55:44 -0700 Subject: [PATCH 122/255] Install bcrypt for managing passwords --- package-lock.json | 857 ++++++++++++++++++++++++++++++++++++++++++++-- package.json | 1 + 2 files changed, 831 insertions(+), 27 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9ec6365..b41b189 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.0", "dependencies": { "async": "^3.2.2", + "bcrypt": "^5.0.1", "cookie-parser": "~1.4.3", "debug": "~2.6.9", "dotenv": "^10.0.0", @@ -26,6 +27,25 @@ "supertest": "^3.0.0" } }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.7.tgz", + "integrity": "sha512-PplSvl4pJ5N3BkVjAdDzpPhVUPdC73JgttkR+LnBx2OORC1GCQsBjUeEuipf9uOaAM1SbxcdZFfR3KDTKm2S0A==", + "dependencies": { + "detect-libc": "^1.0.3", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.5", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, "node_modules/@types/babel-types": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/@types/babel-types/-/babel-types-7.0.4.tgz", @@ -39,6 +59,11 @@ "@types/babel-types": "*" } }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, "node_modules/accepts": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", @@ -81,6 +106,38 @@ "node": ">=0.4.0" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agent-base/node_modules/debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/agent-base/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node_modules/align-text": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", @@ -102,6 +159,44 @@ "node": ">=0.4.2" } }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/are-we-there-yet/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -154,8 +249,7 @@ "node_modules/balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "node_modules/basic-auth": { "version": "2.0.0", @@ -168,6 +262,19 @@ "node": ">= 0.8" } }, + "node_modules/bcrypt": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.0.1.tgz", + "integrity": "sha512-9BTgmrhZM2t1bNuDtrtIMVSmmxZBrJ71n8Wg+YgdjHuIWYF7SjjmCPZFB+/5i/o/PIeRpwVJR3P+NrpIItUjqw==", + "hasInstallScript": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.0", + "node-addon-api": "^3.1.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/body-parser": { "version": "1.18.2", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", @@ -192,7 +299,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -248,6 +354,14 @@ "is-regex": "^1.0.3" } }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "engines": { + "node": ">=10" + } + }, "node_modules/clean-css": { "version": "3.4.28", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-3.4.28.tgz", @@ -273,6 +387,14 @@ "wordwrap": "0.0.2" } }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "bin": { + "color-support": "bin.js" + } + }, "node_modules/combined-stream": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", @@ -305,8 +427,12 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" }, "node_modules/constantinople": { "version": "3.1.2", @@ -403,6 +529,11 @@ "node": ">=0.4.0" } }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, "node_modules/depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -416,6 +547,17 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/diff": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", @@ -443,6 +585,11 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, "node_modules/encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", @@ -634,17 +781,46 @@ "node": ">= 0.6" } }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "node_modules/gauge": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.0.tgz", + "integrity": "sha512-F8sU45yQpjQjxKkm1UOAhf0U/O0aFt//Fl7hsrNVto+patMHjs7dPI9mFOGUKbhrgKm0S3EjW3scMFuQmWSROw==", + "dependencies": { + "ansi-regex": "^5.0.1", + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, "node_modules/glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", @@ -696,6 +872,11 @@ "node": ">=4" } }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, "node_modules/he": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", @@ -719,6 +900,39 @@ "node": ">= 0.6" } }, + "node_modules/https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node_modules/iconv-lite": { "version": "0.4.19", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", @@ -731,7 +945,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -775,6 +988,14 @@ "node": ">=0.4.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, "node_modules/is-promise": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", @@ -843,6 +1064,39 @@ "node": ">=0.10.0" } }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -895,7 +1149,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -909,6 +1162,29 @@ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true }, + "node_modules/minipass": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.5.tgz", + "integrity": "sha512-+8NzxD82XQoNKNrl1d/FSi+X8wAEWR+sbYAfIvub4Nz0d22plFG72CEVVaufV8PNf4qSslFTD8VMOxNVhHCjTw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/mkdirp": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", @@ -991,6 +1267,22 @@ "node": ">= 0.6" } }, + "node_modules/node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==" + }, + "node_modules/node-fetch": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz", + "integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + } + }, "node_modules/nodemailer": { "version": "6.6.5", "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.6.5.tgz", @@ -999,6 +1291,34 @@ "node": ">=6.0.0" } }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/npmlog": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.0.tgz", + "integrity": "sha512-03ppFRGlsyUaQFbGC2C8QWJN/C/K7PsfyD9aQdhVKAQIH4sQBc8WASqFBP7O+Ut4d2oo5LoeoboB3cGdBZSp6Q==", + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.0", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -1030,7 +1350,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "dependencies": { "wrappy": "1" } @@ -1076,7 +1395,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -1457,11 +1775,58 @@ "node": ">=0.10.0" } }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/safe-buffer": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" }, + "node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/send": { "version": "0.16.2", "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", @@ -1499,11 +1864,21 @@ "node": ">= 0.8.0" } }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, "node_modules/setprototypeof": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" }, + "node_modules/signal-exit": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", + "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==" + }, "node_modules/source-map": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", @@ -1552,6 +1927,30 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/superagent": { "version": "3.8.2", "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.2.tgz", @@ -1607,6 +2006,33 @@ "node": ">=4" } }, + "node_modules/tar": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/tar/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/to-fast-properties": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", @@ -1620,6 +2046,11 @@ "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-0.0.1.tgz", "integrity": "sha1-zu78cXp2xDFvEm0LnbqlXX598Bo=" }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, "node_modules/type-is": { "version": "1.6.16", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", @@ -1712,6 +2143,28 @@ "node": ">=0.10.0" } }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, "node_modules/window-size": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", @@ -1740,8 +2193,7 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "node_modules/xtend": { "version": "4.0.2", @@ -1751,6 +2203,11 @@ "node": ">=0.4" } }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/yargs": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", @@ -1764,6 +2221,22 @@ } }, "dependencies": { + "@mapbox/node-pre-gyp": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.7.tgz", + "integrity": "sha512-PplSvl4pJ5N3BkVjAdDzpPhVUPdC73JgttkR+LnBx2OORC1GCQsBjUeEuipf9uOaAM1SbxcdZFfR3KDTKm2S0A==", + "requires": { + "detect-libc": "^1.0.3", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.5", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + } + }, "@types/babel-types": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/@types/babel-types/-/babel-types-7.0.4.tgz", @@ -1777,6 +2250,11 @@ "@types/babel-types": "*" } }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, "accepts": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", @@ -1806,6 +2284,29 @@ } } }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, "align-text": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", @@ -1821,6 +2322,37 @@ "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" + }, + "are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -1870,8 +2402,7 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "basic-auth": { "version": "2.0.0", @@ -1881,6 +2412,15 @@ "safe-buffer": "5.1.1" } }, + "bcrypt": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.0.1.tgz", + "integrity": "sha512-9BTgmrhZM2t1bNuDtrtIMVSmmxZBrJ71n8Wg+YgdjHuIWYF7SjjmCPZFB+/5i/o/PIeRpwVJR3P+NrpIItUjqw==", + "requires": { + "@mapbox/node-pre-gyp": "^1.0.0", + "node-addon-api": "^3.1.0" + } + }, "body-parser": { "version": "1.18.2", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", @@ -1902,7 +2442,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1946,6 +2485,11 @@ "is-regex": "^1.0.3" } }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" + }, "clean-css": { "version": "3.4.28", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-3.4.28.tgz", @@ -1965,6 +2509,11 @@ "wordwrap": "0.0.2" } }, + "color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" + }, "combined-stream": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", @@ -1991,8 +2540,12 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" }, "constantinople": { "version": "3.1.2", @@ -2070,6 +2623,11 @@ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "dev": true }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -2080,6 +2638,11 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" + }, "diff": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", @@ -2101,6 +2664,11 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", @@ -2243,17 +2811,40 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "requires": { + "minipass": "^3.0.0" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "gauge": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.0.tgz", + "integrity": "sha512-F8sU45yQpjQjxKkm1UOAhf0U/O0aFt//Fl7hsrNVto+patMHjs7dPI9mFOGUKbhrgKm0S3EjW3scMFuQmWSROw==", + "requires": { + "ansi-regex": "^5.0.1", + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + } + }, "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", @@ -2293,6 +2884,11 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, "he": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", @@ -2310,6 +2906,30 @@ "statuses": ">= 1.4.0 < 2" } }, + "https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "requires": { + "agent-base": "6", + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, "iconv-lite": { "version": "0.4.19", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", @@ -2319,7 +2939,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -2356,6 +2975,11 @@ } } }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, "is-promise": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", @@ -2412,6 +3036,29 @@ "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -2449,7 +3096,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -2460,6 +3106,23 @@ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true }, + "minipass": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.5.tgz", + "integrity": "sha512-+8NzxD82XQoNKNrl1d/FSi+X8wAEWR+sbYAfIvub4Nz0d22plFG72CEVVaufV8PNf4qSslFTD8VMOxNVhHCjTw==", + "requires": { + "yallist": "^4.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + } + }, "mkdirp": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", @@ -2527,11 +3190,43 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" }, + "node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==" + }, + "node-fetch": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz", + "integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, "nodemailer": { "version": "6.6.5", "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.6.5.tgz", "integrity": "sha512-C/v856DBijUzHcHIgGpQoTrfsH3suKIRAGliIzCstatM2cAa+MYX3LuyCrABiO/cdJTxgBBHXxV1ztiqUwst5A==" }, + "nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "requires": { + "abbrev": "1" + } + }, + "npmlog": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.0.tgz", + "integrity": "sha512-03ppFRGlsyUaQFbGC2C8QWJN/C/K7PsfyD9aQdhVKAQIH4sQBc8WASqFBP7O+Ut4d2oo5LoeoboB3cGdBZSp6Q==", + "requires": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.0", + "set-blocking": "^2.0.0" + } + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -2554,7 +3249,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1" } @@ -2586,8 +3280,7 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-parse": { "version": "1.0.5", @@ -2909,11 +3602,42 @@ "align-text": "^0.1.1" } }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "requires": { + "glob": "^7.1.3" + }, + "dependencies": { + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, "safe-buffer": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "requires": { + "lru-cache": "^6.0.0" + } + }, "send": { "version": "0.16.2", "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", @@ -2945,11 +3669,21 @@ "send": "0.16.2" } }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, "setprototypeof": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" }, + "signal-exit": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", + "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==" + }, "source-map": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", @@ -2991,6 +3725,24 @@ "safe-buffer": "~5.1.0" } }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + }, "superagent": { "version": "3.8.2", "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.2.tgz", @@ -3039,6 +3791,26 @@ "has-flag": "^3.0.0" } }, + "tar": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "dependencies": { + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + } + } + }, "to-fast-properties": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", @@ -3049,6 +3821,11 @@ "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-0.0.1.tgz", "integrity": "sha1-zu78cXp2xDFvEm0LnbqlXX598Bo=" }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, "type-is": { "version": "1.6.16", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", @@ -3114,6 +3891,28 @@ "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=" }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "requires": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, "window-size": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", @@ -3136,14 +3935,18 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "yargs": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", diff --git a/package.json b/package.json index eb6d387..79eeb84 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ }, "dependencies": { "async": "^3.2.2", + "bcrypt": "^5.0.1", "cookie-parser": "~1.4.3", "debug": "~2.6.9", "dotenv": "^10.0.0", From 7a62885e8f908970c29d85b078db026bdf9a93c4 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 24 Nov 2021 17:16:46 -0700 Subject: [PATCH 123/255] Install passport-local --- package-lock.json | 20 ++++++++++++++++++++ package.json | 1 + 2 files changed, 21 insertions(+) diff --git a/package-lock.json b/package-lock.json index b41b189..c402529 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "morgan": "~1.9.0", "nodemailer": "^6.6.5", "passport": "^0.5.0", + "passport-local": "^1.0.0", "pg": "^8.7.1", "pug": "2.0.0-beta11" }, @@ -1383,6 +1384,17 @@ "url": "https://github.com/sponsors/jaredhanson" } }, + "node_modules/passport-local": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz", + "integrity": "sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=", + "dependencies": { + "passport-strategy": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/passport-strategy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", @@ -3272,6 +3284,14 @@ "pause": "0.0.1" } }, + "passport-local": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz", + "integrity": "sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=", + "requires": { + "passport-strategy": "1.x.x" + } + }, "passport-strategy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", diff --git a/package.json b/package.json index 79eeb84..2959fbd 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "morgan": "~1.9.0", "nodemailer": "^6.6.5", "passport": "^0.5.0", + "passport-local": "^1.0.0", "pg": "^8.7.1", "pug": "2.0.0-beta11" }, From 33617a82ecea0c55286986d4093b5f082cdddcab Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 24 Nov 2021 17:56:04 -0700 Subject: [PATCH 124/255] Add passport functions --- app.js | 22 +++++++++++++++++++++ database/accounts/accounts.js | 37 +++++++++++++++++++++++++++++++++++ database/accounts/random.js | 12 ++++++++++++ routes/accounts.js | 11 +++++++++++ 4 files changed, 82 insertions(+) create mode 100644 database/accounts/accounts.js create mode 100644 database/accounts/random.js create mode 100644 routes/accounts.js diff --git a/app.js b/app.js index c773280..0ad96f4 100644 --- a/app.js +++ b/app.js @@ -3,6 +3,11 @@ var express = require('express'); var path = require('path'); var cookieParser = require('cookie-parser'); var logger = require('morgan'); +var random = require('./database/accounts/random'); +const passport = require('passport'); +const session = require('express-session'); +const accounts = require('./database/accounts/accounts'); + var indexRouter = require('./routes/index'); var usersRouter = require('./routes/users'); @@ -11,6 +16,23 @@ var manageRouter = require('./routes/manage'); var app = express(); +// session setup +app.use( + session({ + secret: random.makeid(20), + resave: false, + saveUninitialized: true, + }) +); + +// passport setup +app.use(passport.initialize()); +app.use(passport.session()); + +//passport.use(accounts.createStrategy()); +//passport.serializeUser(accounts.serializeUser()); +//passport.deserializeUser(accounts.deserializeUser()); + // view engine setup app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'pug'); diff --git a/database/accounts/accounts.js b/database/accounts/accounts.js new file mode 100644 index 0000000..67d277a --- /dev/null +++ b/database/accounts/accounts.js @@ -0,0 +1,37 @@ +const database = require('./../database'); +const passport = require('passport'); +const passportLocal = require('passport-local'); + +passport.use(new passportLocal.Strategy((email, password, cb) => { + query = `SELECT id, email, password, admin + FROM accounts.users + WHERE email = $1`; + const result = database.executeQuery(query, [email]); + if(result.length > 0) { + const first = result[0]; + bcrypt.compare(password, first[2], function(err, res) { + if(res) { + cb(null, { id: first[0], email: first[1], admin: first[3] }) + } + else + { + cb(null, false) + } + }) + } else { + cb(null, false) + } +})); + +passport.serializeUser((user, done) => { + done(null, user.id) +}) + +passport.deserializeUser((id, cb) => { + query = `SELECT id, email, admin + FROM accounts.users + WHERE id = $1`; + const result = database.executeQuery(query, [parseInt(id, 10)]); + + cb(null, result[0]); +}); \ No newline at end of file diff --git a/database/accounts/random.js b/database/accounts/random.js new file mode 100644 index 0000000..6d6a57d --- /dev/null +++ b/database/accounts/random.js @@ -0,0 +1,12 @@ +function makeid(length) { + var result = ''; + var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + var charactersLength = characters.length; + for ( var i = 0; i < length; i++ ) { + result += characters.charAt(Math.floor(Math.random() * + charactersLength)); + } + return result; +} + +exports.makeid = makeid; \ No newline at end of file diff --git a/routes/accounts.js b/routes/accounts.js new file mode 100644 index 0000000..5a72c2f --- /dev/null +++ b/routes/accounts.js @@ -0,0 +1,11 @@ +const passport = require('passport'); +var router = express.Router(); +const app = require('../app'); + +router.post('/login', passport.authenticate('local'), (req, res, next) => { + const { user } = req; + + res.json(user); +}); + +module.exports = router; \ No newline at end of file From c5f05eebff4da9a8eecad0d955f6e266d32514d8 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 24 Nov 2021 17:58:28 -0700 Subject: [PATCH 125/255] Add auth router to app.js --- app.js | 2 ++ routes/{accounts.js => auth.js} | 0 2 files changed, 2 insertions(+) rename routes/{accounts.js => auth.js} (100%) diff --git a/app.js b/app.js index 0ad96f4..f657970 100644 --- a/app.js +++ b/app.js @@ -13,6 +13,7 @@ var indexRouter = require('./routes/index'); var usersRouter = require('./routes/users'); var dataRouter = require('./routes/data'); var manageRouter = require('./routes/manage'); +var authRouter = require('./routes/auth'); var app = express(); @@ -47,6 +48,7 @@ app.use('/', indexRouter); app.use('/users', usersRouter); app.use('/data', dataRouter); app.use('/manage', manageRouter); +app.user('/auth', authRouter); // catch 404 and forward to error handler diff --git a/routes/accounts.js b/routes/auth.js similarity index 100% rename from routes/accounts.js rename to routes/auth.js From 7520580d66e5a136203ade7e7f928b33d7696764 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 24 Nov 2021 18:13:33 -0700 Subject: [PATCH 126/255] Add function to create user --- app.js | 2 +- database/accounts/accounts.js | 52 ++++++++++++++++++++++------------- database/database.js | 1 - 3 files changed, 34 insertions(+), 21 deletions(-) diff --git a/app.js b/app.js index f657970..f8cb47b 100644 --- a/app.js +++ b/app.js @@ -48,7 +48,7 @@ app.use('/', indexRouter); app.use('/users', usersRouter); app.use('/data', dataRouter); app.use('/manage', manageRouter); -app.user('/auth', authRouter); +app.use('/auth', authRouter); // catch 404 and forward to error handler diff --git a/database/accounts/accounts.js b/database/accounts/accounts.js index 67d277a..d65de3c 100644 --- a/database/accounts/accounts.js +++ b/database/accounts/accounts.js @@ -1,26 +1,28 @@ const database = require('./../database'); const passport = require('passport'); const passportLocal = require('passport-local'); +const bcrypt = require('bcrypt'); passport.use(new passportLocal.Strategy((email, password, cb) => { - query = `SELECT id, email, password, admin + query = `SELECT user_id, email, password, admin FROM accounts.users WHERE email = $1`; - const result = database.executeQuery(query, [email]); - if(result.length > 0) { - const first = result[0]; - bcrypt.compare(password, first[2], function(err, res) { - if(res) { - cb(null, { id: first[0], email: first[1], admin: first[3] }) - } - else - { + database.executeQuery(query, [email]) + .then(result => { + if(result.length > 0) { + const first = result[0]; + const matches = bcrypt.compareSync(password, first[2]); + if(matches) { + cb(null, { id: first[0], email: first[1], admin: first[3] }) + } + else + { + cb(null, false) + } + } else { cb(null, false) } - }) - } else { - cb(null, false) - } + }); })); passport.serializeUser((user, done) => { @@ -28,10 +30,22 @@ passport.serializeUser((user, done) => { }) passport.deserializeUser((id, cb) => { - query = `SELECT id, email, admin + query = `SELECT user_id, email, admin FROM accounts.users WHERE id = $1`; - const result = database.executeQuery(query, [parseInt(id, 10)]); - - cb(null, result[0]); -}); \ No newline at end of file + database.executeQuery(query, [parseInt(id, 10)]) + .then(result => { + cb(null, result[0]); + }); +}); + + + +async function createUser(email, password) { + const salt = bcrypt.genSaltSync(); + const hash = bcrypt.hashSync(password, salt); + + const query = `INSERT INTO accounts.users(email, password) + VALUES($1, $2)`; + await database.executeQuery(query, [email, hash]); +} \ No newline at end of file diff --git a/database/database.js b/database/database.js index f166980..1d5bc56 100644 --- a/database/database.js +++ b/database/database.js @@ -27,7 +27,6 @@ async function Initialize() { - async function checkForDatabaseInitialization() { const query = `SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'scores'`; let result = await executeQuery(query); From 48c2ee67bd9b5cc9e7292c382bb172606e226c40 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 24 Nov 2021 18:25:27 -0700 Subject: [PATCH 127/255] Add page to create user --- app.js | 2 ++ database/accounts/accounts.js | 4 +++- routes/admin.js | 12 ++++++++++++ routes/auth.js | 9 ++++++++- views/accounts/createuser.pug | 20 ++++++++++++++++++++ 5 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 routes/admin.js create mode 100644 views/accounts/createuser.pug diff --git a/app.js b/app.js index f8cb47b..3efd585 100644 --- a/app.js +++ b/app.js @@ -14,6 +14,7 @@ var usersRouter = require('./routes/users'); var dataRouter = require('./routes/data'); var manageRouter = require('./routes/manage'); var authRouter = require('./routes/auth'); +var adminRouter = require('./routes/admin'); var app = express(); @@ -49,6 +50,7 @@ app.use('/users', usersRouter); app.use('/data', dataRouter); app.use('/manage', manageRouter); app.use('/auth', authRouter); +app.use('/admin', adminRouter); // catch 404 and forward to error handler diff --git a/database/accounts/accounts.js b/database/accounts/accounts.js index d65de3c..5ae6cc9 100644 --- a/database/accounts/accounts.js +++ b/database/accounts/accounts.js @@ -48,4 +48,6 @@ async function createUser(email, password) { const query = `INSERT INTO accounts.users(email, password) VALUES($1, $2)`; await database.executeQuery(query, [email, hash]); -} \ No newline at end of file +} + +exports.createUser = createUser; \ No newline at end of file diff --git a/routes/admin.js b/routes/admin.js new file mode 100644 index 0000000..d032cd1 --- /dev/null +++ b/routes/admin.js @@ -0,0 +1,12 @@ +var express = require('express'); +var router = express.Router(); +const passport = require('passport'); +const app = require('../app'); +const accounts = require('./../database/accounts/accounts'); + + +router.get('/createuser', (req, res, next) => { + res.render('accounts/createuser', { title: 'Create user' }); +}); + +module.exports = router; \ No newline at end of file diff --git a/routes/auth.js b/routes/auth.js index 5a72c2f..38426ea 100644 --- a/routes/auth.js +++ b/routes/auth.js @@ -1,5 +1,7 @@ -const passport = require('passport'); +var express = require('express'); var router = express.Router(); +const passport = require('passport'); +const accounts = require('./../database/accounts/accounts'); const app = require('../app'); router.post('/login', passport.authenticate('local'), (req, res, next) => { @@ -8,4 +10,9 @@ router.post('/login', passport.authenticate('local'), (req, res, next) => { res.json(user); }); +router.post('/register', (req, res, next) => { + accounts.createUser(req.body.email, req.body.password) + .then(res.redirect('/')); +}); + module.exports = router; \ No newline at end of file diff --git a/views/accounts/createuser.pug b/views/accounts/createuser.pug new file mode 100644 index 0000000..c5630c8 --- /dev/null +++ b/views/accounts/createuser.pug @@ -0,0 +1,20 @@ +extends ../layout + +block stylesheets + link(rel='stylesheet', href='/stylesheets/submit.css') + link(rel='stylesheet', href='/stylesheets/form.css') + +block content + div#mobile-view + h1 #{title} + form(action='/auth/register', method='POST') + span(class='form-section') + label Email + span(class='form-section-input') + input(type="email", name="email") + span(class='form-section') + label Password + span(class='form-section-input') + input(type="password", name="password") + span(class='form-section') + button#submit-button(type="submit") Submit \ No newline at end of file From 0a6c56e103d94f45a9260f334e537f5125c24901 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 24 Nov 2021 20:36:00 -0700 Subject: [PATCH 128/255] Fix issues involving editing and submitting games --- public/scripts/manage/game.js | 2 ++ routes/manage.js | 2 +- views/manage/addgame.pug | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/public/scripts/manage/game.js b/public/scripts/manage/game.js index 0cc134b..82d4cb8 100644 --- a/public/scripts/manage/game.js +++ b/public/scripts/manage/game.js @@ -25,6 +25,8 @@ async function initializeForm() { const game = await Data.getGame(gameID); + Form.addHiddenValue('game', gameID, submissionForm); + Form.populateSeasons(seasonDropdown, game.seasonID); Data.getDivision(game.divisionID) .then(data => { diff --git a/routes/manage.js b/routes/manage.js index 9757e69..16f8ca4 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -35,7 +35,7 @@ router.post('/game', function(req, res, next) { if(remove) games.remove(id) .then(res.redirect("/manage")); - else if(id) games.edit(id, divisionId, seasonID, date, team1ID, team2ID, team1Score, team2Score) + else if(id) games.edit(id, divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score) .then(res.redirect('/manage')); else games.add(divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score) .then(res.redirect("/manage")); diff --git a/views/manage/addgame.pug b/views/manage/addgame.pug index 039bfd1..7981148 100644 --- a/views/manage/addgame.pug +++ b/views/manage/addgame.pug @@ -7,7 +7,7 @@ block stylesheets block content div#mobile-view h1 #{title} - form#submission-form(action='./submitgame', method='POST') + form#submission-form(action='./game', method='POST') span(class='form-section') label Year span(class='form-section-input') From 9c7241e7e6c0b58e0eb9748b42059755241879ca Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 24 Nov 2021 20:46:16 -0700 Subject: [PATCH 129/255] Remove console.log from teams.js --- database/scores/teams.js | 1 - 1 file changed, 1 deletion(-) diff --git a/database/scores/teams.js b/database/scores/teams.js index 592796f..20034ef 100644 --- a/database/scores/teams.js +++ b/database/scores/teams.js @@ -69,7 +69,6 @@ async function getFromID(id) { FROM scores.teams WHERE team_id = $1;`; const row = (await database.executeQuery(query, [id]))[0]; - console.log(row); return new Team(id, row[0], row[1]); } From a6fe6d6c7280d639ed9284c381b05c88e1102a1d Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 24 Nov 2021 20:58:49 -0700 Subject: [PATCH 130/255] Create login page --- database/accounts/accounts.js | 21 +++++++++++++-------- routes/auth.js | 14 ++++++++++---- views/accounts/login.pug | 20 ++++++++++++++++++++ 3 files changed, 43 insertions(+), 12 deletions(-) create mode 100644 views/accounts/login.pug diff --git a/database/accounts/accounts.js b/database/accounts/accounts.js index 5ae6cc9..6f000c9 100644 --- a/database/accounts/accounts.js +++ b/database/accounts/accounts.js @@ -1,26 +1,30 @@ const database = require('./../database'); const passport = require('passport'); -const passportLocal = require('passport-local'); +const localStrategy = require('passport-local').Strategy; const bcrypt = require('bcrypt'); -passport.use(new passportLocal.Strategy((email, password, cb) => { + +passport.use(new localStrategy({ + usernameField: 'email', + passwordField: 'password'}, + (username, password, cb) => { query = `SELECT user_id, email, password, admin FROM accounts.users WHERE email = $1`; - database.executeQuery(query, [email]) + database.executeQuery(query, [username]) .then(result => { if(result.length > 0) { const first = result[0]; const matches = bcrypt.compareSync(password, first[2]); if(matches) { - cb(null, { id: first[0], email: first[1], admin: first[3] }) + return cb(null, { id: first[0], email: first[1], admin: first[3] }) } else { - cb(null, false) + return cb(null, false) } } else { - cb(null, false) + return cb(null, false) } }); })); @@ -32,7 +36,7 @@ passport.serializeUser((user, done) => { passport.deserializeUser((id, cb) => { query = `SELECT user_id, email, admin FROM accounts.users - WHERE id = $1`; + WHERE user_id = $1`; database.executeQuery(query, [parseInt(id, 10)]) .then(result => { cb(null, result[0]); @@ -50,4 +54,5 @@ async function createUser(email, password) { await database.executeQuery(query, [email, hash]); } -exports.createUser = createUser; \ No newline at end of file +exports.createUser = createUser; +exports.passport = passport; \ No newline at end of file diff --git a/routes/auth.js b/routes/auth.js index 38426ea..026728c 100644 --- a/routes/auth.js +++ b/routes/auth.js @@ -1,13 +1,19 @@ var express = require('express'); var router = express.Router(); -const passport = require('passport'); const accounts = require('./../database/accounts/accounts'); const app = require('../app'); -router.post('/login', passport.authenticate('local'), (req, res, next) => { - const { user } = req; +router.get('/login', (req, res, next) => { + res.render('accounts/login', { title : "Login" }); +}); - res.json(user); +router.post('/login', + accounts.passport.authenticate('local', { + failureRedirect: '/fail', + successRedirect: '/success', + }), + (req, res, next) => { + console.log(req.user); }); router.post('/register', (req, res, next) => { diff --git a/views/accounts/login.pug b/views/accounts/login.pug new file mode 100644 index 0000000..fb58202 --- /dev/null +++ b/views/accounts/login.pug @@ -0,0 +1,20 @@ +extends ../layout + +block stylesheets + link(rel='stylesheet', href='/stylesheets/submit.css') + link(rel='stylesheet', href='/stylesheets/form.css') + +block content + div#mobile-view + h1 #{title} + form(action='/auth/login', method='POST') + span(class='form-section') + label Email + span(class='form-section-input') + input(type="email", name="email") + span(class='form-section') + label Password + span(class='form-section-input') + input(type="password", name="password") + span(class='form-section') + button#submit-button(type="submit") Submit \ No newline at end of file From 8e8f0e3e7ced5b82b3926e3c8472a682386cd423 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 24 Nov 2021 21:05:33 -0700 Subject: [PATCH 131/255] Change passport require --- routes/auth.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/routes/auth.js b/routes/auth.js index 026728c..53242d5 100644 --- a/routes/auth.js +++ b/routes/auth.js @@ -1,5 +1,6 @@ var express = require('express'); var router = express.Router(); +const passport = require('passport'); const accounts = require('./../database/accounts/accounts'); const app = require('../app'); @@ -8,7 +9,7 @@ router.get('/login', (req, res, next) => { }); router.post('/login', - accounts.passport.authenticate('local', { + passport.authenticate('local', { failureRedirect: '/fail', successRedirect: '/success', }), From ebef80e061a11777fd48d304dc5a2a5d37003fa3 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 24 Nov 2021 21:07:13 -0700 Subject: [PATCH 132/255] Add logout function --- routes/auth.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/routes/auth.js b/routes/auth.js index 53242d5..9b400e5 100644 --- a/routes/auth.js +++ b/routes/auth.js @@ -8,6 +8,11 @@ router.get('/login', (req, res, next) => { res.render('accounts/login', { title : "Login" }); }); +router.get('/logout', (req, res, next) => { + req.logout(); + res.redirect("/"); +}); + router.post('/login', passport.authenticate('local', { failureRedirect: '/fail', From 24197d9bffb9b9dd8881b8ca110b12851175f6cb Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 24 Nov 2021 21:22:17 -0700 Subject: [PATCH 133/255] Add invalid email or password message --- app.js | 4 ++++ package-lock.json | 14 ++++++++++++++ package.json | 1 + routes/auth.js | 7 ++++--- views/accounts/login.pug | 1 + 5 files changed, 24 insertions(+), 3 deletions(-) diff --git a/app.js b/app.js index 3efd585..a379166 100644 --- a/app.js +++ b/app.js @@ -7,6 +7,7 @@ var random = require('./database/accounts/random'); const passport = require('passport'); const session = require('express-session'); const accounts = require('./database/accounts/accounts'); +var flash = require('connect-flash'); var indexRouter = require('./routes/index'); @@ -18,6 +19,9 @@ var adminRouter = require('./routes/admin'); var app = express(); +// flash setup +app.use(flash()); + // session setup app.use( session({ diff --git a/package-lock.json b/package-lock.json index c402529..1903fa7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "async": "^3.2.2", "bcrypt": "^5.0.1", + "connect-flash": "^0.1.1", "cookie-parser": "~1.4.3", "debug": "~2.6.9", "dotenv": "^10.0.0", @@ -430,6 +431,14 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, + "node_modules/connect-flash": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/connect-flash/-/connect-flash-0.1.1.tgz", + "integrity": "sha1-2GMPJtlaf4UfmVax6MxnMvO2qjA=", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", @@ -2554,6 +2563,11 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, + "connect-flash": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/connect-flash/-/connect-flash-0.1.1.tgz", + "integrity": "sha1-2GMPJtlaf4UfmVax6MxnMvO2qjA=" + }, "console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", diff --git a/package.json b/package.json index 2959fbd..c3c687d 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "dependencies": { "async": "^3.2.2", "bcrypt": "^5.0.1", + "connect-flash": "^0.1.1", "cookie-parser": "~1.4.3", "debug": "~2.6.9", "dotenv": "^10.0.0", diff --git a/routes/auth.js b/routes/auth.js index 9b400e5..5d2e910 100644 --- a/routes/auth.js +++ b/routes/auth.js @@ -5,7 +5,7 @@ const accounts = require('./../database/accounts/accounts'); const app = require('../app'); router.get('/login', (req, res, next) => { - res.render('accounts/login', { title : "Login" }); + res.render('accounts/login', { title : "Login", message: req.flash('error') }); }); router.get('/logout', (req, res, next) => { @@ -15,8 +15,9 @@ router.get('/logout', (req, res, next) => { router.post('/login', passport.authenticate('local', { - failureRedirect: '/fail', - successRedirect: '/success', + failureRedirect: '/auth/login', + successRedirect: '/', + failureFlash: "Invalid email or password.", }), (req, res, next) => { console.log(req.user); diff --git a/views/accounts/login.pug b/views/accounts/login.pug index fb58202..13f707e 100644 --- a/views/accounts/login.pug +++ b/views/accounts/login.pug @@ -16,5 +16,6 @@ block content label Password span(class='form-section-input') input(type="password", name="password") + .error #{message} span(class='form-section') button#submit-button(type="submit") Submit \ No newline at end of file From 4828ef2c7c77fa27542e83bff753bd093a285ea1 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 24 Nov 2021 21:40:33 -0700 Subject: [PATCH 134/255] Require admin for certain pages --- database/accounts/accounts.js | 8 +++---- routes/manage.js | 40 +++++++++++++++++++++++++---------- 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/database/accounts/accounts.js b/database/accounts/accounts.js index 6f000c9..6fc0d62 100644 --- a/database/accounts/accounts.js +++ b/database/accounts/accounts.js @@ -45,13 +45,13 @@ passport.deserializeUser((id, cb) => { -async function createUser(email, password) { +async function createUser(email, password, isAdmin) { const salt = bcrypt.genSaltSync(); const hash = bcrypt.hashSync(password, salt); - const query = `INSERT INTO accounts.users(email, password) - VALUES($1, $2)`; - await database.executeQuery(query, [email, hash]); + const query = `INSERT INTO accounts.users(email, password, admin) + VALUES($1, $2, $3)`; + await database.executeQuery(query, [email, hash, isAdmin]); } exports.createUser = createUser; diff --git a/routes/manage.js b/routes/manage.js index 16f8ca4..7fbd517 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -8,18 +8,36 @@ var divisions = require('../database/scores/divisions'); var genders = require('../database/scores/genders'); var teams = require('../database/scores/teams'); +function userLoggedIn(req, res, next) { + if (req.user) { + next(); + } + else { + res.redirect('/auth/login'); + } +} -router.get('/', function(req, res, next) { +function adminLoggedIn(req, res, next) { + if (req.user && req.user[2]) { + next(); + } + else { + res.send('UNAUTHORIZED'); + } +} + + +router.get('/' ,userLoggedIn, function(req, res, next) { res.render('manage', { title: 'Score Management' }); }); -router.get('/game', function(req, res, next) { +router.get('/game', userLoggedIn, function(req, res, next) { let title = req.query.game ? 'Edit Game' : 'Submit Score' res.render('manage/addgame', { title }); }); -router.post('/game', function(req, res, next) { +router.post('/game', userLoggedIn, function(req, res, next) { const seasonID = req.body['year']; const sportID = req.body['sport']; const gender = (req.body['gender'] == "female") ? genders.FEMALE : genders.MALE; @@ -41,11 +59,11 @@ router.post('/game', function(req, res, next) { .then(res.redirect("/manage")); }); -router.get('/season', function(req, res, next) { +router.get('/season', adminLoggedIn, function(req, res, next) { res.render('manage/addseason', { title: 'Add Season', currentYear : (new Date()).getFullYear() }); }); -router.post('/season', function(req, res, next) { +router.post('/season', adminLoggedIn, function(req, res, next) { const year = req.body['year']; const seasonID = req.body['season']; @@ -55,11 +73,11 @@ router.post('/season', function(req, res, next) { else seasons.add(year).then(res.redirect("/manage")); }); -router.get('/sport', function(req, res, next) { +router.get('/sport', adminLoggedIn, function(req, res, next) { res.render('manage/addsport', { title: 'Add Sport' }); }); -router.post('/sport', function(req, res, next) { +router.post('/sport', adminLoggedIn, function(req, res, next) { const name = req.body['name']; const id = req.body['sport']; const remove = req.body['remove']; @@ -69,13 +87,13 @@ router.post('/sport', function(req, res, next) { else sports.add(name).then(res.redirect('/manage')); }); -router.get('/division', function(req, res, next) { +router.get('/division', adminLoggedIn, function(req, res, next) { let title = req.query.division ? 'Edit Division' : 'Add Division' res.render('manage/adddivision', { title }); }); -router.post('/division', function(req, res, next) { +router.post('/division', adminLoggedIn, function(req, res, next) { const name = req.body['name']; const sport = req.body['sport']; const genderName = req.body['gender']; @@ -100,13 +118,13 @@ router.post('/division', function(req, res, next) { } }); -router.get('/team', function(req, res, next) { +router.get('/team', adminLoggedIn, function(req, res, next) { let title = req.query.team ? 'Edit Team' : 'Add Team' res.render('manage/addteam', { title }); }); -router.post('/team', function(req, res, next) { +router.post('/team', adminLoggedIn, function(req, res, next) { const name = req.body['name']; const sport = req.body['sport']; From 173c075aa335d00e11432b60d9c2b4b0b58f1904 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 24 Nov 2021 22:29:29 -0700 Subject: [PATCH 135/255] Add ability to create user from admin panel --- database/accounts/accounts.js | 22 ++++++++++++++++++ public/scripts/data.js | 6 +++++ public/scripts/manage.js | 43 +++++++++++++++++++++++++++++++++++ public/stylesheets/form.css | 9 ++++++++ routes/admin.js | 16 +++++++++++-- routes/auth.js | 17 +++++++++++--- routes/data.js | 15 ++++++++++++ routes/manage.js | 7 +++++- views/accounts/createuser.pug | 4 ++++ views/manage.pug | 1 + 10 files changed, 134 insertions(+), 6 deletions(-) diff --git a/database/accounts/accounts.js b/database/accounts/accounts.js index 6fc0d62..147cadd 100644 --- a/database/accounts/accounts.js +++ b/database/accounts/accounts.js @@ -3,6 +3,14 @@ const passport = require('passport'); const localStrategy = require('passport-local').Strategy; const bcrypt = require('bcrypt'); +class User { + constructor(id, email, isAdmin) { + this.id = id; + this.email = email; + this.isAdmin = isAdmin; + } +} + passport.use(new localStrategy({ usernameField: 'email', @@ -54,5 +62,19 @@ async function createUser(email, password, isAdmin) { await database.executeQuery(query, [email, hash, isAdmin]); } +async function retrieveAll() { + const query = `SELECT user_id, email, admin + FROM accounts.users + ORDER BY email;` + const table = await database.executeQuery(query); + + const accountsList = []; + table.forEach((row) => { + accountsList.push(new User(row[0], row[1], row[2])); + }); + return accountsList; +} + exports.createUser = createUser; +exports.retrieveAll = retrieveAll; exports.passport = passport; \ No newline at end of file diff --git a/public/scripts/data.js b/public/scripts/data.js index 873d4d6..2360aed 100644 --- a/public/scripts/data.js +++ b/public/scripts/data.js @@ -69,4 +69,10 @@ export async function getGame(gameID) { const response = await fetch(`/data/game?game=${gameID}`); const game = await response.json(); return game; +} + +export async function getAccounts() { + const response = await fetch(`/data/accounts`); + const accounts = await response.json(); + return accounts; } \ No newline at end of file diff --git a/public/scripts/manage.js b/public/scripts/manage.js index 8c89bfd..de4a6e2 100644 --- a/public/scripts/manage.js +++ b/public/scripts/manage.js @@ -294,6 +294,49 @@ CATEGORIES.push(new Category( } )); +CATEGORIES.push(new Category( + "accounts", + async function getAccounts() { + return await Data.getAccounts(); + }, + async function listAccountHeaders() { + const headerRow = document.createElement('tr'); + + const emailHeader = document.createElement('th'); + emailHeader.textContent = "Email"; + headerRow.appendChild(emailHeader); + + const spacerHeader = document.createElement('th'); + spacerHeader.classList.add('spacer-column'); + headerRow.appendChild(spacerHeader); + + const adminHeader = document.createElement('th'); + adminHeader.textContent = "Admin?"; + headerRow.appendChild(adminHeader); + + itemsListTable.appendChild(headerRow); + }, + function listAccount(account, row) { + const emailCell = document.createElement('td'); + emailCell.textContent = account.email; + row.appendChild(emailCell); + + const spacerCell = document.createElement('td'); + row.appendChild(spacerCell); + + const adminCell = document.createElement('td'); + adminCell.textContent = account.isAdmin; + row.appendChild(adminCell); + }, + async function addAccount() { + window.location.href = "/manage/account"; + }, + async function editAccount(id) { + window.location.href = `/manage/account?account=${id}`; + } +)); + + async function listItems(category) { diff --git a/public/stylesheets/form.css b/public/stylesheets/form.css index 21abe10..32123e7 100644 --- a/public/stylesheets/form.css +++ b/public/stylesheets/form.css @@ -35,4 +35,13 @@ form { #delete-button { visibility: hidden; + } + + .form-section-checkbox { + flex-direction: row; + align-items: center; + } + + #admin { + width: auto; } \ No newline at end of file diff --git a/routes/admin.js b/routes/admin.js index d032cd1..9e9cfc9 100644 --- a/routes/admin.js +++ b/routes/admin.js @@ -5,8 +5,20 @@ const app = require('../app'); const accounts = require('./../database/accounts/accounts'); -router.get('/createuser', (req, res, next) => { - res.render('accounts/createuser', { title: 'Create user' }); +function adminLoggedIn(req, res, next) { + if (req.user && req.user[2]) { + next(); + } + else { + req.flash('error', 'An admin account is required to access this page.'); + res.redirect('/auth/login'); + } + } + +router.get('/', adminLoggedIn, (req, res, next) => { + res.render }); + + module.exports = router; \ No newline at end of file diff --git a/routes/auth.js b/routes/auth.js index 5d2e910..47a9b09 100644 --- a/routes/auth.js +++ b/routes/auth.js @@ -4,6 +4,17 @@ const passport = require('passport'); const accounts = require('./../database/accounts/accounts'); const app = require('../app'); + +function adminLoggedIn(req, res, next) { + if (req.user && req.user[2]) { + next(); + } + else { + req.flash('error', 'An admin account is required to access this page.'); + res.redirect('/auth/login'); + } + } + router.get('/login', (req, res, next) => { res.render('accounts/login', { title : "Login", message: req.flash('error') }); }); @@ -23,9 +34,9 @@ router.post('/login', console.log(req.user); }); -router.post('/register', (req, res, next) => { - accounts.createUser(req.body.email, req.body.password) - .then(res.redirect('/')); +router.post('/register', adminLoggedIn, (req, res, next) => { + accounts.createUser(req.body.email, req.body.password, !!req.body.admin) + .then(res.redirect('/manage')); }); module.exports = router; \ No newline at end of file diff --git a/routes/data.js b/routes/data.js index 6ad845a..fcb4920 100644 --- a/routes/data.js +++ b/routes/data.js @@ -6,7 +6,17 @@ var genders = require('../database/scores/genders'); var divisions = require('../database/scores/divisions'); var teams = require('../database/scores/teams'); var games = require('../database/scores/games'); +var accounts = require('../database/accounts/accounts'); +function adminLoggedIn(req, res, next) { + if (req.user && req.user[2]) { + next(); + } + else { + req.flash('error', 'An admin account is required to access this page.'); + res.redirect('/auth/login'); + } + } router.get('/sports', function(req, res, next) { sports.retrieveAll() @@ -61,4 +71,9 @@ router.get('/game', function(req, res, next) { .then(data => res.json(data)); }) +router.get('/accounts', adminLoggedIn, function(req, res, next) { + accounts.retrieveAll() + .then(data => res.json(data)); +}) + module.exports = router; \ No newline at end of file diff --git a/routes/manage.js b/routes/manage.js index 7fbd517..756c58d 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -22,7 +22,8 @@ function adminLoggedIn(req, res, next) { next(); } else { - res.send('UNAUTHORIZED'); + req.flash('error', 'An admin account is required to access this page.'); + res.redirect('/auth/login'); } } @@ -136,4 +137,8 @@ router.post('/team', adminLoggedIn, function(req, res, next) { else teams.add(name, sport).then(res.redirect("/manage")); }); +router.get('/account', adminLoggedIn, (req, res, next) => { + res.render('accounts/createuser', { title: 'Create user' }); +}); + module.exports = router; diff --git a/views/accounts/createuser.pug b/views/accounts/createuser.pug index c5630c8..02560ec 100644 --- a/views/accounts/createuser.pug +++ b/views/accounts/createuser.pug @@ -16,5 +16,9 @@ block content label Password span(class='form-section-input') input(type="password", name="password") + span(class='form-section') + span(class='form-section-checkbox') + input#admin(type="checkbox", name="admin") + label(for="admin") Grant admin privileges span(class='form-section') button#submit-button(type="submit") Submit \ No newline at end of file diff --git a/views/manage.pug b/views/manage.pug index c43dcf9..454d1ed 100644 --- a/views/manage.pug +++ b/views/manage.pug @@ -17,6 +17,7 @@ block content option(value="divisions") Divisions option(value="teams") Teams option(value="games") Games + option(value="accounts") Accounts div h2#table-header table#items-list From 4d3b6989e4905f317b0bb293cd7467139f4c0efa Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 25 Nov 2021 12:04:18 -0700 Subject: [PATCH 136/255] Add function to get user information --- database/accounts/accounts.js | 10 ++++++++++ public/scripts/data.js | 6 ++++++ routes/data.js | 5 +++++ 3 files changed, 21 insertions(+) diff --git a/database/accounts/accounts.js b/database/accounts/accounts.js index 147cadd..ef0d040 100644 --- a/database/accounts/accounts.js +++ b/database/accounts/accounts.js @@ -75,6 +75,16 @@ async function retrieveAll() { return accountsList; } +async function getFromID(id) { + const query = `SELECT user_id, email, admin + FROM accounts.users + WHERE user_id = $1;`; + const row = (await database.executeQuery(query, [id]))[0]; + + return new User(id, row[1], row[2]); +} + exports.createUser = createUser; exports.retrieveAll = retrieveAll; +exports.getFromID = getFromID; exports.passport = passport; \ No newline at end of file diff --git a/public/scripts/data.js b/public/scripts/data.js index 2360aed..ae8dc7f 100644 --- a/public/scripts/data.js +++ b/public/scripts/data.js @@ -75,4 +75,10 @@ export async function getAccounts() { const response = await fetch(`/data/accounts`); const accounts = await response.json(); return accounts; +} + +export async function getAccount(accountID) { + const response = await fetch(`/data/account?account=${accountID}`); + const account = await response.json(); + return account; } \ No newline at end of file diff --git a/routes/data.js b/routes/data.js index fcb4920..d010bd1 100644 --- a/routes/data.js +++ b/routes/data.js @@ -76,4 +76,9 @@ router.get('/accounts', adminLoggedIn, function(req, res, next) { .then(data => res.json(data)); }) +router.get('/account', adminLoggedIn, function(req, res, next) { + accounts.getFromID(req.query.account) + .then(data => res.json(data)); +}) + module.exports = router; \ No newline at end of file From d43aba3da1e7cd270b75af4c1febaecc7e947597 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 25 Nov 2021 12:13:46 -0700 Subject: [PATCH 137/255] Remove console.log from teams.js --- public/scripts/manage/team.js | 1 - 1 file changed, 1 deletion(-) diff --git a/public/scripts/manage/team.js b/public/scripts/manage/team.js index d5d8291..10cd386 100644 --- a/public/scripts/manage/team.js +++ b/public/scripts/manage/team.js @@ -21,7 +21,6 @@ async function initializeForm() { Form.populateSports(sportDropdown, team.sportID); - console.log(team.sportID); Form.addHiddenValue('team', teamID, submissionForm); } else { From 1901f33851f1e37682a3e8d26028f09b7ca5fc7f Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 25 Nov 2021 12:17:53 -0700 Subject: [PATCH 138/255] Create client layout for managing users --- public/scripts/manage/account.js | 33 ++++++++++++++++++++++++++++++++ public/stylesheets/form.css | 2 +- routes/manage.js | 4 +++- views/accounts/createuser.pug | 19 +++++++++++------- 4 files changed, 49 insertions(+), 9 deletions(-) create mode 100644 public/scripts/manage/account.js diff --git a/public/scripts/manage/account.js b/public/scripts/manage/account.js new file mode 100644 index 0000000..6b51c2f --- /dev/null +++ b/public/scripts/manage/account.js @@ -0,0 +1,33 @@ +import * as Data from "../data.js"; +import * as Form from "../form.js"; + +const submissionForm = document.getElementById('submission-form'); +const emailTextbox = document.getElementById('email-textbox'); +const passwordTextbox = document.getElementById('password-textbox'); +const adminCheckbox = document.getElementById('admin-checkbox'); +const submitButton = document.getElementById('submit-button'); +const deleteButton = document.getElementById('delete-button'); + +async function Initialize() { + let params = new URLSearchParams(location.search); + let accountID = params.get('account'); + if(accountID) { + const account = await Data.getAccount(accountID); + console.log(account); + + emailTextbox.value = account.email; + + passwordTextbox.placeholder = "leave unchanged"; + + adminCheckbox.checked = account.isAdmin; + + Form.addHiddenValue('account', accountID, submissionForm); + + deleteButton.style.visibility = "visible"; + deleteButton.disabled = false; + } + emailTextbox.disabled = false; + passwordTextbox.disabled = false; + adminCheckbox.disabled = false; +} +Initialize(); \ No newline at end of file diff --git a/public/stylesheets/form.css b/public/stylesheets/form.css index 32123e7..389028c 100644 --- a/public/stylesheets/form.css +++ b/public/stylesheets/form.css @@ -42,6 +42,6 @@ form { align-items: center; } - #admin { + #admin-checkbox { width: auto; } \ No newline at end of file diff --git a/routes/manage.js b/routes/manage.js index 756c58d..e9d4377 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -138,7 +138,9 @@ router.post('/team', adminLoggedIn, function(req, res, next) { }); router.get('/account', adminLoggedIn, (req, res, next) => { - res.render('accounts/createuser', { title: 'Create user' }); + let title = req.query.account ? 'Manage User' : 'Create User' + + res.render('accounts/createuser', { title }); }); module.exports = router; diff --git a/views/accounts/createuser.pug b/views/accounts/createuser.pug index 02560ec..cc5324d 100644 --- a/views/accounts/createuser.pug +++ b/views/accounts/createuser.pug @@ -7,18 +7,23 @@ block stylesheets block content div#mobile-view h1 #{title} - form(action='/auth/register', method='POST') + form#submission-form(action='/auth/register', method='POST') span(class='form-section') label Email span(class='form-section-input') - input(type="email", name="email") + input#email-textbox(type="email", name="email" disabled) span(class='form-section') label Password - span(class='form-section-input') - input(type="password", name="password") + span(class='form-section-input' ) + input#password-textbox(type="password" name="password" disabled) span(class='form-section') span(class='form-section-checkbox') - input#admin(type="checkbox", name="admin") - label(for="admin") Grant admin privileges + input#admin-checkbox(type="checkbox" name="admin" disabled) + label(for="admin-checkbox") Grant admin privileges span(class='form-section') - button#submit-button(type="submit") Submit \ No newline at end of file + button#submit-button(type="submit" disabled) Submit + span(class='form-section') + button#delete-button(type="delete" disabled) Delete + +block scripts + script(src='/scripts/manage/account.js' type="module") \ No newline at end of file From 06765455f62ccba8c4743287d07fe0f7502e445c Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 25 Nov 2021 12:40:19 -0700 Subject: [PATCH 139/255] Add functionality to edit accounts --- database/accounts/accounts.js | 30 +++++++++++++++++++++++++++--- public/scripts/manage/account.js | 16 +++++++++++++++- routes/manage.js | 12 ++++++++++++ views/accounts/createuser.pug | 2 +- 4 files changed, 55 insertions(+), 5 deletions(-) diff --git a/database/accounts/accounts.js b/database/accounts/accounts.js index ef0d040..ce0be9f 100644 --- a/database/accounts/accounts.js +++ b/database/accounts/accounts.js @@ -52,16 +52,39 @@ passport.deserializeUser((id, cb) => { }); +async function generateHash(password) { + return bcrypt.hashSync(password, salt); +} -async function createUser(email, password, isAdmin) { +async function create(email, password, isAdmin) { const salt = bcrypt.genSaltSync(); - const hash = bcrypt.hashSync(password, salt); + const hash = await generateHash(password); const query = `INSERT INTO accounts.users(email, password, admin) VALUES($1, $2, $3)`; await database.executeQuery(query, [email, hash, isAdmin]); } +async function edit(id, email, password, isAdmin) { + if(password) { + const hash = await generateHash(password); + + const query = `UPDATE accounts.users + SET email = $2, + password = $3, + admin = $4 + WHERE user_id = $1;`; + await database.executeQuery(query, [id, email, hash, isAdmin]); + } else { + const query = `UPDATE accounts.users + SET email = $2, + admin = $3 + WHERE user_id = $1;`; + await database.executeQuery(query, [id, email, isAdmin]); + } + return new User(id, email, isAdmin); +} + async function retrieveAll() { const query = `SELECT user_id, email, admin FROM accounts.users @@ -84,7 +107,8 @@ async function getFromID(id) { return new User(id, row[1], row[2]); } -exports.createUser = createUser; +exports.create = create; +exports.edit = edit; exports.retrieveAll = retrieveAll; exports.getFromID = getFromID; exports.passport = passport; \ No newline at end of file diff --git a/public/scripts/manage/account.js b/public/scripts/manage/account.js index 6b51c2f..65c9f4e 100644 --- a/public/scripts/manage/account.js +++ b/public/scripts/manage/account.js @@ -27,7 +27,21 @@ async function Initialize() { deleteButton.disabled = false; } emailTextbox.disabled = false; + emailTextbox.addEventListener('keyup', checkDataValidity); passwordTextbox.disabled = false; + passwordTextbox.addEventListener('keyup', checkDataValidity); adminCheckbox.disabled = false; + checkDataValidity(); } -Initialize(); \ No newline at end of file +Initialize(); + +async function checkDataValidity() { + let dataIsValid = true; + + if(!passwordTextbox.value && !passwordTextbox.placeholder) dataIsValid = false; + if(!emailTextbox.value) dataIsValid = false; + + if(dataIsValid) submitButton.disabled = false; + else submitButton.disabled = true; +} + diff --git a/routes/manage.js b/routes/manage.js index e9d4377..9e038f0 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -7,6 +7,7 @@ var sports = require('../database/scores/sports'); var divisions = require('../database/scores/divisions'); var genders = require('../database/scores/genders'); var teams = require('../database/scores/teams'); +var accounts = require('../database/accounts/accounts'); function userLoggedIn(req, res, next) { if (req.user) { @@ -143,4 +144,15 @@ router.get('/account', adminLoggedIn, (req, res, next) => { res.render('accounts/createuser', { title }); }); +router.post('/account', adminLoggedIn, (req, res, next) => { + const email = req.body.email; + const password = req.body.password; + const isAdmin = !!req.body.admin; + + const accountID = req.body.account; + + if(accountID) accounts.edit(accountID, email, password, isAdmin).then(res.redirect('/manage')); + else accounts.create(req.body.email, req.body.password, !!req.body.admin).then(res.redirect('/manage')); +}); + module.exports = router; diff --git a/views/accounts/createuser.pug b/views/accounts/createuser.pug index cc5324d..36e0acb 100644 --- a/views/accounts/createuser.pug +++ b/views/accounts/createuser.pug @@ -7,7 +7,7 @@ block stylesheets block content div#mobile-view h1 #{title} - form#submission-form(action='/auth/register', method='POST') + form#submission-form(action='/manage/account', method='POST') span(class='form-section') label Email span(class='form-section-input') From 23e85b7af3109f5c7b9ef31a204626f390a4c901 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 25 Nov 2021 12:41:22 -0700 Subject: [PATCH 140/255] Fix bug in accounts.js --- database/accounts/accounts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/accounts/accounts.js b/database/accounts/accounts.js index ce0be9f..869810e 100644 --- a/database/accounts/accounts.js +++ b/database/accounts/accounts.js @@ -53,11 +53,11 @@ passport.deserializeUser((id, cb) => { async function generateHash(password) { + const salt = bcrypt.genSaltSync(); return bcrypt.hashSync(password, salt); } async function create(email, password, isAdmin) { - const salt = bcrypt.genSaltSync(); const hash = await generateHash(password); const query = `INSERT INTO accounts.users(email, password, admin) From 08545bfb5cefb941e93caa051fb352ac927937ef Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 25 Nov 2021 12:49:31 -0700 Subject: [PATCH 141/255] Add functions to remove account --- database/accounts/accounts.js | 9 +++++++++ public/scripts/manage/account.js | 1 + routes/manage.js | 2 ++ 3 files changed, 12 insertions(+) diff --git a/database/accounts/accounts.js b/database/accounts/accounts.js index 869810e..a8bab35 100644 --- a/database/accounts/accounts.js +++ b/database/accounts/accounts.js @@ -85,6 +85,14 @@ async function edit(id, email, password, isAdmin) { return new User(id, email, isAdmin); } +async function remove(id) { + const query = `DELETE FROM accounts.users + WHERE user_id = $1 + RETURNING email, admin;`; + const row = (await database.executeQuery(query, [id]))[0]; + return new User(id, row[0], row[1]); +} + async function retrieveAll() { const query = `SELECT user_id, email, admin FROM accounts.users @@ -109,6 +117,7 @@ async function getFromID(id) { exports.create = create; exports.edit = edit; +exports.remove = remove; exports.retrieveAll = retrieveAll; exports.getFromID = getFromID; exports.passport = passport; \ No newline at end of file diff --git a/public/scripts/manage/account.js b/public/scripts/manage/account.js index 65c9f4e..0ab23a3 100644 --- a/public/scripts/manage/account.js +++ b/public/scripts/manage/account.js @@ -45,3 +45,4 @@ async function checkDataValidity() { else submitButton.disabled = true; } +Form.addRemoveFunction(deleteButton, submissionForm, "account"); \ No newline at end of file diff --git a/routes/manage.js b/routes/manage.js index 9e038f0..fe6f204 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -150,7 +150,9 @@ router.post('/account', adminLoggedIn, (req, res, next) => { const isAdmin = !!req.body.admin; const accountID = req.body.account; + const remove = req.body.remove; + if(remove) accounts.remove(accountID).then(res.redirect('/manage')); if(accountID) accounts.edit(accountID, email, password, isAdmin).then(res.redirect('/manage')); else accounts.create(req.body.email, req.body.password, !!req.body.admin).then(res.redirect('/manage')); }); From d5be13955a0194400eea33f77b2e4100d9fb876a Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 25 Nov 2021 12:55:54 -0700 Subject: [PATCH 142/255] Fix bug regarding deleting games --- routes/manage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/manage.js b/routes/manage.js index fe6f204..c3202f1 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -51,7 +51,7 @@ router.post('/game', userLoggedIn, function(req, res, next) { const team2Score = req.body['team2-score']; const id = req.body['game']; - const remove = req.body['delete']; + const remove = req.body['remove']; if(remove) games.remove(id) .then(res.redirect("/manage")); From b7f032754bb27f718ef1933e0edc298bcc2f7c1d Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 25 Nov 2021 13:03:54 -0700 Subject: [PATCH 143/255] Fix bug in games.js where dropdowns are not being updated --- public/scripts/manage/game.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/public/scripts/manage/game.js b/public/scripts/manage/game.js index 82d4cb8..8338d4c 100644 --- a/public/scripts/manage/game.js +++ b/public/scripts/manage/game.js @@ -67,6 +67,19 @@ async function initializeForm() { team1ScoreTextbox.disabled = false; team2ScoreTextbox.disabled = false; submitButton.disabled = false; + + sportDropdown.onchange = () => { + Form.populateGenders(genderDropdown, sportDropdown.value) + .then(() => { + Form.populateDivisions(divisionDropdown, sportDropdown.value, genderDropdown.value); + }); + Form.populateTeams(team1Dropdown, sportDropdown.value); + Form.populateTeams(team2Dropdown, sportDropdown.value); + }; + + genderDropdown.onchange = () => { + Form.populateDivisions(divisionDropdown, sportDropdown.value, genderDropdown.value); + }; } initializeForm(); From 4a108f606b46832c61289aaea9a967e645d24abb Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 25 Nov 2021 15:41:10 -0700 Subject: [PATCH 144/255] Move register route from auth to manage --- routes/auth.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/routes/auth.js b/routes/auth.js index 47a9b09..25159ab 100644 --- a/routes/auth.js +++ b/routes/auth.js @@ -34,9 +34,6 @@ router.post('/login', console.log(req.user); }); -router.post('/register', adminLoggedIn, (req, res, next) => { - accounts.createUser(req.body.email, req.body.password, !!req.body.admin) - .then(res.redirect('/manage')); -}); + module.exports = router; \ No newline at end of file From d35188537540e7040cbbb1ac0f3ea9998038d7d1 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 25 Nov 2021 18:29:18 -0700 Subject: [PATCH 145/255] Add page with only scores for non-admins --- public/scripts/manage/manage-nonadmin.js | 127 +++++++++++++++++++++++ routes/manage.js | 3 +- views/manage/manage-nonadmin.pug | 16 +++ 3 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 public/scripts/manage/manage-nonadmin.js create mode 100644 views/manage/manage-nonadmin.pug diff --git a/public/scripts/manage/manage-nonadmin.js b/public/scripts/manage/manage-nonadmin.js new file mode 100644 index 0000000..4fc4b02 --- /dev/null +++ b/public/scripts/manage/manage-nonadmin.js @@ -0,0 +1,127 @@ +import * as Data from "./../data.js"; + +const gamesListTable = document.getElementById('games-list'); +const addNewButton = document.getElementById('add-new-button'); + + +function getGenderLetter(genderName) { + return genderName == "female" ? "F" : "M"; +} + +async function listGameHeaders() { + const headerRow = document.createElement('tr'); + + const teamsHeader = document.createElement('th'); + teamsHeader.textContent = "Teams"; + headerRow.appendChild(teamsHeader); + + const scoreHeader = document.createElement('th'); + headerRow.appendChild(scoreHeader); + + const spacerHeader = document.createElement('th'); + spacerHeader.classList.add('spacer-column'); + headerRow.appendChild(spacerHeader); + + const sportNameHeader = document.createElement('th'); + sportNameHeader.textContent = "Sport"; + headerRow.appendChild(sportNameHeader); + + const dateHeader = document.createElement('th'); + dateHeader.textContent = "Date"; + headerRow.appendChild(dateHeader); + + gamesListTable.appendChild(headerRow); +} + + +function listGame(game, row) { + const teamsCell = document.createElement('td'); + const team1NameSpan = document.createElement('span'); + Data.getTeam(game.team1ID) + .then(data => team1NameSpan.textContent = data.name); + teamsCell.appendChild(team1NameSpan); + const team2NameSpan = document.createElement('span'); + Data.getTeam(game.team2ID) + .then(data => team2NameSpan.textContent = data.name); + teamsCell.appendChild(team2NameSpan); + row.appendChild(teamsCell); + + const scoresCell = document.createElement('td'); + const team1ScoreSpan = document.createElement('span'); + team1ScoreSpan.textContent = game.team1Score; + scoresCell.appendChild(team1ScoreSpan); + const team2ScoreSpan = document.createElement('span'); + team2ScoreSpan.textContent = game.team2Score; + scoresCell.appendChild(team2ScoreSpan); + row.appendChild(scoresCell); + + const spacerCell = document.createElement('td'); + row.appendChild(spacerCell); + + const sportCell = document.createElement('td'); + const sportSpan = document.createElement('span'); + const divisionSpan = document.createElement('span'); + divisionSpan.classList.add('flat-content'); + const divisionNameSpan = document.createElement('span'); + const divisionGenderSpan = document.createElement('span'); + divisionSpan.appendChild(divisionNameSpan); + divisionSpan.appendChild(divisionGenderSpan); + Data.getDivision(game.divisionID) + .then(data => { + Data.getSportName(data.sportID) + .then(data => sportSpan.textContent = data); + divisionNameSpan.textContent = data.name; + divisionGenderSpan.textContent = getGenderLetter(data.gender.name); + }); + sportCell.appendChild(sportSpan); + sportCell.appendChild(divisionSpan); + row.appendChild(sportCell); + + const dateCell = document.createElement('td'); + const yearSpan = document.createElement('span'); + yearSpan.textContent = game.date.slice(0,4); + dateCell.appendChild(yearSpan); + const dateSpan = document.createElement('span'); + dateSpan.textContent = game.date.slice(5); + dateCell.appendChild(dateSpan); + row.appendChild(dateCell); +} + +async function addGame() { + window.location.href = "/manage/game"; +} + +async function editGame(id) { + window.location.href = `/manage/game?game=${id}`; +} + + +async function listItems() { + const gamesList = await Data.getGames(); + + await listGameHeaders(); + + gamesList.forEach(game => { + const row = document.createElement('tr'); + + listGame(game, row); + + const manageCell = document.createElement('td'); + + const editSpan = document.createElement('span'); + const editButton = document.createElement('button'); + editButton.textContent = "E"; + editButton.addEventListener('click', () => { + editGame(game.id); + }); + editSpan.appendChild(editButton); + manageCell.appendChild(editSpan); + + row.appendChild(manageCell); + + gamesListTable.appendChild(row); + }); +} +listItems(); + +addNewButton.addEventListener('click', () => addGame()); \ No newline at end of file diff --git a/routes/manage.js b/routes/manage.js index c3202f1..aece4fc 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -30,7 +30,8 @@ function adminLoggedIn(req, res, next) { router.get('/' ,userLoggedIn, function(req, res, next) { - res.render('manage', { title: 'Score Management' }); + if(req.user[2]) res.render('manage', { title: 'Score Management' }); + else res.render('manage/manage-nonadmin', { title: "Manage Games" }); }); router.get('/game', userLoggedIn, function(req, res, next) { diff --git a/views/manage/manage-nonadmin.pug b/views/manage/manage-nonadmin.pug new file mode 100644 index 0000000..f3bb4e6 --- /dev/null +++ b/views/manage/manage-nonadmin.pug @@ -0,0 +1,16 @@ +extends layout + +block stylesheets + link(rel='stylesheet', href='/stylesheets/manage.css') + link(rel='stylesheet', href='/stylesheets/form.css') + +block content + div#mobile-view + h1 #{title} + div + h2#table-header + table#games-list + button#add-new-button Add new... + +block scripts + script(src='/scripts/manage/manage-nonadmin.js' type="module") \ No newline at end of file From 3515be836d0a4e429cdf438c66dbb8abd0c06b8b Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 25 Nov 2021 18:49:31 -0700 Subject: [PATCH 146/255] Add submitter_id column to init_database.sql script --- database/database.js | 1 - database/init_database.sql | 30 ++++++++++++++++-------------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/database/database.js b/database/database.js index 1d5bc56..2f9277e 100644 --- a/database/database.js +++ b/database/database.js @@ -26,7 +26,6 @@ async function Initialize() { } - async function checkForDatabaseInitialization() { const query = `SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'scores'`; let result = await executeQuery(query); diff --git a/database/init_database.sql b/database/init_database.sql index ea8decf..c7de265 100644 --- a/database/init_database.sql +++ b/database/init_database.sql @@ -30,6 +30,18 @@ accounts: BEGIN; +CREATE SCHEMA IF NOT EXISTS accounts; + +CREATE TABLE IF NOT EXISTS accounts.users( + user_id BIGINT GENERATED ALWAYS AS IDENTITY, + email TEXT UNIQUE NOT NULL, + password TEXT NOT NULL, + admin BOOLEAN NOT NULL DEFAULT FALSE, + PRIMARY KEY(user_id) + ); + + + CREATE SCHEMA IF NOT EXISTS scores; @@ -78,7 +90,7 @@ CREATE TABLE IF NOT EXISTS scores.games( team2_id BIGINT, team1_score INTEGER, team2_score INTEGER, -/* submitter_id BIGINT,*/ + submitter_id BIGINT, updated_timestamp TIMESTAMP WITH TIME ZONE DEFAULT now(), PRIMARY KEY(game_id), CONSTRAINT fk_division @@ -92,20 +104,10 @@ CREATE TABLE IF NOT EXISTS scores.games( REFERENCES scores.teams(team_id), CONSTRAINT fk_team2 FOREIGN KEY(team2_id) - REFERENCES scores.teams(team_id) -/* CONSTRAINT fk_submitter + REFERENCES scores.teams(team_id), + CONSTRAINT fk_submitter FOREIGN KEY(submitter_id) - REFERENCES accounts.users(user_id)*/ - ); - - -CREATE SCHEMA IF NOT EXISTS accounts; - -CREATE TABLE IF NOT EXISTS accounts.users( - user_id BIGINT GENERATED ALWAYS AS IDENTITY, - email TEXT UNIQUE NOT NULL, - password TEXT NOT NULL, - admin BOOLEAN NOT NULL DEFAULT FALSE + REFERENCES accounts.users(user_id) ); COMMIT; \ No newline at end of file From bd5b393a278154bde1f6a6f9b0210073b38c4cd6 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 25 Nov 2021 19:21:21 -0700 Subject: [PATCH 147/255] Add panel for non-admins to edit their own games --- database/accounts/accounts.js | 18 ++++++++++++++++++ database/database.js | 6 +++--- database/scores/games.js | 23 +++++++++++++++++++---- public/scripts/data.js | 7 +++++++ public/scripts/manage/manage-nonadmin.js | 2 +- routes/data.js | 5 +++-- routes/manage.js | 5 +++-- 7 files changed, 54 insertions(+), 12 deletions(-) diff --git a/database/accounts/accounts.js b/database/accounts/accounts.js index a8bab35..84c7a35 100644 --- a/database/accounts/accounts.js +++ b/database/accounts/accounts.js @@ -12,6 +12,24 @@ class User { } +async function checkForAdminAccount() { + + const adminUsersQuery = `SELECT * + FROM accounts.users + WHERE admin = true;`; + const adminUsers = await database.executeQuery(adminUsersQuery); + + if(adminUsers.length == 0) { + const passwordHash = await generateHash('admin'); + const createTempAdminQuery = `INSERT INTO accounts.users(email, password, admin) + VALUES('admin@example.com', $1, true);`; + database.executeQuery(createTempAdminQuery, [passwordHash]); + console.log("Created temp admin account 'admin@example.com' with password 'admin'."); + } +} +checkForAdminAccount(); + + passport.use(new localStrategy({ usernameField: 'email', passwordField: 'password'}, diff --git a/database/database.js b/database/database.js index 2f9277e..349e2bc 100644 --- a/database/database.js +++ b/database/database.js @@ -27,13 +27,13 @@ async function Initialize() { async function checkForDatabaseInitialization() { - const query = `SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'scores'`; - let result = await executeQuery(query); + const scoresSchemaExistsQuery = `SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'scores'`; + let result = await executeQuery(scoresSchemaExistsQuery); const scoresSchemaExists = result.length !== 0; if(!scoresSchemaExists) { - Initialize(); + await Initialize(); } } checkForDatabaseInitialization(); diff --git a/database/scores/games.js b/database/scores/games.js index 270941b..3506356 100644 --- a/database/scores/games.js +++ b/database/scores/games.js @@ -19,12 +19,12 @@ class Game { -async function add(divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score) { - const query = `INSERT INTO scores.games(division_id, season_id, game_date, team1_id, team2_id, team1_score, team2_score) - VALUES($1, $2, $3, $4, $5, $6, $7) +async function add(divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score, userID) { + const query = `INSERT INTO scores.games(division_id, season_id, game_date, team1_id, team2_id, team1_score, team2_score, submitter_id) + VALUES($1, $2, $3, $4, $5, $6, $7, $8) RETURNING game_id;`; - const id = (await database.executeQuery(query, [divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score]))[0][0]; + const id = (await database.executeQuery(query, [divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score, userID]))[0][0]; return new Game(id, date, team1ID, team2ID, team1Score, team2Score); } @@ -71,6 +71,20 @@ async function retrieve(teamID, divisionID, seasonID) { return gamesList; } +async function retrieveByUser(userID) { + const query = `SELECT game_id, division_id, season_id, game_date, team1_id, team2_id, team1_score, team2_score + FROM scores.games + WHERE submitter_id = $1 + ORDER BY game_date DESC;`; + const table = await database.executeQuery(query, [userID]); + + const gamesList = []; + table.forEach((row) => { + gamesList.push(new Game(row[0], row[3].toISOString().slice(0,10), row[4], row[5], row[6], row[7], row[1], row[2])); + }); + return gamesList; +} + async function edit(gameID, divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score) { const query = `UPDATE scores.games SET division_id = $2, @@ -100,5 +114,6 @@ async function getFromID(gameID) { exports.add = add; exports.remove = remove; exports.retrieve = retrieve; +exports.retrieveByUser = retrieveByUser; exports.edit = edit; exports.getFromID = getFromID; \ No newline at end of file diff --git a/public/scripts/data.js b/public/scripts/data.js index ae8dc7f..e8c5898 100644 --- a/public/scripts/data.js +++ b/public/scripts/data.js @@ -65,6 +65,13 @@ export async function getGames(teamID = undefined, divisionID = undefined, seaso return gamesList; } +export async function getGamesByUser() { + let URL = '/data/games?user=1'; + const response = await fetch(URL); + const gamesList = await response.json(); + return gamesList; +} + export async function getGame(gameID) { const response = await fetch(`/data/game?game=${gameID}`); const game = await response.json(); diff --git a/public/scripts/manage/manage-nonadmin.js b/public/scripts/manage/manage-nonadmin.js index 4fc4b02..d549030 100644 --- a/public/scripts/manage/manage-nonadmin.js +++ b/public/scripts/manage/manage-nonadmin.js @@ -97,7 +97,7 @@ async function editGame(id) { async function listItems() { - const gamesList = await Data.getGames(); + const gamesList = await Data.getGamesByUser(); await listGameHeaders(); diff --git a/routes/data.js b/routes/data.js index d010bd1..3d71892 100644 --- a/routes/data.js +++ b/routes/data.js @@ -62,8 +62,9 @@ router.get('/team', function(req, res, next) { }) router.get('/games', function(req, res, next) { - games.retrieve(req.query.team, req.query.division, req.query.season) - .then(data => res.json(data)); + const userID = req.user[0]; + if(req.query.user) games.retrieveByUser(userID).then(data => res.json(data)); + else games.retrieve(req.query.team, req.query.division, req.query.season).then(data => res.json(data)); }) router.get('/game', function(req, res, next) { diff --git a/routes/manage.js b/routes/manage.js index aece4fc..b01b477 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -31,7 +31,7 @@ function adminLoggedIn(req, res, next) { router.get('/' ,userLoggedIn, function(req, res, next) { if(req.user[2]) res.render('manage', { title: 'Score Management' }); - else res.render('manage/manage-nonadmin', { title: "Manage Games" }); + else res.render('manage/manage-nonadmin', { title: "My Games" }); }); router.get('/game', userLoggedIn, function(req, res, next) { @@ -50,6 +50,7 @@ router.post('/game', userLoggedIn, function(req, res, next) { const team1Score = req.body['team1-score']; const team2ID = req.body['team2']; const team2Score = req.body['team2-score']; + const userID = req.user[0]; const id = req.body['game']; const remove = req.body['remove']; @@ -58,7 +59,7 @@ router.post('/game', userLoggedIn, function(req, res, next) { .then(res.redirect("/manage")); else if(id) games.edit(id, divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score) .then(res.redirect('/manage')); - else games.add(divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score) + else games.add(divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score, userID) .then(res.redirect("/manage")); }); From 79123c14bd0b4944bc653118a0b7e40efaaebbcc Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 25 Nov 2021 19:33:53 -0700 Subject: [PATCH 148/255] Only allow non-admin users to edit their own games --- database/scores/games.js | 7 ++++--- routes/manage.js | 21 +++++++++++++++------ 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/database/scores/games.js b/database/scores/games.js index 3506356..8792b2e 100644 --- a/database/scores/games.js +++ b/database/scores/games.js @@ -5,7 +5,7 @@ const database = require('./../database'); class Game { - constructor(id, date, team1ID, team2ID, team1Score, team2Score, divisionID, seasonID) { + constructor(id, date, team1ID, team2ID, team1Score, team2Score, divisionID, seasonID, submitterID) { this.id = id; this.date = date; this.team1ID = team1ID; @@ -14,6 +14,7 @@ class Game { this.team2Score = team2Score; this.divisionID = divisionID; this.seasonID = seasonID; + this.submitterID = submitterID; } } @@ -100,11 +101,11 @@ async function edit(gameID, divisionID, seasonID, date, team1ID, team2ID, team1S } async function getFromID(gameID) { - const query = `SELECT game_id, division_id, season_id, game_date, team1_id, team2_id, team1_score, team2_score + const query = `SELECT game_id, division_id, season_id, game_date, team1_id, team2_id, team1_score, team2_score, submitter_id FROM scores.games WHERE game_id = $1;`; const row = (await database.executeQuery(query, [gameID]))[0]; - return new Game(row[0], row[3].toISOString().slice(0,10), row[4], row[5], row[6], row[7], row[1], row[2]); + return new Game(row[0], row[3].toISOString().slice(0,10), row[4], row[5], row[6], row[7], row[1], row[2], row[8]); } diff --git a/routes/manage.js b/routes/manage.js index b01b477..0a9c16e 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -55,12 +55,21 @@ router.post('/game', userLoggedIn, function(req, res, next) { const id = req.body['game']; const remove = req.body['remove']; - if(remove) games.remove(id) - .then(res.redirect("/manage")); - else if(id) games.edit(id, divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score) - .then(res.redirect('/manage')); - else games.add(divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score, userID) - .then(res.redirect("/manage")); + const loggedInUserID = req.user[0]; + const loggedInUserIsAdmin = req.user[2]; + + games.getFromID(id) + .then(game => { + if(!loggedInUserIsAdmin && loggedInUserID != game.submitterID) { + res.status(403).send("ACCESS DENIED"); + } + else if(remove) games.remove(id) + .then(res.redirect("/manage")); + else if(id) games.edit(id, divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score) + .then(res.redirect('/manage')); + else games.add(divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score, userID) + .then(res.redirect("/manage")); + }); }); router.get('/season', adminLoggedIn, function(req, res, next) { From 54978d3c35dba2a22fd2403df5d236342aca46ca Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 25 Nov 2021 19:46:29 -0700 Subject: [PATCH 149/255] Add manage account button for non-admin manage page --- public/stylesheets/style.css | 5 +++++ views/manage/manage-nonadmin.pug | 5 ++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css index ec6953e..06b919e 100644 --- a/public/stylesheets/style.css +++ b/public/stylesheets/style.css @@ -17,4 +17,9 @@ a { .flat-content { flex-direction: row; + justify-content: space-between; +} + +.send-to-right { + margin-left: auto; } \ No newline at end of file diff --git a/views/manage/manage-nonadmin.pug b/views/manage/manage-nonadmin.pug index f3bb4e6..4f9df3a 100644 --- a/views/manage/manage-nonadmin.pug +++ b/views/manage/manage-nonadmin.pug @@ -7,10 +7,13 @@ block stylesheets block content div#mobile-view h1 #{title} + span(class="flat-content") + button#add-new-button Add new... + button#manage-account-button(class="send-to-right") Manage account... + div h2#table-header table#games-list - button#add-new-button Add new... block scripts script(src='/scripts/manage/manage-nonadmin.js' type="module") \ No newline at end of file From e277107b10fe0d01f72686ff876c044d26044f0a Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 12:08:45 -0700 Subject: [PATCH 150/255] Add ability for non-admin users to manage their account --- public/scripts/manage/account.js | 16 ++++++++-- public/scripts/manage/manage-nonadmin.js | 7 ++++- public/stylesheets/submit.css | 4 +++ routes/data.js | 27 +++++++++++++--- routes/manage.js | 39 +++++++++++++++++++----- views/accounts/createuser.pug | 4 ++- 6 files changed, 79 insertions(+), 18 deletions(-) diff --git a/public/scripts/manage/account.js b/public/scripts/manage/account.js index 0ab23a3..40b252f 100644 --- a/public/scripts/manage/account.js +++ b/public/scripts/manage/account.js @@ -4,13 +4,14 @@ import * as Form from "../form.js"; const submissionForm = document.getElementById('submission-form'); const emailTextbox = document.getElementById('email-textbox'); const passwordTextbox = document.getElementById('password-textbox'); +const adminCheckboxSection = document.getElementById('admin-checkbox-section'); const adminCheckbox = document.getElementById('admin-checkbox'); const submitButton = document.getElementById('submit-button'); const deleteButton = document.getElementById('delete-button'); async function Initialize() { let params = new URLSearchParams(location.search); - let accountID = params.get('account'); + let accountID = params.get('account') || (document.getElementById('account-id') ? document.getElementById('account-id').value : null); if(accountID) { const account = await Data.getAccount(accountID); console.log(account); @@ -21,16 +22,25 @@ async function Initialize() { adminCheckbox.checked = account.isAdmin; - Form.addHiddenValue('account', accountID, submissionForm); + if(!document.getElementById('account-id')) { + adminCheckboxSection.style.visibility = "visible"; + adminCheckbox.disabled = false; + + Form.addHiddenValue('account', accountID, submissionForm); + } deleteButton.style.visibility = "visible"; deleteButton.disabled = false; } + else + { + adminCheckboxSection.style.visibility = "visible"; + adminCheckbox.disabled = false; + } emailTextbox.disabled = false; emailTextbox.addEventListener('keyup', checkDataValidity); passwordTextbox.disabled = false; passwordTextbox.addEventListener('keyup', checkDataValidity); - adminCheckbox.disabled = false; checkDataValidity(); } Initialize(); diff --git a/public/scripts/manage/manage-nonadmin.js b/public/scripts/manage/manage-nonadmin.js index d549030..86782aa 100644 --- a/public/scripts/manage/manage-nonadmin.js +++ b/public/scripts/manage/manage-nonadmin.js @@ -2,6 +2,8 @@ import * as Data from "./../data.js"; const gamesListTable = document.getElementById('games-list'); const addNewButton = document.getElementById('add-new-button'); +const manageAccountButton = document.getElementById('manage-account-button'); + function getGenderLetter(genderName) { @@ -124,4 +126,7 @@ async function listItems() { } listItems(); -addNewButton.addEventListener('click', () => addGame()); \ No newline at end of file +addNewButton.addEventListener('click', () => addGame()); +manageAccountButton.addEventListener('click', () => { + window.location.href = '/manage/account'; +}); \ No newline at end of file diff --git a/public/stylesheets/submit.css b/public/stylesheets/submit.css index af4e239..30e64a5 100644 --- a/public/stylesheets/submit.css +++ b/public/stylesheets/submit.css @@ -1,3 +1,7 @@ h1 { text-align: center; +} + +#admin-checkbox-section { + visibility: hidden; } \ No newline at end of file diff --git a/routes/data.js b/routes/data.js index 3d71892..5981784 100644 --- a/routes/data.js +++ b/routes/data.js @@ -10,13 +10,22 @@ var accounts = require('../database/accounts/accounts'); function adminLoggedIn(req, res, next) { if (req.user && req.user[2]) { + next(); + } + else { + req.flash('error', 'An admin account is required to access this page.'); + res.redirect('/auth/login'); + } +} + +function userLoggedIn(req, res, next) { + if (req.user) { next(); } else { - req.flash('error', 'An admin account is required to access this page.'); res.redirect('/auth/login'); } - } +} router.get('/sports', function(req, res, next) { sports.retrieveAll() @@ -77,9 +86,17 @@ router.get('/accounts', adminLoggedIn, function(req, res, next) { .then(data => res.json(data)); }) -router.get('/account', adminLoggedIn, function(req, res, next) { - accounts.getFromID(req.query.account) - .then(data => res.json(data)); +router.get('/account', userLoggedIn, function(req, res, next) { + const userIsAdmin = req.user[2]; + const loggedInAccountID = req.user[0]; + const requestedAccountID = req.query.account; + + if(!userIsAdmin && loggedInAccountID != requestedAccountID) { + res.status(403).send("ACCESS DENIED"); + } else { + accounts.getFromID(req.query.account) + .then(data => res.json(data)); + } }) module.exports = router; \ No newline at end of file diff --git a/routes/manage.js b/routes/manage.js index 0a9c16e..73bdfeb 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -149,23 +149,46 @@ router.post('/team', adminLoggedIn, function(req, res, next) { else teams.add(name, sport).then(res.redirect("/manage")); }); -router.get('/account', adminLoggedIn, (req, res, next) => { - let title = req.query.account ? 'Manage User' : 'Create User' +router.get('/account', userLoggedIn, (req, res, next) => { + const userIsAdmin = req.user[2]; + const accountID = req.user[0]; + + if(userIsAdmin) { + let title = req.query.account ? 'Manage User' : 'Create User' - res.render('accounts/createuser', { title }); + res.render('accounts/createuser', { title }); + } + else { + let title = 'Manage Account'; + + res.render('accounts/createuser', { title, accountID }); + } }); -router.post('/account', adminLoggedIn, (req, res, next) => { +router.post('/account', userLoggedIn, (req, res, next) => { const email = req.body.email; const password = req.body.password; - const isAdmin = !!req.body.admin; const accountID = req.body.account; const remove = req.body.remove; - if(remove) accounts.remove(accountID).then(res.redirect('/manage')); - if(accountID) accounts.edit(accountID, email, password, isAdmin).then(res.redirect('/manage')); - else accounts.create(req.body.email, req.body.password, !!req.body.admin).then(res.redirect('/manage')); + const loggedInAccountIsAdmin = req.user[2]; + const loggedInAccountID = req.user[0]; + + console.log(accountID); + console.log(loggedInAccountID); + + + if(!loggedInAccountIsAdmin && accountID != loggedInAccountID) { + res.status(403).send("ACCESS DENIED"); + } + else { + const isAdmin = loggedInAccountIsAdmin ? !!req.body.admin : false; + + if(remove) accounts.remove(accountID).then(res.redirect('/manage')); + if(accountID) accounts.edit(accountID, email, password, isAdmin).then(res.redirect('/manage')); + else accounts.create(req.body.email, req.body.password, !!req.body.admin).then(res.redirect('/manage')); + } }); module.exports = router; diff --git a/views/accounts/createuser.pug b/views/accounts/createuser.pug index 36e0acb..94a968b 100644 --- a/views/accounts/createuser.pug +++ b/views/accounts/createuser.pug @@ -8,6 +8,8 @@ block content div#mobile-view h1 #{title} form#submission-form(action='/manage/account', method='POST') + if accountID + input#account-id(type="hidden" name="account" value=accountID) span(class='form-section') label Email span(class='form-section-input') @@ -16,7 +18,7 @@ block content label Password span(class='form-section-input' ) input#password-textbox(type="password" name="password" disabled) - span(class='form-section') + span#admin-checkbox-section(class='form-section') span(class='form-section-checkbox') input#admin-checkbox(type="checkbox" name="admin" disabled) label(for="admin-checkbox") Grant admin privileges From 872397d05ed09b0a641d3aa07f5007bad5133ce1 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 12:37:09 -0700 Subject: [PATCH 151/255] Add login/logout buttons --- public/scripts/main.js | 14 ++++++++++++++ public/stylesheets/index.css | 5 ----- public/stylesheets/style.css | 5 +++++ routes/index.js | 2 +- routes/manage.js | 18 +++++++++--------- views/accounts/createuser.pug | 2 -- views/accounts/login.pug | 26 ++++++++++++-------------- views/index.pug | 12 ++++++------ views/layout.pug | 13 +++++++++++-- views/manage.pug | 2 -- views/manage/adddivision.pug | 4 +--- views/manage/addgame.pug | 4 +--- views/manage/addseason.pug | 4 +--- views/manage/addsport.pug | 4 +--- views/manage/addteam.pug | 4 +--- views/manage/manage-nonadmin.pug | 4 +--- 16 files changed, 64 insertions(+), 59 deletions(-) create mode 100644 public/scripts/main.js diff --git a/public/scripts/main.js b/public/scripts/main.js new file mode 100644 index 0000000..ced1e07 --- /dev/null +++ b/public/scripts/main.js @@ -0,0 +1,14 @@ +const logInButton = document.getElementById('login-button'); +const logOutButton = document.getElementById('logout-button'); + +if(logInButton) { + logInButton.addEventListener('click', () => { + window.location.href = "/auth/login"; + }); +} + +if(logOutButton) { + logOutButton.addEventListener('click', () => { + window.location.href = "/auth/logout"; + }); +} \ No newline at end of file diff --git a/public/stylesheets/index.css b/public/stylesheets/index.css index 7a60f19..9bf14cb 100644 --- a/public/stylesheets/index.css +++ b/public/stylesheets/index.css @@ -25,8 +25,3 @@ tr { flex-direction: row; } -#actions-div { - display: flex; - flex-direction: column; - margin-left: auto; -} \ No newline at end of file diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css index 06b919e..5e83f29 100644 --- a/public/stylesheets/style.css +++ b/public/stylesheets/style.css @@ -22,4 +22,9 @@ a { .send-to-right { margin-left: auto; +} + +#actions-div { + display: flex; + margin-left: auto; } \ No newline at end of file diff --git a/routes/index.js b/routes/index.js index 3c536d2..47fe5d4 100644 --- a/routes/index.js +++ b/routes/index.js @@ -3,7 +3,7 @@ var router = express.Router(); /* GET home page. */ router.get('/', function(req, res, next) { - res.render('index', { title: 'View Scores' }); + res.render('index', { title: 'View Scores', userLoggedIn: !!req.user }); }); module.exports = router; diff --git a/routes/manage.js b/routes/manage.js index 73bdfeb..b04cf75 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -30,14 +30,14 @@ function adminLoggedIn(req, res, next) { router.get('/' ,userLoggedIn, function(req, res, next) { - if(req.user[2]) res.render('manage', { title: 'Score Management' }); - else res.render('manage/manage-nonadmin', { title: "My Games" }); + if(req.user[2]) res.render('manage', { title: 'Score Management', userLoggedIn: !!req.user }); + else res.render('manage/manage-nonadmin', { title: "My Games", userLoggedIn: !!req.user }); }); router.get('/game', userLoggedIn, function(req, res, next) { let title = req.query.game ? 'Edit Game' : 'Submit Score' - res.render('manage/addgame', { title }); + res.render('manage/addgame', { title, userLoggedIn: !!req.user }); }); router.post('/game', userLoggedIn, function(req, res, next) { @@ -73,7 +73,7 @@ router.post('/game', userLoggedIn, function(req, res, next) { }); router.get('/season', adminLoggedIn, function(req, res, next) { - res.render('manage/addseason', { title: 'Add Season', currentYear : (new Date()).getFullYear() }); + res.render('manage/addseason', { title: 'Add Season', currentYear : (new Date()).getFullYear(), userLoggedIn: !!req.user }); }); router.post('/season', adminLoggedIn, function(req, res, next) { @@ -87,7 +87,7 @@ router.post('/season', adminLoggedIn, function(req, res, next) { }); router.get('/sport', adminLoggedIn, function(req, res, next) { - res.render('manage/addsport', { title: 'Add Sport' }); + res.render('manage/addsport', { title: 'Add Sport', userLoggedIn: !!req.user }); }); router.post('/sport', adminLoggedIn, function(req, res, next) { @@ -103,7 +103,7 @@ router.post('/sport', adminLoggedIn, function(req, res, next) { router.get('/division', adminLoggedIn, function(req, res, next) { let title = req.query.division ? 'Edit Division' : 'Add Division' - res.render('manage/adddivision', { title }); + res.render('manage/adddivision', { title, userLoggedIn: !!req.user }); }); router.post('/division', adminLoggedIn, function(req, res, next) { @@ -134,7 +134,7 @@ router.post('/division', adminLoggedIn, function(req, res, next) { router.get('/team', adminLoggedIn, function(req, res, next) { let title = req.query.team ? 'Edit Team' : 'Add Team' - res.render('manage/addteam', { title }); + res.render('manage/addteam', { title, userLoggedIn: !!req.user }); }); router.post('/team', adminLoggedIn, function(req, res, next) { @@ -156,12 +156,12 @@ router.get('/account', userLoggedIn, (req, res, next) => { if(userIsAdmin) { let title = req.query.account ? 'Manage User' : 'Create User' - res.render('accounts/createuser', { title }); + res.render('accounts/createuser', { title, userLoggedIn: !!req.user }); } else { let title = 'Manage Account'; - res.render('accounts/createuser', { title, accountID }); + res.render('accounts/createuser', { title, accountID, userLoggedIn: !!req.user }); } }); diff --git a/views/accounts/createuser.pug b/views/accounts/createuser.pug index 94a968b..a87897d 100644 --- a/views/accounts/createuser.pug +++ b/views/accounts/createuser.pug @@ -5,8 +5,6 @@ block stylesheets link(rel='stylesheet', href='/stylesheets/form.css') block content - div#mobile-view - h1 #{title} form#submission-form(action='/manage/account', method='POST') if accountID input#account-id(type="hidden" name="account" value=accountID) diff --git a/views/accounts/login.pug b/views/accounts/login.pug index 13f707e..cbc9044 100644 --- a/views/accounts/login.pug +++ b/views/accounts/login.pug @@ -5,17 +5,15 @@ block stylesheets link(rel='stylesheet', href='/stylesheets/form.css') block content - div#mobile-view - h1 #{title} - form(action='/auth/login', method='POST') - span(class='form-section') - label Email - span(class='form-section-input') - input(type="email", name="email") - span(class='form-section') - label Password - span(class='form-section-input') - input(type="password", name="password") - .error #{message} - span(class='form-section') - button#submit-button(type="submit") Submit \ No newline at end of file + form(action='/auth/login', method='POST') + span(class='form-section') + label Email + span(class='form-section-input') + input(type="email", name="email") + span(class='form-section') + label Password + span(class='form-section-input') + input(type="password", name="password") + .error #{message} + span(class='form-section') + button#submit-button(type="submit") Submit \ No newline at end of file diff --git a/views/index.pug b/views/index.pug index 674d874..3deed78 100644 --- a/views/index.pug +++ b/views/index.pug @@ -4,13 +4,13 @@ block stylesheets link(rel='stylesheet', href='/stylesheets/index.css') link(rel='stylesheet', href='/stylesheets/form.css') +block actions + if userLoggedIn + button#add-score-button Submit score + button#manage-button Manage scores + + block content - div#mobile-view - div#header-div - h1 Score Tracker - div#actions-div - button#add-score-button + - button#manage-button Manage div span(class='form-section') label Year diff --git a/views/layout.pug b/views/layout.pug index e4f85eb..52ad62f 100644 --- a/views/layout.pug +++ b/views/layout.pug @@ -1,10 +1,19 @@ doctype html html head - title= title + title= title + ' - Score Tracker' meta(name='viewport', content='width=device-width, initial-scale=1') link(rel='stylesheet', href='/stylesheets/style.css') block stylesheets body - block content + div#mobile-view + div#actions-div + block actions + if userLoggedIn + button#logout-button Log out + else if userLoggedIn !== undefined + button#login-button Log in + h1 #{title} + block content block scripts + script(src='/scripts/main.js' type="module") diff --git a/views/manage.pug b/views/manage.pug index 454d1ed..63d76ec 100644 --- a/views/manage.pug +++ b/views/manage.pug @@ -5,8 +5,6 @@ block stylesheets link(rel='stylesheet', href='/stylesheets/form.css') block content - div#mobile-view - h1 Management Panel div span(class='form-section') label Category diff --git a/views/manage/adddivision.pug b/views/manage/adddivision.pug index 5db737c..10e7281 100644 --- a/views/manage/adddivision.pug +++ b/views/manage/adddivision.pug @@ -1,12 +1,10 @@ -extends layout +extends ../layout block stylesheets link(rel='stylesheet', href='/stylesheets/submit.css') link(rel='stylesheet', href='/stylesheets/form.css') block content - div#mobile-view - h1 #{title} form#submission-form(action='./division', method='POST') span(class='form-section') label Sport diff --git a/views/manage/addgame.pug b/views/manage/addgame.pug index 7981148..61e7aaa 100644 --- a/views/manage/addgame.pug +++ b/views/manage/addgame.pug @@ -1,12 +1,10 @@ -extends layout +extends ../layout block stylesheets link(rel='stylesheet', href='/stylesheets/submit.css') link(rel='stylesheet', href='/stylesheets/form.css') block content - div#mobile-view - h1 #{title} form#submission-form(action='./game', method='POST') span(class='form-section') label Year diff --git a/views/manage/addseason.pug b/views/manage/addseason.pug index bdb13fd..4050cb2 100644 --- a/views/manage/addseason.pug +++ b/views/manage/addseason.pug @@ -1,12 +1,10 @@ -extends layout +extends ../layout block stylesheets link(rel='stylesheet', href='/stylesheets/submit.css') link(rel='stylesheet', href='/stylesheets/form.css') block content - div#mobile-view - h1 #{title} form(action='./season', method='POST') span(class='form-section') label Ending year diff --git a/views/manage/addsport.pug b/views/manage/addsport.pug index 5251b7e..067b9ac 100644 --- a/views/manage/addsport.pug +++ b/views/manage/addsport.pug @@ -1,12 +1,10 @@ -extends layout +extends ../layout block stylesheets link(rel='stylesheet', href='/stylesheets/submit.css') link(rel='stylesheet', href='/stylesheets/form.css') block content - div#mobile-view - h1#main-header Add Sport form#submission-form(action='./sport', method='POST') span(class='form-section') label Sport name diff --git a/views/manage/addteam.pug b/views/manage/addteam.pug index 3cb6ccb..9f7262c 100644 --- a/views/manage/addteam.pug +++ b/views/manage/addteam.pug @@ -1,12 +1,10 @@ -extends layout +extends ../layout block stylesheets link(rel='stylesheet', href='/stylesheets/submit.css') link(rel='stylesheet', href='/stylesheets/form.css') block content - div#mobile-view - h1 #{title} form#submission-form(action='./team', method='POST') span(class='form-section') label Sport diff --git a/views/manage/manage-nonadmin.pug b/views/manage/manage-nonadmin.pug index 4f9df3a..b639b1a 100644 --- a/views/manage/manage-nonadmin.pug +++ b/views/manage/manage-nonadmin.pug @@ -1,12 +1,10 @@ -extends layout +extends ../layout block stylesheets link(rel='stylesheet', href='/stylesheets/manage.css') link(rel='stylesheet', href='/stylesheets/form.css') block content - div#mobile-view - h1 #{title} span(class="flat-content") button#add-new-button Add new... button#manage-account-button(class="send-to-right") Manage account... From 3201ffb2a9509c52ee536b6421e17b939e78adc3 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 12:46:16 -0700 Subject: [PATCH 152/255] Add "Home" button for navigation --- public/scripts/main.js | 7 +++++++ public/stylesheets/index.css | 3 +++ public/stylesheets/style.css | 5 ++++- routes/index.js | 2 +- views/layout.pug | 2 ++ 5 files changed, 17 insertions(+), 2 deletions(-) diff --git a/public/scripts/main.js b/public/scripts/main.js index ced1e07..aac4daf 100644 --- a/public/scripts/main.js +++ b/public/scripts/main.js @@ -1,5 +1,6 @@ const logInButton = document.getElementById('login-button'); const logOutButton = document.getElementById('logout-button'); +const homeButton = document.getElementById('home-button'); if(logInButton) { logInButton.addEventListener('click', () => { @@ -11,4 +12,10 @@ if(logOutButton) { logOutButton.addEventListener('click', () => { window.location.href = "/auth/logout"; }); +} + +if(homeButton) { + homeButton.addEventListener('click', () => { + window.location.href = '/'; + }); } \ No newline at end of file diff --git a/public/stylesheets/index.css b/public/stylesheets/index.css index 9bf14cb..7e66419 100644 --- a/public/stylesheets/index.css +++ b/public/stylesheets/index.css @@ -25,3 +25,6 @@ tr { flex-direction: row; } +#actions-div { + margin-left: auto; +} \ No newline at end of file diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css index 5e83f29..4582e1e 100644 --- a/public/stylesheets/style.css +++ b/public/stylesheets/style.css @@ -26,5 +26,8 @@ a { #actions-div { display: flex; - margin-left: auto; +} + +#home-button { + margin-right: auto; } \ No newline at end of file diff --git a/routes/index.js b/routes/index.js index 47fe5d4..42fa2b0 100644 --- a/routes/index.js +++ b/routes/index.js @@ -3,7 +3,7 @@ var router = express.Router(); /* GET home page. */ router.get('/', function(req, res, next) { - res.render('index', { title: 'View Scores', userLoggedIn: !!req.user }); + res.render('index', { title: 'View Scores', userLoggedIn: !!req.user, hideHomeButton: true }); }); module.exports = router; diff --git a/views/layout.pug b/views/layout.pug index 52ad62f..11fb12b 100644 --- a/views/layout.pug +++ b/views/layout.pug @@ -8,6 +8,8 @@ html body div#mobile-view div#actions-div + if !hideHomeButton + button#home-button Home block actions if userLoggedIn button#logout-button Log out From bb335c7acac32d1b99d6faaa4ef131f7b325fdc9 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 12:53:54 -0700 Subject: [PATCH 153/255] Fix bug where games don't load if user is not logged in --- public/scripts/index.js | 17 ++++++++++------- routes/data.js | 2 +- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/public/scripts/index.js b/public/scripts/index.js index 843ed4b..0beced6 100644 --- a/public/scripts/index.js +++ b/public/scripts/index.js @@ -177,11 +177,14 @@ genderDropdown.onchange = listDivisions; teamDropdown.onchange = listGames; seasonDropdown.onchange = listGames; +if(addScoreButton) { + addScoreButton.addEventListener('click', () => { + window.location.href = '/manage/game'; + }); +} -addScoreButton.addEventListener('click', () => { - window.location.href = '/manage/game'; -}); - -manageButton.addEventListener('click', () => { - window.location.href = '/manage' -}); \ No newline at end of file +if(manageButton) { + manageButton.addEventListener('click', () => { + window.location.href = '/manage' + }); +} diff --git a/routes/data.js b/routes/data.js index 5981784..c875622 100644 --- a/routes/data.js +++ b/routes/data.js @@ -71,7 +71,7 @@ router.get('/team', function(req, res, next) { }) router.get('/games', function(req, res, next) { - const userID = req.user[0]; + const userID = req.user ? req.user[0] : null; if(req.query.user) games.retrieveByUser(userID).then(data => res.json(data)); else games.retrieve(req.query.team, req.query.division, req.query.season).then(data => res.json(data)); }) From ad4533e71296f1a5caf114849c797f7f7e3d8d1b Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 12:59:48 -0700 Subject: [PATCH 154/255] Adjust layout for non-admin management panel --- views/index.pug | 2 +- views/manage/manage-nonadmin.pug | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/views/index.pug b/views/index.pug index 3deed78..b504698 100644 --- a/views/index.pug +++ b/views/index.pug @@ -7,7 +7,7 @@ block stylesheets block actions if userLoggedIn button#add-score-button Submit score - button#manage-button Manage scores + button#manage-button Manage... block content diff --git a/views/manage/manage-nonadmin.pug b/views/manage/manage-nonadmin.pug index b639b1a..890c739 100644 --- a/views/manage/manage-nonadmin.pug +++ b/views/manage/manage-nonadmin.pug @@ -4,14 +4,14 @@ block stylesheets link(rel='stylesheet', href='/stylesheets/manage.css') link(rel='stylesheet', href='/stylesheets/form.css') -block content - span(class="flat-content") - button#add-new-button Add new... - button#manage-account-button(class="send-to-right") Manage account... +block actions + button#manage-account-button(class="send-to-right") Manage account +block content div - h2#table-header table#games-list + div + button#add-new-button Add new... block scripts script(src='/scripts/manage/manage-nonadmin.js' type="module") \ No newline at end of file From 6c221832fc183d1bb08f53434a9c860ac365c1e7 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 13:07:01 -0700 Subject: [PATCH 155/255] Add 0.25em padding for buttons, dropdowns, and inputs --- public/stylesheets/style.css | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css index 4582e1e..2f0a789 100644 --- a/public/stylesheets/style.css +++ b/public/stylesheets/style.css @@ -30,4 +30,16 @@ a { #home-button { margin-right: auto; +} + +button { + padding: 0.25em; +} + +select { + padding: 0.25em; +} + +input { + padding: 0.25em; } \ No newline at end of file From 3e7e9eeb70eabda18c3ffbff746bf422bd54036d Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 13:16:24 -0700 Subject: [PATCH 156/255] Add margins for buttons --- public/stylesheets/style.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css index 2f0a789..5ba6ad5 100644 --- a/public/stylesheets/style.css +++ b/public/stylesheets/style.css @@ -34,6 +34,7 @@ a { button { padding: 0.25em; + margin: 0em 0.1em; } select { @@ -42,4 +43,8 @@ select { input { padding: 0.25em; +} + +#logout-button { + margin-left: 0.5em; } \ No newline at end of file From e6c4d2347f9464b50b6bd4e65b3c867e64135edd Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 13:17:44 -0700 Subject: [PATCH 157/255] Move submit score button to top left of page --- public/stylesheets/index.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/stylesheets/index.css b/public/stylesheets/index.css index 7e66419..ffcfa14 100644 --- a/public/stylesheets/index.css +++ b/public/stylesheets/index.css @@ -25,6 +25,6 @@ tr { flex-direction: row; } -#actions-div { - margin-left: auto; +#add-score-button { + margin-right: auto; } \ No newline at end of file From 24a93104e428e41e887b19e0047df848812fc98c Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 13:19:20 -0700 Subject: [PATCH 158/255] Set redirect to root when game is added --- routes/manage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/manage.js b/routes/manage.js index b04cf75..227c27d 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -68,7 +68,7 @@ router.post('/game', userLoggedIn, function(req, res, next) { else if(id) games.edit(id, divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score) .then(res.redirect('/manage')); else games.add(divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score, userID) - .then(res.redirect("/manage")); + .then(res.redirect("/")); }); }); From cceda089c01c6680b196ca7e53aad2f798862271 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 13:20:14 -0700 Subject: [PATCH 159/255] Move login button to top right of page on index --- public/stylesheets/index.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/public/stylesheets/index.css b/public/stylesheets/index.css index ffcfa14..d08e1e8 100644 --- a/public/stylesheets/index.css +++ b/public/stylesheets/index.css @@ -27,4 +27,8 @@ tr { #add-score-button { margin-right: auto; +} + +#login-button { + margin-left: auto; } \ No newline at end of file From b1637d97bc036d94b3e46bdb7a4210fba273c158 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 13:40:16 -0700 Subject: [PATCH 160/255] Add data validity check for adding game --- public/scripts/manage/game.js | 84 ++++++++++++++++++++++------------- 1 file changed, 52 insertions(+), 32 deletions(-) diff --git a/public/scripts/manage/game.js b/public/scripts/manage/game.js index 8338d4c..ce80b97 100644 --- a/public/scripts/manage/game.js +++ b/public/scripts/manage/game.js @@ -28,33 +28,25 @@ async function initializeForm() { Form.addHiddenValue('game', gameID, submissionForm); Form.populateSeasons(seasonDropdown, game.seasonID); - Data.getDivision(game.divisionID) - .then(data => { - Form.populateSports(sportDropdown, data.sportID) - .then(() => { - Form.populateGenders(genderDropdown, sportDropdown.value, data.gender.name) - .then(() => { - Form.populateDivisions(divisionDropdown, sportDropdown.value, genderDropdown.value, game.divisionID); - }); - Form.populateTeams(team1Dropdown, sportDropdown.value, game.team1ID); - Form.populateTeams(team2Dropdown, sportDropdown.value, game.team2ID); - }); - }); + const data = await Data.getDivision(game.divisionID) + await Form.populateSports(sportDropdown, data.sportID) + await Form.populateGenders(genderDropdown, sportDropdown.value, data.gender.name) + await Form.populateDivisions(divisionDropdown, sportDropdown.value, genderDropdown.value, game.divisionID); + await Form.populateTeams(team1Dropdown, sportDropdown.value, game.team1ID); + await Form.populateTeams(team2Dropdown, sportDropdown.value, game.team2ID); + dateInput.value = game.date; team1ScoreTextbox.value = game.team1Score; team2ScoreTextbox.value = game.team2Score; } else { - Form.populateSeasons(seasonDropdown); - Form.populateSports(sportDropdown) - .then(() => { - Form.populateGenders(genderDropdown, sportDropdown.value) - .then(() => { - Form.populateDivisions(divisionDropdown, sportDropdown.value, genderDropdown.value); - }); - Form.populateTeams(team1Dropdown, sportDropdown.value); - Form.populateTeams(team2Dropdown, sportDropdown.value); - }); + await Form.populateSeasons(seasonDropdown); + await Form.populateSports(sportDropdown) + await Form.populateGenders(genderDropdown, sportDropdown.value) + await Form.populateDivisions(divisionDropdown, sportDropdown.value, genderDropdown.value); + await Form.populateTeams(team1Dropdown, sportDropdown.value); + await Form.populateTeams(team2Dropdown, sportDropdown.value); + dateInput.value = (new Date()).toISOString().slice(0,10); } seasonDropdown.disabled = false; @@ -66,21 +58,49 @@ async function initializeForm() { team2Dropdown.disabled = false; team1ScoreTextbox.disabled = false; team2ScoreTextbox.disabled = false; - submitButton.disabled = false; - sportDropdown.onchange = () => { - Form.populateGenders(genderDropdown, sportDropdown.value) - .then(() => { - Form.populateDivisions(divisionDropdown, sportDropdown.value, genderDropdown.value); - }); - Form.populateTeams(team1Dropdown, sportDropdown.value); - Form.populateTeams(team2Dropdown, sportDropdown.value); + sportDropdown.onchange = async () => { + await Form.populateGenders(genderDropdown, sportDropdown.value) + await Form.populateDivisions(divisionDropdown, sportDropdown.value, genderDropdown.value); + await Form.populateTeams(team1Dropdown, sportDropdown.value); + await Form.populateTeams(team2Dropdown, sportDropdown.value); + checkDataValidity(); }; - genderDropdown.onchange = () => { - Form.populateDivisions(divisionDropdown, sportDropdown.value, genderDropdown.value); + genderDropdown.onchange = async () => { + await Form.populateDivisions(divisionDropdown, sportDropdown.value, genderDropdown.value); + checkDataValidity(); }; + + dateInput.addEventListener('keyup', checkDataValidity); + team1Dropdown.onchange = checkDataValidity; + team1ScoreTextbox.addEventListener('keyup', checkDataValidity); + team2Dropdown.onchange = checkDataValidity; + team2ScoreTextbox.addEventListener('keyup', checkDataValidity); + + checkDataValidity(); } initializeForm(); +async function checkDataValidity() { + let dataIsValid = true; + + const seasonHasContent = seasonDropdown.options.length; + const sportHasContent = sportDropdown.options.length; + const genderHasContent = genderDropdown.options.length; + const divisionHasContent = divisionDropdown.options.length; + const team1HasContent = team1Dropdown.options.length; + const team2HasContent = team2Dropdown.options.length; + + if(!seasonHasContent || !sportHasContent || !genderHasContent || !divisionHasContent || !team1HasContent || !team2HasContent) dataIsValid = false; + + if(team1Dropdown.selectedIndex == team2Dropdown.selectedIndex) dataIsValid = false; + + if(team1ScoreTextbox.value == "" || team2ScoreTextbox.value == "") dataIsValid = false; + + if(dateInput.value == "") dataIsValid = false; + + submitButton.disabled = !dataIsValid; +} + Form.addRemoveFunction(deleteButton, submissionForm, "game"); From 64a6ba545b123d46d43731ac1eba5fb02b9cda60 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 13:44:01 -0700 Subject: [PATCH 161/255] Adjust error page layout --- public/stylesheets/error.css | 3 +++ views/error.pug | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 public/stylesheets/error.css diff --git a/public/stylesheets/error.css b/public/stylesheets/error.css new file mode 100644 index 0000000..f1ac617 --- /dev/null +++ b/public/stylesheets/error.css @@ -0,0 +1,3 @@ +#mobile-view { + max-width: 100%; +} \ No newline at end of file diff --git a/views/error.pug b/views/error.pug index 51ec12c..43a7357 100644 --- a/views/error.pug +++ b/views/error.pug @@ -1,6 +1,9 @@ extends layout +block stylesheets + link(rel='stylesheet', href='/stylesheets/error.css') + block content h1= message h2= error.status - pre #{error.stack} + pre #{error.stack} \ No newline at end of file From 2fc17c0c4253f7b3baced28ed6ed7a491719aa48 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 13:52:57 -0700 Subject: [PATCH 162/255] Add labels for gender and division for index page --- public/stylesheets/form.css | 4 ++++ views/index.pug | 19 +++++++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/public/stylesheets/form.css b/public/stylesheets/form.css index 389028c..9859f8c 100644 --- a/public/stylesheets/form.css +++ b/public/stylesheets/form.css @@ -44,4 +44,8 @@ form { #admin-checkbox { width: auto; + } + + #sport-gender-division { + flex-direction: row; } \ No newline at end of file diff --git a/views/index.pug b/views/index.pug index b504698..eec3efa 100644 --- a/views/index.pug +++ b/views/index.pug @@ -16,12 +16,19 @@ block content label Year span(class='form-section-input') select#year-dropdown(name="year" class="form-main-dropdown") - span(class='form-section') - label Sport - span(class='form-section-input') - select#sport-dropdown(name="sport" class="form-main-dropdown") - select#gender-dropdown(name="gender") - select#division-dropdown(name="division") + span#sport-gender-division(class='form-section') + span + label Sport + span(class='form-section-input') + select#sport-dropdown(name="sport" class="form-main-dropdown") + span + label Gender + span(class='form-section-input') + select#gender-dropdown(name="gender") + span + label Division + span(class='form-section-input') + select#division-dropdown(name="division") span(class='form-section') label Team span(class='form-section-input') From 150a186aeab45fe582888f3da129bef2f003811f Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 13:59:17 -0700 Subject: [PATCH 163/255] Add labels to game submit page --- public/stylesheets/form.css | 2 +- views/index.pug | 2 +- views/manage/addgame.pug | 47 ++++++++++++++++++++++++------------- 3 files changed, 33 insertions(+), 18 deletions(-) diff --git a/public/stylesheets/form.css b/public/stylesheets/form.css index 9859f8c..43fee2e 100644 --- a/public/stylesheets/form.css +++ b/public/stylesheets/form.css @@ -46,6 +46,6 @@ form { width: auto; } - #sport-gender-division { + .flat-form-section { flex-direction: row; } \ No newline at end of file diff --git a/views/index.pug b/views/index.pug index eec3efa..a1b7d2d 100644 --- a/views/index.pug +++ b/views/index.pug @@ -16,7 +16,7 @@ block content label Year span(class='form-section-input') select#year-dropdown(name="year" class="form-main-dropdown") - span#sport-gender-division(class='form-section') + span(class='form-section flat-form-section') span label Sport span(class='form-section-input') diff --git a/views/manage/addgame.pug b/views/manage/addgame.pug index 61e7aaa..61e8f61 100644 --- a/views/manage/addgame.pug +++ b/views/manage/addgame.pug @@ -10,26 +10,41 @@ block content label Year span(class='form-section-input') select#year-dropdown(name="year" class="form-main-dropdown" disabled) - span(class='form-section') - label Sport - span(class='form-section-input') - select#sport-dropdown(name="sport" class="form-main-dropdown" disabled) - select#gender-dropdown(name="gender" disabled) - select#division-dropdown(name="division" disabled) + span(class='form-section flat-form-section') + span + label Sport + span(class='form-section-input') + select#sport-dropdown(name="sport" class="form-main-dropdown") + span + label Gender + span(class='form-section-input') + select#gender-dropdown(name="gender") + span + label Division + span(class='form-section-input') + select#division-dropdown(name="division") span(class='form-section') label Date of match span(class='form-section-input') input#date-input(type="date", name="date" value=date disabled) - span(class='form-section') - label Your team - span(class='form-section-input') - select#team1-dropdown(name="team1" class="form-main-dropdown" disabled) - input#team1-score-textbox(class="form-score-input", type="number", name="team1-score", value="0" disabled) - span(class='form-section') - label Opponent - span(class='form-section-input') - select#team2-dropdown(name="team2" class="form-main-dropdown" disabled) - input#team2-score-textbox(class="form-score-input", type="number", name="team2-score", value="0" disabled) + span(class='form-section flat-form-section') + span + label Your team + span(class='form-section-input') + select#team1-dropdown(name="team1" class="form-main-dropdown" disabled) + span(class='form-score-input') + label Score + span(class='form-section-input') + input#team1-score-textbox(type="number", name="team1-score", value="0" disabled) + span(class='form-section flat-form-section') + span + label Opponent + span(class='form-section-input') + select#team2-dropdown(name="team2" class="form-main-dropdown" disabled) + span(class='form-score-input') + label Score + span(class='form-section-input') + input#team2-score-textbox(type="number", name="team2-score", value="0" disabled) span(class='form-section') button#submit-button(type="submit" disabled) Submit span(class='form-section') From 644c8fdec33271c44065717167aa2dc53c894b12 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 14:05:06 -0700 Subject: [PATCH 164/255] Add check validity function for add season page --- public/scripts/manage/season.js | 14 ++++++++++++++ views/manage/addseason.pug | 4 ++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/public/scripts/manage/season.js b/public/scripts/manage/season.js index e69de29..82d0204 100644 --- a/public/scripts/manage/season.js +++ b/public/scripts/manage/season.js @@ -0,0 +1,14 @@ +const seasonTextbox = document.getElementById('season-textbox'); +const submitButton = document.getElementById('submit-button'); + +async function checkDataValidity() { + let dataIsValid = true; + + if(seasonTextbox.value == "") dataIsValid = false; + + submitButton.disabled = !dataIsValid; +} +checkDataValidity(); + +seasonTextbox.addEventListener('keyup', checkDataValidity); +seasonTextbox.addEventListener('change', checkDataValidity); diff --git a/views/manage/addseason.pug b/views/manage/addseason.pug index 4050cb2..27783b5 100644 --- a/views/manage/addseason.pug +++ b/views/manage/addseason.pug @@ -9,9 +9,9 @@ block content span(class='form-section') label Ending year span(class='form-section-input') - input(type="number", name="year", value=currentYear) + input#season-textbox(type="number", name="year", value=currentYear) span(class='form-section') - button#submit-button(type="submit") Submit + button#submit-button(type="submit" disabled) Submit block scripts script(src='/scripts/manage/season.js' type="module") \ No newline at end of file From 000905257ea63374c3bffc52dc285f732910f99f Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 14:09:00 -0700 Subject: [PATCH 165/255] Improve division page validity check --- public/scripts/manage/division.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/public/scripts/manage/division.js b/public/scripts/manage/division.js index fb623c5..a542a65 100644 --- a/public/scripts/manage/division.js +++ b/public/scripts/manage/division.js @@ -48,6 +48,11 @@ async function checkDataValidity() { if(!nameTextbox.value) dataIsValid = false; + const sportHasContent = sportDropdown.options.length; + const genderHasContent = genderDropdown.options.length; + + if(!sportHasContent || !genderHasContent) dataIsValid = false; + if(dataIsValid) submitButton.disabled = false; else submitButton.disabled = true; From 459b1a4a1ab2828083af3ac9fdfff5ca50acc0cf Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 14:10:09 -0700 Subject: [PATCH 166/255] Improve add team page validity check --- public/scripts/manage/team.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/public/scripts/manage/team.js b/public/scripts/manage/team.js index 10cd386..4bc643e 100644 --- a/public/scripts/manage/team.js +++ b/public/scripts/manage/team.js @@ -39,9 +39,10 @@ async function checkDataValidity() { if(!nameTextbox.value) dataIsValid = false; + const sportHasContent = sportDropdown.options.length; + if(!sportHasContent) dataIsValid = false; - if(dataIsValid) submitButton.disabled = false; - else submitButton.disabled = true; + submitButton.disabled = !dataIsValid; } Form.addRemoveFunction(deleteButton, submissionForm, "team"); \ No newline at end of file From f2085ec0f2d72b359417a2e09ddd9123e41299c6 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 14:19:53 -0700 Subject: [PATCH 167/255] Add noscript message --- public/stylesheets/style.css | 7 +++++++ views/layout.pug | 2 ++ 2 files changed, 9 insertions(+) diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css index 5ba6ad5..c4252bd 100644 --- a/public/stylesheets/style.css +++ b/public/stylesheets/style.css @@ -47,4 +47,11 @@ input { #logout-button { margin-left: 0.5em; +} + +#noscript-message { + background-color: lightcoral; + padding: 1em; + margin-bottom: 3em; + border-radius: .25em; } \ No newline at end of file diff --git a/views/layout.pug b/views/layout.pug index 11fb12b..80d0238 100644 --- a/views/layout.pug +++ b/views/layout.pug @@ -7,6 +7,8 @@ html block stylesheets body div#mobile-view + noscript + span#noscript-message Please enable JavaScript to run this app. div#actions-div if !hideHomeButton button#home-button Home From 238a22e59797d87248f610858c695e6c22545cc4 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 14:37:26 -0700 Subject: [PATCH 168/255] Add "NOT NULL" constraints to database columns --- database/init_database.sql | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/database/init_database.sql b/database/init_database.sql index c7de265..404addf 100644 --- a/database/init_database.sql +++ b/database/init_database.sql @@ -55,8 +55,8 @@ CREATE TABLE IF NOT EXISTS scores.sports( CREATE TABLE IF NOT EXISTS scores.divisions( division_id BIGINT GENERATED ALWAYS AS IDENTITY, division_name TEXT NOT NULL, - gender VARCHAR(1) CHECK (gender IN ( 'F', 'M' ) ), - sport_id BIGINT, + gender VARCHAR(1) NOT NULL CHECK (gender IN ( 'F', 'M' ) ), + sport_id BIGINT NOT NULL, currently_active BOOLEAN DEFAULT TRUE, PRIMARY KEY(division_id), CONSTRAINT fk_sport @@ -67,7 +67,7 @@ CREATE TABLE IF NOT EXISTS scores.divisions( CREATE TABLE IF NOT EXISTS scores.teams( team_id BIGINT GENERATED ALWAYS AS IDENTITY, team_name TEXT NOT NULL, - sport_id BIGINT, + sport_id BIGINT NOT NULL, currently_active BOOLEAN DEFAULT TRUE, PRIMARY KEY(team_id), CONSTRAINT fk_sport @@ -83,14 +83,14 @@ CREATE TABLE IF NOT EXISTS scores.seasons( CREATE TABLE IF NOT EXISTS scores.games( game_id BIGINT GENERATED ALWAYS AS IDENTITY, - division_id BIGINT, - season_id BIGINT, - game_date DATE, - team1_id BIGINT, - team2_id BIGINT, - team1_score INTEGER, - team2_score INTEGER, - submitter_id BIGINT, + division_id BIGINT NOT NULL, + season_id BIGINT NOT NULL, + game_date DATE NOT NULL, + team1_id BIGINT NOT NULL, + team2_id BIGINT NOT NULL, + team1_score INTEGER NOT NULL, + team2_score INTEGER NOT NULL, + submitter_id BIGINT NOT NULL, updated_timestamp TIMESTAMP WITH TIME ZONE DEFAULT now(), PRIMARY KEY(game_id), CONSTRAINT fk_division From d2d6bbc5143e2c9b85ce97a35d72465b0b81fdb1 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 14:49:08 -0700 Subject: [PATCH 169/255] Fix bug where accounts.js tries to create admin account before database is initialized --- database/accounts/accounts.js | 2 +- database/database.js | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/database/accounts/accounts.js b/database/accounts/accounts.js index 84c7a35..d33d11f 100644 --- a/database/accounts/accounts.js +++ b/database/accounts/accounts.js @@ -27,7 +27,7 @@ async function checkForAdminAccount() { console.log("Created temp admin account 'admin@example.com' with password 'admin'."); } } -checkForAdminAccount(); +database.initializationStatus.then(() => checkForAdminAccount()); passport.use(new localStrategy({ diff --git a/database/database.js b/database/database.js index 349e2bc..7390426 100644 --- a/database/database.js +++ b/database/database.js @@ -36,10 +36,11 @@ async function checkForDatabaseInitialization() { await Initialize(); } } -checkForDatabaseInitialization(); +const initializationStatus = checkForDatabaseInitialization(); -exports.executeQuery = executeQuery; \ No newline at end of file +exports.executeQuery = executeQuery; +exports.initializationStatus = initializationStatus; \ No newline at end of file From 5c783880a7d2c60fd7d03aa6629c7bb9a54ad531 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 15:41:36 -0700 Subject: [PATCH 170/255] Improve error handling for account management --- routes/manage.js | 27 ++++++++++++++++----------- views/accounts/createuser.pug | 1 + 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/routes/manage.js b/routes/manage.js index 227c27d..c995742 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -156,16 +156,16 @@ router.get('/account', userLoggedIn, (req, res, next) => { if(userIsAdmin) { let title = req.query.account ? 'Manage User' : 'Create User' - res.render('accounts/createuser', { title, userLoggedIn: !!req.user }); + res.render('accounts/createuser', { title, userLoggedIn: !!req.user, message: req.flash('error') }); } else { let title = 'Manage Account'; - res.render('accounts/createuser', { title, accountID, userLoggedIn: !!req.user }); + res.render('accounts/createuser', { title, accountID, userLoggedIn: !!req.user, message: req.flash('error') }); } }); -router.post('/account', userLoggedIn, (req, res, next) => { +router.post('/account', userLoggedIn, async function(req, res, next) { const email = req.body.email; const password = req.body.password; @@ -175,19 +175,24 @@ router.post('/account', userLoggedIn, (req, res, next) => { const loggedInAccountIsAdmin = req.user[2]; const loggedInAccountID = req.user[0]; - console.log(accountID); - console.log(loggedInAccountID); - - if(!loggedInAccountIsAdmin && accountID != loggedInAccountID) { res.status(403).send("ACCESS DENIED"); } else { - const isAdmin = loggedInAccountIsAdmin ? !!req.body.admin : false; + try { + const isAdmin = loggedInAccountIsAdmin ? !!req.body.admin : false; - if(remove) accounts.remove(accountID).then(res.redirect('/manage')); - if(accountID) accounts.edit(accountID, email, password, isAdmin).then(res.redirect('/manage')); - else accounts.create(req.body.email, req.body.password, !!req.body.admin).then(res.redirect('/manage')); + if(remove) await accounts.remove(accountID); + else if(accountID) await accounts.edit(accountID, email, password, isAdmin); + else await accounts.create(req.body.email, req.body.password, !!req.body.admin); + + res.redirect('/manage'); + } + catch (err) { + console.error("ERROR: " + err.message); + req.flash("error", "An error has occurred."); + res.redirect('/manage/account'); + } } }); diff --git a/views/accounts/createuser.pug b/views/accounts/createuser.pug index a87897d..dfc1b13 100644 --- a/views/accounts/createuser.pug +++ b/views/accounts/createuser.pug @@ -20,6 +20,7 @@ block content span(class='form-section-checkbox') input#admin-checkbox(type="checkbox" name="admin" disabled) label(for="admin-checkbox") Grant admin privileges + .error #{message} span(class='form-section') button#submit-button(type="submit" disabled) Submit span(class='form-section') From dd0a9c4316053c640057ea877b56b1069812cade Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 15:44:23 -0700 Subject: [PATCH 171/255] Remove unnecessary files --- public/scripts/submit.js | 113 --------------------------------------- routes/admin.js | 24 --------- test/test.js | 10 ---- views/manage/layout.pug | 10 ---- 4 files changed, 157 deletions(-) delete mode 100644 public/scripts/submit.js delete mode 100644 routes/admin.js delete mode 100644 test/test.js delete mode 100644 views/manage/layout.pug diff --git a/public/scripts/submit.js b/public/scripts/submit.js deleted file mode 100644 index 8c3cf81..0000000 --- a/public/scripts/submit.js +++ /dev/null @@ -1,113 +0,0 @@ -import * as Data from "./data.js"; - - -const sportDropdown = document.getElementById('sport-dropdown'); -const seasonDropdown = document.getElementById('year-dropdown'); -const genderDropdown = document.getElementById('gender-dropdown'); -const divisionDropdown = document.getElementById('division-dropdown'); -const team1Dropdown = document.getElementById('team1-dropdown'); -const team2Dropdown = document.getElementById('team2-dropdown'); - - - - - -async function listSeasons() { - seasonDropdown.innerHTML = ""; - - const seasonsList = await Data.getSeasons(); - - seasonsList.forEach(season => { - const option = document.createElement('option'); - option.text = (season.year - 1) + "-" + season.year; - option.value = season.id; - seasonDropdown.appendChild(option); - }); -} -listSeasons(); - -async function listSports() { - sportDropdown.innerHTML = ""; - - const sportsList = await Data.getSports(); - - sportsList.forEach(sport => { - const option = document.createElement('option'); - option.text = sport.name; - option.value = sport.id; - sportDropdown.appendChild(option); - }); - - listGenders(); - listTeams(); -} -listSports(); - -async function listGenders() { - genderDropdown.innerHTML = ""; - - const selectedSportID = sportDropdown.value; - const gendersList = await Data.getGenders(selectedSportID); - - if(selectedSportID) { - gendersList.forEach(gender => { - const option = document.createElement('option'); - option.text = (gender.name == "female") ? "Female" : (gender.name == "male") ? "Male" : ""; - option.value = gender.name; - genderDropdown.appendChild(option); - }); - } - - listDivisions(); -} - -async function listDivisions() { - divisionDropdown.innerHTML = ""; - - const selectedSportID = sportDropdown.value; - const selectedGender = genderDropdown.value; - - if(selectedGender) { - const divisionsList = await Data.getDivisions(selectedSportID, selectedGender); - - divisionsList.forEach(division => { - const option = document.createElement('option'); - option.text = division.name; - option.value = division.id; - divisionDropdown.appendChild(option); - }); - } -} - -async function listTeams() { - team1Dropdown.innerHTML = ""; - team2Dropdown.innerHTML = ""; - - const selectedSportID = sportDropdown.value; - - if(selectedSportID) { - const teamsList = await Data.getTeams(selectedSportID); - - teamsList.forEach(team => { - const optionT1 = document.createElement('option'); - optionT1.text = team.name; - optionT1.value = team.id; - team1Dropdown.appendChild(optionT1); - - const optionT2 = document.createElement('option'); - optionT2.text = team.name; - optionT2.value = team.id; - team2Dropdown.appendChild(optionT2); - }); - } -} - - - - - -sportDropdown.onchange = (() => { - listGenders(); - listTeams(); -}); -genderDropdown.onchange = listDivisions; \ No newline at end of file diff --git a/routes/admin.js b/routes/admin.js deleted file mode 100644 index 9e9cfc9..0000000 --- a/routes/admin.js +++ /dev/null @@ -1,24 +0,0 @@ -var express = require('express'); -var router = express.Router(); -const passport = require('passport'); -const app = require('../app'); -const accounts = require('./../database/accounts/accounts'); - - -function adminLoggedIn(req, res, next) { - if (req.user && req.user[2]) { - next(); - } - else { - req.flash('error', 'An admin account is required to access this page.'); - res.redirect('/auth/login'); - } - } - -router.get('/', adminLoggedIn, (req, res, next) => { - res.render -}); - - - -module.exports = router; \ No newline at end of file diff --git a/test/test.js b/test/test.js deleted file mode 100644 index 4f2ac60..0000000 --- a/test/test.js +++ /dev/null @@ -1,10 +0,0 @@ -const request = require('supertest'); -const app = require('../app'); - -describe('App', function() { - it('has the default page', function(done) { - request(app) - .get('/') - .expect(/Welcome to Express/, done); - }); -}); diff --git a/views/manage/layout.pug b/views/manage/layout.pug deleted file mode 100644 index e4f85eb..0000000 --- a/views/manage/layout.pug +++ /dev/null @@ -1,10 +0,0 @@ -doctype html -html - head - title= title - meta(name='viewport', content='width=device-width, initial-scale=1') - link(rel='stylesheet', href='/stylesheets/style.css') - block stylesheets - body - block content - block scripts From fb414ab9d8c424cb20eb387e36987aad41d43686 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 15:50:43 -0700 Subject: [PATCH 172/255] Add duplicate route functions to separate file --- routes/auth.js | 12 ---------- routes/checkLoginStatus.js | 21 +++++++++++++++++ routes/data.js | 24 ++++---------------- routes/manage.js | 46 +++++++++++++------------------------- 4 files changed, 40 insertions(+), 63 deletions(-) create mode 100644 routes/checkLoginStatus.js diff --git a/routes/auth.js b/routes/auth.js index 25159ab..1e1940b 100644 --- a/routes/auth.js +++ b/routes/auth.js @@ -1,20 +1,8 @@ var express = require('express'); var router = express.Router(); const passport = require('passport'); -const accounts = require('./../database/accounts/accounts'); const app = require('../app'); - -function adminLoggedIn(req, res, next) { - if (req.user && req.user[2]) { - next(); - } - else { - req.flash('error', 'An admin account is required to access this page.'); - res.redirect('/auth/login'); - } - } - router.get('/login', (req, res, next) => { res.render('accounts/login', { title : "Login", message: req.flash('error') }); }); diff --git a/routes/checkLoginStatus.js b/routes/checkLoginStatus.js new file mode 100644 index 0000000..d9fd970 --- /dev/null +++ b/routes/checkLoginStatus.js @@ -0,0 +1,21 @@ +function adminLoggedIn(req, res, next) { + if (req.user && req.user[2]) { + next(); + } + else { + req.flash('error', 'An admin account is required to access this page.'); + res.redirect('/auth/login'); + } +} + +function userLoggedIn(req, res, next) { + if (req.user) { + next(); + } + else { + res.redirect('/auth/login'); + } +} + +exports.admin = adminLoggedIn; +exports.user = userLoggedIn; \ No newline at end of file diff --git a/routes/data.js b/routes/data.js index c875622..d241947 100644 --- a/routes/data.js +++ b/routes/data.js @@ -1,5 +1,6 @@ var express = require('express'); var router = express.Router(); + var sports = require('../database/scores/sports'); var seasons = require('../database/scores/seasons'); var genders = require('../database/scores/genders'); @@ -8,24 +9,7 @@ var teams = require('../database/scores/teams'); var games = require('../database/scores/games'); var accounts = require('../database/accounts/accounts'); -function adminLoggedIn(req, res, next) { - if (req.user && req.user[2]) { - next(); - } - else { - req.flash('error', 'An admin account is required to access this page.'); - res.redirect('/auth/login'); - } -} - -function userLoggedIn(req, res, next) { - if (req.user) { - next(); - } - else { - res.redirect('/auth/login'); - } -} +var checkLoginStatus = require('./checkLoginStatus'); router.get('/sports', function(req, res, next) { sports.retrieveAll() @@ -81,12 +65,12 @@ router.get('/game', function(req, res, next) { .then(data => res.json(data)); }) -router.get('/accounts', adminLoggedIn, function(req, res, next) { +router.get('/accounts', checkLoginStatus.admin, function(req, res, next) { accounts.retrieveAll() .then(data => res.json(data)); }) -router.get('/account', userLoggedIn, function(req, res, next) { +router.get('/account', checkLoginStatus.user, function(req, res, next) { const userIsAdmin = req.user[2]; const loggedInAccountID = req.user[0]; const requestedAccountID = req.query.account; diff --git a/routes/manage.js b/routes/manage.js index c995742..506a5f1 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -1,5 +1,6 @@ var express = require('express'); var router = express.Router(); + var genders = require('../database/scores/genders'); var games = require('../database/scores/games'); var seasons = require('../database/scores/seasons'); @@ -9,38 +10,21 @@ var genders = require('../database/scores/genders'); var teams = require('../database/scores/teams'); var accounts = require('../database/accounts/accounts'); -function userLoggedIn(req, res, next) { - if (req.user) { - next(); - } - else { - res.redirect('/auth/login'); - } -} - -function adminLoggedIn(req, res, next) { - if (req.user && req.user[2]) { - next(); - } - else { - req.flash('error', 'An admin account is required to access this page.'); - res.redirect('/auth/login'); - } -} +var checkLoginStatus = require('./checkLoginStatus'); -router.get('/' ,userLoggedIn, function(req, res, next) { +router.get('/' ,checkLoginStatus.user, function(req, res, next) { if(req.user[2]) res.render('manage', { title: 'Score Management', userLoggedIn: !!req.user }); else res.render('manage/manage-nonadmin', { title: "My Games", userLoggedIn: !!req.user }); }); -router.get('/game', userLoggedIn, function(req, res, next) { +router.get('/game', checkLoginStatus.user, function(req, res, next) { let title = req.query.game ? 'Edit Game' : 'Submit Score' res.render('manage/addgame', { title, userLoggedIn: !!req.user }); }); -router.post('/game', userLoggedIn, function(req, res, next) { +router.post('/game', checkLoginStatus.user, function(req, res, next) { const seasonID = req.body['year']; const sportID = req.body['sport']; const gender = (req.body['gender'] == "female") ? genders.FEMALE : genders.MALE; @@ -72,11 +56,11 @@ router.post('/game', userLoggedIn, function(req, res, next) { }); }); -router.get('/season', adminLoggedIn, function(req, res, next) { +router.get('/season', checkLoginStatus.admin, function(req, res, next) { res.render('manage/addseason', { title: 'Add Season', currentYear : (new Date()).getFullYear(), userLoggedIn: !!req.user }); }); -router.post('/season', adminLoggedIn, function(req, res, next) { +router.post('/season', checkLoginStatus.admin, function(req, res, next) { const year = req.body['year']; const seasonID = req.body['season']; @@ -86,11 +70,11 @@ router.post('/season', adminLoggedIn, function(req, res, next) { else seasons.add(year).then(res.redirect("/manage")); }); -router.get('/sport', adminLoggedIn, function(req, res, next) { +router.get('/sport', checkLoginStatus.admin, function(req, res, next) { res.render('manage/addsport', { title: 'Add Sport', userLoggedIn: !!req.user }); }); -router.post('/sport', adminLoggedIn, function(req, res, next) { +router.post('/sport', checkLoginStatus.admin, function(req, res, next) { const name = req.body['name']; const id = req.body['sport']; const remove = req.body['remove']; @@ -100,13 +84,13 @@ router.post('/sport', adminLoggedIn, function(req, res, next) { else sports.add(name).then(res.redirect('/manage')); }); -router.get('/division', adminLoggedIn, function(req, res, next) { +router.get('/division', checkLoginStatus.admin, function(req, res, next) { let title = req.query.division ? 'Edit Division' : 'Add Division' res.render('manage/adddivision', { title, userLoggedIn: !!req.user }); }); -router.post('/division', adminLoggedIn, function(req, res, next) { +router.post('/division', checkLoginStatus.admin, function(req, res, next) { const name = req.body['name']; const sport = req.body['sport']; const genderName = req.body['gender']; @@ -131,13 +115,13 @@ router.post('/division', adminLoggedIn, function(req, res, next) { } }); -router.get('/team', adminLoggedIn, function(req, res, next) { +router.get('/team', checkLoginStatus.admin, function(req, res, next) { let title = req.query.team ? 'Edit Team' : 'Add Team' res.render('manage/addteam', { title, userLoggedIn: !!req.user }); }); -router.post('/team', adminLoggedIn, function(req, res, next) { +router.post('/team', checkLoginStatus.admin, function(req, res, next) { const name = req.body['name']; const sport = req.body['sport']; @@ -149,7 +133,7 @@ router.post('/team', adminLoggedIn, function(req, res, next) { else teams.add(name, sport).then(res.redirect("/manage")); }); -router.get('/account', userLoggedIn, (req, res, next) => { +router.get('/account', checkLoginStatus.user, (req, res, next) => { const userIsAdmin = req.user[2]; const accountID = req.user[0]; @@ -165,7 +149,7 @@ router.get('/account', userLoggedIn, (req, res, next) => { } }); -router.post('/account', userLoggedIn, async function(req, res, next) { +router.post('/account', checkLoginStatus.user, async function(req, res, next) { const email = req.body.email; const password = req.body.password; From 2ecb26ca3cae27885bd12135ae7a2e36ff25edab Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 15:55:29 -0700 Subject: [PATCH 173/255] Remove admin router reference --- app.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/app.js b/app.js index a379166..9082389 100644 --- a/app.js +++ b/app.js @@ -15,7 +15,6 @@ var usersRouter = require('./routes/users'); var dataRouter = require('./routes/data'); var manageRouter = require('./routes/manage'); var authRouter = require('./routes/auth'); -var adminRouter = require('./routes/admin'); var app = express(); @@ -54,7 +53,6 @@ app.use('/users', usersRouter); app.use('/data', dataRouter); app.use('/manage', manageRouter); app.use('/auth', authRouter); -app.use('/admin', adminRouter); // catch 404 and forward to error handler From 4f3581b1e5ea96c1a291102e2268020f9b12df38 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 16:16:01 -0700 Subject: [PATCH 174/255] Fix bug where genders by sport do not report correctly --- database/scores/genders.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/database/scores/genders.js b/database/scores/genders.js index b59bdb0..17e3ff2 100644 --- a/database/scores/genders.js +++ b/database/scores/genders.js @@ -15,8 +15,8 @@ const FEMALE = new Gender("female"); async function retrieveBySport(sportID) { - const query = `SELECT gender - from scores.divisions + const query = `SELECT DISTINCT(gender) + FROM scores.divisions WHERE sport_id = $1;`; const table = await database.executeQuery(query, [sportID]); From 6705f06e183949af03ca0f247b9d516eaf90b2c6 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 16:16:11 -0700 Subject: [PATCH 175/255] Add better error handling for data.js route --- routes/data.js | 167 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 121 insertions(+), 46 deletions(-) diff --git a/routes/data.js b/routes/data.js index d241947..dfe6b8d 100644 --- a/routes/data.js +++ b/routes/data.js @@ -11,75 +11,150 @@ var accounts = require('../database/accounts/accounts'); var checkLoginStatus = require('./checkLoginStatus'); -router.get('/sports', function(req, res, next) { - sports.retrieveAll() - .then(data => res.json(data)); +router.get('/sports', async function(req, res, next) { + try { + const data = await sports.retrieveAll(); + res.json(data); + } catch(err) { + console.error("ERROR: " + err.message); + res.status(500).send("An error has occurred"); + } }); -router.get('/sport', function(req, res, next) { - sports.getFromID(req.query.sport) - .then(data => res.json(data)); +router.get('/sport', async function(req, res, next) { + try { + const sportID = req.query.sport; + const data = await sports.getFromID(sportID); + res.json(data); + } catch(err) { + console.error("ERROR: " + err.message); + res.status(500).send("An error has occurred"); + } }); -router.get('/seasons', function(req, res, next) { - seasons.retrieveAll() - .then(data => res.json(data)); +router.get('/seasons', async function(req, res, next) { + try { + const data = await seasons.retrieveAll(); + res.json(data); + } catch(err) { + console.error("ERROR: " + err.message); + res.status(500).send("An error has occurred"); + } }) -router.get('/genders', function(req, res, next) { - genders.retrieveBySport(req.query.sport) - .then(data => res.json(data)); +router.get('/genders', async function(req, res, next) { + try { + const sportID = req.query.sport; + const data = await genders.retrieveBySport(sportID); + res.json(data); + } catch(err) { + console.error("ERROR: " + err.message); + res.status(500).send("An error has occurred"); + } }) -router.get('/divisions', function(req, res, next) { - let gender; - if(req.query.gender) gender = (req.query.gender == 'female' ? genders.FEMALE : genders.MALE); +router.get('/divisions', async function(req, res, next) { + try{ + let gender; + if(req.query.gender) gender = (req.query.gender == 'female' ? genders.FEMALE : genders.MALE); - divisions.retrieve(req.query.sport, gender) - .then(data => res.json(data)); + const sportID = req.query.sport; + const data = await divisions.retrieve(sportID, gender); + res.json(data); + } catch(err) { + console.error("ERROR: " + err.message); + res.status(500).send("An error has occurred"); + } }) -router.get('/division', function(req, res, next) { - divisions.getFromID(req.query.division) - .then(data => res.json(data)); +router.get('/division', async function(req, res, next) { + try { + const divisionID = req.query.division; + const data = await divisions.getFromID(divisionID); + res.json(data); + } catch(err) { + console.error("ERROR: " + err.message); + res.status(500).send("An error has occurred"); + } }) -router.get('/teams', function(req, res, next) { - teams.retrieve(req.query.sport) - .then(data => res.json(data)); +router.get('/teams', async function(req, res, next) { + try { + const sportID = req.query.sport; + const data = await teams.retrieve(sportID); + res.json(data); + } catch(err) { + console.error("ERROR: " + err.message); + res.status(500).send("An error has occurred"); + } }) -router.get('/team', function(req, res, next) { - teams.getFromID(req.query.team) - .then(data => res.json(data)); +router.get('/team', async function(req, res, next) { + try { + const teamID = req.query.team; + const data = await teams.getFromID(teamID); + res.json(data); + } catch(err) { + console.error("ERROR: " + err.message); + res.status(500).send("An error has occurred"); + } }) -router.get('/games', function(req, res, next) { - const userID = req.user ? req.user[0] : null; - if(req.query.user) games.retrieveByUser(userID).then(data => res.json(data)); - else games.retrieve(req.query.team, req.query.division, req.query.season).then(data => res.json(data)); +router.get('/games', async function(req, res, next) { + try { + const userID = req.user ? req.user[0] : null; + if(req.query.user) { + const data = await games.retrieveByUser(userID); + res.json(data); + } else { + const seasonID = req.query.season; + const divisionID = req.query.division; + const teamID = req.query.team; + const data = await games.retrieve(teamID, divisionID, seasonID); + res.json(data); + } + } catch(err) { + console.error("ERROR: " + err.message); + res.status(500).send("An error has occurred"); + } }) -router.get('/game', function(req, res, next) { - games.getFromID(req.query.game) - .then(data => res.json(data)); +router.get('/game', async function(req, res, next) { + try { + const gameID = req.query.game; + const data = await games.getFromID(gameID); + res.json(data); + } catch(err) { + console.error("ERROR: " + err.message); + res.status(500).send("An error has occurred"); + } }) -router.get('/accounts', checkLoginStatus.admin, function(req, res, next) { - accounts.retrieveAll() - .then(data => res.json(data)); +router.get('/accounts', checkLoginStatus.admin, async function(req, res, next) { + try { + const data = await accounts.retrieveAll(); + res.json(data); + } catch(err) { + console.error("ERROR: " + err.message); + res.status(500).send("An error has occurred"); + } }) -router.get('/account', checkLoginStatus.user, function(req, res, next) { - const userIsAdmin = req.user[2]; - const loggedInAccountID = req.user[0]; - const requestedAccountID = req.query.account; - - if(!userIsAdmin && loggedInAccountID != requestedAccountID) { - res.status(403).send("ACCESS DENIED"); - } else { - accounts.getFromID(req.query.account) - .then(data => res.json(data)); +router.get('/account', checkLoginStatus.user, async function(req, res, next) { + try{ + const userIsAdmin = req.user[2]; + const loggedInAccountID = req.user[0]; + const requestedAccountID = req.query.account; + + if(!userIsAdmin && loggedInAccountID != requestedAccountID) { + res.status(403).send("ACCESS DENIED"); + } else { + const data = await accounts.getFromID(req.query.account); + res.json(data); + } + } catch(err) { + console.error("ERROR: " + err.message); + res.status(500).send("An error has occurred"); } }) From 292c4a703943b25b368fa02ac492c814f7e5e37e Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 16:17:54 -0700 Subject: [PATCH 176/255] Remove pregenerated users route --- app.js | 2 -- routes/users.js | 9 --------- 2 files changed, 11 deletions(-) delete mode 100644 routes/users.js diff --git a/app.js b/app.js index 9082389..4065b14 100644 --- a/app.js +++ b/app.js @@ -11,7 +11,6 @@ var flash = require('connect-flash'); var indexRouter = require('./routes/index'); -var usersRouter = require('./routes/users'); var dataRouter = require('./routes/data'); var manageRouter = require('./routes/manage'); var authRouter = require('./routes/auth'); @@ -49,7 +48,6 @@ app.use(cookieParser()); app.use(express.static(path.join(__dirname, 'public'))); app.use('/', indexRouter); -app.use('/users', usersRouter); app.use('/data', dataRouter); app.use('/manage', manageRouter); app.use('/auth', authRouter); diff --git a/routes/users.js b/routes/users.js deleted file mode 100644 index 623e430..0000000 --- a/routes/users.js +++ /dev/null @@ -1,9 +0,0 @@ -var express = require('express'); -var router = express.Router(); - -/* GET users listing. */ -router.get('/', function(req, res, next) { - res.send('respond with a resource'); -}); - -module.exports = router; From f9ac3f49cd72caca20c02376a0f3891155ee9464 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 16:29:03 -0700 Subject: [PATCH 177/255] Improve error management for games --- routes/manage.js | 71 +++++++++++++++++++++++----------------- views/manage/addgame.pug | 1 + 2 files changed, 42 insertions(+), 30 deletions(-) diff --git a/routes/manage.js b/routes/manage.js index 506a5f1..44abc09 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -21,39 +21,50 @@ router.get('/' ,checkLoginStatus.user, function(req, res, next) { router.get('/game', checkLoginStatus.user, function(req, res, next) { let title = req.query.game ? 'Edit Game' : 'Submit Score' - res.render('manage/addgame', { title, userLoggedIn: !!req.user }); + res.render('manage/addgame', { title, userLoggedIn: !!req.user, message: req.flash('error') }); }); -router.post('/game', checkLoginStatus.user, function(req, res, next) { - const seasonID = req.body['year']; - const sportID = req.body['sport']; - const gender = (req.body['gender'] == "female") ? genders.FEMALE : genders.MALE; - const divisionID = req.body['division']; - const date = req.body['date']; - const team1ID = req.body['team1']; - const team1Score = req.body['team1-score']; - const team2ID = req.body['team2']; - const team2Score = req.body['team2-score']; - const userID = req.user[0]; +router.post('/game', checkLoginStatus.user, async function(req, res, next) { + try { + const seasonID = req.body['year']; + const sportID = req.body['sport']; + const gender = (req.body['gender'] == "female") ? genders.FEMALE : genders.MALE; + const divisionID = req.body['division']; + const date = req.body['date']; + const team1ID = req.body['team1']; + const team1Score = req.body['team1-score']; + const team2ID = req.body['team2']; + const team2Score = req.body['team2-score']; + const userID = req.user[0]; + + const id = req.body['game']; + const remove = req.body['remove']; + + const loggedInUserID = req.user[0]; + const loggedInUserIsAdmin = req.user[2]; + + const game = id ? await games.getFromID(id) : null; - const id = req.body['game']; - const remove = req.body['remove']; - - const loggedInUserID = req.user[0]; - const loggedInUserIsAdmin = req.user[2]; - - games.getFromID(id) - .then(game => { - if(!loggedInUserIsAdmin && loggedInUserID != game.submitterID) { - res.status(403).send("ACCESS DENIED"); - } - else if(remove) games.remove(id) - .then(res.redirect("/manage")); - else if(id) games.edit(id, divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score) - .then(res.redirect('/manage')); - else games.add(divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score, userID) - .then(res.redirect("/")); - }); + if(!loggedInUserIsAdmin && game && loggedInUserID != game.submitterID) { + res.status(403).send("ACCESS DENIED"); + } + else if(remove) { + await games.remove(id); + res.redirect("/manage"); + } + else if(id) { + await games.edit(id, divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score); + res.redirect('/manage'); + } + else { + await games.add(divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score, userID); + res.redirect('/'); + } + } catch(err) { + console.error("ERROR: " + err.message); + req.flash("error", "An error has occurred."); + res.redirect('/manage/game'); + } }); router.get('/season', checkLoginStatus.admin, function(req, res, next) { diff --git a/views/manage/addgame.pug b/views/manage/addgame.pug index 61e8f61..3a44834 100644 --- a/views/manage/addgame.pug +++ b/views/manage/addgame.pug @@ -45,6 +45,7 @@ block content label Score span(class='form-section-input') input#team2-score-textbox(type="number", name="team2-score", value="0" disabled) + .error #{message} span(class='form-section') button#submit-button(type="submit" disabled) Submit span(class='form-section') From 51783c5dec58b9f689ef33fdc365292c06a73298 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 16:34:26 -0700 Subject: [PATCH 178/255] Add better error management for seasons --- routes/manage.js | 22 +++++++++++++++------- views/manage/addseason.pug | 1 + 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/routes/manage.js b/routes/manage.js index 44abc09..d343952 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -68,17 +68,25 @@ router.post('/game', checkLoginStatus.user, async function(req, res, next) { }); router.get('/season', checkLoginStatus.admin, function(req, res, next) { - res.render('manage/addseason', { title: 'Add Season', currentYear : (new Date()).getFullYear(), userLoggedIn: !!req.user }); + res.render('manage/addseason', { title: 'Add Season', currentYear : (new Date()).getFullYear(), userLoggedIn: !!req.user, message: req.flash('error') }); }); -router.post('/season', checkLoginStatus.admin, function(req, res, next) { - const year = req.body['year']; +router.post('/season', checkLoginStatus.admin, async function(req, res, next) { + try { + const year = req.body['year']; - const seasonID = req.body['season']; - const remove = req.body['remove']; + const seasonID = req.body['season']; + const remove = req.body['remove']; - if(remove) seasons.remove(seasonID).then(res.redirect('/manage')); - else seasons.add(year).then(res.redirect("/manage")); + if(remove) await seasons.remove(seasonID); + else await seasons.add(year); + + res.redirect('/manage'); + } catch(err) { + console.error("ERROR: " + err.message); + req.flash("error", "An error has occurred."); + res.redirect('/manage/season'); + } }); router.get('/sport', checkLoginStatus.admin, function(req, res, next) { diff --git a/views/manage/addseason.pug b/views/manage/addseason.pug index 27783b5..ba43698 100644 --- a/views/manage/addseason.pug +++ b/views/manage/addseason.pug @@ -10,6 +10,7 @@ block content label Ending year span(class='form-section-input') input#season-textbox(type="number", name="year", value=currentYear) + .error #{message} span(class='form-section') button#submit-button(type="submit" disabled) Submit From 3f6251f5bbbf3b1e5d3e4f80e29c5ca7c0c18cb5 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 16:38:46 -0700 Subject: [PATCH 179/255] Improve error handling for sports --- routes/manage.js | 24 ++++++++++++++++-------- views/manage/addsport.pug | 1 + 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/routes/manage.js b/routes/manage.js index d343952..4558cfd 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -90,17 +90,25 @@ router.post('/season', checkLoginStatus.admin, async function(req, res, next) { }); router.get('/sport', checkLoginStatus.admin, function(req, res, next) { - res.render('manage/addsport', { title: 'Add Sport', userLoggedIn: !!req.user }); + res.render('manage/addsport', { title: 'Add Sport', userLoggedIn: !!req.user, message: req.flash('error') }); }); -router.post('/sport', checkLoginStatus.admin, function(req, res, next) { - const name = req.body['name']; - const id = req.body['sport']; - const remove = req.body['remove']; +router.post('/sport', checkLoginStatus.admin, async function(req, res, next) { + try { + const name = req.body['name']; + const id = req.body['sport']; + const remove = req.body['remove']; - if(remove) sports.remove(id).then(res.redirect('/manage')); - else if(id) sports.rename(id, name).then(res.redirect('/manage')); - else sports.add(name).then(res.redirect('/manage')); + if(remove) await sports.remove(id); + else if(id) await sports.rename(id, name); + else await sports.add(name); + + res.redirect('/manage'); + } catch(err) { + console.error("ERROR: " + err.message); + req.flash("error", "An error has occurred."); + res.redirect('/manage/sport'); + } }); router.get('/division', checkLoginStatus.admin, function(req, res, next) { diff --git a/views/manage/addsport.pug b/views/manage/addsport.pug index 067b9ac..b4caa50 100644 --- a/views/manage/addsport.pug +++ b/views/manage/addsport.pug @@ -10,6 +10,7 @@ block content label Sport name span(class='form-section-input') input#name-textbox(type="text" name="name" disabled) + .error #{message} span(class='form-section') button#submit-button(type="submit" disabled) Submit span(class='form-section') From ad361adab1e9605656cd12876386498b2cb6781e Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 16:46:46 -0700 Subject: [PATCH 180/255] Improve error handling for divisions --- routes/manage.js | 45 +++++++++++++++++++----------------- views/manage/adddivision.pug | 1 + 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/routes/manage.js b/routes/manage.js index 4558cfd..9b5fe6e 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -114,31 +114,34 @@ router.post('/sport', checkLoginStatus.admin, async function(req, res, next) { router.get('/division', checkLoginStatus.admin, function(req, res, next) { let title = req.query.division ? 'Edit Division' : 'Add Division' - res.render('manage/adddivision', { title, userLoggedIn: !!req.user }); + res.render('manage/adddivision', { title, userLoggedIn: !!req.user, message: req.flash('error') }); }); -router.post('/division', checkLoginStatus.admin, function(req, res, next) { - const name = req.body['name']; - const sport = req.body['sport']; - const genderName = req.body['gender']; - - const id = req.body['division']; - const remove = req.body['remove']; - - - if(remove) divisions.remove(id).then(res.redirect('/manage')); - else if(id) divisions.rename(id, name).then(res.redirect('/manage')); - else { - if(genderName == "both") { - divisions.add(name, genders.FEMALE, sport) - .then(divisions.add(name, genders.MALE, sport) - .then(res.redirect("/manage"))); - } +router.post('/division', checkLoginStatus.admin, async function(req, res, next) { + try { + const name = req.body['name']; + const sport = req.body['sport']; + const genderName = req.body['gender']; + + const id = req.body['division']; + const remove = req.body['remove']; + + if(remove) await divisions.remove(id); + else if(id) await divisions.rename(id, name); else { - const gender = (genderName == "female") ? genders.FEMALE : genders.MALE; - divisions.add(name, gender, sport) - .then(res.redirect("/manage")); + if(genderName == 'both') { + await divisions.add(name, genders.FEMALE, sport); + await divisions.add(name, genders.MALE, sport); + } else { + const gender = (genderName == "female") ? genders.FEMALE : genders.MALE; + await divisions.add(name, gender, sport); + } } + res.redirect('/manage'); + } catch(err) { + console.error("ERROR: " + err.message); + req.flash("error", "An error has occurred."); + res.redirect('/manage/division'); } }); diff --git a/views/manage/adddivision.pug b/views/manage/adddivision.pug index 10e7281..1a17aaf 100644 --- a/views/manage/adddivision.pug +++ b/views/manage/adddivision.pug @@ -21,6 +21,7 @@ block content label Division name span(class='form-section-input') input#name-textbox(type="text", name="name" disabled) + .error #{message} span(class='form-section') button#submit-button(type="submit" disabled) Submit span(class='form-section') From 63b6ef2c2dd8493a426b16af19351a573644a7a3 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 16:59:51 -0700 Subject: [PATCH 181/255] Improve redirection when error occurs --- public/scripts/manage/sport.js | 3 --- routes/manage.js | 37 ++++++++++++++++++---------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/public/scripts/manage/sport.js b/public/scripts/manage/sport.js index c30fbce..20ad2cb 100644 --- a/public/scripts/manage/sport.js +++ b/public/scripts/manage/sport.js @@ -1,7 +1,6 @@ import * as Data from "../data.js"; -const mainHeader = document.getElementById('main-header'); const nameTextbox = document.getElementById('name-textbox'); const submitButton = document.getElementById('submit-button'); const deleteButton = document.getElementById('delete-button'); @@ -12,8 +11,6 @@ async function initializeForm() { let params = new URLSearchParams(location.search); let sportID = params.get('sport') if(sportID) { - mainHeader.textContent = "Edit Sport"; - const sportName = await Data.getSportName(sportID); nameTextbox.value = sportName; diff --git a/routes/manage.js b/routes/manage.js index 9b5fe6e..e16f9ff 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -25,6 +25,9 @@ router.get('/game', checkLoginStatus.user, function(req, res, next) { }); router.post('/game', checkLoginStatus.user, async function(req, res, next) { + const id = req.body['game']; + const remove = req.body['remove']; + try { const seasonID = req.body['year']; const sportID = req.body['sport']; @@ -37,9 +40,6 @@ router.post('/game', checkLoginStatus.user, async function(req, res, next) { const team2Score = req.body['team2-score']; const userID = req.user[0]; - const id = req.body['game']; - const remove = req.body['remove']; - const loggedInUserID = req.user[0]; const loggedInUserIsAdmin = req.user[2]; @@ -63,7 +63,7 @@ router.post('/game', checkLoginStatus.user, async function(req, res, next) { } catch(err) { console.error("ERROR: " + err.message); req.flash("error", "An error has occurred."); - res.redirect('/manage/game'); + res.redirect('/manage/game' + (id ? `?game=${id}` : '')); } }); @@ -72,12 +72,12 @@ router.get('/season', checkLoginStatus.admin, function(req, res, next) { }); router.post('/season', checkLoginStatus.admin, async function(req, res, next) { + const seasonID = req.body['season']; + const remove = req.body['remove']; + try { const year = req.body['year']; - const seasonID = req.body['season']; - const remove = req.body['remove']; - if(remove) await seasons.remove(seasonID); else await seasons.add(year); @@ -85,19 +85,22 @@ router.post('/season', checkLoginStatus.admin, async function(req, res, next) { } catch(err) { console.error("ERROR: " + err.message); req.flash("error", "An error has occurred."); - res.redirect('/manage/season'); + res.redirect('/manage/season' + (seasonID ? `?season=${seasonID}` : '')); } }); router.get('/sport', checkLoginStatus.admin, function(req, res, next) { - res.render('manage/addsport', { title: 'Add Sport', userLoggedIn: !!req.user, message: req.flash('error') }); + let title = req.query.sport ? 'Edit Sport' : 'Add Sport'; + + res.render('manage/addsport', { title, userLoggedIn: !!req.user, message: req.flash('error') }); }); router.post('/sport', checkLoginStatus.admin, async function(req, res, next) { + const id = req.body['sport']; + const remove = req.body['remove']; + try { const name = req.body['name']; - const id = req.body['sport']; - const remove = req.body['remove']; if(remove) await sports.remove(id); else if(id) await sports.rename(id, name); @@ -107,7 +110,7 @@ router.post('/sport', checkLoginStatus.admin, async function(req, res, next) { } catch(err) { console.error("ERROR: " + err.message); req.flash("error", "An error has occurred."); - res.redirect('/manage/sport'); + res.redirect('/manage/sport' + (id ? `?sport=${id}` : '')); } }); @@ -118,14 +121,14 @@ router.get('/division', checkLoginStatus.admin, function(req, res, next) { }); router.post('/division', checkLoginStatus.admin, async function(req, res, next) { + const id = req.body['division']; + const remove = req.body['remove']; + try { const name = req.body['name']; const sport = req.body['sport']; const genderName = req.body['gender']; - - const id = req.body['division']; - const remove = req.body['remove']; - + if(remove) await divisions.remove(id); else if(id) await divisions.rename(id, name); else { @@ -141,7 +144,7 @@ router.post('/division', checkLoginStatus.admin, async function(req, res, next) } catch(err) { console.error("ERROR: " + err.message); req.flash("error", "An error has occurred."); - res.redirect('/manage/division'); + res.redirect('/manage/division' + (id ? `?division=${id}` : '')); } }); From 6c174f3e02334f486ec5318c2af3a417ffc1026f Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 17:03:09 -0700 Subject: [PATCH 182/255] Improve error handling for teams --- routes/manage.js | 22 ++++++++++++++-------- views/manage/addteam.pug | 1 + 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/routes/manage.js b/routes/manage.js index e16f9ff..5b979fa 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -151,19 +151,25 @@ router.post('/division', checkLoginStatus.admin, async function(req, res, next) router.get('/team', checkLoginStatus.admin, function(req, res, next) { let title = req.query.team ? 'Edit Team' : 'Add Team' - res.render('manage/addteam', { title, userLoggedIn: !!req.user }); + res.render('manage/addteam', { title, userLoggedIn: !!req.user, message: req.flash('error') }); }); -router.post('/team', checkLoginStatus.admin, function(req, res, next) { - const name = req.body['name']; - const sport = req.body['sport']; - +router.post('/team', checkLoginStatus.admin, async function(req, res, next) { const id = req.body['team']; const remove = req.body['remove']; - if(remove) teams.remove(id).then(res.redirect('/manage')); - else if(id) teams.rename(id, name).then(res.redirect('/manage')); - else teams.add(name, sport).then(res.redirect("/manage")); + try { + const name = req.body['name']; + const sport = req.body['sport']; + + if(remove) teams.remove(id).then(res.redirect('/manage')); + else if(id) teams.rename(id, name).then(res.redirect('/manage')); + else teams.add(name, sport).then(res.redirect("/manage")); + } catch(err) { + console.error("ERROR: " + err.message); + req.flash("error", "An error has occurred."); + res.redirect('/manage/team' + (id ? `?team=${id}` : '')); + } }); router.get('/account', checkLoginStatus.user, (req, res, next) => { diff --git a/views/manage/addteam.pug b/views/manage/addteam.pug index 9f7262c..78746a6 100644 --- a/views/manage/addteam.pug +++ b/views/manage/addteam.pug @@ -14,6 +14,7 @@ block content label Team name span(class='form-section-input') input#name-textbox(type="text", name="name" disabled) + .error #{message} span(class='form-section') button#submit-button(type="submit" disabled) Submit span(class='form-section') From 7132273e9ec1cea34f94026fb7dbde272b34a125 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 17:08:13 -0700 Subject: [PATCH 183/255] Improve redirect for failure editing accounts --- routes/manage.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/routes/manage.js b/routes/manage.js index 5b979fa..8432ff8 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -214,7 +214,9 @@ router.post('/account', checkLoginStatus.user, async function(req, res, next) { catch (err) { console.error("ERROR: " + err.message); req.flash("error", "An error has occurred."); - res.redirect('/manage/account'); + let URL = '/manage/account'; + if(loggedInAccountIsAdmin && accountID) URL += `?account=${accountID}`; + res.redirect(URL); } } }); From 15e7163e394c18949b84f761e6beecd3208fbf9a Mon Sep 17 00:00:00 2001 From: Sudoer777 Date: Sat, 27 Nov 2021 01:06:16 +0000 Subject: [PATCH 184/255] Update .node-version --- .node-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.node-version b/.node-version index 2f76972..58a4133 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -8.11.1 +16.13.0 From a68c0eeb54e883a695943a21de05cb008f851b0f Mon Sep 17 00:00:00 2001 From: Sudoer777 Date: Sat, 27 Nov 2021 01:08:02 +0000 Subject: [PATCH 185/255] Update node version in dockerfile --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 43f0bd8..135b43f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:8.11-alpine +FROM node:16.13.0-alpine3.12 WORKDIR /usr/src/app From b5a41dda8b44e375975ae2a889761bc33adefc8a Mon Sep 17 00:00:00 2001 From: Sudoer777 Date: Sat, 27 Nov 2021 01:09:16 +0000 Subject: [PATCH 186/255] Update package.json --- package.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index c3c687d..7b97c2d 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,9 @@ { - "name": "demo", - "version": "0.0.0", + "name": "score-tracker", + "version": "1.0.0-pre", "private": true, "scripts": { "start": "node ./bin/www", - "test": "mocha" }, "dependencies": { "async": "^3.2.2", From 4449ba28de38e63e0e1ef4fea8cc674754802b7f Mon Sep 17 00:00:00 2001 From: Sudoer777 Date: Sat, 27 Nov 2021 01:41:11 +0000 Subject: [PATCH 187/255] Fix error with package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7b97c2d..3179dbc 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "1.0.0-pre", "private": true, "scripts": { - "start": "node ./bin/www", + "start": "node ./bin/www" }, "dependencies": { "async": "^3.2.2", From 64c7af105ae6e6939a4c0a7dd1efe1fc5251eb00 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 18:49:20 -0700 Subject: [PATCH 188/255] Update packages --- package-lock.json | 3088 +++++++++++++++++++++++++++------------------ package.json | 4 +- 2 files changed, 1893 insertions(+), 1199 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1903fa7..fb53dd1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { - "name": "demo", - "version": "0.0.0", + "name": "score-tracker", + "version": "1.0.0-pre", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "demo", - "version": "0.0.0", + "name": "score-tracker", + "version": "1.0.0-pre", "dependencies": { "async": "^3.2.2", "bcrypt": "^5.0.1", @@ -22,13 +22,44 @@ "passport": "^0.5.0", "passport-local": "^1.0.0", "pg": "^8.7.1", - "pug": "2.0.0-beta11" + "pug": "^3.0.2" }, "devDependencies": { - "mocha": "^5.1.1", + "mocha": "^9.1.3", "supertest": "^3.0.0" } }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.16.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.4.tgz", + "integrity": "sha512-6V0qdPUaiVHH3RtZeLIsc+6pDhbYzHR8ogA8w+f+Wc77DuXto19g2QUwveINoS34Uw+W8/hQDGJCx+i4n7xcng==", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", + "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.15.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@mapbox/node-pre-gyp": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.7.tgz", @@ -48,18 +79,11 @@ "node-pre-gyp": "bin/node-pre-gyp" } }, - "node_modules/@types/babel-types": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/@types/babel-types/-/babel-types-7.0.4.tgz", - "integrity": "sha512-WiZhq3SVJHFRgRYLXvpf65XnV6ipVHhnNaNvE8yCimejrGglkg38kEj0JcizqwSHxmPSjcTlig/6JouxLGEhGw==" - }, - "node_modules/@types/babylon": { - "version": "6.16.3", - "resolved": "https://registry.npmjs.org/@types/babylon/-/babylon-6.16.3.tgz", - "integrity": "sha512-lyJ8sW1PbY3uwuvpOBZ9zMYKshMnQpXmeDHh8dj9j2nJm/xrW0FgB5gLSYOArj5X0IfaXnmhFoJnhS4KbqIMug==", - "dependencies": { - "@types/babel-types": "*" - } + "node_modules/@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true }, "node_modules/abbrev": { "version": "1.1.1", @@ -67,40 +91,21 @@ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, "node_modules/accepts": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", - "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", "dependencies": { - "mime-types": "~2.1.18", - "negotiator": "0.6.1" + "mime-types": "~2.1.24", + "negotiator": "0.6.2" }, "engines": { "node": ">= 0.6" } }, "node_modules/acorn": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", - "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-globals": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-3.1.0.tgz", - "integrity": "sha1-/YJw9x+7SZawBPqIDuXUZXOnMb8=", - "dependencies": { - "acorn": "^4.0.4" - } - }, - "node_modules/acorn-globals/node_modules/acorn": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", - "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", "bin": { "acorn": "bin/acorn" }, @@ -140,25 +145,13 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, - "node_modules/align-text": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", - "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", - "dependencies": { - "kind-of": "^3.0.2", - "longest": "^1.0.1", - "repeat-string": "^1.5.2" - }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", - "engines": { - "node": ">=0.4.2" + "node": ">=6" } }, "node_modules/ansi-regex": { @@ -169,6 +162,34 @@ "node": ">=8" } }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/aproba": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", @@ -186,18 +207,11 @@ "node": ">=10" } }, - "node_modules/are-we-there-yet/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true }, "node_modules/array-flatten": { "version": "1.1.1", @@ -209,6 +223,11 @@ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" }, + "node_modules/assert-never": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/assert-never/-/assert-never-1.2.1.tgz", + "integrity": "sha512-TaTivMB6pYI1kXwrFlEhLeGfOqoDNdTxjCdwRfFFkEA30Eu+k48W34nlok2EYWJfFFzqaEmichdNM7th6M5HNw==" + }, "node_modules/async": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/async/-/async-3.2.2.tgz", @@ -220,45 +239,28 @@ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", "dev": true }, - "node_modules/babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "node_modules/babel-walk": { + "version": "3.0.0-canary-5", + "resolved": "https://registry.npmjs.org/babel-walk/-/babel-walk-3.0.0-canary-5.tgz", + "integrity": "sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==", "dependencies": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - } - }, - "node_modules/babel-types": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", - "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", - "dependencies": { - "babel-runtime": "^6.26.0", - "esutils": "^2.0.2", - "lodash": "^4.17.4", - "to-fast-properties": "^1.0.3" - } - }, - "node_modules/babylon": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", - "bin": { - "babylon": "bin/babylon.js" + "@babel/types": "^7.9.6" + }, + "engines": { + "node": ">= 10.0.0" } }, "node_modules/balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/basic-auth": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.0.tgz", - "integrity": "sha1-AV2z81PgLlY3d1X5YnQuiYHnu7o=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", "dependencies": { - "safe-buffer": "5.1.1" + "safe-buffer": "5.1.2" }, "engines": { "node": ">= 0.8" @@ -277,21 +279,30 @@ "node": ">= 10.0.0" } }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/body-parser": { - "version": "1.18.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", - "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", + "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", "dependencies": { "bytes": "3.0.0", "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "~1.1.1", - "http-errors": "~1.6.2", - "iconv-lite": "0.4.19", + "depd": "~1.1.2", + "http-errors": "~1.6.3", + "iconv-lite": "0.4.23", "on-finished": "~2.3.0", - "qs": "6.5.1", - "raw-body": "2.3.2", - "type-is": "~1.6.15" + "qs": "6.5.2", + "raw-body": "2.3.3", + "type-is": "~1.6.16" }, "engines": { "node": ">= 0.8" @@ -306,6 +317,18 @@ "concat-map": "0.0.1" } }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/browser-stdout": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", @@ -328,24 +351,56 @@ "node": ">= 0.8" } }, - "node_modules/camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", - "engines": { - "node": ">=0.10.0" + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/center-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", - "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "node_modules/camelcase": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", + "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "dependencies": { - "align-text": "^0.1.3", - "lazy-cache": "^1.0.3" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, "node_modules/character-parser": { @@ -356,6 +411,27 @@ "is-regex": "^1.0.3" } }, + "node_modules/chokidar": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, "node_modules/chownr": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", @@ -364,31 +440,35 @@ "node": ">=10" } }, - "node_modules/clean-css": { - "version": "3.4.28", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-3.4.28.tgz", - "integrity": "sha1-vxlF6C/ICPVWlebd6uwBQA79A/8=", + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, "dependencies": { - "commander": "2.8.x", - "source-map": "0.4.x" - }, - "bin": { - "cleancss": "bin/cleancss" - }, - "engines": { - "node": ">=0.10.0" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" } }, - "node_modules/cliui": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "dependencies": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", - "wordwrap": "0.0.2" + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "node_modules/color-support": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", @@ -398,9 +478,9 @@ } }, "node_modules/combined-stream": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", - "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, "dependencies": { "delayed-stream": "~1.0.0" @@ -409,21 +489,10 @@ "node": ">= 0.8" } }, - "node_modules/commander": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", - "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", - "dependencies": { - "graceful-readlink": ">= 1.0.0" - }, - "engines": { - "node": ">= 0.6.x" - } - }, "node_modules/component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", "dev": true }, "node_modules/concat-map": { @@ -445,14 +514,12 @@ "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" }, "node_modules/constantinople": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-3.1.2.tgz", - "integrity": "sha512-yePcBqEFhLOqSBtwYOGGS1exHo/s1xjekXiinh4itpNQGCu4KA1euPh1fg07N2wMITZXQkBz75Ntdt1ctGZouw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-4.0.1.tgz", + "integrity": "sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==", "dependencies": { - "@types/babel-types": "^7.0.0", - "@types/babylon": "^6.16.2", - "babel-types": "^6.26.0", - "babylon": "^6.18.0" + "@babel/parser": "^7.6.0", + "@babel/types": "^7.6.1" } }, "node_modules/content-disposition": { @@ -472,19 +539,19 @@ } }, "node_modules/cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", "engines": { "node": ">= 0.6" } }, "node_modules/cookie-parser": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.3.tgz", - "integrity": "sha1-D+MfoZ0AC5X0qt8fU/3CuKIDuqU=", + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", + "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", "dependencies": { - "cookie": "0.3.1", + "cookie": "0.4.1", "cookie-signature": "1.0.6" }, "engines": { @@ -497,21 +564,15 @@ "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, "node_modules/cookiejar": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", - "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz", + "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==", "dev": true }, - "node_modules/core-js": { - "version": "2.5.7", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", - "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==", - "deprecated": "core-js@<3.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Please, upgrade your dependencies to the actual version of core-js." - }, "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true }, "node_modules/debug": { @@ -523,11 +584,15 @@ } }, "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/delayed-stream": { @@ -569,9 +634,9 @@ } }, "node_modules/diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", "dev": true, "engines": { "node": ">=0.3.1" @@ -608,26 +673,30 @@ "node": ">= 0.8" } }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" }, "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", - "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/etag": { @@ -639,13 +708,13 @@ } }, "node_modules/express": { - "version": "4.16.3", - "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz", - "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=", + "version": "4.16.4", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", + "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", "dependencies": { "accepts": "~1.3.5", "array-flatten": "1.1.1", - "body-parser": "1.18.2", + "body-parser": "1.18.3", "content-disposition": "0.5.2", "content-type": "~1.0.4", "cookie": "0.3.1", @@ -662,10 +731,10 @@ "on-finished": "~2.3.0", "parseurl": "~1.3.2", "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.3", - "qs": "6.5.1", + "proxy-addr": "~2.0.4", + "qs": "6.5.2", "range-parser": "~1.2.0", - "safe-buffer": "5.1.1", + "safe-buffer": "5.1.2", "send": "0.16.2", "serve-static": "1.13.2", "setprototypeof": "1.1.0", @@ -696,14 +765,6 @@ "node": ">= 0.8.0" } }, - "node_modules/express-session/node_modules/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/express-session/node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -731,12 +792,32 @@ } ] }, + "node_modules/express/node_modules/cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "dev": true }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/finalhandler": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", @@ -754,14 +835,39 @@ "node": ">= 0.8" } }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, "node_modules/form-data": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", - "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", "dev": true, "dependencies": { "asynckit": "^0.4.0", - "combined-stream": "1.0.6", + "combined-stream": "^1.0.6", "mime-types": "^2.1.12" }, "engines": { @@ -769,16 +875,19 @@ } }, "node_modules/formidable": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.1.tgz", - "integrity": "sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg==", - "deprecated": "Please upgrade to the upcoming v2, currently (until end of February) install using formidable@canary!", - "dev": true + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz", + "integrity": "sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==", + "deprecated": "Please upgrade to latest, formidable@v2 or formidable@v3! Check these notes: https://bit.ly/2ZEqIau", + "dev": true, + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" + } }, "node_modules/forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "engines": { "node": ">= 0.6" } @@ -807,6 +916,20 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -831,10 +954,32 @@ "node": "^12.13.0 || ^14.15.0 || >=16" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -846,12 +991,22 @@ }, "engines": { "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/graceful-readlink": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", - "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } }, "node_modules/growl": { "version": "1.10.5", @@ -874,12 +1029,37 @@ } }, "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "engines": { - "node": ">=4" + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/has-unicode": { @@ -888,9 +1068,9 @@ "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" }, "node_modules/he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true, "bin": { "he": "bin/he" @@ -944,9 +1124,12 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/iconv-lite": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", - "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==", + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, "engines": { "node": ">=0.10.0" } @@ -966,36 +1149,52 @@ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, "node_modules/ipaddr.js": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", - "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "engines": { "node": ">= 0.10" } }, - "node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "node_modules/is-expression": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-3.0.0.tgz", - "integrity": "sha1-Oayqa+f9HzRx3ELHQW5hwkMXrJ8=", + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, "dependencies": { - "acorn": "~4.0.2", - "object-assign": "^4.0.1" - } - }, - "node_modules/is-expression/node_modules/acorn": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", - "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", - "bin": { - "acorn": "bin/acorn" + "binary-extensions": "^2.0.0" }, "engines": { - "node": ">=0.4.0" + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", + "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-expression": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-4.0.0.tgz", + "integrity": "sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==", + "dependencies": { + "acorn": "^7.1.1", + "object-assign": "^4.1.1" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, "node_modules/is-fullwidth-code-point": { @@ -1006,20 +1205,66 @@ "node": ">=8" } }, - "node_modules/is-promise": { + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" }, "node_modules/is-regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "dependencies": { - "has": "^1.0.1" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/isarray": { @@ -1028,11 +1273,29 @@ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, "node_modules/js-stringify": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", "integrity": "sha1-Fzb939lyTyijaCrcYjCufk6Weds=" }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/jstransformer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", @@ -1042,36 +1305,35 @@ "promise": "^7.0.1" } }, - "node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, "dependencies": { - "is-buffer": "^1.1.5" + "p-locate": "^5.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lazy-cache": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", - "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/lodash": { - "version": "4.17.10", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", - "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" - }, - "node_modules/longest": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", - "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", - "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/lru-cache": { @@ -1137,19 +1399,19 @@ } }, "node_modules/mime-db": { - "version": "1.35.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.35.0.tgz", - "integrity": "sha512-JWT/IcCTsB0Io3AhWUMjRqucrHSPsSf2xKLaRldJVULioggvkJvggZ3VXNNSRkCddE6D+BUI4HEIZIA2OjwIvg==", + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", + "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.19", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.19.tgz", - "integrity": "sha512-P1tKYHVSZ6uFo26mtnve4HQFE3koh1UWVkp8YUC+ESBHe945xWSoXuHHiGarDqcEZ+whpCDnlNw5LON0kLo+sw==", + "version": "2.1.34", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", + "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", "dependencies": { - "mime-db": "~1.35.0" + "mime-db": "1.51.0" }, "engines": { "node": ">= 0.6" @@ -1166,12 +1428,6 @@ "node": "*" } }, - "node_modules/minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - }, "node_modules/minipass": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.5.tgz", @@ -1195,68 +1451,86 @@ "node": ">= 8" } }, - "node_modules/mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)", - "dev": true, - "dependencies": { - "minimist": "0.0.8" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, "node_modules/mocha": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", - "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.3.tgz", + "integrity": "sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==", "dev": true, "dependencies": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "commander": "2.15.1", - "debug": "3.1.0", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "glob": "7.1.2", + "chokidar": "3.5.2", + "debug": "4.3.2", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.7", "growl": "1.10.5", - "he": "1.1.1", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", "minimatch": "3.0.4", - "mkdirp": "0.5.1", - "supports-color": "5.4.0" + "ms": "2.1.3", + "nanoid": "3.1.25", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "workerpool": "6.1.5", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" }, "bin": { "_mocha": "bin/_mocha", "mocha": "bin/mocha" }, "engines": { - "node": ">= 4.0.0" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" } }, - "node_modules/mocha/node_modules/commander": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", - "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", - "dev": true - }, "node_modules/mocha/node_modules/debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, "dependencies": { - "ms": "2.0.0" + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, + "node_modules/mocha/node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, "node_modules/morgan": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.0.tgz", - "integrity": "sha1-0B+mxlhZt2/PMbPLU6OCGjEdgFE=", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", + "integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==", "dependencies": { "basic-auth": "~2.0.0", "debug": "2.6.9", - "depd": "~1.1.1", + "depd": "~1.1.2", "on-finished": "~2.3.0", "on-headers": "~1.0.1" }, @@ -1269,10 +1543,22 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, + "node_modules/nanoid": { + "version": "3.1.25", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", + "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/negotiator": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", - "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", "engines": { "node": ">= 0.6" } @@ -1294,9 +1580,9 @@ } }, "node_modules/nodemailer": { - "version": "6.6.5", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.6.5.tgz", - "integrity": "sha512-C/v856DBijUzHcHIgGpQoTrfsH3suKIRAGliIzCstatM2cAa+MYX3LuyCrABiO/cdJTxgBBHXxV1ztiqUwst5A==", + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.7.2.tgz", + "integrity": "sha512-Dz7zVwlef4k5R71fdmxwR8Q39fiboGbu3xgswkzGwczUfjp873rVxt1O46+Fh0j1ORnAC6L9+heI8uUpO6DT7Q==", "engines": { "node": ">=6.0.0" } @@ -1315,6 +1601,15 @@ "node": ">=6" } }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/npmlog": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.0.tgz", @@ -1364,6 +1659,36 @@ "wrappy": "1" } }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/packet-reader": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", @@ -1412,6 +1737,15 @@ "node": ">= 0.4.0" } }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -1421,9 +1755,9 @@ } }, "node_modules/path-parse": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", - "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=" + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "node_modules/path-to-regexp": { "version": "0.1.7", @@ -1509,6 +1843,18 @@ "split2": "^3.1.1" } }, + "node_modules/picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/postgres-array": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", @@ -1545,9 +1891,9 @@ } }, "node_modules/process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, "node_modules/promise": { @@ -1559,135 +1905,133 @@ } }, "node_modules/proxy-addr": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", - "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "dependencies": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.8.0" + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" }, "engines": { "node": ">= 0.10" } }, "node_modules/pug": { - "version": "2.0.0-beta11", - "resolved": "https://registry.npmjs.org/pug/-/pug-2.0.0-beta11.tgz", - "integrity": "sha1-Favmr1AEx+LPRhPksnRlyVRrXwE=", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pug/-/pug-3.0.2.tgz", + "integrity": "sha512-bp0I/hiK1D1vChHh6EfDxtndHji55XP/ZJKwsRqrz6lRia6ZC2OZbdAymlxdVFwd1L70ebrVJw4/eZ79skrIaw==", "dependencies": { - "pug-code-gen": "^1.1.1", - "pug-filters": "^2.1.1", - "pug-lexer": "^3.0.0", - "pug-linker": "^2.0.2", - "pug-load": "^2.0.5", - "pug-parser": "^2.0.2", - "pug-runtime": "^2.0.3", - "pug-strip-comments": "^1.0.2" + "pug-code-gen": "^3.0.2", + "pug-filters": "^4.0.0", + "pug-lexer": "^5.0.1", + "pug-linker": "^4.0.0", + "pug-load": "^3.0.0", + "pug-parser": "^6.0.0", + "pug-runtime": "^3.0.1", + "pug-strip-comments": "^2.0.0" } }, "node_modules/pug-attrs": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-2.0.3.tgz", - "integrity": "sha1-owlflw5kFR972tlX7vVftdeQXRU=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-3.0.0.tgz", + "integrity": "sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==", "dependencies": { - "constantinople": "^3.0.1", - "js-stringify": "^1.0.1", - "pug-runtime": "^2.0.4" + "constantinople": "^4.0.1", + "js-stringify": "^1.0.2", + "pug-runtime": "^3.0.0" } }, "node_modules/pug-code-gen": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-1.1.1.tgz", - "integrity": "sha1-HPcnRO8qA56uajNAyqoRBYcSWOg=", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-3.0.2.tgz", + "integrity": "sha512-nJMhW16MbiGRiyR4miDTQMRWDgKplnHyeLvioEJYbk1RsPI3FuA3saEP8uwnTb2nTJEKBU90NFVWJBk4OU5qyg==", "dependencies": { - "constantinople": "^3.0.1", + "constantinople": "^4.0.1", "doctypes": "^1.1.0", - "js-stringify": "^1.0.1", - "pug-attrs": "^2.0.2", - "pug-error": "^1.3.2", - "pug-runtime": "^2.0.3", - "void-elements": "^2.0.1", - "with": "^5.0.0" + "js-stringify": "^1.0.2", + "pug-attrs": "^3.0.0", + "pug-error": "^2.0.0", + "pug-runtime": "^3.0.0", + "void-elements": "^3.1.0", + "with": "^7.0.0" } }, "node_modules/pug-error": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-1.3.2.tgz", - "integrity": "sha1-U659nSm7A89WRJOgJhCfVMR/XyY=" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-2.0.0.tgz", + "integrity": "sha512-sjiUsi9M4RAGHktC1drQfCr5C5eriu24Lfbt4s+7SykztEOwVZtbFk1RRq0tzLxcMxMYTBR+zMQaG07J/btayQ==" }, "node_modules/pug-filters": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-2.1.5.tgz", - "integrity": "sha512-xkw71KtrC4sxleKiq+cUlQzsiLn8pM5+vCgkChW2E6oNOzaqTSIBKIQ5cl4oheuDzvJYCTSYzRaVinMUrV4YLQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-4.0.0.tgz", + "integrity": "sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A==", "dependencies": { - "clean-css": "^3.3.0", - "constantinople": "^3.0.1", + "constantinople": "^4.0.1", "jstransformer": "1.0.0", - "pug-error": "^1.3.2", - "pug-walk": "^1.1.5", - "resolve": "^1.1.6", - "uglify-js": "^2.6.1" + "pug-error": "^2.0.0", + "pug-walk": "^2.0.0", + "resolve": "^1.15.1" } }, "node_modules/pug-lexer": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-3.1.0.tgz", - "integrity": "sha1-/QhzdtSmdbT1n4/vQiiDQ06VgaI=", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-5.0.1.tgz", + "integrity": "sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w==", "dependencies": { - "character-parser": "^2.1.1", - "is-expression": "^3.0.0", - "pug-error": "^1.3.2" + "character-parser": "^2.2.0", + "is-expression": "^4.0.0", + "pug-error": "^2.0.0" } }, "node_modules/pug-linker": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-2.0.3.tgz", - "integrity": "sha1-szH/olc33eacEntWwQ/xf652bco=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-4.0.0.tgz", + "integrity": "sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw==", "dependencies": { - "pug-error": "^1.3.2", - "pug-walk": "^1.1.2" + "pug-error": "^2.0.0", + "pug-walk": "^2.0.0" } }, "node_modules/pug-load": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-2.0.11.tgz", - "integrity": "sha1-5kjlftET/iwfRdV4WOorrWvAFSc=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-3.0.0.tgz", + "integrity": "sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ==", "dependencies": { - "object-assign": "^4.1.0", - "pug-walk": "^1.1.7" + "object-assign": "^4.1.1", + "pug-walk": "^2.0.0" } }, "node_modules/pug-parser": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-2.0.2.tgz", - "integrity": "sha1-U6aAz9BQOdywwn0CkJS8SnkmibA=", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-6.0.0.tgz", + "integrity": "sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw==", "dependencies": { - "pug-error": "^1.3.2", - "token-stream": "0.0.1" + "pug-error": "^2.0.0", + "token-stream": "1.0.0" } }, "node_modules/pug-runtime": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-2.0.4.tgz", - "integrity": "sha1-4XjhvaaKsujArPybztLFT9iM61g=" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-3.0.1.tgz", + "integrity": "sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg==" }, "node_modules/pug-strip-comments": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-1.0.3.tgz", - "integrity": "sha1-8VWVkiBu3G+FMQ2s9K+0igJa9Z8=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-2.0.0.tgz", + "integrity": "sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ==", "dependencies": { - "pug-error": "^1.3.2" + "pug-error": "^2.0.0" } }, "node_modules/pug-walk": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-1.1.7.tgz", - "integrity": "sha1-wA1cUSi6xYBr7BXSt+fNq+QlMfM=" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-2.0.0.tgz", + "integrity": "sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==" }, "node_modules/qs": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", "engines": { "node": ">=0.6" } @@ -1700,100 +2044,81 @@ "node": ">= 0.8" } }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, "node_modules/range-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", - "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "engines": { "node": ">= 0.6" } }, "node_modules/raw-body": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", - "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", + "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", "dependencies": { "bytes": "3.0.0", - "http-errors": "1.6.2", - "iconv-lite": "0.4.19", + "http-errors": "1.6.3", + "iconv-lite": "0.4.23", "unpipe": "1.0.0" }, "engines": { "node": ">= 0.8" } }, - "node_modules/raw-body/node_modules/depd": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", - "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body/node_modules/http-errors": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", - "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", + "node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", "dependencies": { - "depd": "1.1.1", - "inherits": "2.0.3", - "setprototypeof": "1.0.3", - "statuses": ">= 1.3.1 < 2" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" }, "engines": { - "node": ">= 0.6" + "node": ">= 6" } }, - "node_modules/raw-body/node_modules/setprototypeof": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", - "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" - }, - "node_modules/readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" } }, - "node_modules/regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" - }, - "node_modules/repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true, "engines": { - "node": ">=0.10" + "node": ">=0.10.0" } }, "node_modules/resolve": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", - "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", "dependencies": { - "path-parse": "^1.0.5" - } - }, - "node_modules/right-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", - "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", - "dependencies": { - "align-text": "^0.1.1" + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/rimraf": { @@ -1830,9 +2155,14 @@ } }, "node_modules/safe-buffer": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/semver": { "version": "7.3.5", @@ -1871,6 +2201,15 @@ "node": ">= 0.8.0" } }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, "node_modules/serve-static": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", @@ -1900,17 +2239,6 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==" }, - "node_modules/source-map": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", - "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", - "dependencies": { - "amdefine": ">=0.0.4" - }, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/split2": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", @@ -1919,19 +2247,6 @@ "readable-stream": "^3.0.0" } }, - "node_modules/split2/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/statuses": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", @@ -1941,13 +2256,32 @@ } }, "node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dependencies": { - "safe-buffer": "~5.1.0" + "safe-buffer": "~5.2.0" } }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -1972,10 +2306,22 @@ "node": ">=8" } }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/superagent": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.2.tgz", - "integrity": "sha512-gVH4QfYHcY3P0f/BZzavLreHW3T1v7hG9B+hpMQotGQqurOvhv87GcMCd6LWySmBuf+BDR44TQd0aISjVHLeNQ==", + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz", + "integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==", "dev": true, "dependencies": { "component-emitter": "^1.2.0", @@ -1983,48 +2329,81 @@ "debug": "^3.1.0", "extend": "^3.0.0", "form-data": "^2.3.1", - "formidable": "^1.1.1", + "formidable": "^1.2.0", "methods": "^1.1.1", "mime": "^1.4.1", "qs": "^6.5.1", - "readable-stream": "^2.0.5" + "readable-stream": "^2.3.5" }, "engines": { "node": ">= 4.0" } }, "node_modules/superagent/node_modules/debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "dependencies": { - "ms": "2.0.0" + "ms": "^2.1.1" + } + }, + "node_modules/superagent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/superagent/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/superagent/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" } }, "node_modules/supertest": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-3.1.0.tgz", - "integrity": "sha512-O44AMnmJqx294uJQjfUmEyYOg7d9mylNFsMw/Wkz4evKd1njyPrtCN+U6ZIC7sKtfEVQhfTqFFijlXx8KP/Czw==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-3.4.2.tgz", + "integrity": "sha512-WZWbwceHUo2P36RoEIdXvmqfs47idNNZjCuJOqDz6rvtkk8ym56aU5oglORCpPeXGxT7l9rkJ41+O1lffQXYSA==", "dev": true, "dependencies": { - "methods": "~1.1.2", - "superagent": "3.8.2" + "methods": "^1.1.2", + "superagent": "^3.8.3" }, "engines": { - "node": ">=4.0.0" + "node": ">=6.0.0" } }, "node_modules/supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "dependencies": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, "node_modules/tar": { @@ -2055,17 +2434,29 @@ } }, "node_modules/to-fast-properties": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", - "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", "engines": { - "node": ">=0.10.0" + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" } }, "node_modules/token-stream": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-0.0.1.tgz", - "integrity": "sha1-zu78cXp2xDFvEm0LnbqlXX598Bo=" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-1.0.0.tgz", + "integrity": "sha1-zCAOqyYT9BZtJ/+a/HylbUnfbrQ=" }, "node_modules/tr46": { "version": "0.0.3", @@ -2073,49 +2464,17 @@ "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" }, "node_modules/type-is": { - "version": "1.6.16", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", - "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "dependencies": { "media-typer": "0.3.0", - "mime-types": "~2.1.18" + "mime-types": "~2.1.24" }, "engines": { "node": ">= 0.6" } }, - "node_modules/uglify-js": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", - "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", - "dependencies": { - "source-map": "~0.5.1", - "yargs": "~3.10.0" - }, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - }, - "optionalDependencies": { - "uglify-to-browserify": "~1.0.0" - } - }, - "node_modules/uglify-js/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/uglify-to-browserify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", - "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", - "optional": true - }, "node_modules/uid-safe": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", @@ -2157,9 +2516,9 @@ } }, "node_modules/void-elements": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", - "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha1-YU9/v42AHwu18GYfWy9XhXUOTwk=", "engines": { "node": ">=0.10.0" } @@ -2178,6 +2537,21 @@ "webidl-conversions": "^3.0.0" } }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/wide-align": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", @@ -2186,29 +2560,41 @@ "string-width": "^1.0.2 || 2 || 3 || 4" } }, - "node_modules/window-size": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", - "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/with": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/with/-/with-5.1.1.tgz", - "integrity": "sha1-+k2qktrzLE6pTtRTyB8EaGtXXf4=", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/with/-/with-7.0.2.tgz", + "integrity": "sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==", "dependencies": { - "acorn": "^3.1.0", - "acorn-globals": "^3.0.0" + "@babel/parser": "^7.9.6", + "@babel/types": "^7.9.6", + "assert-never": "^1.2.1", + "babel-walk": "3.0.0-canary-5" + }, + "engines": { + "node": ">= 10.0.0" } }, - "node_modules/wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", + "node_modules/workerpool": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", + "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, "engines": { - "node": ">=0.4.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/wrappy": { @@ -2224,24 +2610,95 @@ "node": ">=0.4" } }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yargs": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, "dependencies": { - "camelcase": "^1.0.2", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", - "window-size": "0.1.0" + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } } }, "dependencies": { + "@babel/helper-validator-identifier": { + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==" + }, + "@babel/parser": { + "version": "7.16.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.4.tgz", + "integrity": "sha512-6V0qdPUaiVHH3RtZeLIsc+6pDhbYzHR8ogA8w+f+Wc77DuXto19g2QUwveINoS34Uw+W8/hQDGJCx+i4n7xcng==" + }, + "@babel/types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", + "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", + "requires": { + "@babel/helper-validator-identifier": "^7.15.7", + "to-fast-properties": "^2.0.0" + } + }, "@mapbox/node-pre-gyp": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.7.tgz", @@ -2258,18 +2715,11 @@ "tar": "^6.1.11" } }, - "@types/babel-types": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/@types/babel-types/-/babel-types-7.0.4.tgz", - "integrity": "sha512-WiZhq3SVJHFRgRYLXvpf65XnV6ipVHhnNaNvE8yCimejrGglkg38kEj0JcizqwSHxmPSjcTlig/6JouxLGEhGw==" - }, - "@types/babylon": { - "version": "6.16.3", - "resolved": "https://registry.npmjs.org/@types/babylon/-/babylon-6.16.3.tgz", - "integrity": "sha512-lyJ8sW1PbY3uwuvpOBZ9zMYKshMnQpXmeDHh8dj9j2nJm/xrW0FgB5gLSYOArj5X0IfaXnmhFoJnhS4KbqIMug==", - "requires": { - "@types/babel-types": "*" - } + "@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true }, "abbrev": { "version": "1.1.1", @@ -2277,33 +2727,18 @@ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, "accepts": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", - "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", "requires": { - "mime-types": "~2.1.18", - "negotiator": "0.6.1" + "mime-types": "~2.1.24", + "negotiator": "0.6.2" } }, "acorn": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", - "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=" - }, - "acorn-globals": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-3.1.0.tgz", - "integrity": "sha1-/YJw9x+7SZawBPqIDuXUZXOnMb8=", - "requires": { - "acorn": "^4.0.4" - }, - "dependencies": { - "acorn": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", - "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=" - } - } + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" }, "agent-base": { "version": "6.0.2", @@ -2328,26 +2763,36 @@ } } }, - "align-text": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", - "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", - "requires": { - "kind-of": "^3.0.2", - "longest": "^1.0.1", - "repeat-string": "^1.5.2" - } - }, - "amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true }, "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, "aproba": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", @@ -2360,20 +2805,14 @@ "requires": { "delegates": "^1.0.0", "readable-stream": "^3.6.0" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } } }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -2384,6 +2823,11 @@ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" }, + "assert-never": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/assert-never/-/assert-never-1.2.1.tgz", + "integrity": "sha512-TaTivMB6pYI1kXwrFlEhLeGfOqoDNdTxjCdwRfFFkEA30Eu+k48W34nlok2EYWJfFFzqaEmichdNM7th6M5HNw==" + }, "async": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/async/-/async-3.2.2.tgz", @@ -2395,42 +2839,25 @@ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", "dev": true }, - "babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "babel-walk": { + "version": "3.0.0-canary-5", + "resolved": "https://registry.npmjs.org/babel-walk/-/babel-walk-3.0.0-canary-5.tgz", + "integrity": "sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==", "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "@babel/types": "^7.9.6" } }, - "babel-types": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", - "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", - "requires": { - "babel-runtime": "^6.26.0", - "esutils": "^2.0.2", - "lodash": "^4.17.4", - "to-fast-properties": "^1.0.3" - } - }, - "babylon": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==" - }, "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "basic-auth": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.0.tgz", - "integrity": "sha1-AV2z81PgLlY3d1X5YnQuiYHnu7o=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", "requires": { - "safe-buffer": "5.1.1" + "safe-buffer": "5.1.2" } }, "bcrypt": { @@ -2442,21 +2869,27 @@ "node-addon-api": "^3.1.0" } }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, "body-parser": { - "version": "1.18.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", - "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", + "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", "requires": { "bytes": "3.0.0", "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "~1.1.1", - "http-errors": "~1.6.2", - "iconv-lite": "0.4.19", + "depd": "~1.1.2", + "http-errors": "~1.6.3", + "iconv-lite": "0.4.23", "on-finished": "~2.3.0", - "qs": "6.5.1", - "raw-body": "2.3.2", - "type-is": "~1.6.15" + "qs": "6.5.2", + "raw-body": "2.3.3", + "type-is": "~1.6.16" } }, "brace-expansion": { @@ -2468,6 +2901,15 @@ "concat-map": "0.0.1" } }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, "browser-stdout": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", @@ -2484,18 +2926,40 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" }, - "camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=" - }, - "center-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", - "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", "requires": { - "align-text": "^0.1.3", - "lazy-cache": "^1.0.3" + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "camelcase": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", + "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, "character-parser": { @@ -2506,56 +2970,71 @@ "is-regex": "^1.0.3" } }, + "chokidar": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, "chownr": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" }, - "clean-css": { - "version": "3.4.28", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-3.4.28.tgz", - "integrity": "sha1-vxlF6C/ICPVWlebd6uwBQA79A/8=", + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, "requires": { - "commander": "2.8.x", - "source-map": "0.4.x" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" } }, - "cliui": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "requires": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", - "wordwrap": "0.0.2" + "color-name": "~1.1.4" } }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "color-support": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" }, "combined-stream": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", - "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, "requires": { "delayed-stream": "~1.0.0" } }, - "commander": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", - "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", - "requires": { - "graceful-readlink": ">= 1.0.0" - } - }, "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", "dev": true }, "concat-map": { @@ -2574,14 +3053,12 @@ "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" }, "constantinople": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-3.1.2.tgz", - "integrity": "sha512-yePcBqEFhLOqSBtwYOGGS1exHo/s1xjekXiinh4itpNQGCu4KA1euPh1fg07N2wMITZXQkBz75Ntdt1ctGZouw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-4.0.1.tgz", + "integrity": "sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==", "requires": { - "@types/babel-types": "^7.0.0", - "@types/babylon": "^6.16.2", - "babel-types": "^6.26.0", - "babylon": "^6.18.0" + "@babel/parser": "^7.6.0", + "@babel/types": "^7.6.1" } }, "content-disposition": { @@ -2595,16 +3072,16 @@ "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" }, "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" }, "cookie-parser": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.3.tgz", - "integrity": "sha1-D+MfoZ0AC5X0qt8fU/3CuKIDuqU=", + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", + "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", "requires": { - "cookie": "0.3.1", + "cookie": "0.4.1", "cookie-signature": "1.0.6" } }, @@ -2614,20 +3091,15 @@ "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, "cookiejar": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", - "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz", + "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==", "dev": true }, - "core-js": { - "version": "2.5.7", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", - "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==" - }, "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true }, "debug": { @@ -2639,9 +3111,10 @@ } }, "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true }, "delayed-stream": { "version": "1.0.0", @@ -2670,9 +3143,9 @@ "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" }, "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", "dev": true }, "doctypes": { @@ -2700,35 +3173,36 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" }, "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, - "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=" - }, "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" }, "express": { - "version": "4.16.3", - "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz", - "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=", + "version": "4.16.4", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", + "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", "requires": { "accepts": "~1.3.5", "array-flatten": "1.1.1", - "body-parser": "1.18.2", + "body-parser": "1.18.3", "content-disposition": "0.5.2", "content-type": "~1.0.4", "cookie": "0.3.1", @@ -2745,10 +3219,10 @@ "on-finished": "~2.3.0", "parseurl": "~1.3.2", "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.3", - "qs": "6.5.1", + "proxy-addr": "~2.0.4", + "qs": "6.5.2", "range-parser": "~1.2.0", - "safe-buffer": "5.1.1", + "safe-buffer": "5.1.2", "send": "0.16.2", "serve-static": "1.13.2", "setprototypeof": "1.1.0", @@ -2756,6 +3230,13 @@ "type-is": "~1.6.16", "utils-merge": "1.0.1", "vary": "~1.1.2" + }, + "dependencies": { + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + } } }, "express-session": { @@ -2773,11 +3254,6 @@ "uid-safe": "~2.1.5" }, "dependencies": { - "cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" - }, "depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -2796,6 +3272,15 @@ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "dev": true }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, "finalhandler": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", @@ -2810,27 +3295,43 @@ "unpipe": "~1.0.0" } }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true + }, "form-data": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", - "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", "dev": true, "requires": { "asynckit": "^0.4.0", - "combined-stream": "1.0.6", + "combined-stream": "^1.0.6", "mime-types": "^2.1.12" } }, "formidable": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.1.tgz", - "integrity": "sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz", + "integrity": "sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==", "dev": true }, "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" }, "fresh": { "version": "0.5.2", @@ -2850,6 +3351,13 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -2871,10 +3379,26 @@ "wide-align": "^1.1.2" } }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -2885,10 +3409,14 @@ "path-is-absolute": "^1.0.0" } }, - "graceful-readlink": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", - "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } }, "growl": { "version": "1.10.5", @@ -2905,20 +3433,33 @@ } }, "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "requires": { + "has-symbols": "^1.0.2" + } + }, "has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" }, "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, "http-errors": { @@ -2957,9 +3498,12 @@ } }, "iconv-lite": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", - "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } }, "inflight": { "version": "1.0.6", @@ -2976,60 +3520,114 @@ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, "ipaddr.js": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", - "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=" + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-core-module": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", + "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", + "requires": { + "has": "^1.0.3" + } }, "is-expression": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-3.0.0.tgz", - "integrity": "sha1-Oayqa+f9HzRx3ELHQW5hwkMXrJ8=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-4.0.0.tgz", + "integrity": "sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==", "requires": { - "acorn": "~4.0.2", - "object-assign": "^4.0.1" - }, - "dependencies": { - "acorn": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", - "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=" - } + "acorn": "^7.1.1", + "object-assign": "^4.1.1" } }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, "is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" }, - "is-promise": { + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-plain-obj": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true + }, + "is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" }, "is-regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "requires": { - "has": "^1.0.1" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" } }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, "js-stringify": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", "integrity": "sha1-Fzb939lyTyijaCrcYjCufk6Weds=" }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, "jstransformer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", @@ -3039,28 +3637,24 @@ "promise": "^7.0.1" } }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, "requires": { - "is-buffer": "^1.1.5" + "p-locate": "^5.0.0" } }, - "lazy-cache": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", - "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=" - }, - "lodash": { - "version": "4.17.10", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", - "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" - }, - "longest": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", - "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + } }, "lru-cache": { "version": "6.0.0", @@ -3106,16 +3700,16 @@ "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" }, "mime-db": { - "version": "1.35.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.35.0.tgz", - "integrity": "sha512-JWT/IcCTsB0Io3AhWUMjRqucrHSPsSf2xKLaRldJVULioggvkJvggZ3VXNNSRkCddE6D+BUI4HEIZIA2OjwIvg==" + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", + "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==" }, "mime-types": { - "version": "2.1.19", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.19.tgz", - "integrity": "sha512-P1tKYHVSZ6uFo26mtnve4HQFE3koh1UWVkp8YUC+ESBHe945xWSoXuHHiGarDqcEZ+whpCDnlNw5LON0kLo+sw==", + "version": "2.1.34", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", + "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", "requires": { - "mime-db": "~1.35.0" + "mime-db": "1.51.0" } }, "minimatch": { @@ -3126,12 +3720,6 @@ "brace-expansion": "^1.1.7" } }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - }, "minipass": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.5.tgz", @@ -3149,59 +3737,71 @@ "yallist": "^4.0.0" } }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, "mocha": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", - "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.3.tgz", + "integrity": "sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==", "dev": true, "requires": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "commander": "2.15.1", - "debug": "3.1.0", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "glob": "7.1.2", + "chokidar": "3.5.2", + "debug": "4.3.2", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.7", "growl": "1.10.5", - "he": "1.1.1", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", "minimatch": "3.0.4", - "mkdirp": "0.5.1", - "supports-color": "5.4.0" + "ms": "2.1.3", + "nanoid": "3.1.25", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "workerpool": "6.1.5", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" }, "dependencies": { - "commander": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", - "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", - "dev": true - }, "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true } } }, "morgan": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.0.tgz", - "integrity": "sha1-0B+mxlhZt2/PMbPLU6OCGjEdgFE=", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", + "integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==", "requires": { "basic-auth": "~2.0.0", "debug": "2.6.9", - "depd": "~1.1.1", + "depd": "~1.1.2", "on-finished": "~2.3.0", "on-headers": "~1.0.1" } @@ -3211,10 +3811,16 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, + "nanoid": { + "version": "3.1.25", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", + "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", + "dev": true + }, "negotiator": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", - "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, "node-addon-api": { "version": "3.2.1", @@ -3230,9 +3836,9 @@ } }, "nodemailer": { - "version": "6.6.5", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.6.5.tgz", - "integrity": "sha512-C/v856DBijUzHcHIgGpQoTrfsH3suKIRAGliIzCstatM2cAa+MYX3LuyCrABiO/cdJTxgBBHXxV1ztiqUwst5A==" + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.7.2.tgz", + "integrity": "sha512-Dz7zVwlef4k5R71fdmxwR8Q39fiboGbu3xgswkzGwczUfjp873rVxt1O46+Fh0j1ORnAC6L9+heI8uUpO6DT7Q==" }, "nopt": { "version": "5.0.0", @@ -3242,6 +3848,12 @@ "abbrev": "1" } }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, "npmlog": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.0.tgz", @@ -3279,6 +3891,24 @@ "wrappy": "1" } }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, "packet-reader": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", @@ -3311,15 +3941,21 @@ "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=" }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-parse": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", - "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=" + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "path-to-regexp": { "version": "0.1.7", @@ -3386,6 +4022,12 @@ "split2": "^3.1.1" } }, + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true + }, "postgres-array": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", @@ -3410,9 +4052,9 @@ } }, "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, "promise": { @@ -3424,216 +4066,193 @@ } }, "proxy-addr": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", - "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "requires": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.8.0" + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" } }, "pug": { - "version": "2.0.0-beta11", - "resolved": "https://registry.npmjs.org/pug/-/pug-2.0.0-beta11.tgz", - "integrity": "sha1-Favmr1AEx+LPRhPksnRlyVRrXwE=", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pug/-/pug-3.0.2.tgz", + "integrity": "sha512-bp0I/hiK1D1vChHh6EfDxtndHji55XP/ZJKwsRqrz6lRia6ZC2OZbdAymlxdVFwd1L70ebrVJw4/eZ79skrIaw==", "requires": { - "pug-code-gen": "^1.1.1", - "pug-filters": "^2.1.1", - "pug-lexer": "^3.0.0", - "pug-linker": "^2.0.2", - "pug-load": "^2.0.5", - "pug-parser": "^2.0.2", - "pug-runtime": "^2.0.3", - "pug-strip-comments": "^1.0.2" + "pug-code-gen": "^3.0.2", + "pug-filters": "^4.0.0", + "pug-lexer": "^5.0.1", + "pug-linker": "^4.0.0", + "pug-load": "^3.0.0", + "pug-parser": "^6.0.0", + "pug-runtime": "^3.0.1", + "pug-strip-comments": "^2.0.0" } }, "pug-attrs": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-2.0.3.tgz", - "integrity": "sha1-owlflw5kFR972tlX7vVftdeQXRU=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-3.0.0.tgz", + "integrity": "sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==", "requires": { - "constantinople": "^3.0.1", - "js-stringify": "^1.0.1", - "pug-runtime": "^2.0.4" + "constantinople": "^4.0.1", + "js-stringify": "^1.0.2", + "pug-runtime": "^3.0.0" } }, "pug-code-gen": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-1.1.1.tgz", - "integrity": "sha1-HPcnRO8qA56uajNAyqoRBYcSWOg=", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-3.0.2.tgz", + "integrity": "sha512-nJMhW16MbiGRiyR4miDTQMRWDgKplnHyeLvioEJYbk1RsPI3FuA3saEP8uwnTb2nTJEKBU90NFVWJBk4OU5qyg==", "requires": { - "constantinople": "^3.0.1", + "constantinople": "^4.0.1", "doctypes": "^1.1.0", - "js-stringify": "^1.0.1", - "pug-attrs": "^2.0.2", - "pug-error": "^1.3.2", - "pug-runtime": "^2.0.3", - "void-elements": "^2.0.1", - "with": "^5.0.0" + "js-stringify": "^1.0.2", + "pug-attrs": "^3.0.0", + "pug-error": "^2.0.0", + "pug-runtime": "^3.0.0", + "void-elements": "^3.1.0", + "with": "^7.0.0" } }, "pug-error": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-1.3.2.tgz", - "integrity": "sha1-U659nSm7A89WRJOgJhCfVMR/XyY=" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-2.0.0.tgz", + "integrity": "sha512-sjiUsi9M4RAGHktC1drQfCr5C5eriu24Lfbt4s+7SykztEOwVZtbFk1RRq0tzLxcMxMYTBR+zMQaG07J/btayQ==" }, "pug-filters": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-2.1.5.tgz", - "integrity": "sha512-xkw71KtrC4sxleKiq+cUlQzsiLn8pM5+vCgkChW2E6oNOzaqTSIBKIQ5cl4oheuDzvJYCTSYzRaVinMUrV4YLQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-4.0.0.tgz", + "integrity": "sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A==", "requires": { - "clean-css": "^3.3.0", - "constantinople": "^3.0.1", + "constantinople": "^4.0.1", "jstransformer": "1.0.0", - "pug-error": "^1.3.2", - "pug-walk": "^1.1.5", - "resolve": "^1.1.6", - "uglify-js": "^2.6.1" + "pug-error": "^2.0.0", + "pug-walk": "^2.0.0", + "resolve": "^1.15.1" } }, "pug-lexer": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-3.1.0.tgz", - "integrity": "sha1-/QhzdtSmdbT1n4/vQiiDQ06VgaI=", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-5.0.1.tgz", + "integrity": "sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w==", "requires": { - "character-parser": "^2.1.1", - "is-expression": "^3.0.0", - "pug-error": "^1.3.2" + "character-parser": "^2.2.0", + "is-expression": "^4.0.0", + "pug-error": "^2.0.0" } }, "pug-linker": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-2.0.3.tgz", - "integrity": "sha1-szH/olc33eacEntWwQ/xf652bco=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-4.0.0.tgz", + "integrity": "sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw==", "requires": { - "pug-error": "^1.3.2", - "pug-walk": "^1.1.2" + "pug-error": "^2.0.0", + "pug-walk": "^2.0.0" } }, "pug-load": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-2.0.11.tgz", - "integrity": "sha1-5kjlftET/iwfRdV4WOorrWvAFSc=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-3.0.0.tgz", + "integrity": "sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ==", "requires": { - "object-assign": "^4.1.0", - "pug-walk": "^1.1.7" + "object-assign": "^4.1.1", + "pug-walk": "^2.0.0" } }, "pug-parser": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-2.0.2.tgz", - "integrity": "sha1-U6aAz9BQOdywwn0CkJS8SnkmibA=", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-6.0.0.tgz", + "integrity": "sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw==", "requires": { - "pug-error": "^1.3.2", - "token-stream": "0.0.1" + "pug-error": "^2.0.0", + "token-stream": "1.0.0" } }, "pug-runtime": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-2.0.4.tgz", - "integrity": "sha1-4XjhvaaKsujArPybztLFT9iM61g=" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-3.0.1.tgz", + "integrity": "sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg==" }, "pug-strip-comments": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-1.0.3.tgz", - "integrity": "sha1-8VWVkiBu3G+FMQ2s9K+0igJa9Z8=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-2.0.0.tgz", + "integrity": "sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ==", "requires": { - "pug-error": "^1.3.2" + "pug-error": "^2.0.0" } }, "pug-walk": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-1.1.7.tgz", - "integrity": "sha1-wA1cUSi6xYBr7BXSt+fNq+QlMfM=" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-2.0.0.tgz", + "integrity": "sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==" }, "qs": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" }, "random-bytes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=" }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, "range-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", - "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" }, "raw-body": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", - "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", + "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", "requires": { "bytes": "3.0.0", - "http-errors": "1.6.2", - "iconv-lite": "0.4.19", + "http-errors": "1.6.3", + "iconv-lite": "0.4.23", "unpipe": "1.0.0" - }, - "dependencies": { - "depd": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", - "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" - }, - "http-errors": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", - "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", - "requires": { - "depd": "1.1.1", - "inherits": "2.0.3", - "setprototypeof": "1.0.3", - "statuses": ">= 1.3.1 < 2" - } - }, - "setprototypeof": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", - "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" - } } }, "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "picomatch": "^2.2.1" } }, - "regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true }, "resolve": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", - "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", "requires": { - "path-parse": "^1.0.5" - } - }, - "right-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", - "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", - "requires": { - "align-text": "^0.1.1" + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" } }, "rimraf": { @@ -3660,9 +4279,14 @@ } }, "safe-buffer": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "semver": { "version": "7.3.5", @@ -3692,6 +4316,15 @@ "statuses": "~1.4.0" } }, + "serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, "serve-static": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", @@ -3718,32 +4351,12 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==" }, - "source-map": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", - "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", - "requires": { - "amdefine": ">=0.0.4" - } - }, "split2": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", "requires": { "readable-stream": "^3.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } } }, "statuses": { @@ -3752,11 +4365,18 @@ "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" }, "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } } }, "string-width": { @@ -3777,10 +4397,16 @@ "ansi-regex": "^5.0.1" } }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, "superagent": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.2.tgz", - "integrity": "sha512-gVH4QfYHcY3P0f/BZzavLreHW3T1v7hG9B+hpMQotGQqurOvhv87GcMCd6LWySmBuf+BDR44TQd0aISjVHLeNQ==", + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz", + "integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==", "dev": true, "requires": { "component-emitter": "^1.2.0", @@ -3788,41 +4414,71 @@ "debug": "^3.1.0", "extend": "^3.0.0", "form-data": "^2.3.1", - "formidable": "^1.1.1", + "formidable": "^1.2.0", "methods": "^1.1.1", "mime": "^1.4.1", "qs": "^6.5.1", - "readable-stream": "^2.0.5" + "readable-stream": "^2.3.5" }, "dependencies": { "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" } } } }, "supertest": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-3.1.0.tgz", - "integrity": "sha512-O44AMnmJqx294uJQjfUmEyYOg7d9mylNFsMw/Wkz4evKd1njyPrtCN+U6ZIC7sKtfEVQhfTqFFijlXx8KP/Czw==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-3.4.2.tgz", + "integrity": "sha512-WZWbwceHUo2P36RoEIdXvmqfs47idNNZjCuJOqDz6rvtkk8ym56aU5oglORCpPeXGxT7l9rkJ41+O1lffQXYSA==", "dev": true, "requires": { - "methods": "~1.1.2", - "superagent": "3.8.2" + "methods": "^1.1.2", + "superagent": "^3.8.3" } }, "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" } }, "tar": { @@ -3846,14 +4502,23 @@ } }, "to-fast-properties": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", - "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } }, "token-stream": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-0.0.1.tgz", - "integrity": "sha1-zu78cXp2xDFvEm0LnbqlXX598Bo=" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-1.0.0.tgz", + "integrity": "sha1-zCAOqyYT9BZtJ/+a/HylbUnfbrQ=" }, "tr46": { "version": "0.0.3", @@ -3861,37 +4526,14 @@ "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" }, "type-is": { - "version": "1.6.16", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", - "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "requires": { "media-typer": "0.3.0", - "mime-types": "~2.1.18" + "mime-types": "~2.1.24" } }, - "uglify-js": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", - "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", - "requires": { - "source-map": "~0.5.1", - "uglify-to-browserify": "~1.0.0", - "yargs": "~3.10.0" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" - } - } - }, - "uglify-to-browserify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", - "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", - "optional": true - }, "uid-safe": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", @@ -3921,9 +4563,9 @@ "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, "void-elements": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", - "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha1-YU9/v42AHwu18GYfWy9XhXUOTwk=" }, "webidl-conversions": { "version": "3.0.1", @@ -3939,6 +4581,15 @@ "webidl-conversions": "^3.0.0" } }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, "wide-align": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", @@ -3947,24 +4598,33 @@ "string-width": "^1.0.2 || 2 || 3 || 4" } }, - "window-size": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", - "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=" - }, "with": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/with/-/with-5.1.1.tgz", - "integrity": "sha1-+k2qktrzLE6pTtRTyB8EaGtXXf4=", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/with/-/with-7.0.2.tgz", + "integrity": "sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==", "requires": { - "acorn": "^3.1.0", - "acorn-globals": "^3.0.0" + "@babel/parser": "^7.9.6", + "@babel/types": "^7.9.6", + "assert-never": "^1.2.1", + "babel-walk": "3.0.0-canary-5" } }, - "wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=" + "workerpool": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", + "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } }, "wrappy": { "version": "1.0.2", @@ -3976,21 +4636,55 @@ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "yargs": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, "requires": { - "camelcase": "^1.0.2", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", - "window-size": "0.1.0" + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" } + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true + }, + "yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "requires": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + } + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true } } } diff --git a/package.json b/package.json index 3179dbc..f67a08c 100644 --- a/package.json +++ b/package.json @@ -20,10 +20,10 @@ "passport": "^0.5.0", "passport-local": "^1.0.0", "pg": "^8.7.1", - "pug": "2.0.0-beta11" + "pug": "^3.0.2" }, "devDependencies": { - "mocha": "^5.1.1", + "mocha": "^9.1.3", "supertest": "^3.0.0" } } From 57558d47af4866234a4573dda2fd0dff06127a73 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 18:55:05 -0700 Subject: [PATCH 189/255] Remove unused dev packages --- package-lock.json | 1602 --------------------------------------------- package.json | 4 - 2 files changed, 1606 deletions(-) diff --git a/package-lock.json b/package-lock.json index fb53dd1..ff72bac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,10 +23,6 @@ "passport-local": "^1.0.0", "pg": "^8.7.1", "pug": "^3.0.2" - }, - "devDependencies": { - "mocha": "^9.1.3", - "supertest": "^3.0.0" } }, "node_modules/@babel/helper-validator-identifier": { @@ -79,12 +75,6 @@ "node-pre-gyp": "bin/node-pre-gyp" } }, - "node_modules/@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", - "dev": true - }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -145,15 +135,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -162,34 +143,6 @@ "node": ">=8" } }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/aproba": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", @@ -207,12 +160,6 @@ "node": ">=10" } }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -233,12 +180,6 @@ "resolved": "https://registry.npmjs.org/async/-/async-3.2.2.tgz", "integrity": "sha512-H0E+qZaDEfx/FY4t7iLRv1W2fFI6+pyCeTw1uN20AQPiwqwM6ojPxHxdLv4z8hi2DtnW9BOckSspLucW7pIE5g==" }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, "node_modules/babel-walk": { "version": "3.0.0-canary-5", "resolved": "https://registry.npmjs.org/babel-walk/-/babel-walk-3.0.0-canary-5.tgz", @@ -279,15 +220,6 @@ "node": ">= 10.0.0" } }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/body-parser": { "version": "1.18.3", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", @@ -317,24 +249,6 @@ "concat-map": "0.0.1" } }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, "node_modules/buffer-writer": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", @@ -363,46 +277,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/camelcase": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", - "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/character-parser": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", @@ -411,27 +285,6 @@ "is-regex": "^1.0.3" } }, - "node_modules/chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", - "dev": true, - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, "node_modules/chownr": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", @@ -440,35 +293,6 @@ "node": ">=10" } }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, "node_modules/color-support": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", @@ -477,24 +301,6 @@ "color-support": "bin.js" } }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -563,18 +369,6 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, - "node_modules/cookiejar": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz", - "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==", - "dev": true - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "dev": true - }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -583,27 +377,6 @@ "ms": "2.0.0" } }, - "node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", @@ -633,15 +406,6 @@ "node": ">=0.10" } }, - "node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/doctypes": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", @@ -673,32 +437,11 @@ "node": ">= 0.8" } }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -800,24 +543,6 @@ "node": ">= 0.6" } }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/finalhandler": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", @@ -835,55 +560,6 @@ "node": ">= 0.8" } }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "bin": { - "flat": "cli.js" - } - }, - "node_modules/form-data": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", - "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/formidable": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz", - "integrity": "sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==", - "deprecated": "Please upgrade to latest, formidable@v2 or formidable@v3! Check these notes: https://bit.ly/2ZEqIau", - "dev": true, - "funding": { - "url": "https://ko-fi.com/tunnckoCore/commissions" - } - }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -916,20 +592,6 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -954,15 +616,6 @@ "node": "^12.13.0 || ^14.15.0 || >=16" } }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, "node_modules/get-intrinsic": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", @@ -976,47 +629,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true, - "engines": { - "node": ">=4.x" - } - }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -1028,15 +640,6 @@ "node": ">= 0.4.0" } }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/has-symbols": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", @@ -1067,15 +670,6 @@ "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "bin": { - "he": "bin/he" - } - }, "node_modules/http-errors": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", @@ -1156,18 +750,6 @@ "node": ">= 0.10" } }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/is-core-module": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", @@ -1188,15 +770,6 @@ "object-assign": "^4.1.1" } }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -1205,36 +778,6 @@ "node": ">=8" } }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/is-promise": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", @@ -1255,47 +798,11 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, "node_modules/js-stringify": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", "integrity": "sha1-Fzb939lyTyijaCrcYjCufk6Weds=" }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/jstransformer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", @@ -1305,37 +812,6 @@ "promise": "^7.0.1" } }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -1451,78 +927,6 @@ "node": ">= 8" } }, - "node_modules/mocha": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.3.tgz", - "integrity": "sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==", - "dev": true, - "dependencies": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.2", - "debug": "4.3.2", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.1.7", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "3.0.4", - "ms": "2.1.3", - "nanoid": "3.1.25", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "which": "2.0.2", - "workerpool": "6.1.5", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" - } - }, - "node_modules/mocha/node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/mocha/node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/mocha/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, "node_modules/morgan": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", @@ -1543,18 +947,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, - "node_modules/nanoid": { - "version": "3.1.25", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", - "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", - "dev": true, - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, "node_modules/negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", @@ -1601,15 +993,6 @@ "node": ">=6" } }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/npmlog": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.0.tgz", @@ -1659,36 +1042,6 @@ "wrappy": "1" } }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/packet-reader": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", @@ -1737,15 +1090,6 @@ "node": ">= 0.4.0" } }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -1843,18 +1187,6 @@ "split2": "^3.1.1" } }, - "node_modules/picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/postgres-array": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", @@ -1890,12 +1222,6 @@ "node": ">=0.10.0" } }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true - }, "node_modules/promise": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", @@ -2044,15 +1370,6 @@ "node": ">= 0.8" } }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -2088,27 +1405,6 @@ "node": ">= 6" } }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/resolve": { "version": "1.20.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", @@ -2201,15 +1497,6 @@ "node": ">= 0.8.0" } }, - "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, "node_modules/serve-static": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", @@ -2306,106 +1593,6 @@ "node": ">=8" } }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/superagent": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz", - "integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==", - "dev": true, - "dependencies": { - "component-emitter": "^1.2.0", - "cookiejar": "^2.1.0", - "debug": "^3.1.0", - "extend": "^3.0.0", - "form-data": "^2.3.1", - "formidable": "^1.2.0", - "methods": "^1.1.1", - "mime": "^1.4.1", - "qs": "^6.5.1", - "readable-stream": "^2.3.5" - }, - "engines": { - "node": ">= 4.0" - } - }, - "node_modules/superagent/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/superagent/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/superagent/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/superagent/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/supertest": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-3.4.2.tgz", - "integrity": "sha512-WZWbwceHUo2P36RoEIdXvmqfs47idNNZjCuJOqDz6rvtkk8ym56aU5oglORCpPeXGxT7l9rkJ41+O1lffQXYSA==", - "dev": true, - "dependencies": { - "methods": "^1.1.2", - "superagent": "^3.8.3" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, "node_modules/tar": { "version": "6.1.11", "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", @@ -2441,18 +1628,6 @@ "node": ">=4" } }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, "node_modules/token-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-1.0.0.tgz", @@ -2537,21 +1712,6 @@ "webidl-conversions": "^3.0.0" } }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/wide-align": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", @@ -2574,29 +1734,6 @@ "node": ">= 10.0.0" } }, - "node_modules/workerpool": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", - "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", - "dev": true - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -2610,73 +1747,10 @@ "node": ">=0.4" } }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } } }, "dependencies": { @@ -2715,12 +1789,6 @@ "tar": "^6.1.11" } }, - "@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", - "dev": true - }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -2763,36 +1831,11 @@ } } }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, "aproba": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", @@ -2807,12 +1850,6 @@ "readable-stream": "^3.6.0" } }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -2833,12 +1870,6 @@ "resolved": "https://registry.npmjs.org/async/-/async-3.2.2.tgz", "integrity": "sha512-H0E+qZaDEfx/FY4t7iLRv1W2fFI6+pyCeTw1uN20AQPiwqwM6ojPxHxdLv4z8hi2DtnW9BOckSspLucW7pIE5g==" }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, "babel-walk": { "version": "3.0.0-canary-5", "resolved": "https://registry.npmjs.org/babel-walk/-/babel-walk-3.0.0-canary-5.tgz", @@ -2869,12 +1900,6 @@ "node-addon-api": "^3.1.0" } }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, "body-parser": { "version": "1.18.3", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", @@ -2901,21 +1926,6 @@ "concat-map": "0.0.1" } }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, "buffer-writer": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", @@ -2935,33 +1945,6 @@ "get-intrinsic": "^1.0.2" } }, - "camelcase": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", - "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, "character-parser": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", @@ -2970,73 +1953,16 @@ "is-regex": "^1.0.3" } }, - "chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - } - }, "chownr": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, "color-support": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true - }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -3090,18 +2016,6 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, - "cookiejar": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz", - "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==", - "dev": true - }, - "core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "dev": true - }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -3110,18 +2024,6 @@ "ms": "2.0.0" } }, - "decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true - }, "delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", @@ -3142,12 +2044,6 @@ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" }, - "diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true - }, "doctypes": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", @@ -3173,23 +2069,11 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -3266,21 +2150,6 @@ } } }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, "finalhandler": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", @@ -3295,39 +2164,6 @@ "unpipe": "~1.0.0" } }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true - }, - "form-data": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", - "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "formidable": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz", - "integrity": "sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==", - "dev": true - }, "forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -3351,13 +2187,6 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -3379,12 +2208,6 @@ "wide-align": "^1.1.2" } }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, "get-intrinsic": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", @@ -3395,35 +2218,6 @@ "has-symbols": "^1.0.1" } }, - "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true - }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -3432,12 +2226,6 @@ "function-bind": "^1.1.1" } }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, "has-symbols": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", @@ -3456,12 +2244,6 @@ "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true - }, "http-errors": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", @@ -3524,15 +2306,6 @@ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, "is-core-module": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", @@ -3550,38 +2323,11 @@ "object-assign": "^4.1.1" } }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, "is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true - }, "is-promise": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", @@ -3596,38 +2342,11 @@ "has-tostringtag": "^1.0.0" } }, - "is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, "js-stringify": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", "integrity": "sha1-Fzb939lyTyijaCrcYjCufk6Weds=" }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, "jstransformer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", @@ -3637,25 +2356,6 @@ "promise": "^7.0.1" } }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - } - }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -3737,63 +2437,6 @@ "yallist": "^4.0.0" } }, - "mocha": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.3.tgz", - "integrity": "sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==", - "dev": true, - "requires": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.2", - "debug": "4.3.2", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.1.7", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "3.0.4", - "ms": "2.1.3", - "nanoid": "3.1.25", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "which": "2.0.2", - "workerpool": "6.1.5", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "dependencies": { - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "requires": { - "ms": "2.1.2" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - } - } - }, "morgan": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", @@ -3811,12 +2454,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, - "nanoid": { - "version": "3.1.25", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", - "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", - "dev": true - }, "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", @@ -3848,12 +2485,6 @@ "abbrev": "1" } }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, "npmlog": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.0.tgz", @@ -3891,24 +2522,6 @@ "wrappy": "1" } }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, "packet-reader": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", @@ -3941,12 +2554,6 @@ "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=" }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -4022,12 +2629,6 @@ "split2": "^3.1.1" } }, - "picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", - "dev": true - }, "postgres-array": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", @@ -4051,12 +2652,6 @@ "xtend": "^4.0.0" } }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true - }, "promise": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", @@ -4196,15 +2791,6 @@ "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=" }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, "range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -4231,21 +2817,6 @@ "util-deprecate": "^1.0.1" } }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, "resolve": { "version": "1.20.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", @@ -4316,15 +2887,6 @@ "statuses": "~1.4.0" } }, - "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - }, "serve-static": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", @@ -4397,90 +2959,6 @@ "ansi-regex": "^5.0.1" } }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "superagent": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz", - "integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==", - "dev": true, - "requires": { - "component-emitter": "^1.2.0", - "cookiejar": "^2.1.0", - "debug": "^3.1.0", - "extend": "^3.0.0", - "form-data": "^2.3.1", - "formidable": "^1.2.0", - "methods": "^1.1.1", - "mime": "^1.4.1", - "qs": "^6.5.1", - "readable-stream": "^2.3.5" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "supertest": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-3.4.2.tgz", - "integrity": "sha512-WZWbwceHUo2P36RoEIdXvmqfs47idNNZjCuJOqDz6rvtkk8ym56aU5oglORCpPeXGxT7l9rkJ41+O1lffQXYSA==", - "dev": true, - "requires": { - "methods": "^1.1.2", - "superagent": "^3.8.3" - } - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, "tar": { "version": "6.1.11", "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", @@ -4506,15 +2984,6 @@ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, "token-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-1.0.0.tgz", @@ -4581,15 +3050,6 @@ "webidl-conversions": "^3.0.0" } }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, "wide-align": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", @@ -4609,23 +3069,6 @@ "babel-walk": "3.0.0-canary-5" } }, - "workerpool": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", - "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", - "dev": true - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -4636,55 +3079,10 @@ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - }, - "yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true - }, - "yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "requires": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - } - }, - "yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true } } } diff --git a/package.json b/package.json index f67a08c..91a43c2 100644 --- a/package.json +++ b/package.json @@ -21,9 +21,5 @@ "passport-local": "^1.0.0", "pg": "^8.7.1", "pug": "^3.0.2" - }, - "devDependencies": { - "mocha": "^9.1.3", - "supertest": "^3.0.0" } } From c6f436b439550cff4923ad94b96d446e6a0b561c Mon Sep 17 00:00:00 2001 From: Sudoer777 Date: Sat, 27 Nov 2021 02:09:23 +0000 Subject: [PATCH 190/255] Rename environment variables --- .env.example | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.env.example b/.env.example index 11d1e69..c86842e 100644 --- a/.env.example +++ b/.env.example @@ -1,14 +1,14 @@ NODE_ENV=development -PGUSER=dbuser -PGHOST=database.server.com -PGPASSWORD=dbuserpassword -PGDATABASE=mydatabase -PGPORT=5432 +DATABASE_URL=database.server.com +DATABASE_USER=dbuser +DATABASE_PASSWORD=dbuserpassword +DATABASE_NAME=mydatabase +DATABASE_PORT=5432 -MAIL_FROM=fromaddress@example.com -MAIL_HOST=smtp.smtphost.net -MAIL_PORT=465 -MAIL_SECURE=true -MAIL_USER=username -MAIL_PASS=password +#MAIL_FROM=fromaddress@example.com +#MAIL_HOST=smtp.smtphost.net +#MAIL_PORT=465 +#MAIL_SECURE=true +#MAIL_USER=username +#MAIL_PASS=password From faf252417d46fc903650c0e5f0237806624a5d7d Mon Sep 17 00:00:00 2001 From: Sudoer777 Date: Sat, 27 Nov 2021 02:13:47 +0000 Subject: [PATCH 191/255] Undo name changes made in previous commit --- .env.example | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.env.example b/.env.example index c86842e..bdbddab 100644 --- a/.env.example +++ b/.env.example @@ -1,10 +1,10 @@ NODE_ENV=development -DATABASE_URL=database.server.com -DATABASE_USER=dbuser -DATABASE_PASSWORD=dbuserpassword -DATABASE_NAME=mydatabase -DATABASE_PORT=5432 +PGUSER=dbuser +PGHOST=database.server.com +PGPASSWORD=dbuserpassword +PGDATABASE=mydatabase +PGPORT=5432 #MAIL_FROM=fromaddress@example.com #MAIL_HOST=smtp.smtphost.net From a76a3a30fb4fa98415bd4e018afaa5b6e74e447f Mon Sep 17 00:00:00 2001 From: Sudoer777 Date: Sat, 27 Nov 2021 03:37:44 +0000 Subject: [PATCH 192/255] Rename from cvcs-score-tracker to score-tracker (cherry picked from commit 194b49a0f921c27ee09a4b42a4806ebbf000d02c) --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7249f59..4873d5d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -14,7 +14,7 @@ deploy: only: - testing variables: - GIT_REMOTE_URL: ssh://dokku@ssh.dokku.sudoer.ch:1337/cvcs-score-tracker + GIT_REMOTE_URL: ssh://dokku@ssh.dokku.sudoer.ch:1337/score-tracker GIT_SSH_COMMAND: 'ssh -o StrictHostKeyChecking=no' script: - dokku-deploy From 2bec2b4be5fa5935a110888cbee4695a83f6e730 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 21:28:20 -0700 Subject: [PATCH 193/255] Add functions to get latest game --- database/scores/games.js | 13 ++++++++++++- public/scripts/data.js | 8 ++++++++ routes/data.js | 6 +++++- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/database/scores/games.js b/database/scores/games.js index 8792b2e..e01352c 100644 --- a/database/scores/games.js +++ b/database/scores/games.js @@ -108,6 +108,16 @@ async function getFromID(gameID) { return new Game(row[0], row[3].toISOString().slice(0,10), row[4], row[5], row[6], row[7], row[1], row[2], row[8]); } +async function getLatest(userID = undefined) { + if(userID) { + const games = await retrieveByUser(userID); + return games[0]; + } else { + const games = await retrieve(); + return games[0]; + } +} + @@ -117,4 +127,5 @@ exports.remove = remove; exports.retrieve = retrieve; exports.retrieveByUser = retrieveByUser; exports.edit = edit; -exports.getFromID = getFromID; \ No newline at end of file +exports.getFromID = getFromID; +exports.getLatest = getLatest; \ No newline at end of file diff --git a/public/scripts/data.js b/public/scripts/data.js index e8c5898..28ec018 100644 --- a/public/scripts/data.js +++ b/public/scripts/data.js @@ -78,6 +78,14 @@ export async function getGame(gameID) { return game; } +export async function getLatestGame(userID = undefined) { + let URL = `/data/game?`; + if(userID) URL += `user=${userID}`; + const response = await fetch(URL); + const game = await response.json(); + return game; +} + export async function getAccounts() { const response = await fetch(`/data/accounts`); const accounts = await response.json(); diff --git a/routes/data.js b/routes/data.js index dfe6b8d..ae4fac3 100644 --- a/routes/data.js +++ b/routes/data.js @@ -122,7 +122,11 @@ router.get('/games', async function(req, res, next) { router.get('/game', async function(req, res, next) { try { const gameID = req.query.game; - const data = await games.getFromID(gameID); + const userID = req.query.user; + + let data; + if(gameID) data = await games.getFromID(gameID); + else data = await games.getLatest(userID); res.json(data); } catch(err) { console.error("ERROR: " + err.message); From 62434279760395699118c1dbdabe840e62ad8ea3 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 21:44:32 -0700 Subject: [PATCH 194/255] Index page updates to latest score --- public/scripts/form.js | 2 +- public/scripts/index.js | 130 ++++++++++++---------------------------- 2 files changed, 39 insertions(+), 93 deletions(-) diff --git a/public/scripts/form.js b/public/scripts/form.js index 5132ea9..631b404 100644 --- a/public/scripts/form.js +++ b/public/scripts/form.js @@ -84,7 +84,7 @@ export async function populateDivisions (divisionDropdown, selectedSportID, sele } } -export async function populateTeams(teamDropdown, selectedSportID, selectedTeamID) { +export async function populateTeams(teamDropdown, selectedSportID, selectedTeamID = undefined) { teamDropdown.innerHTML = ""; if(selectedSportID) { diff --git a/public/scripts/index.js b/public/scripts/index.js index 0beced6..180f8b3 100644 --- a/public/scripts/index.js +++ b/public/scripts/index.js @@ -1,4 +1,5 @@ import * as Data from "./data.js"; +import * as Form from "./form.js"; const sportDropdown = document.getElementById('sport-dropdown'); const seasonDropdown = document.getElementById('year-dropdown'); @@ -15,93 +16,48 @@ const manageButton = document.getElementById('manage-button'); -async function listSeasons() { - seasonDropdown.innerHTML = ""; - - const seasonsList = await Data.getSeasons(); +async function initializeForm() { + let latestGame; - seasonsList.forEach(season => { - const option = document.createElement('option'); - option.text = (season.year - 1) + "-" + season.year; - option.value = season.id; - seasonDropdown.appendChild(option); - }); -} -listSeasons(); - -async function listSports() { - sportDropdown.innerHTML = ""; - - const sportsList = await Data.getSports(); - - sportsList.forEach(sport => { - const option = document.createElement('option'); - option.text = sport.name; - option.value = sport.id; - sportDropdown.appendChild(option); - }); - - listGenders(); -} -listSports(); - -async function listGenders() { - genderDropdown.innerHTML = ""; - - const selectedSportID = sportDropdown.value; - const gendersList = await Data.getGenders(selectedSportID); - - if(selectedSportID) { - gendersList.forEach(gender => { - const option = document.createElement('option'); - option.text = (gender.name == "female") ? "Female" : (gender.name == "male") ? "Male" : ""; - option.value = gender.name; - genderDropdown.appendChild(option); - }); + try { + latestGame = await Data.getLatestGame(); + } catch { + latestGame = null; } - listDivisions(); -} + if(latestGame) { + Form.populateSeasons(seasonDropdown, latestGame.seasonID); -async function listDivisions() { - divisionDropdown.innerHTML = ""; - - const selectedSportID = sportDropdown.value; - const selectedGender = genderDropdown.value; - - if(selectedGender) { - const divisionsList = await Data.getDivisions(selectedSportID, selectedGender); - - divisionsList.forEach(division => { - const option = document.createElement('option'); - option.text = division.name; - option.value = division.id; - divisionDropdown.appendChild(option); - }); - } - listTeams(); -} - -async function listTeams() { - teamDropdown.innerHTML = ""; - - const selectedSportID = sportDropdown.value; - - if(selectedSportID) { - const teamsList = await Data.getTeams(selectedSportID); - - teamsList.forEach(team => { - const option = document.createElement('option'); - option.text = team.name; - option.value = team.id; - teamDropdown.appendChild(option); - }); + const division = await Data.getDivision(latestGame.divisionID); + await Form.populateSports(sportDropdown, division.sportID); + await Form.populateGenders(genderDropdown, sportDropdown.value, division.gender.name); + await Form.populateDivisions(divisionDropdown, sportDropdown.value, genderDropdown.value, latestGame.divisionID); + await Form.populateTeams(teamDropdown, sportDropdown.value, latestGame.team1ID); + } else { + Form.populateSeasons(seasonDropdown); + await Form.populateSports(sportDropdown); + await Form.populateGenders(genderDropdown, sportDropdown.value); + await Form.populateDivisions(divisionDropdown, sportDropdown.value, genderDropdown.value); + await Form.populateTeams(teamDropdown, sportDropdown.value); } - listGames(); -} + sportDropdown.onchange = async () => { + await Form.populateGenders(genderDropdown, sportDropdown.value) + await Form.populateDivisions(divisionDropdown, sportDropdown.value, genderDropdown.value); + await Form.populateTeams(teamDropdown, sportDropdown.value); + }; -async function listGames() { + genderDropdown.onchange = async () => { + await Form.populateDivisions(divisionDropdown, sportDropdown.value, genderDropdown.value); + }; + + loadTable(); + + +} +initializeForm(); + +async function loadTable() { gamesTable.innerHTML = ""; gamesTableHeader.textContent = ""; noScoresMessage.textContent = ""; @@ -146,8 +102,8 @@ async function listGames() { } } } -async function setupGamesTableHeaders() { +async function setupGamesTableHeaders() { const row = document.createElement('tr'); const scoresHeader = document.createElement('th'); @@ -167,16 +123,6 @@ async function setupGamesTableHeaders() { - - -sportDropdown.onchange = (() => { - listGenders(); - listTeams(); -}); -genderDropdown.onchange = listDivisions; -teamDropdown.onchange = listGames; -seasonDropdown.onchange = listGames; - if(addScoreButton) { addScoreButton.addEventListener('click', () => { window.location.href = '/manage/game'; @@ -187,4 +133,4 @@ if(manageButton) { manageButton.addEventListener('click', () => { window.location.href = '/manage' }); -} +} \ No newline at end of file From 8ab01bb6a4967bfbb48b2d0e1f25706672b224dc Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 21:57:02 -0700 Subject: [PATCH 195/255] Automatically fill last submitted game details when submitting new game --- public/scripts/data.js | 4 ++-- public/scripts/manage/game.js | 24 ++++++++++++++++++------ routes/data.js | 6 ++++-- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/public/scripts/data.js b/public/scripts/data.js index 28ec018..9a1c12d 100644 --- a/public/scripts/data.js +++ b/public/scripts/data.js @@ -78,9 +78,9 @@ export async function getGame(gameID) { return game; } -export async function getLatestGame(userID = undefined) { +export async function getLatestGame(ofUser = false) { let URL = `/data/game?`; - if(userID) URL += `user=${userID}`; + if(ofUser) URL += `ofuser=1`; const response = await fetch(URL); const game = await response.json(); return game; diff --git a/public/scripts/manage/game.js b/public/scripts/manage/game.js index ce80b97..9b0e86e 100644 --- a/public/scripts/manage/game.js +++ b/public/scripts/manage/game.js @@ -40,12 +40,24 @@ async function initializeForm() { team2ScoreTextbox.value = game.team2Score; } else { - await Form.populateSeasons(seasonDropdown); - await Form.populateSports(sportDropdown) - await Form.populateGenders(genderDropdown, sportDropdown.value) - await Form.populateDivisions(divisionDropdown, sportDropdown.value, genderDropdown.value); - await Form.populateTeams(team1Dropdown, sportDropdown.value); - await Form.populateTeams(team2Dropdown, sportDropdown.value); + try { + const game = await Data.getLatestGame(true); + + Form.populateSeasons(seasonDropdown, game.seasonID); + const data = await Data.getDivision(game.divisionID) + await Form.populateSports(sportDropdown, data.sportID) + await Form.populateGenders(genderDropdown, sportDropdown.value, data.gender.name) + await Form.populateDivisions(divisionDropdown, sportDropdown.value, genderDropdown.value, game.divisionID); + await Form.populateTeams(team1Dropdown, sportDropdown.value, game.team1ID); + await Form.populateTeams(team2Dropdown, sportDropdown.value, game.team2ID); + } catch { + await Form.populateSeasons(seasonDropdown); + await Form.populateSports(sportDropdown) + await Form.populateGenders(genderDropdown, sportDropdown.value) + await Form.populateDivisions(divisionDropdown, sportDropdown.value, genderDropdown.value); + await Form.populateTeams(team1Dropdown, sportDropdown.value); + await Form.populateTeams(team2Dropdown, sportDropdown.value); + } dateInput.value = (new Date()).toISOString().slice(0,10); } diff --git a/routes/data.js b/routes/data.js index ae4fac3..b6769d5 100644 --- a/routes/data.js +++ b/routes/data.js @@ -122,11 +122,13 @@ router.get('/games', async function(req, res, next) { router.get('/game', async function(req, res, next) { try { const gameID = req.query.game; - const userID = req.query.user; + const ofUser = req.query.ofuser; + const currentUserID = req.user ? req.user[0] : null; let data; if(gameID) data = await games.getFromID(gameID); - else data = await games.getLatest(userID); + else if(ofUser) data = await games.getLatest(currentUserID); + else data = await games.getLatest(); res.json(data); } catch(err) { console.error("ERROR: " + err.message); From 0787a124046504dcfe83f99e070c73a196967f5c Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 22:00:43 -0700 Subject: [PATCH 196/255] Rename management page --- routes/manage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/manage.js b/routes/manage.js index 8432ff8..113f31a 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -14,7 +14,7 @@ var checkLoginStatus = require('./checkLoginStatus'); router.get('/' ,checkLoginStatus.user, function(req, res, next) { - if(req.user[2]) res.render('manage', { title: 'Score Management', userLoggedIn: !!req.user }); + if(req.user[2]) res.render('manage', { title: 'Management Panel', userLoggedIn: !!req.user }); else res.render('manage/manage-nonadmin', { title: "My Games", userLoggedIn: !!req.user }); }); From a309a8216ceeaecf6054bae16da23fb028eb959f Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 22:04:51 -0700 Subject: [PATCH 197/255] Prevent text wrapping for date in index page --- public/scripts/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/public/scripts/index.js b/public/scripts/index.js index 180f8b3..ef28b6f 100644 --- a/public/scripts/index.js +++ b/public/scripts/index.js @@ -92,6 +92,7 @@ async function loadTable() { const dateCell = document.createElement('td'); dateCell.textContent = game.date; + dateCell.style['white-space'] = 'nowrap'; row.appendChild(dateCell); gamesTable.appendChild(row); From 0fc1e03a3689e54bf4b193959dee23d4ea557ef4 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 22:09:33 -0700 Subject: [PATCH 198/255] Update program version --- package-lock.json | 104 ++++++++++++++++++++++------------------------ package.json | 2 +- 2 files changed, 51 insertions(+), 55 deletions(-) diff --git a/package-lock.json b/package-lock.json index ff72bac..946bdbd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "score-tracker", - "version": "1.0.0-pre", + "version": "1.0.1-pre", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "score-tracker", - "version": "1.0.0-pre", + "version": "1.0.1-pre", "dependencies": { "async": "^3.2.2", "bcrypt": "^5.0.1", @@ -629,6 +629,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -927,6 +946,17 @@ "node": ">= 8" } }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/morgan": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", @@ -1431,25 +1461,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/rimraf/node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -1609,17 +1620,6 @@ "node": ">= 10" } }, - "node_modules/tar/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -2218,6 +2218,19 @@ "has-symbols": "^1.0.1" } }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -2437,6 +2450,11 @@ "yallist": "^4.0.0" } }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + }, "morgan": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", @@ -2832,21 +2850,6 @@ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "requires": { "glob": "^7.1.3" - }, - "dependencies": { - "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } } }, "safe-buffer": { @@ -2970,13 +2973,6 @@ "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" - }, - "dependencies": { - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" - } } }, "to-fast-properties": { diff --git a/package.json b/package.json index 91a43c2..08dee1d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "score-tracker", - "version": "1.0.0-pre", + "version": "1.0.1-pre", "private": true, "scripts": { "start": "node ./bin/www" From 1388891ef5833d2cc88ea0ffd9af8860b33830ab Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 22:21:52 -0700 Subject: [PATCH 199/255] Update table on dropdown change for index --- public/scripts/index.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/public/scripts/index.js b/public/scripts/index.js index ef28b6f..1b70ca3 100644 --- a/public/scripts/index.js +++ b/public/scripts/index.js @@ -40,17 +40,24 @@ async function initializeForm() { await Form.populateDivisions(divisionDropdown, sportDropdown.value, genderDropdown.value); await Form.populateTeams(teamDropdown, sportDropdown.value); } + + seasonDropdown.onchange = loadTable; sportDropdown.onchange = async () => { await Form.populateGenders(genderDropdown, sportDropdown.value) await Form.populateDivisions(divisionDropdown, sportDropdown.value, genderDropdown.value); await Form.populateTeams(teamDropdown, sportDropdown.value); + loadTable(); }; genderDropdown.onchange = async () => { await Form.populateDivisions(divisionDropdown, sportDropdown.value, genderDropdown.value); + loadTable(); }; + divisionDropdown.onchange = loadTable; + teamDropdown.onchange = loadTable; + loadTable(); From b9b4e25aecbcf0b6e3b1637002b9f670edde1fc1 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 22:29:19 -0700 Subject: [PATCH 200/255] Add ability to select category via url in management page --- public/scripts/manage.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/public/scripts/manage.js b/public/scripts/manage.js index de4a6e2..32ca8dd 100644 --- a/public/scripts/manage.js +++ b/public/scripts/manage.js @@ -367,10 +367,18 @@ async function listItems(category) { itemsListTable.appendChild(row); }); } +if(window.location.hash) { + let correctIndex; + let index = 0; + CATEGORIES.forEach(category => { + if(window.location.hash == '#' + category.name) correctIndex = index; + index++; + }) + if(correctIndex || correctIndex === 0) categoryDropdown.selectedIndex = correctIndex; +} listItems(CATEGORIES[categoryDropdown.selectedIndex]); - categoryDropdown.onchange = () => { listItems(CATEGORIES[categoryDropdown.selectedIndex]); }; From fcf9d28dd670cc05f2b81b73ea213d8f204f70e8 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 22:33:44 -0700 Subject: [PATCH 201/255] Direct user to specific section in manage page when leaving an edit page --- routes/manage.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/routes/manage.js b/routes/manage.js index 113f31a..a5cdc1b 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -50,11 +50,11 @@ router.post('/game', checkLoginStatus.user, async function(req, res, next) { } else if(remove) { await games.remove(id); - res.redirect("/manage"); + res.redirect('/manage#games'); } else if(id) { await games.edit(id, divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score); - res.redirect('/manage'); + res.redirect('/manage#games'); } else { await games.add(divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score, userID); @@ -81,7 +81,7 @@ router.post('/season', checkLoginStatus.admin, async function(req, res, next) { if(remove) await seasons.remove(seasonID); else await seasons.add(year); - res.redirect('/manage'); + res.redirect('/manage#seasons'); } catch(err) { console.error("ERROR: " + err.message); req.flash("error", "An error has occurred."); @@ -106,7 +106,7 @@ router.post('/sport', checkLoginStatus.admin, async function(req, res, next) { else if(id) await sports.rename(id, name); else await sports.add(name); - res.redirect('/manage'); + res.redirect('/manage#sports'); } catch(err) { console.error("ERROR: " + err.message); req.flash("error", "An error has occurred."); @@ -140,7 +140,7 @@ router.post('/division', checkLoginStatus.admin, async function(req, res, next) await divisions.add(name, gender, sport); } } - res.redirect('/manage'); + res.redirect('/manage#divisions'); } catch(err) { console.error("ERROR: " + err.message); req.flash("error", "An error has occurred."); @@ -162,9 +162,9 @@ router.post('/team', checkLoginStatus.admin, async function(req, res, next) { const name = req.body['name']; const sport = req.body['sport']; - if(remove) teams.remove(id).then(res.redirect('/manage')); - else if(id) teams.rename(id, name).then(res.redirect('/manage')); - else teams.add(name, sport).then(res.redirect("/manage")); + if(remove) teams.remove(id).then(res.redirect('/manage#teams')); + else if(id) teams.rename(id, name).then(res.redirect('/manage#teams')); + else teams.add(name, sport).then(res.redirect('/manage#teams')); } catch(err) { console.error("ERROR: " + err.message); req.flash("error", "An error has occurred."); @@ -209,7 +209,7 @@ router.post('/account', checkLoginStatus.user, async function(req, res, next) { else if(accountID) await accounts.edit(accountID, email, password, isAdmin); else await accounts.create(req.body.email, req.body.password, !!req.body.admin); - res.redirect('/manage'); + res.redirect('/manage#accounts'); } catch (err) { console.error("ERROR: " + err.message); From c3d5447ce01df35ebc3d8f43a29e4eb44c787b1c Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 22:39:20 -0700 Subject: [PATCH 202/255] Prevent zooming in --- views/layout.pug | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/layout.pug b/views/layout.pug index 80d0238..e05e793 100644 --- a/views/layout.pug +++ b/views/layout.pug @@ -2,7 +2,7 @@ doctype html html head title= title + ' - Score Tracker' - meta(name='viewport', content='width=device-width, initial-scale=1') + meta(name='viewport', content='width=device-width, minimum-scale=1.0, maximum-scale=1.0, initial-scale=1, user-scalable=no') link(rel='stylesheet', href='/stylesheets/style.css') block stylesheets body From 63ab6e41ac04947b1281f5c5f3a5ccfe342c128b Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 23:09:05 -0700 Subject: [PATCH 203/255] Add link for about page --- public/stylesheets/style.css | 5 +++++ views/layout.pug | 3 +++ 2 files changed, 8 insertions(+) diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css index c4252bd..a3f1d85 100644 --- a/public/stylesheets/style.css +++ b/public/stylesheets/style.css @@ -54,4 +54,9 @@ input { padding: 1em; margin-bottom: 3em; border-radius: .25em; +} + +#about-footer { + margin-top: 3em; + text-align: center; } \ No newline at end of file diff --git a/views/layout.pug b/views/layout.pug index e05e793..04577b9 100644 --- a/views/layout.pug +++ b/views/layout.pug @@ -19,5 +19,8 @@ html button#login-button Log in h1 #{title} block content + div#about-footer + a(href="/about") Help/About + block scripts script(src='/scripts/main.js' type="module") From ebb432b5671526147a7b16c8bb1dcce121a8bec2 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 23:14:29 -0700 Subject: [PATCH 204/255] Create about page --- app.js | 2 ++ public/stylesheets/index.css | 2 +- public/stylesheets/style.css | 9 +++++++++ routes/about.js | 8 ++++++++ views/about.pug | 13 +++++++++++++ 5 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 routes/about.js create mode 100644 views/about.pug diff --git a/app.js b/app.js index 4065b14..9764a31 100644 --- a/app.js +++ b/app.js @@ -14,6 +14,7 @@ var indexRouter = require('./routes/index'); var dataRouter = require('./routes/data'); var manageRouter = require('./routes/manage'); var authRouter = require('./routes/auth'); +var aboutRouter = require('./routes/about'); var app = express(); @@ -51,6 +52,7 @@ app.use('/', indexRouter); app.use('/data', dataRouter); app.use('/manage', manageRouter); app.use('/auth', authRouter); +app.use('/about', aboutRouter); // catch 404 and forward to error handler diff --git a/public/stylesheets/index.css b/public/stylesheets/index.css index d08e1e8..eb71b13 100644 --- a/public/stylesheets/index.css +++ b/public/stylesheets/index.css @@ -31,4 +31,4 @@ tr { #login-button { margin-left: auto; -} \ No newline at end of file +} diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css index a3f1d85..1255c30 100644 --- a/public/stylesheets/style.css +++ b/public/stylesheets/style.css @@ -59,4 +59,13 @@ input { #about-footer { margin-top: 3em; text-align: center; +} + + +p { + line-height: 2; +} + +a { + color: black; } \ No newline at end of file diff --git a/routes/about.js b/routes/about.js new file mode 100644 index 0000000..7162c62 --- /dev/null +++ b/routes/about.js @@ -0,0 +1,8 @@ +var express = require('express'); +var router = express.Router(); + +router.get('/', function(req, res, next) { + res.render('about', { title: 'About Score Tracker', hideHomeButton: false }); +}); + +module.exports = router; diff --git a/views/about.pug b/views/about.pug new file mode 100644 index 0000000..d22f563 --- /dev/null +++ b/views/about.pug @@ -0,0 +1,13 @@ +extends layout + +block stylesheets + link(rel='stylesheet', href='/stylesheets/index.css') + +block actions + + + +block content + p Created by #[a(href="https://ethanreece.com") Ethan Reece], a student at #[a(href="https://colevalleychristian.org") Cole Valley Christian Schools]. + p Need help? Found a bug? Email: #[a(href="mailto:scoretrackerhelp@ethanreece.com") scoretrackerhelp@ethanreece.com] + p #[a(href="https://gitlab.sudoer.ch/sudoer777/score-tracker") Git repo] \ No newline at end of file From 2261c3e0ac3c49534a7392b76ed73dd23728a11e Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 23:18:13 -0700 Subject: [PATCH 205/255] Update version to 1.0.2-pre --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 946bdbd..8058b96 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "score-tracker", - "version": "1.0.1-pre", + "version": "1.0.2-pre", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "score-tracker", - "version": "1.0.1-pre", + "version": "1.0.2-pre", "dependencies": { "async": "^3.2.2", "bcrypt": "^5.0.1", diff --git a/package.json b/package.json index 08dee1d..890e07e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "score-tracker", - "version": "1.0.1-pre", + "version": "1.0.2-pre", "private": true, "scripts": { "start": "node ./bin/www" From 33a2499981ff8ca4da4cca1b0f8947e9d946d104 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 23:47:25 -0700 Subject: [PATCH 206/255] =?UTF-8?q?Change=20edit=20button=20from=20"E"=20t?= =?UTF-8?q?o=20"=E2=9C=8E"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/scripts/manage.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/scripts/manage.js b/public/scripts/manage.js index 32ca8dd..2ab7cbd 100644 --- a/public/scripts/manage.js +++ b/public/scripts/manage.js @@ -355,7 +355,8 @@ async function listItems(category) { const editSpan = document.createElement('span'); const editButton = document.createElement('button'); - editButton.textContent = "E"; + editButton.textContent = "✎"; + editButton.style['font-size'] = '1.25em'; editButton.addEventListener('click', () => { CATEGORIES[categoryDropdown.selectedIndex].editItem(item.id); }); From ff7e87a5f948407bd5a6d03d8a6f0b86b8913436 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 26 Nov 2021 23:50:35 -0700 Subject: [PATCH 207/255] Update version to 1.0.3-pre --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8058b96..90eb91e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "score-tracker", - "version": "1.0.2-pre", + "version": "1.0.3-pre", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "score-tracker", - "version": "1.0.2-pre", + "version": "1.0.3-pre", "dependencies": { "async": "^3.2.2", "bcrypt": "^5.0.1", diff --git a/package.json b/package.json index 890e07e..b504c93 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "score-tracker", - "version": "1.0.2-pre", + "version": "1.0.3-pre", "private": true, "scripts": { "start": "node ./bin/www" From fa13b227d25bb7daedbacab9a840c5da7b5c43c6 Mon Sep 17 00:00:00 2001 From: Sudoer777 Date: Sat, 27 Nov 2021 08:19:04 +0000 Subject: [PATCH 208/255] Update README.md --- README.md | 50 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index c5659bc..854032e 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,49 @@ -### Node Express template project +# Score Tracker -This project is based on a GitLab [Project Template](https://docs.gitlab.com/ee/gitlab-basics/create-project.html). +Main repository: https://gitlab.sudoer.ch/sudoer777/score-tracker/ -Improvements can be proposed in the [original project](https://gitlab.com/gitlab-org/project-templates/express). +A web app designed to collect and display scores for sports -### CI/CD with Auto DevOps +## Branches -This template is compatible with [Auto DevOps](https://docs.gitlab.com/ee/topics/autodevops/). +- [main](https://gitlab.sudoer.ch/sudoer777/score-tracker/-/tree/main) - Stable, production-ready code +- [testing](https://gitlab.sudoer.ch/sudoer777/score-tracker/-/tree/testing) - Nearly complete code being tested +- [develop](https://gitlab.sudoer.ch/sudoer777/score-tracker/-/tree/develop) - Unstable code under development -If Auto DevOps is not already enabled for this project, you can [turn it on](https://docs.gitlab.com/ee/topics/autodevops/#enabling-auto-devops) in the project settings. +## Installation -### Developing with Gitpod +This repository is designed to be pushed to Heroku/Dokku/etc. -This template has a fully-automated dev setup for [Gitpod](https://docs.gitlab.com/ee/integration/gitpod.html). +### Requirements -If you open this project in Gitpod, you'll get all Node dependencies pre-installed and Express will open a web preview. +- PostgreSQL (with empty database created and an account to access it) + +### Environment Variables + +- `NODE_ENV` - set to `production`, `testing`, or `development` +- `PGHOST` - set to your database URL +- `PGPORT` - set to the database port +- `PGDATABASE` - set to the name of your database (i.e. `scoretrackerdb`) +- `PGUSER` - set to the user for managing the database +- `PGPASSWORD` - set to the password for that user + +## Code + +This program uses Node.js/Express.js for the backend, PostgreSQL for the database (with node-postgres), and Passport.js for managing users and sessions. + +### Structure + +- `database` folder contains backend scripts for managing and storing data. +- `mail` folder (currenly unused) contains backend scripts for sending emails. +- `public` folder contains publically accessible scripts and stylesheets for frontend. + - `scripts` folder contains scripts used by specific webpages. + - `stylesheets` folder contains CSS for various webpages. +- `routes` folder contains various routes used by the program. + - `about.js` directs to the about page (`/about`). + - `auth.js` deals with logging in and out (`/auth/*`). + - `checkLoginStatus.js` contains functions for checking the login status of the current user. + - `data.js` sends various data to the client in JSON format (`/data/*`). + - `index.js` directs to the home page (`/`). + - `manage.js` contains various functions that allows the user to add and edit items through the web browser (`/manage/*`). +- `views` folder contains pug templates for each webpage, and a `layout` template for the base layout of each page. +- `.env.example` is a template for the environment variables in a development workspace. Rename to `.env` and change values as needed. From 6621bf3d7e5fc49f742acd435b2c47693f004692 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 1 Dec 2021 11:04:18 -0700 Subject: [PATCH 209/255] Create migration to add latest migration to database --- database/migrations/1_add_metadata_table.sql | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 database/migrations/1_add_metadata_table.sql diff --git a/database/migrations/1_add_metadata_table.sql b/database/migrations/1_add_metadata_table.sql new file mode 100644 index 0000000..2bfb449 --- /dev/null +++ b/database/migrations/1_add_metadata_table.sql @@ -0,0 +1,11 @@ +BEGIN; + +CREATE TABLE IF NOT EXISTS metadata( + property TEXT UNIQUE NOT NULL, + value TEXT NOT NULL +); + +INSERT INTO metadata(property, value) + VALUES("latest_migration", "1"); + +COMMIT; \ No newline at end of file From 8bf2b8a275065cca5b66f13eae354108376467b7 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 1 Dec 2021 11:13:37 -0700 Subject: [PATCH 210/255] Add migration to add full_name column to accounts table --- database/migrations/2_add_account_names.sql | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 database/migrations/2_add_account_names.sql diff --git a/database/migrations/2_add_account_names.sql b/database/migrations/2_add_account_names.sql new file mode 100644 index 0000000..efb1da5 --- /dev/null +++ b/database/migrations/2_add_account_names.sql @@ -0,0 +1,10 @@ +BEGIN; + +ALTER TABLE accounts.users +ADD COLUMN full_name TEXT; + +UPDATE metadata +SET value = '2' +WHERE property = "latest_migration"; + +COMMIT; \ No newline at end of file From dd85df48c5db8e40dc379ceaaaa318f4db9cb14e Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 1 Dec 2021 11:15:13 -0700 Subject: [PATCH 211/255] Update database initialization script --- database/init_database.sql | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/database/init_database.sql b/database/init_database.sql index 404addf..83ff6c4 100644 --- a/database/init_database.sql +++ b/database/init_database.sql @@ -37,6 +37,7 @@ CREATE TABLE IF NOT EXISTS accounts.users( email TEXT UNIQUE NOT NULL, password TEXT NOT NULL, admin BOOLEAN NOT NULL DEFAULT FALSE, + full_name TEXT NOT NULL, PRIMARY KEY(user_id) ); @@ -110,4 +111,13 @@ CREATE TABLE IF NOT EXISTS scores.games( REFERENCES accounts.users(user_id) ); + +CREATE TABLE IF NOT EXISTS metadata( + property TEXT UNIQUE NOT NULL, + value TEXT NOT NULL +); + +INSERT INTO metadata(property, value) + VALUES("latest_migration", "2"); + COMMIT; \ No newline at end of file From b5d42a4cd308e3efb431a1163a45c8fc599d02cf Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 2 Dec 2021 12:49:37 -0700 Subject: [PATCH 212/255] Add built-in database migration support --- database/database.js | 31 ++++++++++++++++--- .../{1_add_metadata_table.sql => 1.sql} | 4 ++- .../{2_add_account_names.sql => 2.sql} | 4 ++- 3 files changed, 33 insertions(+), 6 deletions(-) rename database/migrations/{1_add_metadata_table.sql => 1.sql} (67%) rename database/migrations/{2_add_account_names.sql => 2.sql} (56%) diff --git a/database/database.js b/database/database.js index 7390426..08c1a37 100644 --- a/database/database.js +++ b/database/database.js @@ -27,17 +27,40 @@ async function Initialize() { async function checkForDatabaseInitialization() { - const scoresSchemaExistsQuery = `SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'scores'`; - let result = await executeQuery(scoresSchemaExistsQuery); + const databaseIsSetupQuery = `SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'scores'`; + let result = await executeQuery(databaseIsSetupQuery); - const scoresSchemaExists = result.length !== 0; + const databaseIsSetup = result.length !== 0; - if(!scoresSchemaExists) { + if(!databaseIsSetup) { await Initialize(); } + + + let latestMigration; + try { + const latestMigrationQuery = `SELECT value FROM metadata WHERE property = 'latest_migration';`; + latestMigration = (await executeQuery(latestMigrationQuery))[0][0]; + } catch { + latestMigration = 0; + } + + + await performMigrations(latestMigration); } const initializationStatus = checkForDatabaseInitialization(); +async function performMigrations(currentMigration) { + const migrationFileList = fs.readdirSync('database/migrations'); + const latestMigration = +migrationFileList[migrationFileList.length - 1].slice(0, 1); + + for(let i = currentMigration + 1; i <= latestMigration; i++) { + const sql = fs.readFileSync(`database/migrations/${i}.sql`).toString(); + await executeQuery(sql); + console.log(`Performed database migration ${i}`); + } +} + diff --git a/database/migrations/1_add_metadata_table.sql b/database/migrations/1.sql similarity index 67% rename from database/migrations/1_add_metadata_table.sql rename to database/migrations/1.sql index 2bfb449..f9a5c78 100644 --- a/database/migrations/1_add_metadata_table.sql +++ b/database/migrations/1.sql @@ -1,3 +1,5 @@ +/* ADD METADATA TABLE */ + BEGIN; CREATE TABLE IF NOT EXISTS metadata( @@ -6,6 +8,6 @@ CREATE TABLE IF NOT EXISTS metadata( ); INSERT INTO metadata(property, value) - VALUES("latest_migration", "1"); + VALUES('latest_migration', '1'); COMMIT; \ No newline at end of file diff --git a/database/migrations/2_add_account_names.sql b/database/migrations/2.sql similarity index 56% rename from database/migrations/2_add_account_names.sql rename to database/migrations/2.sql index efb1da5..6a22cdb 100644 --- a/database/migrations/2_add_account_names.sql +++ b/database/migrations/2.sql @@ -1,3 +1,5 @@ +/* ADD ACCOUNT NAME COLUMN */ + BEGIN; ALTER TABLE accounts.users @@ -5,6 +7,6 @@ ADD COLUMN full_name TEXT; UPDATE metadata SET value = '2' -WHERE property = "latest_migration"; +WHERE property = 'latest_migration'; COMMIT; \ No newline at end of file From 10bc7addb771300de26cfe7a9995e0eac0bdac93 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 2 Dec 2021 13:06:02 -0700 Subject: [PATCH 213/255] Add name textbox for account management page --- views/accounts/createuser.pug | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/views/accounts/createuser.pug b/views/accounts/createuser.pug index dfc1b13..b0c0ca2 100644 --- a/views/accounts/createuser.pug +++ b/views/accounts/createuser.pug @@ -8,10 +8,14 @@ block content form#submission-form(action='/manage/account', method='POST') if accountID input#account-id(type="hidden" name="account" value=accountID) + span(class='form-section') + label Name + span(class='form-section-input') + input#name-textbox(type="text" name="name" disabled) span(class='form-section') label Email span(class='form-section-input') - input#email-textbox(type="email", name="email" disabled) + input#email-textbox(type="email" name="email" disabled) span(class='form-section') label Password span(class='form-section-input' ) From 9f5ef9239b5a6a61e1b603d8e835be53f4bb5222 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 2 Dec 2021 13:13:14 -0700 Subject: [PATCH 214/255] Edit account.js database functions to deal with names --- database/accounts/accounts.js | 39 +++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/database/accounts/accounts.js b/database/accounts/accounts.js index d33d11f..8334d27 100644 --- a/database/accounts/accounts.js +++ b/database/accounts/accounts.js @@ -4,10 +4,11 @@ const localStrategy = require('passport-local').Strategy; const bcrypt = require('bcrypt'); class User { - constructor(id, email, isAdmin) { + constructor(id, email, isAdmin, name) { this.id = id; this.email = email; this.isAdmin = isAdmin; + this.name = name; } } @@ -75,62 +76,64 @@ async function generateHash(password) { return bcrypt.hashSync(password, salt); } -async function create(email, password, isAdmin) { +async function create(email, password, isAdmin, name) { const hash = await generateHash(password); - const query = `INSERT INTO accounts.users(email, password, admin) - VALUES($1, $2, $3)`; - await database.executeQuery(query, [email, hash, isAdmin]); + const query = `INSERT INTO accounts.users(email, password, admin, name) + VALUES($1, $2, $3, $4)`; + await database.executeQuery(query, [email, hash, isAdmin, name]); } -async function edit(id, email, password, isAdmin) { +async function edit(id, email, password, isAdmin, name) { if(password) { const hash = await generateHash(password); const query = `UPDATE accounts.users SET email = $2, password = $3, - admin = $4 + admin = $4, + name = $5 WHERE user_id = $1;`; - await database.executeQuery(query, [id, email, hash, isAdmin]); + await database.executeQuery(query, [id, email, hash, isAdmin, name]); } else { const query = `UPDATE accounts.users SET email = $2, - admin = $3 + admin = $3, + name = $4 WHERE user_id = $1;`; - await database.executeQuery(query, [id, email, isAdmin]); + await database.executeQuery(query, [id, email, isAdmin, name]); } - return new User(id, email, isAdmin); + return new User(id, email, isAdmin, name); } async function remove(id) { const query = `DELETE FROM accounts.users WHERE user_id = $1 - RETURNING email, admin;`; + RETURNING email, admin, name;`; const row = (await database.executeQuery(query, [id]))[0]; - return new User(id, row[0], row[1]); + return new User(id, row[0], row[1], row[2]); } async function retrieveAll() { - const query = `SELECT user_id, email, admin + const query = `SELECT user_id, email, admin, name FROM accounts.users - ORDER BY email;` + ORDER BY name;`; const table = await database.executeQuery(query); const accountsList = []; table.forEach((row) => { - accountsList.push(new User(row[0], row[1], row[2])); + accountsList.push(new User(row[0], row[1], row[2], row[3])); }); return accountsList; } async function getFromID(id) { - const query = `SELECT user_id, email, admin + const query = `SELECT user_id, email, admin, name FROM accounts.users WHERE user_id = $1;`; const row = (await database.executeQuery(query, [id]))[0]; - return new User(id, row[1], row[2]); + return new User(id, row[1], row[2], row[3]); } exports.create = create; From 9bcb3adfe2d7762f4e3d78fd5e1239c7b2334c43 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 2 Dec 2021 13:16:07 -0700 Subject: [PATCH 215/255] Add API support for managing account names --- routes/manage.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/routes/manage.js b/routes/manage.js index a5cdc1b..b1d8809 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -189,6 +189,7 @@ router.get('/account', checkLoginStatus.user, (req, res, next) => { }); router.post('/account', checkLoginStatus.user, async function(req, res, next) { + const name = req.body.name; const email = req.body.email; const password = req.body.password; @@ -206,8 +207,8 @@ router.post('/account', checkLoginStatus.user, async function(req, res, next) { const isAdmin = loggedInAccountIsAdmin ? !!req.body.admin : false; if(remove) await accounts.remove(accountID); - else if(accountID) await accounts.edit(accountID, email, password, isAdmin); - else await accounts.create(req.body.email, req.body.password, !!req.body.admin); + else if(accountID) await accounts.edit(accountID, email, password, isAdmin, name); + else await accounts.create(email, password, !!req.body.admin, name); res.redirect('/manage#accounts'); } From 73da74a87060f50849ec2fecf9795465c572149b Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 2 Dec 2021 13:21:18 -0700 Subject: [PATCH 216/255] Add GUI elements to manage account names --- public/scripts/manage/account.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/public/scripts/manage/account.js b/public/scripts/manage/account.js index 40b252f..4b11006 100644 --- a/public/scripts/manage/account.js +++ b/public/scripts/manage/account.js @@ -2,6 +2,7 @@ import * as Data from "../data.js"; import * as Form from "../form.js"; const submissionForm = document.getElementById('submission-form'); +const nameTextbox = document.getElementById('name-textbox'); const emailTextbox = document.getElementById('email-textbox'); const passwordTextbox = document.getElementById('password-textbox'); const adminCheckboxSection = document.getElementById('admin-checkbox-section'); @@ -16,6 +17,8 @@ async function Initialize() { const account = await Data.getAccount(accountID); console.log(account); + nameTextbox.value = account.name; + emailTextbox.value = account.email; passwordTextbox.placeholder = "leave unchanged"; @@ -37,6 +40,8 @@ async function Initialize() { adminCheckboxSection.style.visibility = "visible"; adminCheckbox.disabled = false; } + nameTextbox.disabled = false; + nameTextbox.addEventListener('keyup', checkDataValidity); emailTextbox.disabled = false; emailTextbox.addEventListener('keyup', checkDataValidity); passwordTextbox.disabled = false; @@ -49,6 +54,7 @@ async function checkDataValidity() { let dataIsValid = true; if(!passwordTextbox.value && !passwordTextbox.placeholder) dataIsValid = false; + if(!nameTextbox.value) dataIsValid = false; if(!emailTextbox.value) dataIsValid = false; if(dataIsValid) submitButton.disabled = false; From 6c84b0433c2f798fb044106c758d365272a77e8e Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 2 Dec 2021 13:22:47 -0700 Subject: [PATCH 217/255] Fix bug in accounts.js database file --- database/accounts/accounts.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/database/accounts/accounts.js b/database/accounts/accounts.js index 8334d27..921a1cf 100644 --- a/database/accounts/accounts.js +++ b/database/accounts/accounts.js @@ -79,7 +79,7 @@ async function generateHash(password) { async function create(email, password, isAdmin, name) { const hash = await generateHash(password); - const query = `INSERT INTO accounts.users(email, password, admin, name) + const query = `INSERT INTO accounts.users(email, password, admin, full_name) VALUES($1, $2, $3, $4)`; await database.executeQuery(query, [email, hash, isAdmin, name]); } @@ -92,14 +92,14 @@ async function edit(id, email, password, isAdmin, name) { SET email = $2, password = $3, admin = $4, - name = $5 + full_name = $5 WHERE user_id = $1;`; await database.executeQuery(query, [id, email, hash, isAdmin, name]); } else { const query = `UPDATE accounts.users SET email = $2, admin = $3, - name = $4 + full_name = $4 WHERE user_id = $1;`; await database.executeQuery(query, [id, email, isAdmin, name]); } @@ -109,15 +109,15 @@ async function edit(id, email, password, isAdmin, name) { async function remove(id) { const query = `DELETE FROM accounts.users WHERE user_id = $1 - RETURNING email, admin, name;`; + RETURNING email, admin, full_name;`; const row = (await database.executeQuery(query, [id]))[0]; return new User(id, row[0], row[1], row[2]); } async function retrieveAll() { - const query = `SELECT user_id, email, admin, name + const query = `SELECT user_id, email, admin, full_name FROM accounts.users - ORDER BY name;`; + ORDER BY full_name;`; const table = await database.executeQuery(query); const accountsList = []; @@ -128,7 +128,7 @@ async function retrieveAll() { } async function getFromID(id) { - const query = `SELECT user_id, email, admin, name + const query = `SELECT user_id, email, admin, full_name FROM accounts.users WHERE user_id = $1;`; const row = (await database.executeQuery(query, [id]))[0]; From e9e085e804d154dbc9945d9526c2f885bd0fc4fb Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 2 Dec 2021 13:23:37 -0700 Subject: [PATCH 218/255] Remove console.log --- public/scripts/manage/account.js | 1 - 1 file changed, 1 deletion(-) diff --git a/public/scripts/manage/account.js b/public/scripts/manage/account.js index 4b11006..f9a7370 100644 --- a/public/scripts/manage/account.js +++ b/public/scripts/manage/account.js @@ -15,7 +15,6 @@ async function Initialize() { let accountID = params.get('account') || (document.getElementById('account-id') ? document.getElementById('account-id').value : null); if(accountID) { const account = await Data.getAccount(accountID); - console.log(account); nameTextbox.value = account.name; From d72e20b067f345a830f54c2abf237e92726cc241 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 2 Dec 2021 13:26:29 -0700 Subject: [PATCH 219/255] Add name column to account management interface --- public/scripts/manage.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/public/scripts/manage.js b/public/scripts/manage.js index 2ab7cbd..f6fd2fe 100644 --- a/public/scripts/manage.js +++ b/public/scripts/manage.js @@ -302,6 +302,10 @@ CATEGORIES.push(new Category( async function listAccountHeaders() { const headerRow = document.createElement('tr'); + const nameHeader = document.createElement('th'); + nameHeader.textContent = "Name"; + headerRow.appendChild(nameHeader); + const emailHeader = document.createElement('th'); emailHeader.textContent = "Email"; headerRow.appendChild(emailHeader); @@ -316,7 +320,11 @@ CATEGORIES.push(new Category( itemsListTable.appendChild(headerRow); }, - function listAccount(account, row) { + function listAccount(account, row) { + const nameCell = document.createElement('td'); + nameCell.textContent = account.name; + row.appendChild(nameCell); + const emailCell = document.createElement('td'); emailCell.textContent = account.email; row.appendChild(emailCell); From aecdee3b5199303cc792adcc3124c242fdfd7ad9 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Thu, 2 Dec 2021 13:35:24 -0700 Subject: [PATCH 220/255] Add submitter to games in management panel --- database/scores/games.js | 8 ++++---- public/scripts/manage.js | 9 +++++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/database/scores/games.js b/database/scores/games.js index e01352c..4c5b22e 100644 --- a/database/scores/games.js +++ b/database/scores/games.js @@ -41,14 +41,14 @@ async function retrieve(teamID, divisionID, seasonID) { let table; if(teamID && divisionID && seasonID) { - const query = `SELECT game_id, division_id, season_id, game_date, team1_id, team2_id, team1_score, team2_score + const query = `SELECT game_id, division_id, season_id, game_date, team1_id, team2_id, team1_score, team2_score, submitter_id FROM scores.games WHERE (team1_id = $1 OR team2_id = $1) AND division_id = $2 AND season_id = $3 ORDER BY game_date DESC;`; table = await database.executeQuery(query, [teamID,divisionID,seasonID]); } else { - const query = `SELECT game_id, division_id, season_id, game_date, team1_id, team2_id, team1_score, team2_score + const query = `SELECT game_id, division_id, season_id, game_date, team1_id, team2_id, team1_score, team2_score, submitter_id FROM scores.games ORDER BY game_date DESC;`; table = await database.executeQuery(query); @@ -63,10 +63,10 @@ async function retrieve(teamID, divisionID, seasonID) { const teamScore = opponentIsTeam2 ? row[6] : row[7]; const opponentScore = opponentIsTeam2 ? row[7] : row[6]; - gamesList.push(new Game(row[0], row[3].toISOString().slice(0,10), teamID, opponentID, teamScore, opponentScore, row[1], row[2])); + gamesList.push(new Game(row[0], row[3].toISOString().slice(0,10), teamID, opponentID, teamScore, opponentScore, row[1], row[2], row[8])); } else { - gamesList.push(new Game(row[0], row[3].toISOString().slice(0,10), row[4], row[5], row[6], row[7], row[1], row[2])); + gamesList.push(new Game(row[0], row[3].toISOString().slice(0,10), row[4], row[5], row[6], row[7], row[1], row[2], row[8])); } }); return gamesList; diff --git a/public/scripts/manage.js b/public/scripts/manage.js index f6fd2fe..2b958f9 100644 --- a/public/scripts/manage.js +++ b/public/scripts/manage.js @@ -232,6 +232,10 @@ CATEGORIES.push(new Category( dateHeader.textContent = "Date"; headerRow.appendChild(dateHeader); + const submitterHeader = document.createElement('th'); + submitterHeader.textContent = "Submitter"; + headerRow.appendChild(submitterHeader); + itemsListTable.appendChild(headerRow); }, function listGame(game, row) { @@ -285,6 +289,11 @@ CATEGORIES.push(new Category( dateSpan.textContent = game.date.slice(5); dateCell.appendChild(dateSpan); row.appendChild(dateCell); + + const submitterCell = document.createElement('td'); + Data.getAccount(game.submitterID) + .then(data => submitterCell.textContent = data.name); + row.appendChild(submitterCell); }, async function addGame() { window.location.href = "/manage/game"; From 4e8e27113fa5e0e8820fe1155a9dbda0d4b8fa1e Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Fri, 3 Dec 2021 11:28:54 -0700 Subject: [PATCH 221/255] Edit scores.games table in database init script --- database/init_database.sql | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/database/init_database.sql b/database/init_database.sql index 83ff6c4..29e4a6f 100644 --- a/database/init_database.sql +++ b/database/init_database.sql @@ -15,14 +15,14 @@ scores: *season_id* | school_year games: - *game_id* | ~division_id~ | ~season_id~ | game_date | ~team1_id~ | ~team2_id~ | team1_score | team2_score | ~submitter_id~ | updated_timestamp + *game_id* | ~division_id~ | ~season_id~ | game_date | ~team1_id~ | ~team2_id~ | team1_score | team2_score | ~submitter_id~ | updated_timestamp | submitter_name accounts: users: - *user_id* | email | password | admin + *user_id* | email | password | admin | full_name */ @@ -91,7 +91,8 @@ CREATE TABLE IF NOT EXISTS scores.games( team2_id BIGINT NOT NULL, team1_score INTEGER NOT NULL, team2_score INTEGER NOT NULL, - submitter_id BIGINT NOT NULL, + submitter_name TEXT, + submitter_id BIGINT, updated_timestamp TIMESTAMP WITH TIME ZONE DEFAULT now(), PRIMARY KEY(game_id), CONSTRAINT fk_division From 5abc125a52f664c95592c0e7bb378c95e00d9e61 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 6 Dec 2021 10:39:24 -0700 Subject: [PATCH 222/255] Add migration script to add optional submitter_name column --- database/migrations/3.sql | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 database/migrations/3.sql diff --git a/database/migrations/3.sql b/database/migrations/3.sql new file mode 100644 index 0000000..117779a --- /dev/null +++ b/database/migrations/3.sql @@ -0,0 +1,15 @@ +/* ADD OPTIONAL SUBMITTER NAME COLUMN IN GAMES TABLE */ + +BEGIN; + +ALTER TABLE scores.games ALTER COLUMN submitter_id DROP NOT NULL; + +ALTER TABLE scores.games +ADD COLUMN submitter_name TEXT; + + +UPDATE metadata +SET value = '3' +WHERE property = 'latest_migration'; + +COMMIT; \ No newline at end of file From 47fdf2ccb86310fb218c58ef0a84987e6b1077a4 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 6 Dec 2021 10:43:40 -0700 Subject: [PATCH 223/255] Add /submit to redirect to /manage/game --- routes/index.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/routes/index.js b/routes/index.js index 42fa2b0..c18f5a5 100644 --- a/routes/index.js +++ b/routes/index.js @@ -1,9 +1,12 @@ var express = require('express'); var router = express.Router(); -/* GET home page. */ router.get('/', function(req, res, next) { res.render('index', { title: 'View Scores', userLoggedIn: !!req.user, hideHomeButton: true }); }); +router.get('/submit', function(req, res, next) { + res.redirect('/manage/game'); +}); + module.exports = router; From 06a7970fe7319036bff7bb1dabc7de5e3bf7dcc8 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 6 Dec 2021 10:51:17 -0700 Subject: [PATCH 224/255] Add PUBLIC_SUBMIT_PAGE environment variable setting --- .env.example | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.env.example b/.env.example index bdbddab..d942911 100644 --- a/.env.example +++ b/.env.example @@ -6,6 +6,8 @@ PGPASSWORD=dbuserpassword PGDATABASE=mydatabase PGPORT=5432 +PUBLIC_SUBMIT_PAGE=false + #MAIL_FROM=fromaddress@example.com #MAIL_HOST=smtp.smtphost.net #MAIL_PORT=465 From 7f6fd92abcb735041b902806071e0d0595cad2e9 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 6 Dec 2021 11:27:15 -0700 Subject: [PATCH 225/255] Add proper redirects for PUBLIC_SUBMIT_PAGE variable --- routes/manage.js | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/routes/manage.js b/routes/manage.js index b1d8809..60e5b96 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -12,16 +12,32 @@ var accounts = require('../database/accounts/accounts'); var checkLoginStatus = require('./checkLoginStatus'); +if (process.env.NODE_ENV !== 'production' || process.env.NODE_ENV !== 'testing') { + require('dotenv').config(); +} + router.get('/' ,checkLoginStatus.user, function(req, res, next) { if(req.user[2]) res.render('manage', { title: 'Management Panel', userLoggedIn: !!req.user }); else res.render('manage/manage-nonadmin', { title: "My Games", userLoggedIn: !!req.user }); }); -router.get('/game', checkLoginStatus.user, function(req, res, next) { - let title = req.query.game ? 'Edit Game' : 'Submit Score' - - res.render('manage/addgame', { title, userLoggedIn: !!req.user, message: req.flash('error') }); +router.get('/game', function(req, res, next) { + if(!(process.env.PUBLIC_SUBMIT_PAGE && process.env.PUBLIC_SUBMIT_PAGE.toLowerCase() == 'true')) { + if (req.user) { + next(); + } + else { + res.redirect('/auth/login'); + }; + } else { + next(); + } + }, + function(req, res, next) { + let title = req.query.game ? 'Edit Game' : 'Submit Score' + + res.render('manage/addgame', { title, userLoggedIn: !!req.user, message: req.flash('error') }); }); router.post('/game', checkLoginStatus.user, async function(req, res, next) { From 0e6c1212925394351879274209da98d7f365d43c Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 6 Dec 2021 11:32:58 -0700 Subject: [PATCH 226/255] Add textbox in game submissions for submitter name --- views/manage/addgame.pug | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/views/manage/addgame.pug b/views/manage/addgame.pug index 3a44834..69bd3c0 100644 --- a/views/manage/addgame.pug +++ b/views/manage/addgame.pug @@ -45,6 +45,11 @@ block content label Score span(class='form-section-input') input#team2-score-textbox(type="number", name="team2-score", value="0" disabled) + if !userLoggedIn + span(class='form-section') + label Your name + span(class='form-section-input') + input#name-input(type="text" name="name" disabled) .error #{message} span(class='form-section') button#submit-button(type="submit" disabled) Submit From a113b1ee3879ce6293e419cb2371914344e01098 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 6 Dec 2021 11:33:04 -0700 Subject: [PATCH 227/255] Add semicolon to manage.js --- routes/manage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/manage.js b/routes/manage.js index 60e5b96..d03bb2d 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -35,7 +35,7 @@ router.get('/game', function(req, res, next) { } }, function(req, res, next) { - let title = req.query.game ? 'Edit Game' : 'Submit Score' + let title = req.query.game ? 'Edit Game' : 'Submit Score'; res.render('manage/addgame', { title, userLoggedIn: !!req.user, message: req.flash('error') }); }); From ac30b71b48f7b0707f62dc242ccedbdd847b8e5b Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 6 Dec 2021 11:43:30 -0700 Subject: [PATCH 228/255] Complete addition of name textbox to game submit form --- public/scripts/manage/game.js | 7 +++++++ views/manage/addgame.pug | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/public/scripts/manage/game.js b/public/scripts/manage/game.js index 9b0e86e..5d0bd29 100644 --- a/public/scripts/manage/game.js +++ b/public/scripts/manage/game.js @@ -12,6 +12,7 @@ const team1Dropdown = document.getElementById('team1-dropdown'); const team2Dropdown = document.getElementById('team2-dropdown'); const team1ScoreTextbox = document.getElementById('team1-score-textbox'); const team2ScoreTextbox = document.getElementById('team2-score-textbox'); +const nameTextbox = document.getElementById('name-textbox'); const submitButton = document.getElementById('submit-button'); const deleteButton = document.getElementById('delete-button'); @@ -70,6 +71,9 @@ async function initializeForm() { team2Dropdown.disabled = false; team1ScoreTextbox.disabled = false; team2ScoreTextbox.disabled = false; + if(nameTextbox) { + nameTextbox.disabled = false; + } sportDropdown.onchange = async () => { await Form.populateGenders(genderDropdown, sportDropdown.value) @@ -89,6 +93,7 @@ async function initializeForm() { team1ScoreTextbox.addEventListener('keyup', checkDataValidity); team2Dropdown.onchange = checkDataValidity; team2ScoreTextbox.addEventListener('keyup', checkDataValidity); + if(nameTextbox) nameTextbox.addEventListener('keyup', checkDataValidity); checkDataValidity(); } @@ -112,6 +117,8 @@ async function checkDataValidity() { if(dateInput.value == "") dataIsValid = false; + if(nameTextbox && nameTextbox.value == "") dataIsValid = false; + submitButton.disabled = !dataIsValid; } diff --git a/views/manage/addgame.pug b/views/manage/addgame.pug index 69bd3c0..bdc362c 100644 --- a/views/manage/addgame.pug +++ b/views/manage/addgame.pug @@ -49,7 +49,7 @@ block content span(class='form-section') label Your name span(class='form-section-input') - input#name-input(type="text" name="name" disabled) + input#name-textbox(type="text" name="name" disabled) .error #{message} span(class='form-section') button#submit-button(type="submit" disabled) Submit From 5d111df401eb313b4099b5cbc33103c2ecf74556 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Tue, 7 Dec 2021 10:32:59 -0700 Subject: [PATCH 229/255] Fix bug where database migrations wouldn't occur --- database/database.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/database/database.js b/database/database.js index 08c1a37..c0b7257 100644 --- a/database/database.js +++ b/database/database.js @@ -40,7 +40,7 @@ async function checkForDatabaseInitialization() { let latestMigration; try { const latestMigrationQuery = `SELECT value FROM metadata WHERE property = 'latest_migration';`; - latestMigration = (await executeQuery(latestMigrationQuery))[0][0]; + latestMigration = +((await executeQuery(latestMigrationQuery))[0][0]); } catch { latestMigration = 0; } @@ -54,7 +54,7 @@ async function performMigrations(currentMigration) { const migrationFileList = fs.readdirSync('database/migrations'); const latestMigration = +migrationFileList[migrationFileList.length - 1].slice(0, 1); - for(let i = currentMigration + 1; i <= latestMigration; i++) { + for(let i = +currentMigration + 1; i <= latestMigration; i++) { const sql = fs.readFileSync(`database/migrations/${i}.sql`).toString(); await executeQuery(sql); console.log(`Performed database migration ${i}`); From 7a25037cb40dd32f8fd8f6ebf1b95f66c2a02522 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Tue, 7 Dec 2021 10:33:41 -0700 Subject: [PATCH 230/255] Update migration version in database init script --- database/init_database.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/init_database.sql b/database/init_database.sql index 29e4a6f..8cd8747 100644 --- a/database/init_database.sql +++ b/database/init_database.sql @@ -119,6 +119,6 @@ CREATE TABLE IF NOT EXISTS metadata( ); INSERT INTO metadata(property, value) - VALUES("latest_migration", "2"); + VALUES("latest_migration", "3"); COMMIT; \ No newline at end of file From c882c4f402a211a98b119f99cbce694ffea64de6 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Tue, 7 Dec 2021 10:42:36 -0700 Subject: [PATCH 231/255] Allow games to be submitted without login --- database/scores/games.js | 21 +++++++++++++++------ routes/manage.js | 34 ++++++++++++++++++++++++++-------- 2 files changed, 41 insertions(+), 14 deletions(-) diff --git a/database/scores/games.js b/database/scores/games.js index 4c5b22e..aa33213 100644 --- a/database/scores/games.js +++ b/database/scores/games.js @@ -5,7 +5,7 @@ const database = require('./../database'); class Game { - constructor(id, date, team1ID, team2ID, team1Score, team2Score, divisionID, seasonID, submitterID) { + constructor(id, date, team1ID, team2ID, team1Score, team2Score, divisionID, seasonID, submitterID, submitterName) { this.id = id; this.date = date; this.team1ID = team1ID; @@ -15,17 +15,26 @@ class Game { this.divisionID = divisionID; this.seasonID = seasonID; this.submitterID = submitterID; + this.submitterName = submitterName; } } -async function add(divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score, userID) { - const query = `INSERT INTO scores.games(division_id, season_id, game_date, team1_id, team2_id, team1_score, team2_score, submitter_id) - VALUES($1, $2, $3, $4, $5, $6, $7, $8) - RETURNING game_id;`; +async function add(divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score, submitterID, submitterName = undefined) { + let id; + if(submitterName) { + const query = `INSERT INTO scores.games(division_id, season_id, game_date, team1_id, team2_id, team1_score, team2_score, submitter_name) + VALUES($1, $2, $3, $4, $5, $6, $7, $8) + RETURNING game_id;`; + id = (await database.executeQuery(query, [divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score, submitterName]))[0][0]; + } else { + const query = `INSERT INTO scores.games(division_id, season_id, game_date, team1_id, team2_id, team1_score, team2_score, submitter_id) + VALUES($1, $2, $3, $4, $5, $6, $7, $8) + RETURNING game_id;`; + id = (await database.executeQuery(query, [divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score, submitterID]))[0][0]; + } - const id = (await database.executeQuery(query, [divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score, userID]))[0][0]; return new Game(id, date, team1ID, team2ID, team1Score, team2Score); } diff --git a/routes/manage.js b/routes/manage.js index d03bb2d..bad5f4c 100644 --- a/routes/manage.js +++ b/routes/manage.js @@ -29,7 +29,7 @@ router.get('/game', function(req, res, next) { } else { res.redirect('/auth/login'); - }; + } } else { next(); } @@ -40,7 +40,19 @@ router.get('/game', function(req, res, next) { res.render('manage/addgame', { title, userLoggedIn: !!req.user, message: req.flash('error') }); }); -router.post('/game', checkLoginStatus.user, async function(req, res, next) { +router.post('/game', function(req, res, next) { + if(!(process.env.PUBLIC_SUBMIT_PAGE && process.env.PUBLIC_SUBMIT_PAGE.toLowerCase() == 'true')) { + if (req.user) { + next(); + } + else { + res.redirect('/auth/login'); + } + } else { + next(); + } +}, + async function(req, res, next) { const id = req.body['game']; const remove = req.body['remove']; @@ -54,14 +66,20 @@ router.post('/game', checkLoginStatus.user, async function(req, res, next) { const team1Score = req.body['team1-score']; const team2ID = req.body['team2']; const team2Score = req.body['team2-score']; - const userID = req.user[0]; - - const loggedInUserID = req.user[0]; - const loggedInUserIsAdmin = req.user[2]; + const submitterName = req.body['name']; + + let submitterID; + let loggedInUserID; + let loggedInUserIsAdmin; + if(req.user) { + submitterID = req.user[0]; + loggedInUserID = req.user[0]; + loggedInUserIsAdmin = req.user[2]; + } const game = id ? await games.getFromID(id) : null; - if(!loggedInUserIsAdmin && game && loggedInUserID != game.submitterID) { + if((!loggedInUserIsAdmin && game && loggedInUserID != game.submitterID) || (!req.user && game)) { res.status(403).send("ACCESS DENIED"); } else if(remove) { @@ -73,7 +91,7 @@ router.post('/game', checkLoginStatus.user, async function(req, res, next) { res.redirect('/manage#games'); } else { - await games.add(divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score, userID); + await games.add(divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score, submitterID, submitterName); res.redirect('/'); } } catch(err) { From cd1f17803ba2e5b46be760993e002ea2ef2fd21f Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Tue, 7 Dec 2021 10:50:29 -0700 Subject: [PATCH 232/255] Return submitterName on games retreive from database function --- database/scores/games.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/database/scores/games.js b/database/scores/games.js index aa33213..5680c58 100644 --- a/database/scores/games.js +++ b/database/scores/games.js @@ -50,14 +50,14 @@ async function retrieve(teamID, divisionID, seasonID) { let table; if(teamID && divisionID && seasonID) { - const query = `SELECT game_id, division_id, season_id, game_date, team1_id, team2_id, team1_score, team2_score, submitter_id + const query = `SELECT game_id, division_id, season_id, game_date, team1_id, team2_id, team1_score, team2_score, submitter_id, submitter_name FROM scores.games WHERE (team1_id = $1 OR team2_id = $1) AND division_id = $2 AND season_id = $3 ORDER BY game_date DESC;`; table = await database.executeQuery(query, [teamID,divisionID,seasonID]); } else { - const query = `SELECT game_id, division_id, season_id, game_date, team1_id, team2_id, team1_score, team2_score, submitter_id + const query = `SELECT game_id, division_id, season_id, game_date, team1_id, team2_id, team1_score, team2_score, submitter_id, submitter_name FROM scores.games ORDER BY game_date DESC;`; table = await database.executeQuery(query); @@ -72,10 +72,10 @@ async function retrieve(teamID, divisionID, seasonID) { const teamScore = opponentIsTeam2 ? row[6] : row[7]; const opponentScore = opponentIsTeam2 ? row[7] : row[6]; - gamesList.push(new Game(row[0], row[3].toISOString().slice(0,10), teamID, opponentID, teamScore, opponentScore, row[1], row[2], row[8])); + gamesList.push(new Game(row[0], row[3].toISOString().slice(0,10), teamID, opponentID, teamScore, opponentScore, row[1], row[2], row[8], row[9])); } else { - gamesList.push(new Game(row[0], row[3].toISOString().slice(0,10), row[4], row[5], row[6], row[7], row[1], row[2], row[8])); + gamesList.push(new Game(row[0], row[3].toISOString().slice(0,10), row[4], row[5], row[6], row[7], row[1], row[2], row[8], row[9])); } }); return gamesList; From 119aae76e5b26b1015e13a8acf285aba316d9f30 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Tue, 7 Dec 2021 10:55:34 -0700 Subject: [PATCH 233/255] Display submitter name in admin panel for games submitted without account --- public/scripts/manage.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/public/scripts/manage.js b/public/scripts/manage.js index 2b958f9..a77a7d6 100644 --- a/public/scripts/manage.js +++ b/public/scripts/manage.js @@ -291,8 +291,13 @@ CATEGORIES.push(new Category( row.appendChild(dateCell); const submitterCell = document.createElement('td'); - Data.getAccount(game.submitterID) - .then(data => submitterCell.textContent = data.name); + if(game.submitterID) { + Data.getAccount(game.submitterID) + .then(data => submitterCell.textContent = data.name); + } else { + submitterCell.textContent = game.submitterName; + console.log(game.submitterName); + } row.appendChild(submitterCell); }, async function addGame() { From 3c274c995a841f4270bf91f1caff2338dab9d0f0 Mon Sep 17 00:00:00 2001 From: Sudoer777 Date: Tue, 7 Dec 2021 18:20:35 +0000 Subject: [PATCH 234/255] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 854032e..3835188 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ This repository is designed to be pushed to Heroku/Dokku/etc. - `PGDATABASE` - set to the name of your database (i.e. `scoretrackerdb`) - `PGUSER` - set to the user for managing the database - `PGPASSWORD` - set to the password for that user +- `PUBLIC_SUBMIT_PAGE` (default: `false`) - set to `true` to allow score submissions without an account ## Code From 0402209290299e5436e734b9f5982d59056d4c18 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 7 Mar 2022 12:01:58 -0700 Subject: [PATCH 235/255] Add new fetch.js route --- app.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app.js b/app.js index 9764a31..da69f40 100644 --- a/app.js +++ b/app.js @@ -15,6 +15,7 @@ var dataRouter = require('./routes/data'); var manageRouter = require('./routes/manage'); var authRouter = require('./routes/auth'); var aboutRouter = require('./routes/about'); +var fetchRouter = require('./routes/fetch'); var app = express(); @@ -53,6 +54,7 @@ app.use('/data', dataRouter); app.use('/manage', manageRouter); app.use('/auth', authRouter); app.use('/about', aboutRouter); +app.use('/fetch', fetchRouter); // catch 404 and forward to error handler From cc66467197e8384e132d7549b3d4bcbec3ec0b90 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 7 Mar 2022 12:21:17 -0700 Subject: [PATCH 236/255] Get seasons from a single request --- public/scripts/form.js | 14 +++++++--- public/scripts/index.js | 12 +++++++-- routes/fetch.js | 60 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 6 deletions(-) create mode 100644 routes/fetch.js diff --git a/public/scripts/form.js b/public/scripts/form.js index 631b404..96dadc6 100644 --- a/public/scripts/form.js +++ b/public/scripts/form.js @@ -20,11 +20,17 @@ export async function populateSports(sportDropdown, selectedSportID = undefined) if(selectedSportIndex) sportDropdown.selectedIndex = selectedSportIndex; } -export async function populateSeasons(seasonDropdown, selectedSeasonID = undefined) { +export async function populateSeasons(seasonDropdown, selectedSeasonID = undefined, data = undefined) { seasonDropdown.innerHTML = ""; - - const seasonsList = await Data.getSeasons(); + let seasonsList; + + if(data) { + seasonsList = data.seasons; + } else { + seasonsList = await Data.getSeasons(); + } + console.log(data); let currentIndex = 0; let selectedSeasonIndex; seasonsList.forEach(season => { @@ -33,7 +39,7 @@ export async function populateSeasons(seasonDropdown, selectedSeasonID = undefin option.value = season.id; seasonDropdown.appendChild(option); - if(season.id == selectedSeasonID) selectedSeasonIndex = currentIndex; + if(season.id == selectedSeasonID || (data && season.id == data.latestGame.seasonID)) selectedSeasonIndex = currentIndex; currentIndex++; }); diff --git a/public/scripts/index.js b/public/scripts/index.js index 1b70ca3..a30d681 100644 --- a/public/scripts/index.js +++ b/public/scripts/index.js @@ -17,7 +17,15 @@ const manageButton = document.getElementById('manage-button'); async function initializeForm() { - let latestGame; + const data = await (await fetch(`/fetch/index/dropdown`)).json(); + + await Form.populateSeasons(seasonDropdown, null, data); + await Form.populateSports(sportDropdown, null, data); + await Form.populateGenders(genderDropdown, null, data); + await Form.populateDivisions(divisionDropdown, null, null, null, data); + await Form.populateTeams(teamDropdown, null, null, data); + + /*let latestGame; try { latestGame = await Data.getLatestGame(); @@ -39,7 +47,7 @@ async function initializeForm() { await Form.populateGenders(genderDropdown, sportDropdown.value); await Form.populateDivisions(divisionDropdown, sportDropdown.value, genderDropdown.value); await Form.populateTeams(teamDropdown, sportDropdown.value); - } + }*/ seasonDropdown.onchange = loadTable; diff --git a/routes/fetch.js b/routes/fetch.js new file mode 100644 index 0000000..756c6df --- /dev/null +++ b/routes/fetch.js @@ -0,0 +1,60 @@ +var express = require('express'); +var router = express.Router(); + +var sports = require('../database/scores/sports'); +var seasons = require('../database/scores/seasons'); +var genders = require('../database/scores/genders'); +var divisions = require('../database/scores/divisions'); +var teams = require('../database/scores/teams'); +var games = require('../database/scores/games'); +var accounts = require('../database/accounts/accounts'); + +var checkLoginStatus = require('./checkLoginStatus'); + +router.get('/index/dropdown', async function(req, res, next) { + let latestGame; + let seasonsData; + let sportsData; + let gendersData; + let divisionsData; + let teamsData; + + try { + latestGame = await games.getLatest(); + } catch { + latestGame = null; + } + + + try { + if(latestGame) { + let division = await divisions.getFromID(latestGame.divisionID); + + seasonsData = await seasons.retrieveAll(); + sportsData = await sports.retrieveAll(); + gendersData = await genders.retrieveBySport(division.sportID); + divisionsData = await divisions.retrieve(division.sportID); + teamsData = await teams.retrieve(division.sportID); + } else { + seasonsData = await seasons.retrieveAll(); + sportsData = await sports.retrieveAll(); + gendersData = await genders.retrieveBySport(sportsData[0].id); + divisionsData = await divisions.retrieve(sportsData[0].id); + teamsData = await teams.retrieve(sportsData[0].id); + } + + res.json({ + seasons : seasonsData, + sports : sportsData, + genders : gendersData, + divisons : divisionsData, + teams: teamsData, + latestGame + }); + } catch(err) { + console.error("ERROR: " + err.message); + res.status(500).send("An error has occurred"); + } +}); + +module.exports = router; \ No newline at end of file From 1e78e379135d1148699eff68eecbcd8aded0384f Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 7 Mar 2022 12:27:10 -0700 Subject: [PATCH 237/255] Add sports to single request --- public/scripts/form.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/public/scripts/form.js b/public/scripts/form.js index 96dadc6..a693463 100644 --- a/public/scripts/form.js +++ b/public/scripts/form.js @@ -1,9 +1,14 @@ import * as Data from "./data.js"; -export async function populateSports(sportDropdown, selectedSportID = undefined) { +export async function populateSports(sportDropdown, selectedSportID = undefined, data = undefined) { sportDropdown.innerHTML = ""; - const sportsList = await Data.getSports(); + let sportsList; + if(data) { + sportsList = data.sports; + } else { + sportsList = await Data.getSports(); + } let currentIndex = 0; let selectedSportIndex; @@ -13,7 +18,7 @@ export async function populateSports(sportDropdown, selectedSportID = undefined) option.value = sport.id; sportDropdown.appendChild(option); - if(sport.id == selectedSportID) selectedSportIndex = currentIndex; + if(sport.id == selectedSportID || (data && sport.id == data.latestGame.sportID)) selectedSportIndex = currentIndex; currentIndex++; }); @@ -30,7 +35,7 @@ export async function populateSeasons(seasonDropdown, selectedSeasonID = undefin } else { seasonsList = await Data.getSeasons(); } - console.log(data); + let currentIndex = 0; let selectedSeasonIndex; seasonsList.forEach(season => { From b2f66b3e34be7a6b4c3658843afc2548c4cd3cf7 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 7 Mar 2022 21:31:04 -0700 Subject: [PATCH 238/255] Populate all dropdowns in a single AJAX request --- public/scripts/form.js | 45 ++++++++++++++++++++++++++++++++--------- public/scripts/index.js | 26 +----------------------- routes/fetch.js | 14 +++++++------ 3 files changed, 44 insertions(+), 41 deletions(-) diff --git a/public/scripts/form.js b/public/scripts/form.js index a693463..59e2e3d 100644 --- a/public/scripts/form.js +++ b/public/scripts/form.js @@ -51,11 +51,17 @@ export async function populateSeasons(seasonDropdown, selectedSeasonID = undefin if(selectedSeasonIndex) seasonDropdown.selectedIndex = selectedSeasonIndex; } -export async function populateGenders(genderDropdown, selectedSportID, selectedGender = undefined) { +export async function populateGenders(genderDropdown, selectedSportID, selectedGender = undefined, data = undefined) { genderDropdown.innerHTML = ""; - const gendersList = await Data.getGenders(selectedSportID); - + let gendersList; + if(data) { + gendersList = data.genders; + selectedSportID = data.latestGame.sportID; + } else { + gendersList = await Data.getGenders(selectedSportID); + } + if(selectedSportID) { let currentIndex = 0; let selectedGenderIndex; @@ -65,7 +71,7 @@ export async function populateGenders(genderDropdown, selectedSportID, selectedG option.value = gender.name; genderDropdown.appendChild(option); - if(gender.name == selectedGender) selectedGenderIndex = currentIndex; + if(gender.name == selectedGender || (data && gender.name == data.latestGame.gender.name)) selectedGenderIndex = currentIndex; currentIndex++; }); @@ -73,11 +79,21 @@ export async function populateGenders(genderDropdown, selectedSportID, selectedG } } -export async function populateDivisions (divisionDropdown, selectedSportID, selectedGender, selectedDivisionID = undefined) { +export async function populateDivisions (divisionDropdown, selectedSportID, selectedGender, selectedDivisionID = undefined, data = undefined) { divisionDropdown.innerHTML = ""; + if(data) { + selectedSportID = data.latestGame.sportID; + selectedGender = data.latestGame.gender; + } if(selectedSportID && selectedGender) { - const divisionsList = await Data.getDivisions(selectedSportID, selectedGender); + let divisionsList; + + if(data) { + divisionsList = data.divisions; + } else { + divisionsList = await Data.getDivisions(selectedSportID, selectedGender); + } let currentIndex = 0; let selectedDivisionIndex; @@ -87,7 +103,7 @@ export async function populateDivisions (divisionDropdown, selectedSportID, sele option.value = division.id; divisionDropdown.appendChild(option); - if(division.id == selectedDivisionID) selectedDivisionIndex = currentIndex; + if(division.id == selectedDivisionID || (data && division.id == data.latestGame.divisionID)) selectedDivisionIndex = currentIndex; currentIndex++; }); @@ -95,11 +111,20 @@ export async function populateDivisions (divisionDropdown, selectedSportID, sele } } -export async function populateTeams(teamDropdown, selectedSportID, selectedTeamID = undefined) { +export async function populateTeams(teamDropdown, selectedSportID, selectedTeamID = undefined, data = undefined) { teamDropdown.innerHTML = ""; + if(data) { + selectedSportID = data.latestGame.sportID; + } + if(selectedSportID) { - const teamsList = await Data.getTeams(selectedSportID); + let teamsList; + if(data) { + teamsList = data.teams; + } else { + teamsList = await Data.getTeams(selectedSportID); + } let currentIndex = 0; let selectedTeamIndex; @@ -109,7 +134,7 @@ export async function populateTeams(teamDropdown, selectedSportID, selectedTeamI option.value = team.id; teamDropdown.appendChild(option); - if(team.id == selectedTeamID) selectedTeamIndex = currentIndex; + if(team.id == selectedTeamID || (data && team.id == data.latestGame.team1ID)) selectedTeamIndex = currentIndex; currentIndex++; }); diff --git a/public/scripts/index.js b/public/scripts/index.js index a30d681..ae7bd01 100644 --- a/public/scripts/index.js +++ b/public/scripts/index.js @@ -21,33 +21,9 @@ async function initializeForm() { await Form.populateSeasons(seasonDropdown, null, data); await Form.populateSports(sportDropdown, null, data); - await Form.populateGenders(genderDropdown, null, data); + await Form.populateGenders(genderDropdown, null, null, data); await Form.populateDivisions(divisionDropdown, null, null, null, data); await Form.populateTeams(teamDropdown, null, null, data); - - /*let latestGame; - - try { - latestGame = await Data.getLatestGame(); - } catch { - latestGame = null; - } - - if(latestGame) { - Form.populateSeasons(seasonDropdown, latestGame.seasonID); - - const division = await Data.getDivision(latestGame.divisionID); - await Form.populateSports(sportDropdown, division.sportID); - await Form.populateGenders(genderDropdown, sportDropdown.value, division.gender.name); - await Form.populateDivisions(divisionDropdown, sportDropdown.value, genderDropdown.value, latestGame.divisionID); - await Form.populateTeams(teamDropdown, sportDropdown.value, latestGame.team1ID); - } else { - Form.populateSeasons(seasonDropdown); - await Form.populateSports(sportDropdown); - await Form.populateGenders(genderDropdown, sportDropdown.value); - await Form.populateDivisions(divisionDropdown, sportDropdown.value, genderDropdown.value); - await Form.populateTeams(teamDropdown, sportDropdown.value); - }*/ seasonDropdown.onchange = loadTable; diff --git a/routes/fetch.js b/routes/fetch.js index 756c6df..cbe203a 100644 --- a/routes/fetch.js +++ b/routes/fetch.js @@ -21,6 +21,10 @@ router.get('/index/dropdown', async function(req, res, next) { try { latestGame = await games.getLatest(); + + let division = await divisions.getFromID(latestGame.divisionID); + latestGame.sportID = division.sportID; + latestGame.gender = division.gender; } catch { latestGame = null; } @@ -28,13 +32,11 @@ router.get('/index/dropdown', async function(req, res, next) { try { if(latestGame) { - let division = await divisions.getFromID(latestGame.divisionID); - seasonsData = await seasons.retrieveAll(); sportsData = await sports.retrieveAll(); - gendersData = await genders.retrieveBySport(division.sportID); - divisionsData = await divisions.retrieve(division.sportID); - teamsData = await teams.retrieve(division.sportID); + gendersData = await genders.retrieveBySport(latestGame.sportID); + divisionsData = await divisions.retrieve(latestGame.sportID); + teamsData = await teams.retrieve(latestGame.sportID); } else { seasonsData = await seasons.retrieveAll(); sportsData = await sports.retrieveAll(); @@ -47,7 +49,7 @@ router.get('/index/dropdown', async function(req, res, next) { seasons : seasonsData, sports : sportsData, genders : gendersData, - divisons : divisionsData, + divisions : divisionsData, teams: teamsData, latestGame }); From 36de9b37734c66275a91d7a567acde4f60bc4a59 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 7 Mar 2022 23:07:26 -0700 Subject: [PATCH 239/255] Speed up loading for home page --- public/scripts/index.js | 6 +++--- routes/fetch.js | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/public/scripts/index.js b/public/scripts/index.js index ae7bd01..3d77b89 100644 --- a/public/scripts/index.js +++ b/public/scripts/index.js @@ -60,7 +60,8 @@ async function loadTable() { if(selectedTeamID && selectedDivisionID) { gamesTableHeader.textContent = `Scores for ${teamDropdown.options[teamDropdown.selectedIndex].text}`; - const gamesList = await Data.getGames(selectedTeamID, selectedDivisionID, selectedSeasonID); + const requestURL = `/fetch/index/scores?team=${+selectedTeamID}&division=${+selectedDivisionID}&season=${+selectedSeasonID}`; + const gamesList = await (await fetch(requestURL)).json(); if(gamesList.length > 0) { await setupGamesTableHeaders(); @@ -77,8 +78,7 @@ async function loadTable() { row.appendChild(scoreCell); const opponentCell = document.createElement('td'); - Data.getTeam(game.team2ID) - .then(data => opponentCell.textContent = data.name); + opponentCell.textContent = game.opponent.name; row.appendChild(opponentCell); const dateCell = document.createElement('td'); diff --git a/routes/fetch.js b/routes/fetch.js index cbe203a..5abc17d 100644 --- a/routes/fetch.js +++ b/routes/fetch.js @@ -59,4 +59,20 @@ router.get('/index/dropdown', async function(req, res, next) { } }); +router.get('/index/scores', async function (req, res, next) { + try { + const seasonID = req.query.season; + const divisionID = req.query.division; + const teamID = req.query.team; + const data = await games.retrieve(teamID, divisionID, seasonID); + for (const game of data) { + game.opponent = await teams.getFromID(game.team2ID); + } + res.json(data); + } catch(err) { + console.error("ERROR: " + err.message); + res.status(500).send("An error has occurred"); + } +}); + module.exports = router; \ No newline at end of file From 9ea7162a0c6d91cb0df4901fc2950957f08bf8c9 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Mon, 7 Mar 2022 23:19:07 -0700 Subject: [PATCH 240/255] Add loading scores message --- public/scripts/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/public/scripts/index.js b/public/scripts/index.js index 3d77b89..6813bba 100644 --- a/public/scripts/index.js +++ b/public/scripts/index.js @@ -59,9 +59,11 @@ async function loadTable() { if(selectedTeamID && selectedDivisionID) { gamesTableHeader.textContent = `Scores for ${teamDropdown.options[teamDropdown.selectedIndex].text}`; - + + noScoresMessage.textContent = "Loading scores..."; const requestURL = `/fetch/index/scores?team=${+selectedTeamID}&division=${+selectedDivisionID}&season=${+selectedSeasonID}`; const gamesList = await (await fetch(requestURL)).json(); + noScoresMessage.textContent = ""; if(gamesList.length > 0) { await setupGamesTableHeaders(); From 703c8bfc5b642e06267610149c981f05ea7c3572 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Tue, 8 Mar 2022 10:38:28 -0700 Subject: [PATCH 241/255] Add loading screen for index --- public/scripts/index.js | 5 +++++ public/stylesheets/index.css | 4 ++++ views/index.pug | 3 ++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/public/scripts/index.js b/public/scripts/index.js index 6813bba..de182c7 100644 --- a/public/scripts/index.js +++ b/public/scripts/index.js @@ -1,6 +1,7 @@ import * as Data from "./data.js"; import * as Form from "./form.js"; +const dropdownsDiv = document.getElementById('dropdowns-div'); const sportDropdown = document.getElementById('sport-dropdown'); const seasonDropdown = document.getElementById('year-dropdown'); const genderDropdown = document.getElementById('gender-dropdown'); @@ -11,6 +12,7 @@ const gamesTableHeader = document.getElementById('games-table-header'); const noScoresMessage = document.getElementById('no-scores-message'); const addScoreButton = document.getElementById('add-score-button'); const manageButton = document.getElementById('manage-button'); +const loadingSpan = document.getElementById('loading'); @@ -42,6 +44,9 @@ async function initializeForm() { divisionDropdown.onchange = loadTable; teamDropdown.onchange = loadTable; + loadingSpan.textContent = ''; + dropdownsDiv.style.visibility = 'visible'; + loadTable(); diff --git a/public/stylesheets/index.css b/public/stylesheets/index.css index eb71b13..bf0a4ae 100644 --- a/public/stylesheets/index.css +++ b/public/stylesheets/index.css @@ -32,3 +32,7 @@ tr { #login-button { margin-left: auto; } + +#dropdowns-div { + visibility: hidden; +} \ No newline at end of file diff --git a/views/index.pug b/views/index.pug index a1b7d2d..1a22bbe 100644 --- a/views/index.pug +++ b/views/index.pug @@ -11,7 +11,8 @@ block actions block content - div + span#loading Loading... + div#dropdowns-div span(class='form-section') label Year span(class='form-section-input') From 7ec36ed7f24a5554150b92e72bc9d7d6305a28a7 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Tue, 8 Mar 2022 10:55:23 -0700 Subject: [PATCH 242/255] Fine tune loading for submit page --- public/scripts/form.js | 9 +++++-- public/scripts/manage/game.js | 25 +++++++++-------- public/stylesheets/form.css | 6 ++++- routes/fetch.js | 51 +++++++++++++++++++++++++++++++++++ views/manage/addgame.pug | 1 + 5 files changed, 78 insertions(+), 14 deletions(-) diff --git a/public/scripts/form.js b/public/scripts/form.js index 59e2e3d..acc3eaa 100644 --- a/public/scripts/form.js +++ b/public/scripts/form.js @@ -111,7 +111,7 @@ export async function populateDivisions (divisionDropdown, selectedSportID, sele } } -export async function populateTeams(teamDropdown, selectedSportID, selectedTeamID = undefined, data = undefined) { +export async function populateTeams(teamDropdown, selectedSportID, selectedTeamID = undefined, data = undefined, useOpponent = false) { teamDropdown.innerHTML = ""; if(data) { @@ -128,13 +128,18 @@ export async function populateTeams(teamDropdown, selectedSportID, selectedTeamI let currentIndex = 0; let selectedTeamIndex; + + if(data) { + selectedTeamID = useOpponent ? data.latestGame.team2ID : data.latestGame.team1ID; + } + teamsList.forEach(team => { const option = document.createElement('option'); option.text = team.name; option.value = team.id; teamDropdown.appendChild(option); - if(team.id == selectedTeamID || (data && team.id == data.latestGame.team1ID)) selectedTeamIndex = currentIndex; + if(team.id == selectedTeamID) selectedTeamIndex = currentIndex; currentIndex++; }); diff --git a/public/scripts/manage/game.js b/public/scripts/manage/game.js index 5d0bd29..c61d05b 100644 --- a/public/scripts/manage/game.js +++ b/public/scripts/manage/game.js @@ -15,6 +15,7 @@ const team2ScoreTextbox = document.getElementById('team2-score-textbox'); const nameTextbox = document.getElementById('name-textbox'); const submitButton = document.getElementById('submit-button'); const deleteButton = document.getElementById('delete-button'); +const loadingSpan = document.getElementById('loading-span'); async function initializeForm() { @@ -41,24 +42,23 @@ async function initializeForm() { team2ScoreTextbox.value = game.team2Score; } else { - try { - const game = await Data.getLatestGame(true); + /*try {*/ + const data = await (await fetch(`/fetch/index/dropdown`)).json(); - Form.populateSeasons(seasonDropdown, game.seasonID); - const data = await Data.getDivision(game.divisionID) - await Form.populateSports(sportDropdown, data.sportID) - await Form.populateGenders(genderDropdown, sportDropdown.value, data.gender.name) - await Form.populateDivisions(divisionDropdown, sportDropdown.value, genderDropdown.value, game.divisionID); - await Form.populateTeams(team1Dropdown, sportDropdown.value, game.team1ID); - await Form.populateTeams(team2Dropdown, sportDropdown.value, game.team2ID); - } catch { + await Form.populateSeasons(seasonDropdown, null, data); + await Form.populateSports(sportDropdown, null, data); + await Form.populateGenders(genderDropdown, null, null, data); + await Form.populateDivisions(divisionDropdown, null, null, null, data); + await Form.populateTeams(team1Dropdown, null, null, data); + await Form.populateTeams(team2Dropdown, null, null, data, true); + /*} catch { await Form.populateSeasons(seasonDropdown); await Form.populateSports(sportDropdown) await Form.populateGenders(genderDropdown, sportDropdown.value) await Form.populateDivisions(divisionDropdown, sportDropdown.value, genderDropdown.value); await Form.populateTeams(team1Dropdown, sportDropdown.value); await Form.populateTeams(team2Dropdown, sportDropdown.value); - } + }*/ dateInput.value = (new Date()).toISOString().slice(0,10); } @@ -95,6 +95,9 @@ async function initializeForm() { team2ScoreTextbox.addEventListener('keyup', checkDataValidity); if(nameTextbox) nameTextbox.addEventListener('keyup', checkDataValidity); + loadingSpan.textContent = ''; + submissionForm.style.visibility = 'visible'; + checkDataValidity(); } initializeForm(); diff --git a/public/stylesheets/form.css b/public/stylesheets/form.css index 43fee2e..c040b27 100644 --- a/public/stylesheets/form.css +++ b/public/stylesheets/form.css @@ -48,4 +48,8 @@ form { .flat-form-section { flex-direction: row; - } \ No newline at end of file + } + +#submission-form { + visibility: hidden; +} \ No newline at end of file diff --git a/routes/fetch.js b/routes/fetch.js index 5abc17d..fddaefe 100644 --- a/routes/fetch.js +++ b/routes/fetch.js @@ -75,4 +75,55 @@ router.get('/index/scores', async function (req, res, next) { } }); + +router.get('/submit', async function(req, res, next) { + let latestGame; + let seasonsData; + let sportsData; + let gendersData; + let divisionsData; + let teamsData; + + const userID = req.user ? req.user[0] : null; + + try { + latestGame = await games.getLatest(userID); + + let division = await divisions.getFromID(latestGame.divisionID); + latestGame.sportID = division.sportID; + latestGame.gender = division.gender; + } catch { + latestGame = null; + } + + + try { + if(latestGame) { + seasonsData = await seasons.retrieveAll(); + sportsData = await sports.retrieveAll(); + gendersData = await genders.retrieveBySport(latestGame.sportID); + divisionsData = await divisions.retrieve(latestGame.sportID); + teamsData = await teams.retrieve(latestGame.sportID); + } else { + seasonsData = await seasons.retrieveAll(); + sportsData = await sports.retrieveAll(); + gendersData = await genders.retrieveBySport(sportsData[0].id); + divisionsData = await divisions.retrieve(sportsData[0].id); + teamsData = await teams.retrieve(sportsData[0].id); + } + + res.json({ + seasons : seasonsData, + sports : sportsData, + genders : gendersData, + divisions : divisionsData, + teams: teamsData, + latestGame + }); + } catch(err) { + console.error("ERROR: " + err.message); + res.status(500).send("An error has occurred"); + } +}); + module.exports = router; \ No newline at end of file diff --git a/views/manage/addgame.pug b/views/manage/addgame.pug index bdc362c..dc56f5d 100644 --- a/views/manage/addgame.pug +++ b/views/manage/addgame.pug @@ -5,6 +5,7 @@ block stylesheets link(rel='stylesheet', href='/stylesheets/form.css') block content + span#loading-span Loading... form#submission-form(action='./game', method='POST') span(class='form-section') label Year From 42ac77ec6ffcf0983a34c66fe940cf80a275dc15 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 9 Mar 2022 10:20:53 -0700 Subject: [PATCH 243/255] Add loading message to manage page --- public/scripts/manage.js | 5 +++++ views/manage.pug | 1 + 2 files changed, 6 insertions(+) diff --git a/public/scripts/manage.js b/public/scripts/manage.js index a77a7d6..04ebb1c 100644 --- a/public/scripts/manage.js +++ b/public/scripts/manage.js @@ -3,6 +3,7 @@ import * as Data from "./data.js"; const categoryDropdown = document.getElementById('category-dropdown'); const itemsListTable = document.getElementById('items-list'); const addNewButton = document.getElementById('add-new-button'); +const loadingSpan = document.getElementById('loading-message'); function getGenderLetter(genderName) { @@ -362,6 +363,8 @@ CATEGORIES.push(new Category( async function listItems(category) { + loadingSpan.textContent = "Loading..."; + itemsListTable.innerHTML = ""; const itemsList = await category.getItems(); @@ -389,6 +392,8 @@ async function listItems(category) { itemsListTable.appendChild(row); }); + + loadingSpan.textContent = ''; } if(window.location.hash) { let correctIndex; diff --git a/views/manage.pug b/views/manage.pug index 63d76ec..bd8a228 100644 --- a/views/manage.pug +++ b/views/manage.pug @@ -18,6 +18,7 @@ block content option(value="accounts") Accounts div h2#table-header + span#loading-message Loading... table#items-list button#add-new-button Add new... From 18628e3fe5326ce3fb22bcc086a11be4e4591b5d Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 9 Mar 2022 10:28:28 -0700 Subject: [PATCH 244/255] Speed up loading divisions on manage page --- public/scripts/manage.js | 6 +++--- routes/fetch.js | 10 ++++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/public/scripts/manage.js b/public/scripts/manage.js index 04ebb1c..a96bac9 100644 --- a/public/scripts/manage.js +++ b/public/scripts/manage.js @@ -115,7 +115,7 @@ CATEGORIES.push(new Category( CATEGORIES.push(new Category( "divisions", async function getDivisions() { - return await Data.getDivisions(); + return await (await fetch('/fetch/manage/divisions')).json(); }, async function listDivisionHeaders() { const headerRow = document.createElement('tr'); @@ -151,8 +151,8 @@ CATEGORIES.push(new Category( row.appendChild(spacerCell); const sportCell = document.createElement('td'); - Data.getSportName(division.sportID) - .then(data => sportCell.textContent = data); + let sportName = division.sport.name; + sportCell.textContent = sportName; row.appendChild(sportCell); }, async function addDivision() { diff --git a/routes/fetch.js b/routes/fetch.js index fddaefe..28a98ea 100644 --- a/routes/fetch.js +++ b/routes/fetch.js @@ -126,4 +126,14 @@ router.get('/submit', async function(req, res, next) { } }); +router.get('/manage/divisions', async function (req, res, next) { + const data = await divisions.retrieve(); + + for(const division of data) { + division.sport = await sports.getFromID(division.sportID); + } + + res.json(data); +}); + module.exports = router; \ No newline at end of file From 05473e07ad4949504a1abbfe116cd75e228dee07 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 9 Mar 2022 10:32:47 -0700 Subject: [PATCH 245/255] Speed up loading teams --- public/scripts/manage.js | 6 +++--- routes/fetch.js | 10 ++++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/public/scripts/manage.js b/public/scripts/manage.js index a96bac9..6fc6b07 100644 --- a/public/scripts/manage.js +++ b/public/scripts/manage.js @@ -166,7 +166,7 @@ CATEGORIES.push(new Category( CATEGORIES.push(new Category( "teams", async function getTeams() { - return await Data.getTeams(); + return await (await fetch('/fetch/manage/teams')).json(); }, async function listTeamHeaders() { const headerRow = document.createElement('tr'); @@ -194,8 +194,8 @@ CATEGORIES.push(new Category( row.appendChild(spacerCell); const sportCell = document.createElement('td'); - Data.getSportName(team.sportID) - .then(data => sportCell.textContent = data); + let sportName = team.sport.name; + sportCell.textContent = sportName; row.appendChild(sportCell); }, async function addTeam() { diff --git a/routes/fetch.js b/routes/fetch.js index 28a98ea..c140ead 100644 --- a/routes/fetch.js +++ b/routes/fetch.js @@ -136,4 +136,14 @@ router.get('/manage/divisions', async function (req, res, next) { res.json(data); }); +router.get('/manage/teams', async function (req, res, next) { + const data = await teams.retrieve(); + + for(const team of data) { + team.sport = await sports.getFromID(team.sportID); + } + + res.json(data); +}); + module.exports = router; \ No newline at end of file From 55a6fc7d7b1a81fa82ba56f5fda98d6043ce8e95 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 9 Mar 2022 10:52:02 -0700 Subject: [PATCH 246/255] Improve loading for manage games page --- public/scripts/manage.js | 27 ++++++++++++--------------- routes/fetch.js | 27 +++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/public/scripts/manage.js b/public/scripts/manage.js index 6fc6b07..db29cad 100644 --- a/public/scripts/manage.js +++ b/public/scripts/manage.js @@ -209,7 +209,7 @@ CATEGORIES.push(new Category( CATEGORIES.push(new Category( "games", async function getGames() { - return await Data.getGames(); + return await (await fetch('/fetch/manage/games')).json(); }, async function listGameHeaders() { const headerRow = document.createElement('tr'); @@ -242,12 +242,12 @@ CATEGORIES.push(new Category( function listGame(game, row) { const teamsCell = document.createElement('td'); const team1NameSpan = document.createElement('span'); - Data.getTeam(game.team1ID) - .then(data => team1NameSpan.textContent = data.name); + let team1Name = game.team1.name; + team1NameSpan.textContent = team1Name; teamsCell.appendChild(team1NameSpan); const team2NameSpan = document.createElement('span'); - Data.getTeam(game.team2ID) - .then(data => team2NameSpan.textContent = data.name); + let team2Name = game.team2.name; + team2NameSpan.textContent = team2Name; teamsCell.appendChild(team2NameSpan); row.appendChild(teamsCell); @@ -271,13 +271,11 @@ CATEGORIES.push(new Category( const divisionGenderSpan = document.createElement('span'); divisionSpan.appendChild(divisionNameSpan); divisionSpan.appendChild(divisionGenderSpan); - Data.getDivision(game.divisionID) - .then(data => { - Data.getSportName(data.sportID) - .then(data => sportSpan.textContent = data); - divisionNameSpan.textContent = data.name; - divisionGenderSpan.textContent = getGenderLetter(data.gender.name); - }); + let divisionName = game.division.name; + let sportName = game.sport.name; + divisionNameSpan.textContent = divisionName; + sportSpan.textContent = sportName; + divisionGenderSpan.textContent = getGenderLetter(game.division.gender.name); sportCell.appendChild(sportSpan); sportCell.appendChild(divisionSpan); row.appendChild(sportCell); @@ -293,11 +291,10 @@ CATEGORIES.push(new Category( const submitterCell = document.createElement('td'); if(game.submitterID) { - Data.getAccount(game.submitterID) - .then(data => submitterCell.textContent = data.name); + let submitterName = game.submitter.name; + submitterCell.textContent = submitterName; } else { submitterCell.textContent = game.submitterName; - console.log(game.submitterName); } row.appendChild(submitterCell); }, diff --git a/routes/fetch.js b/routes/fetch.js index c140ead..a8b317e 100644 --- a/routes/fetch.js +++ b/routes/fetch.js @@ -146,4 +146,31 @@ router.get('/manage/teams', async function (req, res, next) { res.json(data); }); +router.get('/manage/games', checkLoginStatus.user, async function (req, res, next) { + try{ + const userIsAdmin = req.user[2]; + const loggedInAccountID = req.user[0]; + + if(!userIsAdmin) { + res.status(403).send("ACCESS DENIED"); + } else { + const data = await games.retrieve(); + + for(const game of data) { + game.team1 = await teams.getFromID(game.team1ID); + game.team2 = await teams.getFromID(game.team2ID); + game.division = await divisions.getFromID(game.divisionID); + game.sport = await sports.getFromID(game.division.sportID); + game.submitter = game.submitterName || (await accounts.getFromID(game.submitterID)); + } + + res.json(data); + } + } catch(err) { + console.error("ERROR: " + err.message); + res.status(500).send("An error has occurred"); + } + +}); + module.exports = router; \ No newline at end of file From 5adf9274128feb6960278b1c0a64b755896413b8 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 9 Mar 2022 10:54:50 -0700 Subject: [PATCH 247/255] Make submission form visible for sport edit page --- public/scripts/manage/sport.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/public/scripts/manage/sport.js b/public/scripts/manage/sport.js index 20ad2cb..7ab54e7 100644 --- a/public/scripts/manage/sport.js +++ b/public/scripts/manage/sport.js @@ -26,6 +26,8 @@ async function initializeForm() { nameTextbox.disabled = false; nameTextbox.addEventListener('keyup', checkDataValidity); + + submissionForm.style.visibility = 'visible'; } initializeForm(); From 492d10f6359098d148bae5a890e18018f9d65681 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 9 Mar 2022 10:56:35 -0700 Subject: [PATCH 248/255] Add loading message to sport submit form --- public/scripts/manage/sport.js | 2 ++ views/manage/addsport.pug | 1 + 2 files changed, 3 insertions(+) diff --git a/public/scripts/manage/sport.js b/public/scripts/manage/sport.js index 7ab54e7..b44b41a 100644 --- a/public/scripts/manage/sport.js +++ b/public/scripts/manage/sport.js @@ -5,6 +5,7 @@ const nameTextbox = document.getElementById('name-textbox'); const submitButton = document.getElementById('submit-button'); const deleteButton = document.getElementById('delete-button'); const submissionForm = document.getElementById('submission-form'); +const loadingSpan = document.getElementById('loading-message'); async function initializeForm() { @@ -27,6 +28,7 @@ async function initializeForm() { nameTextbox.addEventListener('keyup', checkDataValidity); + loadingSpan.textContent = ''; submissionForm.style.visibility = 'visible'; } initializeForm(); diff --git a/views/manage/addsport.pug b/views/manage/addsport.pug index b4caa50..6530488 100644 --- a/views/manage/addsport.pug +++ b/views/manage/addsport.pug @@ -5,6 +5,7 @@ block stylesheets link(rel='stylesheet', href='/stylesheets/form.css') block content + span#loading-message Loading... form#submission-form(action='./sport', method='POST') span(class='form-section') label Sport name From ee6353f013950e434e5e20655ca18f02fcc72682 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 9 Mar 2022 11:00:17 -0700 Subject: [PATCH 249/255] Add loading message to division --- public/scripts/manage/division.js | 4 ++++ views/manage/adddivision.pug | 1 + 2 files changed, 5 insertions(+) diff --git a/public/scripts/manage/division.js b/public/scripts/manage/division.js index a542a65..1d75742 100644 --- a/public/scripts/manage/division.js +++ b/public/scripts/manage/division.js @@ -8,6 +8,7 @@ const genderDropdown = document.getElementById('gender-dropdown'); const nameTextbox = document.getElementById('name-textbox'); const submitButton = document.getElementById('submit-button'); const deleteButton = document.getElementById('delete-button'); +const loadingSpan = document.getElementById('loading-message'); async function initializeForm() { @@ -40,6 +41,9 @@ async function initializeForm() { nameTextbox.disabled = false; nameTextbox.addEventListener('keyup', checkDataValidity); + + loadingSpan.textContent = ''; + submissionForm.style.visibility = 'visible'; } initializeForm(); diff --git a/views/manage/adddivision.pug b/views/manage/adddivision.pug index 1a17aaf..3471bc5 100644 --- a/views/manage/adddivision.pug +++ b/views/manage/adddivision.pug @@ -5,6 +5,7 @@ block stylesheets link(rel='stylesheet', href='/stylesheets/form.css') block content + span#loading-message Loading... form#submission-form(action='./division', method='POST') span(class='form-section') label Sport From 59e671e1ec6c5448b8cd43a338a795623d7ece0a Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 9 Mar 2022 11:00:27 -0700 Subject: [PATCH 250/255] Add loading message to team management --- public/scripts/manage/team.js | 5 +++++ views/manage/addteam.pug | 1 + 2 files changed, 6 insertions(+) diff --git a/public/scripts/manage/team.js b/public/scripts/manage/team.js index 4bc643e..cd5ac95 100644 --- a/public/scripts/manage/team.js +++ b/public/scripts/manage/team.js @@ -7,6 +7,8 @@ const sportDropdown = document.getElementById('sport-dropdown'); const nameTextbox = document.getElementById('name-textbox'); const submitButton = document.getElementById('submit-button'); const deleteButton = document.getElementById('delete-button'); +const loadingSpan = document.getElementById('loading-message'); + async function initializeForm() { let params = new URLSearchParams(location.search); @@ -31,6 +33,9 @@ async function initializeForm() { nameTextbox.disabled = false; nameTextbox.addEventListener('keyup', checkDataValidity); + + loadingSpan.textContent = ''; + submissionForm.style.visibility = 'visible'; } initializeForm(); diff --git a/views/manage/addteam.pug b/views/manage/addteam.pug index 78746a6..17d42d3 100644 --- a/views/manage/addteam.pug +++ b/views/manage/addteam.pug @@ -5,6 +5,7 @@ block stylesheets link(rel='stylesheet', href='/stylesheets/form.css') block content + span#loading-message Loading... form#submission-form(action='./team', method='POST') span(class='form-section') label Sport From 3a54ce0d561e4169b4ce76a1ce2a8318133eb811 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 9 Mar 2022 11:02:00 -0700 Subject: [PATCH 251/255] Improve loading for account editing --- public/scripts/manage/account.js | 4 ++++ views/accounts/createuser.pug | 1 + 2 files changed, 5 insertions(+) diff --git a/public/scripts/manage/account.js b/public/scripts/manage/account.js index f9a7370..3e14638 100644 --- a/public/scripts/manage/account.js +++ b/public/scripts/manage/account.js @@ -9,6 +9,7 @@ const adminCheckboxSection = document.getElementById('admin-checkbox-section'); const adminCheckbox = document.getElementById('admin-checkbox'); const submitButton = document.getElementById('submit-button'); const deleteButton = document.getElementById('delete-button'); +const loadingSpan = document.getElementById('loading-message'); async function Initialize() { let params = new URLSearchParams(location.search); @@ -46,6 +47,9 @@ async function Initialize() { passwordTextbox.disabled = false; passwordTextbox.addEventListener('keyup', checkDataValidity); checkDataValidity(); + + loadingSpan.textContent = ''; + submissionForm.style.visibility = 'visible'; } Initialize(); diff --git a/views/accounts/createuser.pug b/views/accounts/createuser.pug index b0c0ca2..a43aad9 100644 --- a/views/accounts/createuser.pug +++ b/views/accounts/createuser.pug @@ -5,6 +5,7 @@ block stylesheets link(rel='stylesheet', href='/stylesheets/form.css') block content + span#loading-message Loading... form#submission-form(action='/manage/account', method='POST') if accountID input#account-id(type="hidden" name="account" value=accountID) From 29605613c0461c1b9dc83a90daf5023a313bcef3 Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 9 Mar 2022 16:55:07 -0700 Subject: [PATCH 252/255] Improve loading for editing divisions --- public/scripts/manage/division.js | 8 ++++++-- routes/fetch.js | 12 ++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/public/scripts/manage/division.js b/public/scripts/manage/division.js index 1d75742..8474b4b 100644 --- a/public/scripts/manage/division.js +++ b/public/scripts/manage/division.js @@ -15,7 +15,7 @@ async function initializeForm() { let params = new URLSearchParams(location.search); let divisionID = params.get('division'); if(divisionID) { - const division = await Data.getDivision(divisionID); + const division = await (await fetch(`/fetch/manage/division?division=${divisionID}`)).json(); nameTextbox.value = division.name; @@ -27,7 +27,11 @@ async function initializeForm() { if(gender == 'female') genderDropdown.selectedIndex = 1; else genderDropdown.selectedIndex = 2; - Form.populateSports(sportDropdown, division.sportID); + let data = {}; + data.sports = [division.sport]; + data.latestGame = {sportID : division.sportID }; + + Form.populateSports(sportDropdown, null, data); Form.addHiddenValue('division', divisionID, submissionForm); } diff --git a/routes/fetch.js b/routes/fetch.js index a8b317e..f807190 100644 --- a/routes/fetch.js +++ b/routes/fetch.js @@ -136,6 +136,18 @@ router.get('/manage/divisions', async function (req, res, next) { res.json(data); }); +router.get('/manage/division', async function (req, res, next) { + try { + const divisionID = req.query.division; + const data = await divisions.getFromID(divisionID); + data.sport = await sports.getFromID(data.sportID); + res.json(data); + } catch(err) { + console.error("ERROR: " + err.message); + res.status(500).send("An error has occurred"); + } +}); + router.get('/manage/teams', async function (req, res, next) { const data = await teams.retrieve(); From 8d830735fffa6e3363afb3b674830ad8146dfb6f Mon Sep 17 00:00:00 2001 From: sudoer777 <78781902+sudoer777@users.noreply.github.com> Date: Wed, 9 Mar 2022 17:57:18 -0700 Subject: [PATCH 253/255] Add better loading for editing teams --- public/scripts/manage/team.js | 8 ++++++-- routes/data.js | 2 +- routes/fetch.js | 12 ++++++++++++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/public/scripts/manage/team.js b/public/scripts/manage/team.js index cd5ac95..a7eadce 100644 --- a/public/scripts/manage/team.js +++ b/public/scripts/manage/team.js @@ -14,14 +14,18 @@ async function initializeForm() { let params = new URLSearchParams(location.search); let teamID = params.get('team'); if(teamID) { - const team = await Data.getTeam(teamID); + const team = await (await fetch(`/fetch/manage/team?team=${teamID}`)).json(); nameTextbox.value = team.name; deleteButton.style.visibility = "visible"; deleteButton.disabled = false; - Form.populateSports(sportDropdown, team.sportID); + let data = {}; + data.sports = [team.sport]; + data.latestGame = {sportID : team.sportID }; + + Form.populateSports(sportDropdown, null, data); Form.addHiddenValue('team', teamID, submissionForm); } diff --git a/routes/data.js b/routes/data.js index b6769d5..55c1970 100644 --- a/routes/data.js +++ b/routes/data.js @@ -98,7 +98,7 @@ router.get('/team', async function(req, res, next) { console.error("ERROR: " + err.message); res.status(500).send("An error has occurred"); } -}) +}); router.get('/games', async function(req, res, next) { try { diff --git a/routes/fetch.js b/routes/fetch.js index f807190..4958fd3 100644 --- a/routes/fetch.js +++ b/routes/fetch.js @@ -158,6 +158,18 @@ router.get('/manage/teams', async function (req, res, next) { res.json(data); }); +router.get('/manage/team', async function (req, res, next) { + try { + const teamID = req.query.team; + const data = await teams.getFromID(teamID); + data.sport = await sports.getFromID(data.sportID); + res.json(data); + } catch(err) { + console.error("ERROR: " + err.message); + res.status(500).send("An error has occurred"); + } +}); + router.get('/manage/games', checkLoginStatus.user, async function (req, res, next) { try{ const userIsAdmin = req.user[2]; From 6ceeed1ef6a853894a7425905746fbcd66883aae Mon Sep 17 00:00:00 2001 From: Sudoer777 Date: Thu, 10 Mar 2022 01:08:05 +0000 Subject: [PATCH 254/255] Update README.md --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3835188..6d558c8 100644 --- a/README.md +++ b/README.md @@ -12,11 +12,11 @@ A web app designed to collect and display scores for sports ## Installation -This repository is designed to be pushed to Heroku/Dokku/etc. +This repository can be cloned and then pushed to Heroku/Dokku/etc. ### Requirements -- PostgreSQL (with empty database created and an account to access it) +- PostgreSQL (with an empty database created and an account to access it) ### Environment Variables @@ -32,6 +32,8 @@ This repository is designed to be pushed to Heroku/Dokku/etc. This program uses Node.js/Express.js for the backend, PostgreSQL for the database (with node-postgres), and Passport.js for managing users and sessions. +To view the code, clone the repository and open it in VSCode/VSCodium. + ### Structure - `database` folder contains backend scripts for managing and storing data. @@ -44,6 +46,7 @@ This program uses Node.js/Express.js for the backend, PostgreSQL for the databas - `auth.js` deals with logging in and out (`/auth/*`). - `checkLoginStatus.js` contains functions for checking the login status of the current user. - `data.js` sends various data to the client in JSON format (`/data/*`). + - `fetch.js` sends more specific data formatted for specific pages in JSON format (`/fetch/*`) - `index.js` directs to the home page (`/`). - `manage.js` contains various functions that allows the user to add and edit items through the web browser (`/manage/*`). - `views` folder contains pug templates for each webpage, and a `layout` template for the base layout of each page. From 946316d4b53b6bd5f104c22d4bb130c693acbe80 Mon Sep 17 00:00:00 2001 From: Sudoer777 Date: Thu, 10 Mar 2022 01:15:32 +0000 Subject: [PATCH 255/255] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b504c93..2e017a4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "score-tracker", - "version": "1.0.3-pre", + "version": "1.2.0", "private": true, "scripts": { "start": "node ./bin/www"