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 001/169] 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 002/169] 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 003/169] 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 004/169] 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 005/169] 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 006/169] 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 007/169] 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 008/169] 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 009/169] 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 010/169] 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 011/169] 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 012/169] 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 013/169] 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 014/169] 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 015/169] 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 016/169] 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 017/169] 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 018/169] 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 019/169] 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 020/169] 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 021/169] 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 022/169] 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 023/169] 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 024/169] 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 025/169] 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 026/169] 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 027/169] 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 028/169] 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 029/169] 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 030/169] 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 031/169] 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 032/169] 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 033/169] 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 034/169] 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 035/169] 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 036/169] 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 037/169] 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 038/169] 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 039/169] 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 040/169] 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 041/169] 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 042/169] 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 043/169] 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 044/169] 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 045/169] 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 046/169] 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 047/169] 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 048/169] 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 049/169] 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 050/169] 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 051/169] 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 052/169] 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 053/169] 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 054/169] 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 055/169] 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 056/169] 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 057/169] 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 058/169] 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 059/169] 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 060/169] 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 061/169] 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 062/169] 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 063/169] 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 064/169] 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 065/169] 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 066/169] 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 067/169] 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 068/169] 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 069/169] 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 070/169] 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 071/169] 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 072/169] 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 073/169] 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 074/169] 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 075/169] 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 076/169] 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 077/169] 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 078/169] 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 079/169] 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 080/169] 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 081/169] 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 082/169] 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 083/169] 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 084/169] 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 085/169] 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 086/169] 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 087/169] 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 088/169] 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 089/169] 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 090/169] 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 091/169] 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 092/169] 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 093/169] 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 094/169] 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 095/169] 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 096/169] 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 097/169] 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 098/169] 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 099/169] 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 100/169] 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 101/169] 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 102/169] 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 103/169] 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 104/169] 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 105/169] 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 106/169] 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 107/169] 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 108/169] 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 109/169] 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 110/169] 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 111/169] 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 112/169] 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 113/169] 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 114/169] 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 115/169] 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 116/169] 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 117/169] 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 118/169] 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 119/169] 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 120/169] 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 121/169] 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 122/169] 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 123/169] 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 124/169] 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 125/169] 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 126/169] 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 127/169] 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 128/169] 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 129/169] 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 130/169] 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 131/169] 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 132/169] 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 133/169] 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 134/169] 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 135/169] 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 136/169] 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 137/169] 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 138/169] 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 139/169] 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 140/169] 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 141/169] 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 142/169] 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 143/169] 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 144/169] 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 145/169] 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 146/169] 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 147/169] 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 148/169] 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 149/169] 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 150/169] 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 151/169] 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 152/169] 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 153/169] 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 154/169] 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 155/169] 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 156/169] 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 157/169] 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 158/169] 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 159/169] 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 160/169] 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 161/169] 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 162/169] 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 163/169] 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 164/169] 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 165/169] 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 166/169] 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 167/169] 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 168/169] 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 169/169] 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",