diff --git a/.eslintrc.js b/.eslintrc.js index a605438..a55a318 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -6,7 +6,10 @@ module.exports = { node: true, jquery: true, }, - extends: 'airbnb-base', + extends: [ + 'airbnb-base', + 'plugin:sonarjs/recommended', + ], globals: { Atomics: 'readonly', SharedArrayBuffer: 'readonly', @@ -21,6 +24,10 @@ module.exports = { parserOptions: { ecmaVersion: 2021, }, + plugins: [ + 'unicorn', + 'sonarjs', + ], rules: { indent: [ 'error', @@ -60,6 +67,24 @@ module.exports = { json: 'always', }, ], + // unicorn + 'unicorn/numeric-separators-style': 'error', + 'unicorn/prefer-query-selector': 'error', + 'unicorn/catch-error-name': 'error', + 'unicorn/no-negated-condition': 'error', + 'unicorn/better-regex': 'error', + 'unicorn/consistent-function-scoping': 'error', + 'unicorn/prefer-array-flat-map': 'error', + 'unicorn/prefer-array-find': 'error', + 'unicorn/prefer-regexp-test': 'error', + 'unicorn/consistent-destructuring': 'error', + 'unicorn/prefer-date-now': 'error', + 'unicorn/prefer-ternary': 'error', + 'unicorn/prefer-dom-node-append': 'error', + 'unicorn/explicit-length-check': 'error', + 'unicorn/prefer-at': 'error', + // sonarjs + 'sonarjs/cognitive-complexity': 0, }, ignorePatterns: [ '*.min.js', diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..996ec80 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,14 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "npm", + "script": "lint", + "problemMatcher": [ + "$eslint-stylish" + ], + "label": "npm: lint", + "detail": "eslint ./server/scripts/**" + } + ] +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 90e0bbe..e0c3e3c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,8 @@ "eslint": "^8.21.0", "eslint-config-airbnb-base": "^15.0.0", "eslint-plugin-import": "^2.26.0", + "eslint-plugin-sonarjs": "^0.17.0", + "eslint-plugin-unicorn": "^45.0.2", "express": "^4.17.1", "gulp": "^4.0.2", "gulp-concat": "^2.6.1", @@ -34,6 +36,127 @@ "webpack-stream": "^7.0.0" } }, + "node_modules/@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.1.2.tgz", + "integrity": "sha512-7qELuQWWjVDdVsFQ5+beUl+KPczrEDA7S3zM4QUd/bJl7oXgsmpXaEVqrRTnOBqenOV4rWf2kVZk2Ot085zPWA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, "node_modules/@eslint/eslintrc": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", @@ -230,6 +353,12 @@ "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==", "dev": true }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", + "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", + "dev": true + }, "node_modules/@webassemblyjs/ast": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", @@ -1273,6 +1402,18 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -1431,6 +1572,21 @@ "node": ">=6.0" } }, + "node_modules/ci-info": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.1.tgz", + "integrity": "sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, "node_modules/class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", @@ -1458,6 +1614,27 @@ "node": ">= 4.0" } }, + "node_modules/clean-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clean-regexp/-/clean-regexp-1.0.0.tgz", + "integrity": "sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/clean-regexp/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -2379,6 +2556,195 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, + "node_modules/eslint-plugin-sonarjs": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.17.0.tgz", + "integrity": "sha512-jtGtxI49UbJJeJj7CVRLI3+LLH+y+hkR3GOOwM7vBbci9DEFIRGCWvEd2BJScrzltZ6D6iubukTAfc9cyG7sdw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/eslint-plugin-unicorn": { + "version": "45.0.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-45.0.2.tgz", + "integrity": "sha512-Y0WUDXRyGDMcKLiwgL3zSMpHrXI00xmdyixEGIg90gHnj0PcHY4moNv3Ppje/kDivdAy5vUeUr7z211ImPv2gw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.19.1", + "@eslint-community/eslint-utils": "^4.1.2", + "ci-info": "^3.6.1", + "clean-regexp": "^1.0.0", + "esquery": "^1.4.0", + "indent-string": "^4.0.0", + "is-builtin-module": "^3.2.0", + "jsesc": "^3.0.2", + "lodash": "^4.17.21", + "pluralize": "^8.0.0", + "read-pkg-up": "^7.0.1", + "regexp-tree": "^0.1.24", + "regjsparser": "^0.9.1", + "safe-regex": "^2.1.1", + "semver": "^7.3.8", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=14.18" + }, + "funding": { + "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" + }, + "peerDependencies": { + "eslint": ">=8.28.0" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/safe-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz", + "integrity": "sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==", + "dev": true, + "dependencies": { + "regexp-tree": "~0.1.1" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/eslint-scope": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", @@ -4199,6 +4565,21 @@ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "dev": true }, + "node_modules/is-builtin-module": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.0.tgz", + "integrity": "sha512-phDA4oSGt7vl1n5tJvTWooWWAsXLY+2xCnxNqvKhGEzujg+A43wPlPOyDg3C8XQHN+6k/JTQWJ/j0dQh/qr+Hw==", + "dev": true, + "dependencies": { + "builtin-modules": "^3.3.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -4665,6 +5046,12 @@ "url": "https://opencollective.com/js-sdsl" } }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -4677,12 +5064,23 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true, - "peer": true + "dev": true }, "node_modules/json-schema-traverse": { "version": "0.4.1", @@ -4816,6 +5214,12 @@ "node": ">=0.10.0" } }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, "node_modules/load-json-file": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", @@ -4869,6 +5273,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, "node_modules/lodash.clone": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz", @@ -4899,6 +5309,18 @@ "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==", "dev": true }, + "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==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/luxon": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.2.1.tgz", @@ -5262,6 +5684,15 @@ "node": ">= 0.6" } }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -5824,6 +6255,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/param-case": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", @@ -6091,6 +6531,15 @@ "node": ">=0.10.0" } }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", @@ -6535,6 +6984,15 @@ "node": ">=0.10.0" } }, + "node_modules/regexp-tree": { + "version": "0.1.24", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.24.tgz", + "integrity": "sha512-s2aEVuLhvnVJW6s/iPgEGK6R+/xngd2jNQ+xy4bXNDKxZKJH6jpPHY6kVeVv1IeLCHgswRj+Kl3ELaDjG6V1iw==", + "dev": true, + "bin": { + "regexp-tree": "bin/regexp-tree" + } + }, "node_modules/regexp.prototype.flags": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", @@ -6564,6 +7022,27 @@ "url": "https://github.com/sponsors/mysticatea" } }, + "node_modules/regjsparser": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "dev": true, + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + } + }, "node_modules/relateurl": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", @@ -7626,6 +8105,18 @@ "node": ">=4" } }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -8767,6 +9258,12 @@ "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", "dev": true }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/yargs": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.2.tgz", @@ -8812,6 +9309,99 @@ } }, "dependencies": { + "@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dev": true, + "requires": { + "@babel/highlight": "^7.18.6" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + }, + "@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@eslint-community/eslint-utils": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.1.2.tgz", + "integrity": "sha512-7qELuQWWjVDdVsFQ5+beUl+KPczrEDA7S3zM4QUd/bJl7oXgsmpXaEVqrRTnOBqenOV4rWf2kVZk2Ot085zPWA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^3.3.0" + } + }, "@eslint/eslintrc": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", @@ -8974,6 +9564,12 @@ "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==", "dev": true }, + "@types/normalize-package-data": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", + "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", + "dev": true + }, "@webassemblyjs/ast": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", @@ -9806,6 +10402,12 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true + }, "bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -9928,6 +10530,12 @@ "dev": true, "peer": true }, + "ci-info": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.1.tgz", + "integrity": "sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w==", + "dev": true + }, "class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", @@ -9949,6 +10557,23 @@ "source-map": "~0.6.0" } }, + "clean-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clean-regexp/-/clean-regexp-1.0.0.tgz", + "integrity": "sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + }, + "dependencies": { + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + } + } + }, "clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -10702,6 +11327,143 @@ } } }, + "eslint-plugin-sonarjs": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.17.0.tgz", + "integrity": "sha512-jtGtxI49UbJJeJj7CVRLI3+LLH+y+hkR3GOOwM7vBbci9DEFIRGCWvEd2BJScrzltZ6D6iubukTAfc9cyG7sdw==", + "dev": true, + "requires": {} + }, + "eslint-plugin-unicorn": { + "version": "45.0.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-45.0.2.tgz", + "integrity": "sha512-Y0WUDXRyGDMcKLiwgL3zSMpHrXI00xmdyixEGIg90gHnj0PcHY4moNv3Ppje/kDivdAy5vUeUr7z211ImPv2gw==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.19.1", + "@eslint-community/eslint-utils": "^4.1.2", + "ci-info": "^3.6.1", + "clean-regexp": "^1.0.0", + "esquery": "^1.4.0", + "indent-string": "^4.0.0", + "is-builtin-module": "^3.2.0", + "jsesc": "^3.0.2", + "lodash": "^4.17.21", + "pluralize": "^8.0.0", + "read-pkg-up": "^7.0.1", + "regexp-tree": "^0.1.24", + "regjsparser": "^0.9.1", + "safe-regex": "^2.1.1", + "semver": "^7.3.8", + "strip-indent": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + } + }, + "safe-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz", + "integrity": "sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==", + "dev": true, + "requires": { + "regexp-tree": "~0.1.1" + } + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } + } + }, "eslint-scope": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", @@ -12159,6 +12921,15 @@ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "dev": true }, + "is-builtin-module": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.0.tgz", + "integrity": "sha512-phDA4oSGt7vl1n5tJvTWooWWAsXLY+2xCnxNqvKhGEzujg+A43wPlPOyDg3C8XQHN+6k/JTQWJ/j0dQh/qr+Hw==", + "dev": true, + "requires": { + "builtin-modules": "^3.3.0" + } + }, "is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -12489,6 +13260,12 @@ "integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==", "dev": true }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, "js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -12498,12 +13275,17 @@ "argparse": "^2.0.1" } }, + "jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true + }, "json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true, - "peer": true + "dev": true }, "json-schema-traverse": { "version": "0.4.1", @@ -12612,6 +13394,12 @@ } } }, + "lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, "load-json-file": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", @@ -12652,6 +13440,12 @@ "p-locate": "^5.0.0" } }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, "lodash.clone": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz", @@ -12682,6 +13476,15 @@ "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==", "dev": true }, + "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==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, "luxon": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.2.1.tgz", @@ -12961,6 +13764,12 @@ "mime-db": "1.52.0" } }, + "min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true + }, "minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -13402,6 +14211,12 @@ "aggregate-error": "^3.0.0" } }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, "param-case": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", @@ -13605,6 +14420,12 @@ } } }, + "pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true + }, "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", @@ -13954,6 +14775,12 @@ } } }, + "regexp-tree": { + "version": "0.1.24", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.24.tgz", + "integrity": "sha512-s2aEVuLhvnVJW6s/iPgEGK6R+/xngd2jNQ+xy4bXNDKxZKJH6jpPHY6kVeVv1IeLCHgswRj+Kl3ELaDjG6V1iw==", + "dev": true + }, "regexp.prototype.flags": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", @@ -13971,6 +14798,23 @@ "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", "dev": true }, + "regjsparser": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "dev": true + } + } + }, "relateurl": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", @@ -14795,6 +15639,15 @@ "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true }, + "strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "requires": { + "min-indent": "^1.0.0" + } + }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -15688,6 +16541,12 @@ "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", "dev": true }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "yargs": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.2.tgz", diff --git a/package.json b/package.json index 875b9f1..24f4106 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,11 @@ "devDependencies": { "del": "^6.0.0", "ejs": "^3.1.5", + "eslint": "^8.21.0", "eslint-config-airbnb-base": "^15.0.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-sonarjs": "^0.17.0", + "eslint-plugin-unicorn": "^45.0.2", "express": "^4.17.1", "gulp": "^4.0.2", "gulp-concat": "^2.6.1", @@ -40,9 +44,6 @@ "suncalc": "^1.8.0", "swiped-events": "^1.1.4", "terser-webpack-plugin": "^5.3.6", - "webpack-stream": "^7.0.0", - "eslint": "^8.21.0", - "eslint-plugin-import": "^2.26.0" - }, - "dependencies": {} + "webpack-stream": "^7.0.0" + } } diff --git a/server/scripts/data/.eslintrc.js b/server/scripts/data/.eslintrc.js new file mode 100644 index 0000000..b90682b --- /dev/null +++ b/server/scripts/data/.eslintrc.js @@ -0,0 +1,6 @@ +module.exports = { + rules: { + // unicorn + 'unicorn/numeric-separators-style': 0, + }, +}; diff --git a/server/scripts/index.mjs b/server/scripts/index.mjs index 89bb7df..8940d7d 100644 --- a/server/scripts/index.mjs +++ b/server/scripts/index.mjs @@ -22,35 +22,38 @@ const categories = [ 'Postal', 'Populated Place', ]; const category = categories.join(','); +const TXT_ADDRESS_SELECTOR = '#txtAddress'; +const TOGGLE_FULL_SCREEN_SELECTOR = '#ToggleFullScreen'; +const BNT_GET_GPS_SELECTOR = '#btnGetGps'; const init = () => { - document.getElementById('txtAddress').addEventListener('focus', (e) => { + document.querySelector(TXT_ADDRESS_SELECTOR).addEventListener('focus', (e) => { e.target.select(); }); registerRefreshData(loadData); - document.getElementById('NavigateMenu').addEventListener('click', btnNavigateMenuClick); - document.getElementById('NavigateRefresh').addEventListener('click', btnNavigateRefreshClick); - document.getElementById('NavigateNext').addEventListener('click', btnNavigateNextClick); - document.getElementById('NavigatePrevious').addEventListener('click', btnNavigatePreviousClick); - document.getElementById('NavigatePlay').addEventListener('click', btnNavigatePlayClick); - document.getElementById('ToggleFullScreen').addEventListener('click', btnFullScreenClick); - const btnGetGps = document.getElementById('btnGetGps'); + document.querySelector('#NavigateMenu').addEventListener('click', btnNavigateMenuClick); + document.querySelector('#NavigateRefresh').addEventListener('click', btnNavigateRefreshClick); + document.querySelector('#NavigateNext').addEventListener('click', btnNavigateNextClick); + document.querySelector('#NavigatePrevious').addEventListener('click', btnNavigatePreviousClick); + document.querySelector('#NavigatePlay').addEventListener('click', btnNavigatePlayClick); + document.querySelector(TOGGLE_FULL_SCREEN_SELECTOR).addEventListener('click', btnFullScreenClick); + const btnGetGps = document.querySelector(BNT_GET_GPS_SELECTOR); btnGetGps.addEventListener('click', btnGetGpsClick); if (!navigator.geolocation) btnGetGps.style.display = 'none'; - document.getElementById('divTwc').addEventListener('click', () => { + document.querySelector('#divTwc').addEventListener('click', () => { if (document.fullscreenElement) updateFullScreenNavigate(); }); - document.getElementById('txtAddress').addEventListener('keydown', (key) => { if (key.code === 'Enter') formSubmit(); }); - document.getElementById('btnGetLatLng').addEventListener('click', () => formSubmit()); + document.querySelector(TXT_ADDRESS_SELECTOR).addEventListener('keydown', (key) => { if (key.code === 'Enter') formSubmit(); }); + document.querySelector('#btnGetLatLng').addEventListener('click', () => formSubmit()); document.addEventListener('keydown', documentKeydown); document.addEventListener('touchmove', (e) => { if (fullScreenOverride) e.preventDefault(); }); - $('#txtAddress').devbridgeAutocomplete({ + $(TXT_ADDRESS_SELECTOR).devbridgeAutocomplete({ serviceUrl: 'https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/suggest', deferRequestBy: 300, paramName: 'text', @@ -75,7 +78,7 @@ const init = () => { }); const formSubmit = () => { - const ac = $('#txtAddress').devbridgeAutocomplete(); + const ac = $(TXT_ADDRESS_SELECTOR).devbridgeAutocomplete(); if (ac.suggestions[0]) $(ac.suggestionsContainer.children[0]).trigger('click'); return false; }; @@ -85,7 +88,7 @@ const init = () => { const latLon = localStorage.getItem('latLon'); const fromGPS = localStorage.getItem('latLonFromGPS'); if (query && latLon && !fromGPS) { - const txtAddress = document.getElementById('txtAddress'); + const txtAddress = document.querySelector(TXT_ADDRESS_SELECTOR); txtAddress.value = query; loadData(JSON.parse(latLon)); } @@ -96,14 +99,14 @@ const init = () => { const play = localStorage.getItem('play'); if (play === null || play === 'true') postMessage('navButton', 'play'); - document.getElementById('btnClearQuery').addEventListener('click', () => { - document.getElementById('spanCity').innerHTML = ''; - document.getElementById('spanState').innerHTML = ''; - document.getElementById('spanStationId').innerHTML = ''; - document.getElementById('spanRadarId').innerHTML = ''; - document.getElementById('spanZoneId').innerHTML = ''; + document.querySelector('#btnClearQuery').addEventListener('click', () => { + document.querySelector('#spanCity').innerHTML = ''; + document.querySelector('#spanState').innerHTML = ''; + document.querySelector('#spanStationId').innerHTML = ''; + document.querySelector('#spanRadarId').innerHTML = ''; + document.querySelector('#spanZoneId').innerHTML = ''; - document.getElementById('chkAutoRefresh').checked = true; + document.querySelector('#chkAutoRefresh').checked = true; localStorage.removeItem('autoRefresh'); localStorage.removeItem('play'); @@ -112,12 +115,12 @@ const init = () => { localStorage.removeItem('latLonQuery'); localStorage.removeItem('latLon'); localStorage.removeItem('latLonFromGPS'); - document.getElementById('btnGetGps').classList.remove('active'); + document.querySelector(BNT_GET_GPS_SELECTOR).classList.remove('active'); }); // swipe functionality - document.getElementById('container').addEventListener('swiped-left', () => swipeCallBack('left')); - document.getElementById('container').addEventListener('swiped-right', () => swipeCallBack('right')); + document.querySelector('#container').addEventListener('swiped-left', () => swipeCallBack('left')); + document.querySelector('#container').addEventListener('swiped-right', () => swipeCallBack('right')); }; const autocompleteOnSelect = async (suggestion, elem) => { @@ -135,7 +138,7 @@ const autocompleteOnSelect = async (suggestion, elem) => { const loc = data.locations[0]; if (loc) { localStorage.removeItem('latLonFromGPS'); - document.getElementById('btnGetGps').classList.remove('active'); + document.querySelector(BNT_GET_GPS_SELECTOR).classList.remove('active'); doRedirectToGeometry(loc.feature.geometry); } else { console.error('An unexpected error occurred. Please try a different search string.'); @@ -145,7 +148,7 @@ const autocompleteOnSelect = async (suggestion, elem) => { const doRedirectToGeometry = (geom, haveDataCallback) => { const latLon = { lat: round2(geom.y, 4), lon: round2(geom.x, 4) }; // Save the query - localStorage.setItem('latLonQuery', document.getElementById('txtAddress').value); + localStorage.setItem('latLonQuery', document.querySelector(TXT_ADDRESS_SELECTOR).value); localStorage.setItem('latLon', JSON.stringify(latLon)); // get the data @@ -153,10 +156,10 @@ const doRedirectToGeometry = (geom, haveDataCallback) => { }; const btnFullScreenClick = () => { - if (!document.fullscreenElement) { - enterFullScreen(); - } else { + if (document.fullscreenElement) { exitFullscreen(); + } else { + enterFullScreen(); } if (isPlaying()) { @@ -171,7 +174,7 @@ const btnFullScreenClick = () => { }; const enterFullScreen = () => { - const element = document.getElementById('divTwc'); + const element = document.querySelector('#divTwc'); // Supports most browsers and their versions. const requestMethod = element.requestFullScreen || element.webkitRequestFullScreen @@ -189,7 +192,7 @@ const enterFullScreen = () => { updateFullScreenNavigate(); // change hover text and image - const img = document.getElementById('ToggleFullScreen'); + const img = document.querySelector(TOGGLE_FULL_SCREEN_SELECTOR); img.src = 'images/nav/ic_fullscreen_exit_white_24dp_2x.png'; img.title = 'Exit fullscreen'; }; @@ -213,7 +216,7 @@ const exitFullscreen = () => { } resize(); // change hover text and image - const img = document.getElementById('ToggleFullScreen'); + const img = document.querySelector(TOGGLE_FULL_SCREEN_SELECTOR); img.src = 'images/nav/ic_fullscreen_white_24dp_2x.png'; img.title = 'Enter fullscreen'; }; @@ -232,7 +235,7 @@ const loadData = (_latLon, haveDataCallback) => { // if there's no data stop if (!latLon) return; - document.getElementById('txtAddress').blur(); + document.querySelector(TXT_ADDRESS_SELECTOR).blur(); stopAutoRefreshTimer(); latLonReceived(latLon, haveDataCallback); }; @@ -276,8 +279,9 @@ let navigateFadeIntervalId = null; const updateFullScreenNavigate = () => { document.activeElement.blur(); - document.getElementById('divTwcBottom').classList.remove('hidden'); - document.getElementById('divTwcBottom').classList.add('visible'); + const divTwcBottom = document.querySelector('#divTwcBottom'); + divTwcBottom.classList.remove('hidden'); + divTwcBottom.classList.add('visible'); if (navigateFadeIntervalId) { clearTimeout(navigateFadeIntervalId); @@ -286,8 +290,8 @@ const updateFullScreenNavigate = () => { navigateFadeIntervalId = setTimeout(() => { if (document.fullscreenElement) { - document.getElementById('divTwcBottom').classList.remove('visible'); - document.getElementById('divTwcBottom').classList.add('hidden'); + divTwcBottom.classList.remove('visible'); + divTwcBottom.classList.add('hidden'); } }, 2000); }; @@ -355,7 +359,7 @@ const getPosition = async () => new Promise((resolve) => { const btnGetGpsClick = async () => { if (!navigator.geolocation) return; - const btn = document.getElementById('btnGetGps'); + const btn = document.querySelector(BNT_GET_GPS_SELECTOR); // toggle first if (btn.classList.contains('active')) { @@ -371,7 +375,7 @@ const btnGetGpsClick = async () => { const position = await getPosition(); const { latitude, longitude } = position.coords; - const txtAddress = document.getElementById('txtAddress'); + const txtAddress = document.querySelector(TXT_ADDRESS_SELECTOR); txtAddress.value = `${round2(latitude, 4)}, ${round2(longitude, 4)}`; doRedirectToGeometry({ y: latitude, x: longitude }, (point) => { diff --git a/server/scripts/modules/currentweather.mjs b/server/scripts/modules/currentweather.mjs index 5f89c84..bde1f20 100644 --- a/server/scripts/modules/currentweather.mjs +++ b/server/scripts/modules/currentweather.mjs @@ -59,8 +59,8 @@ class CurrentWeather extends WeatherDisplay { observations = undefined; throw new Error(`Unable to get observations: ${station.properties.stationIdentifier}, trying next station`); } - } catch (e) { - console.error(e); + } catch (error) { + console.error(error); } } // test for data received diff --git a/server/scripts/modules/currentweatherscroll.mjs b/server/scripts/modules/currentweatherscroll.mjs index c90f3d2..ca8655b 100644 --- a/server/scripts/modules/currentweatherscroll.mjs +++ b/server/scripts/modules/currentweatherscroll.mjs @@ -75,12 +75,10 @@ const screens = [ // wind (data) => { - let text = ''; - if (data.WindSpeed > 0) { - text = `Wind: ${data.WindDirection} ${data.WindSpeed} ${data.WindUnit}`; - } else { - text = 'Wind: Calm'; - } + let text = data.WindSpeed > 0 + ? `Wind: ${data.WindDirection} ${data.WindSpeed} ${data.WindUnit}` + : 'Wind: Calm'; + if (data.WindGust > 0) { text += ` Gusts to ${data.WindGust}`; } @@ -88,7 +86,10 @@ const screens = [ }, // visibility - (data) => `Visib: ${data.Visibility} ${data.VisibilityUnit} Ceiling: ${data.Ceiling === 0 ? 'Unlimited' : `${data.Ceiling} ${data.CeilingUnit}`}`, + (data) => { + const distance = `${data.Ceiling} ${data.CeilingUnit}`; + return `Visib: ${data.Visibility} ${data.VisibilityUnit} Ceiling: ${data.Ceiling === 0 ? 'Unlimited' : distance}`; + }, ]; // internal draw function with preset parameters diff --git a/server/scripts/modules/extendedforecast.mjs b/server/scripts/modules/extendedforecast.mjs index d37685c..321b358 100644 --- a/server/scripts/modules/extendedforecast.mjs +++ b/server/scripts/modules/extendedforecast.mjs @@ -31,9 +31,9 @@ class ExtendedForecast extends WeatherDisplay { retryCount: 3, stillWaiting: () => this.stillWaiting(), }); - } catch (e) { + } catch (error) { console.error('Unable to get extended forecast'); - console.error(e.status, e.responseJSON); + console.error(error.status, error.responseJSON); this.setStatus(STATUS.failed); return; } @@ -52,7 +52,10 @@ class ExtendedForecast extends WeatherDisplay { // create each day template const days = forecast.map((Day) => { - const fill = {}; + const fill = { + icon: { type: 'img', src: Day.icon }, + condition: Day.text, + }; fill.date = Day.dayName; const { low } = Day; @@ -61,10 +64,6 @@ class ExtendedForecast extends WeatherDisplay { } const { high } = Day; fill['value-hi'] = Math.round(high); - fill.condition = Day.text; - - // draw the icon - fill.icon = { type: 'img', src: Day.icon }; // return the filled template return this.fillTemplate('day', fill); @@ -123,13 +122,13 @@ const parse = (fullForecast) => { const shortenExtendedForecastText = (long) => { const regexList = [ - [/ and /ig, ' '], - [/Slight /ig, ''], - [/Chance /ig, ''], - [/Very /ig, ''], - [/Patchy /ig, ''], - [/Areas /ig, ''], - [/Dense /ig, ''], + [/ and /gi, ' '], + [/slight /gi, ''], + [/chance /gi, ''], + [/very /gi, ''], + [/patchy /gi, ''], + [/areas /gi, ''], + [/dense /gi, ''], [/Thunderstorm/g, 'T\'Storm'], ]; // run all regexes @@ -144,10 +143,10 @@ const shortenExtendedForecastText = (long) => { let short1 = conditions[0].substr(0, 10); let short2 = ''; if (conditions[1]) { - if (!short1.endsWith('.')) { - short2 = conditions[1].substr(0, 10); - } else { + if (short1.endsWith('.')) { short1 = short1.replace(/\./, ''); + } else { + short2 = conditions[1].substr(0, 10); } if (short2 === 'Blowing') { diff --git a/server/scripts/modules/hazards.mjs b/server/scripts/modules/hazards.mjs index b2914f4..7bb2bfb 100644 --- a/server/scripts/modules/hazards.mjs +++ b/server/scripts/modules/hazards.mjs @@ -40,9 +40,9 @@ class Hazards extends WeatherDisplay { // show alert indicator if (this.data.length > 0) alert.classList.add('show'); - } catch (e) { + } catch (error) { console.error('Get hourly forecast failed'); - console.error(e.status, e.responseJSON); + console.error(error.status, error.responseJSON); if (this.isEnabled) this.setStatus(STATUS.failed); // return undefined to other subscribers this.getDataCallback(undefined); diff --git a/server/scripts/modules/hourly.mjs b/server/scripts/modules/hourly.mjs index cb566bb..819b4e2 100644 --- a/server/scripts/modules/hourly.mjs +++ b/server/scripts/modules/hourly.mjs @@ -34,9 +34,9 @@ class Hourly extends WeatherDisplay { try { // get the forecast forecast = await json(weatherParameters.forecastGridData, { retryCount: 3, stillWaiting: () => this.stillWaiting() }); - } catch (e) { + } catch (error) { console.error('Get hourly forecast failed'); - console.error(e.status, e.responseJSON); + console.error(error.status, error.responseJSON); if (this.isEnabled) this.setStatus(STATUS.failed); // return undefined to other subscribers this.getDataCallback(undefined); @@ -183,7 +183,7 @@ const expand = (data) => { result.push(item.value); // push data array } // timestamp is after now // increment start time by 1 hour - startTime += 3600000; + startTime += 3_600_000; } while (startTime < endTime && result.length < 24); }); // for each value diff --git a/server/scripts/modules/icons.mjs b/server/scripts/modules/icons.mjs index a851161..a9895f4 100644 --- a/server/scripts/modules/icons.mjs +++ b/server/scripts/modules/icons.mjs @@ -1,3 +1,4 @@ +/* eslint-disable unicorn/consistent-function-scoping */ /* spell-checker: disable */ const getWeatherRegionalIconFromIconLink = (link, _isNightTime) => { @@ -8,7 +9,7 @@ const getWeatherRegionalIconFromIconLink = (link, _isNightTime) => { // grab everything after the last slash ending at any of these: ?&, const afterLastSlash = link.toLowerCase().match(/[^/]+$/)[0]; - let conditionName = afterLastSlash.match(/(.*?)[,?&.]/)[1]; + let conditionName = afterLastSlash.match(/(.*?)[&,.?]/)[1]; // using probability as a crude heavy/light indication where possible const value = +(link.match(/,(\d{2,3})/) ?? [0, 100])[1]; @@ -156,7 +157,7 @@ const getWeatherIconFromIconLink = (link, _isNightTime) => { // grab everything after the last slash ending at any of these: ?&, const afterLastSlash = link.toLowerCase().match(/[^/]+$/)[0]; - let conditionName = afterLastSlash.match(/(.*?)[,?&.]/)[1]; + let conditionName = afterLastSlash.match(/(.*?)[&,.?]/)[1]; // using probability as a crude heavy/light indication where possible const value = +(link.match(/,(\d{2,3})/) ?? [0, 100])[1]; diff --git a/server/scripts/modules/latestobservations.mjs b/server/scripts/modules/latestobservations.mjs index f883bdc..98cba31 100644 --- a/server/scripts/modules/latestobservations.mjs +++ b/server/scripts/modules/latestobservations.mjs @@ -50,7 +50,7 @@ class LatestObservations extends WeatherDisplay { this.data = actualConditions.slice(0, this.MaximumRegionalStations); // test for at least one station - if (this.data.length < 1) { + if (this.data.length === 0) { this.setStatus(STATUS.noData); return; } @@ -73,10 +73,12 @@ class LatestObservations extends WeatherDisplay { const Temperature = Math.round(celsiusToFahrenheit(condition.temperature.value)); const WindSpeed = Math.round(kphToMph(condition.windSpeed.value)); - const fill = {}; - fill.location = locationCleanup(condition.city).substr(0, 14); - fill.temp = Temperature; - fill.weather = shortenCurrentConditions(condition.textDescription).substr(0, 9); + const fill = { + location: locationCleanup(condition.city).substr(0, 14), + temp: Temperature, + weather: shortenCurrentConditions(condition.textDescription).substr(0, 9), + }; + if (WindSpeed > 0) { fill.wind = windDirection + (Array(6 - windDirection.length - WindSpeed.toString().length).join(' ')) + WindSpeed.toString(); } else if (WindSpeed === 'NA') { @@ -128,7 +130,7 @@ const getStations = async (stations) => { StationId: station.id, city: station.city, }; - } catch (e) { + } catch (error) { console.log(`Unable to get latest observations for ${station.id}`); return false; } diff --git a/server/scripts/modules/localforecast.mjs b/server/scripts/modules/localforecast.mjs index d54d6fd..92d60a8 100644 --- a/server/scripts/modules/localforecast.mjs +++ b/server/scripts/modules/localforecast.mjs @@ -66,9 +66,9 @@ class LocalForecast extends WeatherDisplay { retryCount: 3, stillWaiting: () => this.stillWaiting(), }); - } catch (e) { + } catch (error) { console.error(`GetWeatherForecast failed: ${weatherParameters.forecast}`); - console.error(e.status, e.responseJSON); + console.error(error.status, error.responseJSON); this.setStatus(STATUS.failed); return false; } diff --git a/server/scripts/modules/navigation.mjs b/server/scripts/modules/navigation.mjs index 0de648a..29ebd70 100644 --- a/server/scripts/modules/navigation.mjs +++ b/server/scripts/modules/navigation.mjs @@ -16,7 +16,8 @@ const weatherParameters = {}; // auto refresh const AUTO_REFRESH_INTERVAL_MS = 500; -const AUTO_REFRESH_TIME_MS = 600000; // 10 min. +const AUTO_REFRESH_TIME_MS = 600_000; // 10 min. +const CHK_AUTO_REFRESH_SELECTOR = '#chkAutoRefresh'; let AutoRefreshIntervalId = null; let AutoRefreshCountMs = 0; @@ -28,25 +29,19 @@ const init = async () => { // auto refresh const autoRefresh = localStorage.getItem('autoRefresh'); if (!autoRefresh || autoRefresh === 'true') { - document.getElementById('chkAutoRefresh').checked = true; + document.querySelector(CHK_AUTO_REFRESH_SELECTOR).checked = true; } else { - document.getElementById('chkAutoRefresh').checked = false; + document.querySelector(CHK_AUTO_REFRESH_SELECTOR).checked = false; } - document.getElementById('chkAutoRefresh').addEventListener('change', autoRefreshChange); + document.querySelector(CHK_AUTO_REFRESH_SELECTOR).addEventListener('change', autoRefreshChange); generateCheckboxes(); }; const message = (data) => { // dispatch event - if (!data.type) return; - switch (data.type) { - case 'navButton': - handleNavButton(data.message); - break; - - default: - console.error(`Unknown event ${data.type}`); - } + if (!data.type) return false; + if (data.type === 'navButton') return handleNavButton(data.message); + return console.error(`Unknown event ${data.type}`); }; const getWeather = async (latLon, haveDataCallback) => { @@ -61,6 +56,7 @@ const getWeather = async (latLon, haveDataCallback) => { const StationId = stations.features[0].properties.stationIdentifier; let { city } = point.properties.relativeLocation.properties; + const { state } = point.properties.relativeLocation.properties; if (StationId in StationInfo) { city = StationInfo[StationId].city; @@ -76,7 +72,7 @@ const getWeather = async (latLon, haveDataCallback) => { weatherParameters.stationId = StationId; weatherParameters.weatherOffice = point.properties.cwa; weatherParameters.city = city; - weatherParameters.state = point.properties.relativeLocation.properties.state; + weatherParameters.state = state; weatherParameters.timeZone = point.properties.timeZone; weatherParameters.forecast = point.properties.forecast; weatherParameters.forecastGridData = point.properties.forecastGridData; @@ -87,7 +83,7 @@ const getWeather = async (latLon, haveDataCallback) => { // draw the progress canvas and hide others hideAllCanvases(); - document.getElementById('loading').style.display = 'none'; + document.querySelector('#loading').style.display = 'none'; if (progress) { await progress.drawCanvas(); progress.showCanvas(); @@ -200,9 +196,7 @@ const loadDisplay = (direction) => { if (displays[idx].status === STATUS.loaded && displays[idx].timing.totalScreens > 0) break; } // if new display index is less than current display a wrap occurred, test for reload timeout - if (idx <= curIdx) { - if (refreshCheck()) return; - } + if (idx <= curIdx && refreshCheck()) return; const newDisplay = displays[idx]; // hide all displays hideAllCanvases(); @@ -212,15 +206,12 @@ const loadDisplay = (direction) => { }; // get the current display index or value -const currentDisplayIndex = () => { - const index = displays.findIndex((display) => display.active); - return index; -}; +const currentDisplayIndex = () => displays.findIndex((display) => display.active); const currentDisplay = () => displays[currentDisplayIndex()]; const setPlaying = (newValue) => { playing = newValue; - const playButton = document.getElementById('NavigatePlay'); + const playButton = document.querySelector('#NavigatePlay'); localStorage.setItem('play', playing); if (playing) { @@ -272,14 +263,14 @@ const getDisplay = (index) => displays[index]; // resize the container on a page resize const resize = () => { - const widthZoomPercent = (document.getElementById('divTwcBottom').getBoundingClientRect().width) / 640; + const widthZoomPercent = (document.querySelector('#divTwcBottom').getBoundingClientRect().width) / 640; const heightZoomPercent = (window.innerHeight) / 480; const scale = Math.min(widthZoomPercent, heightZoomPercent); if (scale < 1.0 || document.fullscreenElement) { - document.getElementById('container').style.zoom = scale; + document.querySelector('#container').style.zoom = scale; } else { - document.getElementById('container').style.zoom = 1; + document.querySelector('#container').style.zoom = 1; } }; @@ -297,7 +288,7 @@ const registerDisplay = (display) => { }; const generateCheckboxes = () => { - const availableDisplays = document.getElementById('enabledDisplays'); + const availableDisplays = document.querySelector('#enabledDisplays'); if (!availableDisplays) return; // generate checkboxes @@ -314,11 +305,11 @@ const registerProgress = (_progress) => { }; const populateWeatherParameters = (params) => { - document.getElementById('spanCity').innerHTML = `${params.city}, `; - document.getElementById('spanState').innerHTML = params.state; - document.getElementById('spanStationId').innerHTML = params.stationId; - document.getElementById('spanRadarId').innerHTML = params.radarId; - document.getElementById('spanZoneId').innerHTML = params.zoneId; + document.querySelector('#spanCity').innerHTML = `${params.city}, `; + document.querySelector('#spanState').innerHTML = params.state; + document.querySelector('#spanStationId').innerHTML = params.stationId; + document.querySelector('#spanRadarId').innerHTML = params.radarId; + document.querySelector('#spanZoneId').innerHTML = params.zoneId; }; const autoRefreshChange = (e) => { @@ -335,12 +326,12 @@ const autoRefreshChange = (e) => { const AssignLastUpdate = (date) => { if (date) { - document.getElementById('spanLastRefresh').innerHTML = date.toLocaleString('en-US', { + document.querySelector('#spanLastRefresh').innerHTML = date.toLocaleString('en-US', { weekday: 'short', month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: 'numeric', second: 'numeric', timeZoneName: 'short', }); - if (document.getElementById('chkAutoRefresh').checked) startAutoRefreshTimer(); + if (document.querySelector(CHK_AUTO_REFRESH_SELECTOR).checked) startAutoRefreshTimer(); } else { - document.getElementById('spanLastRefresh').innerHTML = '(none)'; + document.querySelector('#spanLastRefresh').innerHTML = '(none)'; } }; @@ -367,7 +358,7 @@ const startAutoRefreshTimer = () => { RemainingMs = 0; } const dt = new Date(RemainingMs); - document.getElementById('spanRefreshCountDown').innerHTML = `${dt.getMinutes() < 10 ? `0${dt.getMinutes()}` : dt.getMinutes()}:${dt.getSeconds() < 10 ? `0${dt.getSeconds()}` : dt.getSeconds()}`; + document.querySelector('#spanRefreshCountDown').innerHTML = `${dt.getMinutes().toString().padStart(2, '0')}:${dt.getSeconds().toString().padStart(2, '0')}`; // Time has elapsed. if (AutoRefreshCountMs >= AUTO_REFRESH_TIME_MS && !isPlaying()) loadTwcData(); @@ -378,7 +369,7 @@ const startAutoRefreshTimer = () => { const stopAutoRefreshTimer = () => { if (AutoRefreshIntervalId) { window.clearInterval(AutoRefreshIntervalId); - document.getElementById('spanRefreshCountDown').innerHTML = '--:--'; + document.querySelector('#spanRefreshCountDown').innerHTML = '--:--'; AutoRefreshIntervalId = null; } }; diff --git a/server/scripts/modules/progress.mjs b/server/scripts/modules/progress.mjs index 499e8f2..266922f 100644 --- a/server/scripts/modules/progress.mjs +++ b/server/scripts/modules/progress.mjs @@ -18,7 +18,7 @@ class Progress extends WeatherDisplay { // setup event listener for dom-required initialization document.addEventListener('DOMContentLoaded', () => { - this.version = document.getElementById('version').innerHTML; + this.version = document.querySelector('#version').innerHTML; this.elem.querySelector('.container').addEventListener('click', this.lineClick.bind(this)); }); @@ -36,9 +36,9 @@ class Progress extends WeatherDisplay { if (!displays) return; const lines = displays.map((display, index) => { if (display.showOnProgress === false) return false; - const fill = {}; - - fill.name = display.name; + const fill = { + name: display.name, + }; const statusClass = calcStatusClass(display.status); diff --git a/server/scripts/modules/radar.mjs b/server/scripts/modules/radar.mjs index f0b3894..5412efa 100644 --- a/server/scripts/modules/radar.mjs +++ b/server/scripts/modules/radar.mjs @@ -71,25 +71,24 @@ class Radar extends WeatherDisplay { const lists = (await Promise.all(baseUrls.map(async (url) => { try { // get a list of available radars - const radarHtml = await text(url, { cors: true }); - return radarHtml; - } catch (e) { + return text(url, { cors: true }); + } catch (error) { console.log('Unable to get list of radars'); - console.error(e); + console.error(error); this.setStatus(STATUS.failed); return false; } }))).filter((d) => d); // convert to an array of gif urls - const pngs = lists.map((html, htmlIdx) => { + const pngs = lists.flatMap((html, htmlIdx) => { const parser = new DOMParser(); const xmlDoc = parser.parseFromString(html, 'text/html'); // add the base url const base = xmlDoc.createElement('base'); base.href = baseUrls[htmlIdx]; xmlDoc.head.append(base); - const anchors = xmlDoc.getElementsByTagName('a'); + const anchors = xmlDoc.querySelectorAll('a'); const urls = []; Array.from(anchors).forEach((elem) => { if (elem.innerHTML?.includes('.png') && elem.innerHTML?.includes('n0r_')) { @@ -97,7 +96,7 @@ class Radar extends WeatherDisplay { } }); return urls; - }).flat(); + }); // get the last few images const sortedPngs = pngs.sort((a, b) => (Date(a) < Date(b) ? -1 : 1)); diff --git a/server/scripts/modules/regionalforecast-utils.mjs b/server/scripts/modules/regionalforecast-utils.mjs index 0ec2a65..02e434d 100644 --- a/server/scripts/modules/regionalforecast-utils.mjs +++ b/server/scripts/modules/regionalforecast-utils.mjs @@ -28,9 +28,9 @@ const getRegionalObservation = async (point, city) => { preloadImg(icon); // return the observation return observation.properties; - } catch (e) { + } catch (error) { console.log(`Unable to get regional observations for ${city.Name ?? city.city}`); - console.error(e.status, e.responseJSON); + console.error(error.status, error.responseJSON); return false; } }; @@ -195,7 +195,7 @@ const getXYForCityHI = (City, MaxLatitude, MinLongitude) => { }; // to fit on the map, remove anything after punctuation and then limit to 15 characters -const formatCity = (city) => city.match(/[^-;/\\,]*/)[0].substr(0, 12); +const formatCity = (city) => city.match(/[^,/;\\-]*/)[0].substr(0, 12); export { buildForecast, diff --git a/server/scripts/modules/regionalforecast.mjs b/server/scripts/modules/regionalforecast.mjs index 9b32e4d..862bf2e 100644 --- a/server/scripts/modules/regionalforecast.mjs +++ b/server/scripts/modules/regionalforecast.mjs @@ -92,7 +92,7 @@ class RegionalForecast extends WeatherDisplay { // format the observation the same as the forecast const regionalObservation = { - daytime: !!observation.icon.match(/\/day\//), + daytime: !!/\/day\//.test(observation.icon), temperature: celsiusToFahrenheit(observation.temperature.value), name: utils.formatCity(city.city), icon: observation.icon, @@ -113,9 +113,9 @@ class RegionalForecast extends WeatherDisplay { utils.buildForecast(forecast.properties.periods[1], city, cityXY), utils.buildForecast(forecast.properties.periods[2], city, cityXY), ]; - } catch (e) { + } catch (error) { console.log(`No regional forecast data for '${city.name ?? city.city}'`); - console.log(e); + console.log(error); return false; } })); @@ -159,11 +159,9 @@ class RegionalForecast extends WeatherDisplay { const dayName = forecastDate.toLocaleString({ weekday: 'long' }); titleTop.innerHTML = 'Forecast for'; // draw the title - if (data[0][this.screenIndex].daytime) { - titleBottom.innerHTML = dayName; - } else { - titleBottom.innerHTML = `${dayName} Night`; - } + titleBottom.innerHTML = data[0][this.screenIndex].daytime + ? dayName + : `${dayName} Night`; } // draw the map @@ -182,9 +180,11 @@ class RegionalForecast extends WeatherDisplay { const { temperature } = period; fill.temp = temperature; + const { x, y } = period; + const elem = this.fillTemplate('location', fill); - elem.style.left = `${period.x}px`; - elem.style.top = `${period.y}px`; + elem.style.left = `${x}px`; + elem.style.top = `${y}px`; return elem; }); diff --git a/server/scripts/modules/travelforecast.mjs b/server/scripts/modules/travelforecast.mjs index e575274..e0ea914 100644 --- a/server/scripts/modules/travelforecast.mjs +++ b/server/scripts/modules/travelforecast.mjs @@ -45,9 +45,9 @@ class TravelForecast extends WeatherDisplay { name: city.Name, icon: getWeatherRegionalIconFromIconLink(forecast.properties.periods[todayShift].icon), }; - } catch (e) { + } catch (error) { console.error(`GetTravelWeather for ${city.Name} failed`); - console.error(e.status, e.responseJSON); + console.error(error.status, error.responseJSON); return { name: city.Name, error: true }; } }); @@ -77,10 +77,9 @@ class TravelForecast extends WeatherDisplay { const lines = cities.map((city) => { if (city.error) return false; - const fillValues = {}; - - // city name - fillValues.city = city; + const fillValues = { + city, + }; // check for forecast data if (city.icon) { @@ -94,8 +93,9 @@ class TravelForecast extends WeatherDisplay { fillValues.low = lowString; fillValues.high = highString; + const { icon } = city; - fillValues.icon = { type: 'img', src: city.icon }; + fillValues.icon = { type: 'img', src: icon }; } else { fillValues.error = 'NO TRAVEL DATA AVAILABLE'; } diff --git a/server/scripts/modules/utils/fetch.mjs b/server/scripts/modules/utils/fetch.mjs index 58256a5..2c2698f 100644 --- a/server/scripts/modules/utils/fetch.mjs +++ b/server/scripts/modules/utils/fetch.mjs @@ -21,7 +21,7 @@ const fetchAsync = async (_url, responseType, _params = {}) => { if (params.cors === true) corsUrl = rewriteUrl(_url); const url = new URL(corsUrl, `${window.location.origin}/`); // match the security protocol when not on localhost - url.protocol = window.location.hostname !== 'localhost' ? window.location.protocol : url.protocol; + url.protocol = window.location.hostname === 'localhost' ? url.protocol : window.location.protocol; // add parameters if necessary if (params.data) { Object.keys(params.data).forEach((key) => { @@ -73,7 +73,7 @@ const doFetch = (url, params) => new Promise((resolve, reject) => { // out of retries return resolve(response); }) - .catch((e) => reject(e)); + .catch((error) => reject(error)); }); const delay = (time, func, ...args) => new Promise((resolve) => { @@ -87,8 +87,8 @@ const retryDelay = (retryNumber) => { case 1: return 1000; case 2: return 2000; case 3: return 5000; - case 4: return 10000; - default: return 30000; + case 4: return 10_000; + default: return 30_000; } }; diff --git a/server/scripts/modules/utils/string.mjs b/server/scripts/modules/utils/string.mjs index d0c6ddd..ca20650 100644 --- a/server/scripts/modules/utils/string.mjs +++ b/server/scripts/modules/utils/string.mjs @@ -2,11 +2,11 @@ const locationCleanup = (input) => { // regexes to run const regexes = [ // "Chicago / West Chicago", removes before slash - /^[A-Za-z ]+ \/ /, + /^[ A-Za-z]+ \/ /, // "Chicago/Waukegan" removes before slash - /^[A-Za-z ]+\//, + /^[ A-Za-z]+\//, // "Chicago, Chicago O'hare" removes before comma - /^[A-Za-z ]+, /, + /^[ A-Za-z]+, /, ]; // run all regexes diff --git a/server/scripts/modules/utils/units.mjs b/server/scripts/modules/utils/units.mjs index d587ba2..fea90b4 100644 --- a/server/scripts/modules/utils/units.mjs +++ b/server/scripts/modules/utils/units.mjs @@ -2,11 +2,11 @@ const round2 = (value, decimals) => Math.trunc(value * 10 ** decimals) / 10 ** decimals; -const kphToMph = (Kph) => Math.round(Kph / 1.60934); +const kphToMph = (Kph) => Math.round(Kph / 1.609_34); const celsiusToFahrenheit = (Celsius) => Math.round((Celsius * 9) / 5 + 32); -const kilometersToMiles = (Kilometers) => Math.round(Kilometers / 1.60934); +const kilometersToMiles = (Kilometers) => Math.round(Kilometers / 1.609_34); const metersToFeet = (Meters) => Math.round(Meters / 0.3048); -const pascalToInHg = (Pascal) => round2(Pascal * 0.0002953, 2); +const pascalToInHg = (Pascal) => round2(Pascal * 0.000_295_3, 2); export { kphToMph, diff --git a/server/scripts/modules/utils/weather.mjs b/server/scripts/modules/utils/weather.mjs index 702aadd..cc06c70 100644 --- a/server/scripts/modules/utils/weather.mjs +++ b/server/scripts/modules/utils/weather.mjs @@ -3,9 +3,9 @@ import { json } from './fetch.mjs'; const getPoint = async (lat, lon) => { try { return await json(`https://api.weather.gov/points/${lat},${lon}`); - } catch (e) { + } catch (error) { console.log(`Unable to get point ${lat}, ${lon}`); - console.error(e); + console.error(error); return false; } }; diff --git a/server/scripts/modules/weatherdisplay.mjs b/server/scripts/modules/weatherdisplay.mjs index e1bc535..543d675 100644 --- a/server/scripts/modules/weatherdisplay.mjs +++ b/server/scripts/modules/weatherdisplay.mjs @@ -53,11 +53,7 @@ class WeatherDisplay { // get the saved status of the checkbox let savedStatus = window.localStorage.getItem(`display-enabled: ${this.elemId}`); if (savedStatus === null) savedStatus = defaultEnabled; - if (savedStatus === 'true' || savedStatus === true) { - this.isEnabled = true; - } else { - this.isEnabled = false; - } + this.isEnabled = !!((savedStatus === 'true' || savedStatus === true)); // refresh (or initially store the state of the checkbox) window.localStorage.setItem(`display-enabled: ${this.elemId}`, this.isEnabled); @@ -253,17 +249,13 @@ class WeatherDisplay { if (nextScreenIndex === this.screenIndex) return; // test for -1 (no screen displayed yet) - if (nextScreenIndex === -1) { - this.screenIndex = 0; - } else { - this.screenIndex = nextScreenIndex; - } + this.screenIndex = nextScreenIndex === -1 ? 0 : nextScreenIndex; // call the appropriate screen index change method - if (!this.screenIndexChange) { - await this.drawCanvas(); - } else { + if (this.screenIndexChange) { this.screenIndexChange(this.screenIndex); + } else { + await this.drawCanvas(); } this.showCanvas(); } @@ -377,7 +369,7 @@ class WeatherDisplay { loadTemplates() { this.templates = {}; - this.elem = document.getElementById(`${this.elemId}-html`); + this.elem = document.querySelector(`#${this.elemId}-html`); if (!this.elem) return; const templates = this.elem.querySelectorAll('.template'); templates.forEach((template) => { diff --git a/ws4kp.code-workspace b/ws4kp.code-workspace index 96e5731..c537ffc 100644 --- a/ws4kp.code-workspace +++ b/ws4kp.code-workspace @@ -27,6 +27,7 @@ "Pngs", "PRECIP", "rtrim", + "sonarjs", "T", "T'storm", "uscomp",