diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..d2f689c7 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +/src/thirdParty/ +node_modules \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..39e9cac9 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,16 @@ +module.exports = { + env: { + browser: true, + es2021: true, + webextensions: true, + jquery: true + }, + extends: ['standard'], + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 12, + sourceType: 'module' + }, + plugins: ['@typescript-eslint'], + rules: {} +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..b512c09d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..2354a257 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2048 @@ +{ + "name": "tufast_tud", + "version": "5.6.4", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "dev": true + }, + "@babel/highlight": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "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" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + } + } + }, + "@eslint/eslintrc": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", + "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + } + } + }, + "@humanwhocodes/config-array": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + } + }, + "@humanwhocodes/object-schema": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz", + "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", + "dev": true + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@types/json-schema": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "dev": true + }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true + }, + "@typescript-eslint/eslint-plugin": { + "version": "4.31.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.31.2.tgz", + "integrity": "sha512-w63SCQ4bIwWN/+3FxzpnWrDjQRXVEGiTt9tJTRptRXeFvdZc/wLiz3FQUwNQ2CVoRGI6KUWMNUj/pk63noUfcA==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "4.31.2", + "@typescript-eslint/scope-manager": "4.31.2", + "debug": "^4.3.1", + "functional-red-black-tree": "^1.0.1", + "regexpp": "^3.1.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/experimental-utils": { + "version": "4.31.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.31.2.tgz", + "integrity": "sha512-3tm2T4nyA970yQ6R3JZV9l0yilE2FedYg8dcXrTar34zC9r6JB7WyBQbpIVongKPlhEMjhQ01qkwrzWy38Bk1Q==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.7", + "@typescript-eslint/scope-manager": "4.31.2", + "@typescript-eslint/types": "4.31.2", + "@typescript-eslint/typescript-estree": "4.31.2", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + } + }, + "@typescript-eslint/parser": { + "version": "4.31.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.31.2.tgz", + "integrity": "sha512-EcdO0E7M/sv23S/rLvenHkb58l3XhuSZzKf6DBvLgHqOYdL6YFMYVtreGFWirxaU2mS1GYDby3Lyxco7X5+Vjw==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "4.31.2", + "@typescript-eslint/types": "4.31.2", + "@typescript-eslint/typescript-estree": "4.31.2", + "debug": "^4.3.1" + } + }, + "@typescript-eslint/scope-manager": { + "version": "4.31.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.31.2.tgz", + "integrity": "sha512-2JGwudpFoR/3Czq6mPpE8zBPYdHWFGL6lUNIGolbKQeSNv4EAiHaR5GVDQaLA0FwgcdcMtRk+SBJbFGL7+La5w==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.31.2", + "@typescript-eslint/visitor-keys": "4.31.2" + } + }, + "@typescript-eslint/types": { + "version": "4.31.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.31.2.tgz", + "integrity": "sha512-kWiTTBCTKEdBGrZKwFvOlGNcAsKGJSBc8xLvSjSppFO88AqGxGNYtF36EuEYG6XZ9vT0xX8RNiHbQUKglbSi1w==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "4.31.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.31.2.tgz", + "integrity": "sha512-ieBq8U9at6PvaC7/Z6oe8D3czeW5d//Fo1xkF/s9394VR0bg/UaMYPdARiWyKX+lLEjY3w/FNZJxitMsiWv+wA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.31.2", + "@typescript-eslint/visitor-keys": "4.31.2", + "debug": "^4.3.1", + "globby": "^11.0.3", + "is-glob": "^4.0.1", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "4.31.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.31.2.tgz", + "integrity": "sha512-PrBId7EQq2Nibns7dd/ch6S6/M4/iwLM9McbgeEbCXfxdwRUNxJ4UNreJ6Gh3fI2GNKNrWnQxKL7oCPmngKBug==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.31.2", + "eslint-visitor-keys": "^2.0.0" + } + }, + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "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" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "array-includes": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.3.tgz", + "integrity": "sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.2", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.5" + } + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "array.prototype.flat": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz", + "integrity": "sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1" + } + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "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": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "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 + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "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==", + "dev": true + }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.18.6", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.6.tgz", + "integrity": "sha512-kAeIT4cku5eNLNuUKhlmtuk1/TRZvQoYccn6TO0cSVdf1kzB0T7+dYuVK9MWM7l+/53W2Q8M7N2c6MQvhXFcUQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.4", + "is-string": "^1.0.7", + "object-inspect": "^1.11.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "eslint": { + "version": "7.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", + "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", + "dev": true, + "requires": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.3", + "@humanwhocodes/config-array": "^0.5.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.1.2", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.9", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + } + } + }, + "eslint-config-standard": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-16.0.3.tgz", + "integrity": "sha512-x4fmJL5hGqNJKGHSjnLdgA6U6h1YW/G2dW9fA+cyVur4SK6lyue8+UgNKWlZtUDTXvgKDD/Oa3GQjmB5kjtVvg==", + "dev": true + }, + "eslint-import-resolver-node": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", + "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "dev": true, + "requires": { + "debug": "^3.2.7", + "resolve": "^1.20.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "eslint-module-utils": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.2.tgz", + "integrity": "sha512-QG8pcgThYOuqxupd06oYTZoNOGaUdTY1PqK+oS6ElF6vs4pBdk/aYxFVQQXzcrAqp9m7cl7lb2ubazX+g16k2Q==", + "dev": true, + "requires": { + "debug": "^3.2.7", + "pkg-dir": "^2.0.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "eslint-plugin-es": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", + "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", + "dev": true, + "requires": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" + }, + "dependencies": { + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "eslint-plugin-import": { + "version": "2.24.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.24.2.tgz", + "integrity": "sha512-hNVtyhiEtZmpsabL4neEj+6M5DCLgpYyG9nzJY8lZQeQXEn5UPW1DpUdsMHMXsq98dbNm7nt1w9ZMSVpfJdi8Q==", + "dev": true, + "requires": { + "array-includes": "^3.1.3", + "array.prototype.flat": "^1.2.4", + "debug": "^2.6.9", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-module-utils": "^2.6.2", + "find-up": "^2.0.0", + "has": "^1.0.3", + "is-core-module": "^2.6.0", + "minimatch": "^3.0.4", + "object.values": "^1.1.4", + "pkg-up": "^2.0.0", + "read-pkg-up": "^3.0.0", + "resolve": "^1.20.0", + "tsconfig-paths": "^3.11.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "eslint-plugin-node": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", + "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", + "dev": true, + "requires": { + "eslint-plugin-es": "^3.0.0", + "eslint-utils": "^2.0.0", + "ignore": "^5.1.1", + "minimatch": "^3.0.4", + "resolve": "^1.10.1", + "semver": "^6.1.0" + }, + "dependencies": { + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "eslint-plugin-promise": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-5.1.0.tgz", + "integrity": "sha512-NGmI6BH5L12pl7ScQHbg7tvtk4wPxxj8yPHH47NvSmMtFneC077PSeY3huFj06ZWZvtbfxSPt3RuOQD5XcR4ng==", + "dev": true + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^2.0.0" + } + }, + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + }, + "espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "requires": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-glob": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", + "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.2.tgz", + "integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globals": { + "version": "13.11.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.11.0.tgz", + "integrity": "sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "globby": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", + "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + } + }, + "graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflight": { + "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" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "requires": { + "has-bigints": "^1.0.1" + } + }, + "is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "dev": true + }, + "is-core-module": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.6.0.tgz", + "integrity": "sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-glob": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.2.tgz", + "integrity": "sha512-ZZTOjRcDjuAAAv2cTBQP/lL59ZTArx77+7UzHdWW/XB1mrfp7DEaVpKmZ0XIzx+M7AxfhKcqV+nMetUQmFifwg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-negative-zero": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", + "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-number-object": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", + "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "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": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", + "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" + } + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, + "minimatch": { + "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" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "object-inspect": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", + "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "object.values": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.4.tgz", + "integrity": "sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.2" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + }, + "pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", + "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "dependencies": { + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + } + } + }, + "read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + } + }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.10.tgz", + "integrity": "sha512-oie3/+gKf7QtpitB0LYLETe+k8SifzsX4KixvpOsbI6S0kRiRQ5MKOio8eMSAKQ17N06+wdEOXRiId+zOxo0hA==", + "dev": true + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "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" + } + }, + "table": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", + "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", + "dev": true, + "requires": { + "ajv": "^8.0.1", + "lodash.clonedeep": "^4.5.0", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.6.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.3.tgz", + "integrity": "sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + } + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "tsconfig-paths": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.11.0.tgz", + "integrity": "sha512-7ecdYDnIdmv639mmDwslG6KQg1Z9STTz1j7Gcz0xa+nshh/gKDAHcPxRbWOsA3SPp0tXP2leTcY9Kw+NAkfZzA==", + "dev": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.0", + "strip-bom": "^3.0.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + }, + "typescript": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz", + "integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==" + }, + "unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + } + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "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 + } + } +} diff --git a/package.json b/package.json index 4882ed4f..257b6e08 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,8 @@ "description": "Browser Extension for higher productivity with TU Dresden IT-Services 🚀", "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "lint": "npx eslint --fix .", + "test": "npm run lint" }, "repository": { "type": "git", @@ -30,5 +31,17 @@ "bugs": { "url": "https://github.com/TUfast-TUD/TUfast_TUD/issues" }, - "homepage": "https://github.com/TUfast-TUD/TUfast_TUD#readme" + "homepage": "https://github.com/TUfast-TUD/TUfast_TUD#readme", + "devDependencies": { + "@typescript-eslint/eslint-plugin": "^4.31.2", + "@typescript-eslint/parser": "^4.31.2", + "eslint": "^7.32.0", + "eslint-config-standard": "^16.0.3", + "eslint-plugin-import": "^2.24.2", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^5.1.0" + }, + "dependencies": { + "typescript": "^4.4.3" + } } diff --git a/src/background.js b/src/background.js index 75b13f89..3e53310b 100644 --- a/src/background.js +++ b/src/background.js @@ -1,33 +1,38 @@ +/* eslint-disable no-async-promise-executor */ 'use strict' -//start fetchOWA if activated and user data exists +// eslint-disable-next-line no-unused-vars +const isChrome = navigator.userAgent.includes('Chrome/') // attention: no failsave browser detection | also for new edge! +const isFirefox = navigator.userAgent.includes('Firefox/') // attention: no failsave browser detection + +// start fetchOWA if activated and user data exists chrome.storage.local.get(['enabledOWAFetch', 'NumberOfUnreadMails'], (resp) => { - userDataExists().then((userDataExists) => { - if (userDataExists && resp.enabledOWAFetch) { - enableOWAFetch() //start owa fetch - setBadgeUnreadMails(resp.NumberOfUnreadMails) //read number of unread mails from storage and display badge - console.log("Activated OWA fetch.") - } else { console.log("No OWAfetch registered") } - }) + userDataExists().then((userDataExists) => { + if (userDataExists && resp.enabledOWAFetch) { + enableOWAFetch() // start owa fetch + setBadgeUnreadMails(resp.NumberOfUnreadMails) // read number of unread mails from storage and display badge + console.log('Activated OWA fetch.') + } else { console.log('No OWAfetch registered') } + }) }) -//enable rating -chrome.storage.local.set({ratingEnabledFlag: true}, function () {}) +// disable star rating +chrome.storage.local.set({ ratingEnabledFlag: false }, function () {}) -//DOESNT WORK IN RELEASE VERSION +// DOESNT WORK IN RELEASE VERSION chrome.storage.local.get(['openSettingsOnReload'], (resp) => { - if (resp.openSettingsOnReload) openSettingsPage() - chrome.storage.local.set({ openSettingsOnReload: false }, function () { }) + if (resp.openSettingsOnReload) openSettingsPage() + chrome.storage.local.set({ openSettingsOnReload: false }, function () { }) }) -//set browserIcon +// set browserIcon chrome.storage.local.get(['selectedRocketIcon'], (resp) => { - try { - let r = JSON.parse(resp.selectedRocketIcon) - chrome.browserAction.setIcon({ - path: r.link - }) - } catch (e) { console.log("Cannot set rocket icon: " + e) } + try { + const r = JSON.parse(resp.selectedRocketIcon) + chrome.browserAction.setIcon({ + path: r.link + }) + } catch (e) { console.log('Cannot set rocket icon: ' + e) } }) console.log('Loaded TUfast') @@ -43,404 +48,392 @@ chrome.storage.local.set({ loggedOutCloudstore: false }, function () { }) chrome.storage.local.set({ loggedOutTex: false }, function () { }) chrome.storage.local.set({ loggedOutTumed: false }, function () { }) chrome.storage.local.set({ loggedOutGitlab: false }, function () { }) -chrome.storage.local.get(["pdfInNewTab"], function (result) { - if (result.pdfInNewTab) { - enableHeaderListener(true) - } +chrome.storage.local.get(['pdfInNewTab'], function (result) { + if (result.pdfInNewTab) { + enableHeaderListener(true) + } }) - chrome.runtime.onInstalled.addListener(async (details) => { - const reason = details.reason - switch (reason) { - case 'install': - console.log('TUfast installed.') - openSettingsPage("first_visit") //open settings page - chrome.storage.local.set({ installed: true }, function () { }) - chrome.storage.local.set({ showed_50_clicks: false }, function () { }) - chrome.storage.local.set({ showed_100_clicks: false }, function () { }) - chrome.storage.local.set({ isEnabled: false }, function () { }) - chrome.storage.local.set({ fwdEnabled: true }, function () { }) - chrome.storage.local.set({ mostLiklySubmittedReview: false }, function () { }) - chrome.storage.local.set({ removedReviewBanner: false }, function () { }) - chrome.storage.local.set({ neverShowedReviewBanner: true }, function () { }) - chrome.storage.local.set({ encryption_level: 2 }, function () { }) - chrome.storage.local.set({ meine_kurse: false }, function () { }) - chrome.storage.local.set({ favoriten: false }, function () { }) - //chrome.storage.local.set({openSettingsPageParam: false}, function() {}) - chrome.storage.local.set({ seenInOpalAfterDashbaordUpdate: 0 }, function () { }) - chrome.storage.local.set({ dashboardDisplay: "favoriten" }, function () { }) - chrome.storage.local.set({ additionalNotificationOnNewMail: false }) - chrome.storage.local.set({ NumberOfUnreadMails: "undefined" }) - chrome.storage.local.set({ removedOpalBanner: false }, function () { }) - chrome.storage.local.set({ nameIsTUfast: true }, function () { }) - chrome.storage.local.set({ enabledOWAFetch: false }, function () { }) - chrome.storage.local.set({ colorfulRocket: "black" }, function () { }) - chrome.storage.local.set({ PRObadge: false }, function () { }) - chrome.storage.local.set({ flakeState: false }, function () { }) - chrome.storage.local.set({ availableRockets: ["RI_default"] }, function () { }) - chrome.storage.local.set({ foundEasteregg: false }, function () { }) - chrome.storage.local.set({ hisqisPimpedTable: true }, function () { }) - chrome.storage.local.set({ openSettingsOnReload: false }, function () { }) - chrome.storage.local.set({ selectedRocketIcon: '{"id": "RI_default", "link": "assets/icons/RocketIcons/default_128px.png"}' }, function () { }) - chrome.storage.local.set({ pdfInInline: false }, function () { }) - chrome.storage.local.set({ pdfInNewTab: false }, function () { }) - chrome.storage.local.set({ studiengang: "general" }, function () { }) - chrome.storage.local.set({ updateCustomizeStudiengang: false }, function () { }) - chrome.storage.local.set({ TUfastCampInvite1: false }, function () { }) - chrome.storage.local.set({ theme: 'system' }) - break - case 'update': - //check if encryption is already on level 2. This should be the case for every install now. But I'll leave this here anyway - chrome.storage.local.get(['encryption_level'], (resp) => { - if (!(resp.encryption_level === 2)) { - console.log('Upgrading encryption standard to level 2...') - chrome.storage.local.get(['asdf', 'fdsa'], function (result) { - setUserData({ asdf: atob(result.asdf), fdsa: atob(result.fdsa) }) - chrome.storage.local.set({ encryption_level: 2 }, function () { }) - }) - } - }) - //check if the type of courses is selected which should be display in the dasbhaord. If not, set to default - chrome.storage.local.get(['dashboardDisplay'], (resp) => { - if (resp.dashboardDisplay === null || resp.dashboardDisplay === undefined || resp.dashboardDisplay === "") { - chrome.storage.local.set({ dashboardDisplay: "favoriten" }, function () { }) - } - }) - //check if mostLiklySubmittedReview - chrome.storage.local.get(['mostLiklySubmittedReview'], (resp) => { - if (resp.mostLiklySubmittedReview === null || resp.mostLiklySubmittedReview === undefined || resp.mostLiklySubmittedReview === "") { - chrome.storage.local.set({ mostLiklySubmittedReview: false }, function () { }) - } - }) - //check if removedReviewBanner - chrome.storage.local.get(['removedReviewBanner'], (resp) => { - if (resp.removedReviewBanner === null || resp.removedReviewBanner === undefined || resp.removedReviewBanner === "") { - chrome.storage.local.set({ removedReviewBanner: false }, function () { }) - } - }) - //check if neverShowedReviewBanner - chrome.storage.local.get(['neverShowedReviewBanner'], (resp) => { - if (resp.neverShowedReviewBanner === null || resp.neverShowedReviewBanner === undefined || resp.neverShowedReviewBanner === "") { - chrome.storage.local.set({ neverShowedReviewBanner: true }, function () { }) - } - }) - //check if seenInOpalAfterDashbaordUpdate exists - chrome.storage.local.get(['seenInOpalAfterDashbaordUpdate'], (resp) => { - if (resp.seenInOpalAfterDashbaordUpdate === null || resp.seenInOpalAfterDashbaordUpdate === undefined || resp.seenInOpalAfterDashbaordUpdate === "") { - chrome.storage.local.set({ seenInOpalAfterDashbaordUpdate: 0 }, function () { }) - } - }) - //check if enabledOWAFetch exists - chrome.storage.local.get(['enabledOWAFetch'], (resp) => { - if (resp.enabledOWAFetch === null || resp.enabledOWAFetch === undefined || resp.enabledOWAFetch === "") { - chrome.storage.local.set({ enabledOWAFetch: false }, function () { }) - chrome.storage.local.set({ NumberOfUnreadMails: "undefined" }) - chrome.storage.local.set({ additionalNotificationOnNewMail: false }) - } - }) - //check, whether flake state exists. If not, initialize with false. - chrome.storage.local.get(['flakeState'], function (result) { - if (result.flakeState === undefined || result.flakeState === null) { //set to true, so that state will be false! - chrome.storage.local.set({ flakeState: false }, function () { }) - } - }) - //check if ShowedFirefoxBanner - chrome.storage.local.get(['showedFirefoxBanner'], function (result) { - if (result.showedFirefoxBanner === undefined || result.showedFirefoxBanner === null) { - chrome.storage.local.set({ showedFirefoxBanner: false }, function () { }) - } - }) - //check if showedUnreadMailCounterBanner - chrome.storage.local.get(['showedUnreadMailCounterBanner'], function (result) { - if (result.showedUnreadMailCounterBanner === undefined || result.showedUnreadMailCounterBanner === null) { - chrome.storage.local.set({ showedUnreadMailCounterBanner: false }, function () { }) - } - }) - //check if openSettingsOnReload - chrome.storage.local.get(['openSettingsOnReload'], function (result) { - if (result.openSettingsOnReload === undefined || result.openSettingsOnReload === null) { - chrome.storage.local.set({ openSettingsOnReload: false }, function () { }) - } - }) - //check if availableRockets - chrome.storage.local.get(['availableRockets'], function (result) { - if (result.availableRockets === undefined || result.availableRockets === null) { - chrome.storage.local.set({ availableRockets: ["RI_default"] }, function () { }) - } - }) - //check if selectedRocketIcon - chrome.storage.local.get(['selectedRocketIcon'], function (result) { - if (result.selectedRocketIcon === undefined || result.selectedRocketIcon === null) { - chrome.storage.local.set({ selectedRocketIcon: '{"id": "RI_default", "link": "assets/icons/RocketIcons/default_128px.png"}' }, function () { }) - } - }) - //check if hisqisPimpedTable - chrome.storage.local.get(['hisqisPimpedTable'], function (result) { - if (result.hisqisPimpedTable === undefined || result.hisqisPimpedTable === null) { - chrome.storage.local.set({ hisqisPimpedTable: true }, function () { }) - } - }) - //if easteregg was discovered in an earlier version: enable and select specific rocket! - chrome.storage.local.get(['Rocket', "foundEasteregg", "saved_click_counter", "availableRockets"], function (result) { - let avRockets = result.availableRockets - if (result.saved_click_counter > 250 && !avRockets.includes("RI4")) avRockets.push("RI4") - if (result.saved_click_counter > 2500 && !avRockets.includes("RI5")) avRockets.push("RI5") - if (result.Rocket === "colorful" && result.foundEasteregg === undefined) { - chrome.storage.local.set({ foundEasteregg: true }, function () { }) - chrome.storage.local.set({ selectedRocketIcon: '{"id": "RI3", "link": "assets/icons/RocketIcons/3_120px.png"}' }, function () { }) - avRockets.push("RI3") - chrome.browserAction.setIcon({ - path: "assets/icons/RocketIcons/3_120px.png" - }) - } - chrome.storage.local.set({ "availableRockets": avRockets }) - - }) - //seen customized studiengang - chrome.storage.local.get(['updateCustomizeStudiengang'], function (result) { - if (result.updateCustomizeStudiengang === undefined || result.updateCustomizeStudiengang === null) { - chrome.storage.local.set({ updateCustomizeStudiengang: false }, function () { }) - } - }) - //selected studiengang - chrome.storage.local.get(['studiengang'], function (result) { - if (result.studiengang === undefined || result.studiengang === null) { - chrome.storage.local.set({ studiengang: "general" }, function () { }) - } - }) - //selected theme - chrome.storage.local.get(['theme'], (res) => { - if (res.theme === undefined || res.theme === null) { - chrome.storage.local.set({ theme: 'system' }) - } - }) - //if not yet invite shown: show, and set shown to true - //chrome.storage.local.get(['TUfastCampInvite1'], (res) => { - // let today = new Date(); - // let max_invite_date = new Date(2021, 8, 30) //27.09.2021; month is zero based - // if (!res.TUfastCampInvite1 === true && today < max_invite_date) { - // chrome.storage.local.set({ TUfastCampInvite1: true }, function () { }) - // chrome.tabs.create(({ url: "TUfastCamp.html" })) - // } - // }) - - - break - default: - console.log('Other install events within the browser for TUfast.') - break - } + const reason = details.reason + switch (reason) { + case 'install': + console.log('TUfast installed.') + openSettingsPage('first_visit') // open settings page + chrome.storage.local.set({ installed: true }, function () { }) + chrome.storage.local.set({ showed_50_clicks: false }, function () { }) + chrome.storage.local.set({ showed_100_clicks: false }, function () { }) + chrome.storage.local.set({ isEnabled: false }, function () { }) + chrome.storage.local.set({ fwdEnabled: true }, function () { }) + chrome.storage.local.set({ mostLiklySubmittedReview: false }, function () { }) + chrome.storage.local.set({ removedReviewBanner: false }, function () { }) + chrome.storage.local.set({ neverShowedReviewBanner: true }, function () { }) + chrome.storage.local.set({ encryption_level: 2 }, function () { }) + chrome.storage.local.set({ meine_kurse: false }, function () { }) + chrome.storage.local.set({ favoriten: false }, function () { }) + // chrome.storage.local.set({openSettingsPageParam: false}, function() {}) + chrome.storage.local.set({ seenInOpalAfterDashbaordUpdate: 0 }, function () { }) + chrome.storage.local.set({ dashboardDisplay: 'favoriten' }, function () { }) + chrome.storage.local.set({ additionalNotificationOnNewMail: false }) + chrome.storage.local.set({ NumberOfUnreadMails: 'undefined' }) + chrome.storage.local.set({ removedOpalBanner: false }, function () { }) + chrome.storage.local.set({ nameIsTUfast: true }, function () { }) + chrome.storage.local.set({ enabledOWAFetch: false }, function () { }) + chrome.storage.local.set({ colorfulRocket: 'black' }, function () { }) + chrome.storage.local.set({ PRObadge: false }, function () { }) + chrome.storage.local.set({ flakeState: false }, function () { }) + chrome.storage.local.set({ availableRockets: ['RI_default'] }, function () { }) + chrome.storage.local.set({ foundEasteregg: false }, function () { }) + chrome.storage.local.set({ hisqisPimpedTable: true }, function () { }) + chrome.storage.local.set({ openSettingsOnReload: false }, function () { }) + chrome.storage.local.set({ selectedRocketIcon: '{"id": "RI_default", "link": "assets/icons/RocketIcons/default_128px.png"}' }, function () { }) + chrome.storage.local.set({ pdfInInline: false }, function () { }) + chrome.storage.local.set({ pdfInNewTab: false }, function () { }) + chrome.storage.local.set({ studiengang: 'general' }, function () { }) + chrome.storage.local.set({ updateCustomizeStudiengang: false }, function () { }) + chrome.storage.local.set({ TUfastCampInvite1: false }, function () { }) + chrome.storage.local.set({ theme: 'system' }) + break + case 'update': + // check if encryption is already on level 2. This should be the case for every install now. But I'll leave this here anyway + chrome.storage.local.get(['encryption_level'], (resp) => { + if (!(resp.encryption_level === 2)) { + console.log('Upgrading encryption standard to level 2...') + chrome.storage.local.get(['asdf', 'fdsa'], function (result) { + setUserData({ asdf: atob(result.asdf), fdsa: atob(result.fdsa) }) + chrome.storage.local.set({ encryption_level: 2 }, function () { }) + }) + } + }) + // check if the type of courses is selected which should be display in the dasbhaord. If not, set to default + chrome.storage.local.get(['dashboardDisplay'], (resp) => { + if (resp.dashboardDisplay === null || resp.dashboardDisplay === undefined || resp.dashboardDisplay === '') { + chrome.storage.local.set({ dashboardDisplay: 'favoriten' }, function () { }) + } + }) + // check if mostLiklySubmittedReview + chrome.storage.local.get(['mostLiklySubmittedReview'], (resp) => { + if (resp.mostLiklySubmittedReview === null || resp.mostLiklySubmittedReview === undefined || resp.mostLiklySubmittedReview === '') { + chrome.storage.local.set({ mostLiklySubmittedReview: false }, function () { }) + } + }) + // check if removedReviewBanner + chrome.storage.local.get(['removedReviewBanner'], (resp) => { + if (resp.removedReviewBanner === null || resp.removedReviewBanner === undefined || resp.removedReviewBanner === '') { + chrome.storage.local.set({ removedReviewBanner: false }, function () { }) + } + }) + // check if neverShowedReviewBanner + chrome.storage.local.get(['neverShowedReviewBanner'], (resp) => { + if (resp.neverShowedReviewBanner === null || resp.neverShowedReviewBanner === undefined || resp.neverShowedReviewBanner === '') { + chrome.storage.local.set({ neverShowedReviewBanner: true }, function () { }) + } + }) + // check if seenInOpalAfterDashbaordUpdate exists + chrome.storage.local.get(['seenInOpalAfterDashbaordUpdate'], (resp) => { + if (resp.seenInOpalAfterDashbaordUpdate === null || resp.seenInOpalAfterDashbaordUpdate === undefined || resp.seenInOpalAfterDashbaordUpdate === '') { + chrome.storage.local.set({ seenInOpalAfterDashbaordUpdate: 0 }, function () { }) + } + }) + // check if enabledOWAFetch exists + chrome.storage.local.get(['enabledOWAFetch'], (resp) => { + if (resp.enabledOWAFetch === null || resp.enabledOWAFetch === undefined || resp.enabledOWAFetch === '') { + chrome.storage.local.set({ enabledOWAFetch: false }, function () { }) + chrome.storage.local.set({ NumberOfUnreadMails: 'undefined' }) + chrome.storage.local.set({ additionalNotificationOnNewMail: false }) + } + }) + // check, whether flake state exists. If not, initialize with false. + chrome.storage.local.get(['flakeState'], function (result) { + if (result.flakeState === undefined || result.flakeState === null) { // set to true, so that state will be false! + chrome.storage.local.set({ flakeState: false }, function () { }) + } + }) + // check if ShowedFirefoxBanner + chrome.storage.local.get(['showedFirefoxBanner'], function (result) { + if (result.showedFirefoxBanner === undefined || result.showedFirefoxBanner === null) { + chrome.storage.local.set({ showedFirefoxBanner: false }, function () { }) + } + }) + // check if showedUnreadMailCounterBanner + chrome.storage.local.get(['showedUnreadMailCounterBanner'], function (result) { + if (result.showedUnreadMailCounterBanner === undefined || result.showedUnreadMailCounterBanner === null) { + chrome.storage.local.set({ showedUnreadMailCounterBanner: false }, function () { }) + } + }) + // check if openSettingsOnReload + chrome.storage.local.get(['openSettingsOnReload'], function (result) { + if (result.openSettingsOnReload === undefined || result.openSettingsOnReload === null) { + chrome.storage.local.set({ openSettingsOnReload: false }, function () { }) + } + }) + // check if availableRockets + chrome.storage.local.get(['availableRockets'], function (result) { + if (result.availableRockets === undefined || result.availableRockets === null) { + chrome.storage.local.set({ availableRockets: ['RI_default'] }, function () { }) + } + }) + // check if selectedRocketIcon + chrome.storage.local.get(['selectedRocketIcon'], function (result) { + if (result.selectedRocketIcon === undefined || result.selectedRocketIcon === null) { + chrome.storage.local.set({ selectedRocketIcon: '{"id": "RI_default", "link": "assets/icons/RocketIcons/default_128px.png"}' }, function () { }) + } + }) + // check if hisqisPimpedTable + chrome.storage.local.get(['hisqisPimpedTable'], function (result) { + if (result.hisqisPimpedTable === undefined || result.hisqisPimpedTable === null) { + chrome.storage.local.set({ hisqisPimpedTable: true }, function () { }) + } + }) + // if easteregg was discovered in an earlier version: enable and select specific rocket! + chrome.storage.local.get(['Rocket', 'foundEasteregg', 'saved_click_counter', 'availableRockets'], function (result) { + const avRockets = result.availableRockets + if (result.saved_click_counter > 250 && !avRockets.includes('RI4')) avRockets.push('RI4') + if (result.saved_click_counter > 2500 && !avRockets.includes('RI5')) avRockets.push('RI5') + if (result.Rocket === 'colorful' && result.foundEasteregg === undefined) { + chrome.storage.local.set({ foundEasteregg: true }, function () { }) + chrome.storage.local.set({ selectedRocketIcon: '{"id": "RI3", "link": "assets/icons/RocketIcons/3_120px.png"}' }, function () { }) + avRockets.push('RI3') + chrome.browserAction.setIcon({ + path: 'assets/icons/RocketIcons/3_120px.png' + }) + } + chrome.storage.local.set({ availableRockets: avRockets }) + }) + // seen customized studiengang + chrome.storage.local.get(['updateCustomizeStudiengang'], function (result) { + if (result.updateCustomizeStudiengang === undefined || result.updateCustomizeStudiengang === null) { + chrome.storage.local.set({ updateCustomizeStudiengang: false }, function () { }) + } + }) + // selected studiengang + chrome.storage.local.get(['studiengang'], function (result) { + if (result.studiengang === undefined || result.studiengang === null) { + chrome.storage.local.set({ studiengang: 'general' }, function () { }) + } + }) + // selected theme + chrome.storage.local.get(['theme'], (res) => { + if (res.theme === undefined || res.theme === null) { + chrome.storage.local.set({ theme: 'system' }) + } + }) + // if not yet invite shown: show, and set shown to true + // chrome.storage.local.get(['TUfastCampInvite1'], (res) => { + // const today = new Date() + // const max_invite_date = new Date(2021, 8, 30) // 27.09.2021; month is zero based + // if (!res.TUfastCampInvite1 === true && today < max_invite_date) { + // chrome.storage.local.set({ TUfastCampInvite1: true }, function () { }) + // chrome.tabs.create(({ url: 'TUfastCamp.html' })) + // } + // }) + + break + default: + console.log('Other install events within the browser for TUfast.') + break + } }) -//checks, if user currently uses owa in browser -function owaIsOpened() { - return new Promise(async (resolve, reject) => { - let uri = "msx.tu-dresden.de" - let tabs = await getAllChromeTabs() - tabs.forEach(function (tab) { - if ((tab.url).includes(uri)) { - console.log("currentyl opened owa") - resolve(true) - } - }) - resolve(false) - }) +// checks, if user currently uses owa in browser +function owaIsOpened () { + return new Promise(async (resolve, reject) => { + const uri = 'msx.tu-dresden.de' + const tabs = await getAllChromeTabs() + tabs.forEach(function (tab) { + if ((tab.url).includes(uri)) { + console.log('currentyl opened owa') + resolve(true) + } + }) + resolve(false) + }) } -function getAllChromeTabs() { - return new Promise(async (res, rej) => { - await chrome.tabs.query({}, function (tabs) { - res(tabs) - }) - }) +function getAllChromeTabs () { + return new Promise(async (resolve, reject) => { + await chrome.tabs.query({}, function (tabs) { + resolve(tabs) + }) + }) } -//check if user stored login data -async function loginDataExists() { - getUserData().then((userData) => { - if (userData.asdf === undefined || userData.fdsa === undefined) { - return false - } else { - return true - } - }) +// check if user stored login data +// eslint-disable-next-line no-unused-vars +async function loginDataExists () { + getUserData().then((userData) => { + if (userData.asdf === undefined || userData.fdsa === undefined) { + return false + } else { + return true + } + }) } - -//start OWA fetch funtion based on interval -function enableOWAFetch() { - //first, clear all alarms - console.log("starting to fetch from owa...") - owaFetch() - chrome.alarms.clearAll(() => { - chrome.alarms.create("fetchOWAAlarm", { delayInMinutes: 1, periodInMinutes: 5 }) - chrome.alarms.onAlarm.addListener(async (alarm) => { - owaFetch() - }) - }) +// start OWA fetch funtion based on interval +function enableOWAFetch () { + // first, clear all alarms + console.log('starting to fetch from owa...') + owaFetch() + chrome.alarms.clearAll(() => { + chrome.alarms.create('fetchOWAAlarm', { delayInMinutes: 1, periodInMinutes: 5 }) + chrome.alarms.onAlarm.addListener(async (alarm) => { + owaFetch() + }) + }) } -async function owaFetch() { - //dont logout if user is currently using owa in browser - let logout = true - if (await owaIsOpened()) { - logout = false - } - - console.log("executing fetch ...") - - //get user data - let asdf = "" - let fdsa = "" - - await getUserData().then((userData) => { - asdf = userData.asdf - fdsa = userData.fdsa - }) - - //call fetch - let mailInfoJson = await fetchOWA(asdf, fdsa, logout) - - //check # of unread mails - let numberUnreadMails = await countUnreadMsg(mailInfoJson) - console.log("Unread mails in OWA: " + numberUnreadMails) - - //alert on new Mail - await chrome.storage.local.get(['NumberOfUnreadMails', "additionalNotificationOnNewMail"], (result) => { - if (!(result.NumberOfUnreadMails === undefined || result.NumberOfUnreadMails === "undefined") && result.additionalNotificationOnNewMail) { - if (result.NumberOfUnreadMails < numberUnreadMails) { - if (confirm("Neue Mail in deinem TU Dresden Postfach!\nDruecke 'Ok' um OWA zu oeffnen.")) { - let URL = "https://msx.tu-dresden.de/owa/auth/logon.aspx?url=https%3a%2f%2fmsx.tu-dresden.de%2fowa&reason=0" - chrome.tabs.create({ url: URL }) - } - } - } - }) - - //set badge and local storage - await chrome.storage.local.set({ NumberOfUnreadMails: numberUnreadMails }) - setBadgeUnreadMails(numberUnreadMails) +async function owaFetch () { + // dont logout if user is currently using owa in browser + let logout = true + if (await owaIsOpened()) { + logout = false + } + + console.log('executing fetch ...') + + // get user data + let asdf = '' + let fdsa = '' + + await getUserData().then((userData) => { + asdf = userData.asdf + fdsa = userData.fdsa + }) + + // call fetch + const mailInfoJson = await fetchOWA(asdf, fdsa, logout) + + // check # of unread mails + const numberUnreadMails = await countUnreadMsg(mailInfoJson) + console.log('Unread mails in OWA: ' + numberUnreadMails) + + // alert on new Mail + await chrome.storage.local.get(['NumberOfUnreadMails', 'additionalNotificationOnNewMail'], (result) => { + if (!(result.NumberOfUnreadMails === undefined || result.NumberOfUnreadMails === 'undefined') && result.additionalNotificationOnNewMail) { + if (result.NumberOfUnreadMails < numberUnreadMails) { + if (confirm("Neue Mail in deinem TU Dresden Postfach!\nDruecke 'Ok' um OWA zu oeffnen.")) { + const URL = 'https://msx.tu-dresden.de/owa/auth/logon.aspx?url=https%3a%2f%2fmsx.tu-dresden.de%2fowa&reason=0' + chrome.tabs.create({ url: URL }) + } + } + } + }) + + // set badge and local storage + await chrome.storage.local.set({ NumberOfUnreadMails: numberUnreadMails }) + setBadgeUnreadMails(numberUnreadMails) } -function disableOwaFetch() { - console.log("stopped owa connection") - setBadgeUnreadMails(0) - chrome.storage.local.set({ NumberOfUnreadMails: "undefined" }) - chrome.alarms.clearAll(() => { }) +function disableOwaFetch () { + console.log('stopped owa connection') + setBadgeUnreadMails(0) + chrome.storage.local.set({ NumberOfUnreadMails: 'undefined' }) + chrome.alarms.clearAll(() => { }) } -function readMailOWA(NrUnreadMails) { - //set badge and local storage - chrome.storage.local.set({ NumberOfUnreadMails: NrUnreadMails }) - setBadgeUnreadMails(NrUnreadMails) +function readMailOWA (NrUnreadMails) { + // set badge and local storage + chrome.storage.local.set({ NumberOfUnreadMails: NrUnreadMails }) + setBadgeUnreadMails(NrUnreadMails) } -function setBadgeUnreadMails(numberUnreadMails) { - //set badge - if (numberUnreadMails == 0) { - show_badge("", '#4cb749') - } - else if (numberUnreadMails > 99) { - show_badge("99+", '#4cb749') - } - else { - show_badge(numberUnreadMails.toString(), '#4cb749') - } +function setBadgeUnreadMails (numberUnreadMails) { + // set badge + if (numberUnreadMails === 0) { + showBadge('', '#4cb749') + } else if (numberUnreadMails > 99) { + showBadge('99+', '#4cb749') + } else { + showBadge(numberUnreadMails.toString(), '#4cb749') + } } -//show badge when extension is triggered +// show badge when extension is triggered chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) { - switch (request.cmd) { - case 'show_ok_badge': - //show_badge('Login', '#4cb749', request.timeout) - break - case 'no_login_data': - //alert("Bitte gib deinen Nutzernamen und Passwort in der TUfast Erweiterung an! Klicke dafür auf das Erweiterungssymbol oben rechts.") - //show_badge("Error", '#ff0000', 10000) - break - case 'perform_login': - break - case 'clear_badge': - //show_badge("", "#ffffff", 0) - break - case 'save_clicks': - save_clicks(request.click_count) - break - case 'get_user_data': - getUserData().then((userData) => sendResponse(userData)) - break - case 'set_user_data': - setUserData(request.userData) - break - case 'read_mail_owa': - readMailOWA(request.NrUnreadMails) - break - case 'logged_out': - loggedOut(request.portal) - break - case "enable_owa_fetch": - enableOWAFetch() - break - case "disable_owa_fetch": - disableOwaFetch() - break - case 'reload_extension': - chrome.runtime.reload() - break - case 'save_courses': - saveCourses(request.course_list) - break - case 'open_settings_page': - openSettingsPage(request.params) - break - case 'open_share_page': - openSharePage() - break - case 'register_addition_content_scripts': - regAddContentScripts() - break - case 'open_shortcut_settings': - let isChrome = navigator.userAgent.includes("Chrome/") //attention: no failsave browser detection | also for new edge! - let isFirefox = navigator.userAgent.includes("Firefox/") //attention: no failsave browser detection - if (isFirefox) { chrome.tabs.create({ url: "https://support.mozilla.org/de/kb/tastenkombinationen-fur-erweiterungen-verwalten" }) } - else { chrome.tabs.create({ url: "chrome://extensions/shortcuts" }) } //for chrome and everything else - break - case 'toggle_pdf_inline_setting': - enableHeaderListener(request.enabled) - break - case "update_rocket_logo_easteregg": - chrome.browserAction.setIcon({ - path: "assets/icons/RocketIcons/3_120px.png" - }) - break - default: - console.log('Cmd not found!') - break - } - return true //required for async sendResponse - + switch (request.cmd) { + case 'show_ok_badge': + // show_badge('Login', '#4cb749', request.timeout) + break + case 'no_login_data': + // alert("Bitte gib deinen Nutzernamen und Passwort in der TUfast Erweiterung an! Klicke dafür auf das Erweiterungssymbol oben rechts.") + // show_badge("Error", '#ff0000', 10000) + break + case 'perform_login': + break + case 'clear_badge': + // show_badge("", "#ffffff", 0) + break + case 'save_clicks': + saveClicks(request.click_count) + break + case 'get_user_data': + getUserData().then((userData) => sendResponse(userData)) + break + case 'set_user_data': + setUserData(request.userData) + break + case 'read_mail_owa': + readMailOWA(request.NrUnreadMails) + break + case 'logged_out': + loggedOut(request.portal) + break + case 'enable_owa_fetch': + enableOWAFetch() + break + case 'disable_owa_fetch': + disableOwaFetch() + break + case 'reload_extension': + chrome.runtime.reload() + break + case 'save_courses': + saveCourses(request.course_list) + break + case 'open_settings_page': + openSettingsPage(request.params) + break + case 'open_share_page': + openSharePage() + break + case 'open_shortcut_settings': + if (isFirefox) { chrome.tabs.create({ url: 'https://support.mozilla.org/de/kb/tastenkombinationen-fur-erweiterungen-verwalten' }) } else { chrome.tabs.create({ url: 'chrome://extensions/shortcuts' }) } // for chrome and everything else + break + case 'toggle_pdf_inline_setting': + enableHeaderListener(request.enabled) + break + case 'update_rocket_logo_easteregg': + chrome.browserAction.setIcon({ + path: 'assets/icons/RocketIcons/3_120px.png' + }) + break + default: + console.log('Cmd not found!') + break + } + return true // required for async sendResponse }) - -//register hotkeys +// register hotkeys chrome.commands.onCommand.addListener(function (command) { - console.log('Detected command: ' + command) - switch (command) { - case 'open_opal_hotkey': - chrome.tabs.update({ url: "https://bildungsportal.sachsen.de/opal/home/" }) - save_clicks(2) - break - case 'open_owa_hotkey': - save_clicks(2) - chrome.tabs.update({ url: "https://msx.tu-dresden.de/owa/" }) - break - case 'open_jexam_hotkey': - chrome.tabs.update({ url: "https://jexam.inf.tu-dresden.de/" }) - save_clicks(2) - default: - break - } + console.log('Detected command: ' + command) + switch (command) { + case 'open_opal_hotkey': + chrome.tabs.update({ url: 'https://bildungsportal.sachsen.de/opal/home/' }) + saveClicks(2) + break + case 'open_owa_hotkey': + saveClicks(2) + chrome.tabs.update({ url: 'https://msx.tu-dresden.de/owa/' }) + break + case 'open_jexam_hotkey': + chrome.tabs.update({ url: 'https://jexam.inf.tu-dresden.de/' }) + saveClicks(2) + break + default: + break + } }) /** @@ -448,387 +441,382 @@ chrome.commands.onCommand.addListener(function (command) { * modify http header from opal, to view pdf in browser without the need to download it * @param {true} enabled flag to enable/ disable listener */ -function enableHeaderListener(enabled) { - if (enabled) { - chrome.webRequest.onHeadersReceived.addListener( - headerListenerFunc, - { - urls: [ - "https://bildungsportal.sachsen.de/opal/downloadering*", - "https://bildungsportal.sachsen.de/opal/*.pdf", - ], - }, - ["blocking", "responseHeaders"] - ) - } else { - chrome.webRequest.onHeadersReceived.removeListener(headerListenerFunc) - } +function enableHeaderListener (enabled) { + if (enabled) { + chrome.webRequest.onHeadersReceived.addListener( + headerListenerFunc, + { + urls: [ + 'https://bildungsportal.sachsen.de/opal/downloadering*', + 'https://bildungsportal.sachsen.de/opal/*.pdf' + ] + }, + ['blocking', 'responseHeaders'] + ) + } else { + chrome.webRequest.onHeadersReceived.removeListener(headerListenerFunc) + } } -function headerListenerFunc(details) { - let header = details.responseHeaders.find( - e => e.name.toLowerCase() === "content-disposition" - ) - if (!header.value.includes(".pdf")) return //only for pdf - header.value = "inline" - return { responseHeaders: details.responseHeaders } +function headerListenerFunc (details) { + const header = details.responseHeaders.find( + e => e.name.toLowerCase() === 'content-disposition' + ) + if (!header.value.includes('.pdf')) return // only for pdf + header.value = 'inline' + return { responseHeaders: details.responseHeaders } } -//open settings (=options) page, if required set params -function openSettingsPage(params) { - if (params) { - chrome.storage.local.set({ openSettingsPageParam: params }, function () { - chrome.runtime.openOptionsPage() - }) - } else { - chrome.runtime.openOptionsPage() - } - return +// open settings (=options) page, if required set params +function openSettingsPage (params) { + if (params) { + chrome.storage.local.set({ openSettingsPageParam: params }, function () { + chrome.runtime.openOptionsPage() + }) + } else { + chrome.runtime.openOptionsPage() + } } -function openSharePage() { - chrome.tabs.create(({ url: "share.html" })) +function openSharePage () { + chrome.tabs.create(({ url: 'share.html' })) } -//timeout is 2000 default -function loggedOut(portal) { - let timeout = 2000 - if (portal === "loggedOutCloudstore") { timeout = 7000 } - let loggedOutPortal = {} - loggedOutPortal[portal] = true - chrome.storage.local.set(loggedOutPortal, function () { }) - setTimeout(function () { - loggedOutPortal[portal] = false - chrome.storage.local.set(loggedOutPortal, function () { }) - }, timeout) +// timeout is 2000 default +function loggedOut (portal) { + let timeout = 2000 + if (portal === 'loggedOutCloudstore') { timeout = 7000 } + const loggedOutPortal = {} + loggedOutPortal[portal] = true + chrome.storage.local.set(loggedOutPortal, function () { }) + setTimeout(function () { + loggedOutPortal[portal] = false + chrome.storage.local.set(loggedOutPortal, function () { }) + }, timeout) } -//show badge -function show_badge(Text, Color, timeout) { - chrome.browserAction.setBadgeText({ text: Text }) - chrome.browserAction.setBadgeBackgroundColor({ color: Color }) - //setTimeout(function() { - // chrome.browserAction.setBadgeText({text: ""}); - //}, timeout); +// show badge +function showBadge (Text, Color, timeout) { + chrome.browserAction.setBadgeText({ text: Text }) + chrome.browserAction.setBadgeBackgroundColor({ color: Color }) + // setTimeout(function() { + // chrome.browserAction.setBadgeText({text: ""}); + // }, timeout); } -//save_click_counter -function save_clicks(counter) { - //load number of saved clicks and add counter! - var saved_clicks = 0 - chrome.storage.local.get(['saved_click_counter'], (result) => { - saved_clicks = (result.saved_click_counter === undefined) ? 0 : result.saved_click_counter - chrome.storage.local.set({ saved_click_counter: saved_clicks + counter }, function () { - console.log('You just saved yourself ' + counter + " clicks!") - }) - //make rocketIcons available if appropriate - chrome.storage.local.get(["availableRockets"], (resp) => { - let avRockets = resp.availableRockets - if (result.saved_click_counter > 250 && !avRockets.includes("RI4")) avRockets.push("RI4") - if (result.saved_click_counter > 2500 && !avRockets.includes("RI5")) avRockets.push("RI5") - chrome.storage.local.set({ "availableRockets": avRockets }) - }) - }) +// save_click_counter +function saveClicks (counter) { + // load number of saved clicks and add counter! + let savedClicks = 0 + chrome.storage.local.get(['saved_click_counter'], (result) => { + savedClicks = (result.saved_click_counter === undefined) ? 0 : result.saved_click_counter + chrome.storage.local.set({ saved_click_counter: savedClicks + counter }, function () { + console.log('You just saved yourself ' + counter + ' clicks!') + }) + // make rocketIcons available if appropriate + chrome.storage.local.get(['availableRockets'], (resp) => { + const avRockets = resp.availableRockets + if (result.saved_click_counter > 250 && !avRockets.includes('RI4')) avRockets.push('RI4') + if (result.saved_click_counter > 2500 && !avRockets.includes('RI5')) avRockets.push('RI5') + chrome.storage.local.set({ availableRockets: avRockets }) + }) + }) } -//////////////// FUNCTIONS FOR ENCRYPTION AND USERDATA HANDLING //////////////// +/// ///////////// FUNCTIONS FOR ENCRYPTION AND USERDATA HANDLING //////////////// // info: asdf = username | fdsa = password -//create hash from input-string (can also be json of course) -//output hash is always of same length and is of type buffer -function hashDigest(string) { - return new Promise(async (resolve, reject) => { - const encoder = new TextEncoder() - const hashBuffer = await crypto.subtle.digest('SHA-256', encoder.encode(string)) - resolve(hashBuffer) - }) -} - -//get key for encryption (format: buffer) -function getKeyBuffer() { - return new Promise((resolve, reject) => { - let sysInfo = "" - let isChrome = navigator.userAgent.includes("Chrome/") //attention: no failsave browser detection | also for new edge! - let isFirefox = navigator.userAgent.includes("Firefox/") //attention: no failsave browser detection - - //key differs between browsers, because different APIs - if (isFirefox) { - sysInfo = sysInfo + window.navigator.hardwareConcurrency - chrome.runtime.getPlatformInfo(async (info) => { - sysInfo = sysInfo + JSON.stringify(info) - //create key - let keyBuffer = await crypto.subtle.importKey('raw', await hashDigest(sysInfo), - { name: "AES-CBC", }, - false, - ['encrypt', 'decrypt']) - resolve(keyBuffer) - }) - //chrome, edge and everything else - } else { - chrome.system.cpu.getInfo(info => { - delete info['processors'] - delete info['temperatures'] - sysInfo = sysInfo + JSON.stringify(info) - chrome.runtime.getPlatformInfo(async (info) => { - sysInfo = sysInfo + JSON.stringify(info) - //create key - let keyBuffer = await crypto.subtle.importKey('raw', await hashDigest(sysInfo), - { name: "AES-CBC", }, - false, - ['encrypt', 'decrypt']) - resolve(keyBuffer) - }) - }) - } - }) +// create hash from input-string (can also be json of course) +// output hash is always of same length and is of type buffer +function hashDigest (string) { + return new Promise(async (resolve, reject) => { + const encoder = new TextEncoder() + const hashBuffer = await crypto.subtle.digest('SHA-256', encoder.encode(string)) + resolve(hashBuffer) + }) } -//this functions saved user login-data locally. -//user data is encrypted using the crpyto-js library (aes-cbc). The encryption key is created from pc-information with system.cpu -//a lot of encoding and transforming needs to be done, in order to provide all values in the right format. -async function setUserData(userData) { - //collect all required information for encryption in the right format - let userDataConcat = userData.asdf + '@@@@@' + userData.fdsa - let encoder = new TextEncoder() - let userDataEncoded = encoder.encode(userDataConcat) - let keyBuffer = await getKeyBuffer() - let iv = crypto.getRandomValues(new Uint8Array(16)) - - //encrypt - let userDataEncrypted = await crypto.subtle.encrypt( - { - name: "AES-CBC", - iv: iv - }, - keyBuffer, - userDataEncoded - ) - - //adjust format to save encrypted data in lokal storage - userDataEncrypted = Array.from(new Uint8Array(userDataEncrypted)) - userDataEncrypted = userDataEncrypted.map(byte => String.fromCharCode(byte)).join('') - userDataEncrypted = btoa(userDataEncrypted) - iv = Array.from(iv).map(b => ('00' + b.toString(16)).slice(-2)).join('') - chrome.storage.local.set({ Data: iv + userDataEncrypted }, function () { }) +// get key for encryption (format: buffer) +function getKeyBuffer () { + return new Promise((resolve, reject) => { + let sysInfo = '' + // key differs between browsers, because different APIs + if (isFirefox) { + sysInfo = sysInfo + window.navigator.hardwareConcurrency + chrome.runtime.getPlatformInfo(async (info) => { + sysInfo = sysInfo + JSON.stringify(info) + // create key + const keyBuffer = await crypto.subtle.importKey('raw', await hashDigest(sysInfo), + { name: 'AES-CBC' }, + false, + ['encrypt', 'decrypt']) + resolve(keyBuffer) + }) + // chrome, edge and everything else + } else { + chrome.system.cpu.getInfo(info => { + delete info.processors + delete info.temperatures + sysInfo = sysInfo + JSON.stringify(info) + chrome.runtime.getPlatformInfo(async (info) => { + sysInfo = sysInfo + JSON.stringify(info) + // create key + const keyBuffer = await crypto.subtle.importKey('raw', await hashDigest(sysInfo), + { name: 'AES-CBC' }, + false, + ['encrypt', 'decrypt']) + resolve(keyBuffer) + }) + }) + } + }) } -//check if username, password exist -function userDataExists() { - return new Promise(async (resolve, reject) => { - let userData = await getUserData() - if (userData.asdf === undefined || userData.fdsa === undefined) { - resolve(false) - return - } - resolve(true) - return - }) +// this functions saved user login-data locally. +// user data is encrypted using the crpyto-js library (aes-cbc). The encryption key is created from pc-information with system.cpu +// a lot of encoding and transforming needs to be done, in order to provide all values in the right format. +async function setUserData (userData) { + // collect all required information for encryption in the right format + const userDataConcat = userData.asdf + '@@@@@' + userData.fdsa + const encoder = new TextEncoder() + const userDataEncoded = encoder.encode(userDataConcat) + const keyBuffer = await getKeyBuffer() + let iv = crypto.getRandomValues(new Uint8Array(16)) + + // encrypt + let userDataEncrypted = await crypto.subtle.encrypt( + { + name: 'AES-CBC', + iv: iv + }, + keyBuffer, + userDataEncoded + ) + + // adjust format to save encrypted data in lokal storage + userDataEncrypted = Array.from(new Uint8Array(userDataEncrypted)) + userDataEncrypted = userDataEncrypted.map(byte => String.fromCharCode(byte)).join('') + userDataEncrypted = btoa(userDataEncrypted) + iv = Array.from(iv).map(b => ('00' + b.toString(16)).slice(-2)).join('') + chrome.storage.local.set({ Data: iv + userDataEncrypted }, function () { }) } -//return {asdf: "", fdsa: ""} -//decrypt and return user data -//a lot of encoding and transforming needs to be done, in order to provide all values in the right format -async function getUserData() { - return new Promise(async (resolve, reject) => { - //get required data for decryption - let keyBuffer = await getKeyBuffer() - chrome.storage.local.get(['Data'], async (Data) => { - //check if Data exists, else return - if (Data.Data === undefined || Data.Data === "undefined" || Data.Data === null) { - resolve({ asdf: undefined, fdsa: undefined }) - return - } - let iv = await Data.Data.slice(0, 32).match(/.{2}/g).map(byte => parseInt(byte, 16)) - iv = new Uint8Array(iv) - let userDataEncrypted = atob(Data.Data.slice(32)) - userDataEncrypted = new Uint8Array(userDataEncrypted.match(/[\s\S]/g).map(ch => ch.charCodeAt(0))) - - //decrypt - let UserData = await crypto.subtle.decrypt( - { - name: "AES-CBC", - iv: iv - }, - keyBuffer, - userDataEncrypted - ) - - //adjust to useable format - UserData = new TextDecoder().decode(UserData) - UserData = UserData.split("@@@@@") - resolve({ asdf: UserData[0], fdsa: UserData[1] }) - }) - }) +// check if username, password exist +function userDataExists () { + return new Promise(async (resolve, reject) => { + const userData = await getUserData() + if (userData.asdf === undefined || userData.fdsa === undefined) { + resolve(false) + return + } + resolve(true) + }) } -//////////////// END FUNCTIONS FOR ENCRYPTION AND USERDATA HANDLING //////////////// - -//save parsed courses -//course_list = {type:"", list:[{link:link, name: name}, ...]} -function saveCourses(course_list) { - course_list.list.sort((a, b) => (a.name > b.name) ? 1 : -1) - switch (course_list.type) { - case 'favoriten': - chrome.storage.local.set({ favoriten: JSON.stringify(course_list.list) }, function () { }) - console.log('saved Favoriten in TUfast') - break - case 'meine_kurse': - chrome.storage.local.set({ meine_kurse: JSON.stringify(course_list.list) }, function () { }) - console.log('saved Meine Kurse in TUfast') - break - default: - break - } +// return {asdf: "", fdsa: ""} +// decrypt and return user data +// a lot of encoding and transforming needs to be done, in order to provide all values in the right format +async function getUserData () { + return new Promise(async (resolve, reject) => { + // get required data for decryption + const keyBuffer = await getKeyBuffer() + chrome.storage.local.get(['Data'], async (Data) => { + // check if Data exists, else return + if (Data.Data === undefined || Data.Data === 'undefined' || Data.Data === null) { + resolve({ asdf: undefined, fdsa: undefined }) + return + } + let iv = await Data.Data.slice(0, 32).match(/.{2}/g).map(byte => parseInt(byte, 16)) + iv = new Uint8Array(iv) + let userDataEncrypted = atob(Data.Data.slice(32)) + userDataEncrypted = new Uint8Array(userDataEncrypted.match(/[\s\S]/g).map(ch => ch.charCodeAt(0))) + + // decrypt + let UserData = await crypto.subtle.decrypt( + { + name: 'AES-CBC', + iv: iv + }, + keyBuffer, + userDataEncrypted + ) + + // adjust to useable format + UserData = new TextDecoder().decode(UserData) + UserData = UserData.split('@@@@@') + resolve({ asdf: UserData[0], fdsa: UserData[1] }) + }) + }) } -//get parsed courses -//return course_list = [{link:link, name: name}, ...] -function loadCourses(type) { - switch (type) { - case "favoriten": - chrome.storage.local.get(['favoriten'], function (result) { - console.log(JSON.parse(result.favoriten)) - }) - break - case "meine_kurse": - chrome.storage.local.get(['meine_kurse'], function (result) { - console.log(JSON.parse(result.meine_kurse)) - }) - break - default: - break - } +/// ///////////// END FUNCTIONS FOR ENCRYPTION AND USERDATA HANDLING //////////////// + +// save parsed courses +// course_list = {type:"", list:[{link:link, name: name}, ...]} +function saveCourses (courseList) { + courseList.list.sort((a, b) => (a.name > b.name) ? 1 : -1) + switch (courseList.type) { + case 'favoriten': + chrome.storage.local.set({ favoriten: JSON.stringify(courseList.list) }, function () { }) + console.log('saved Favoriten in TUfast') + break + case 'meine_kurse': + chrome.storage.local.set({ meine_kurse: JSON.stringify(courseList.list) }, function () { }) + console.log('saved Meine Kurse in TUfast') + break + default: + break + } } -//function for custom URIEncoding -function customURIEncoding(string) { - string = encodeURIComponent(string) - string = string.replace("!", "%21").replace("'", "%27").replace("(", "%28").replace(")", "%29").replace("~", "%7E") - return string +// get parsed courses +// return course_list = [{link:link, name: name}, ...] +// function loadCourses (type) { +// switch (type) { +// case 'favoriten': +// chrome.storage.local.get(['favoriten'], function (result) { +// console.log(JSON.parse(result.favoriten)) +// }) +// break +// case 'meine_kurse': +// chrome.storage.local.get(['meine_kurse'], function (result) { +// console.log(JSON.parse(result.meine_kurse)) +// }) +// break +// default: +// break +// } +// } + +// function for custom URIEncoding +function customURIEncoding (string) { + string = encodeURIComponent(string) + string = string.replace('!', '%21').replace("'", '%27').replace('(', '%28').replace(')', '%29').replace('~', '%7E') + return string } -//function to log msx.tu-dresden.de/owa/ and retrieve the .json containing information about EMails -function fetchOWA(username, password, logout) { - return new Promise((resolve, reject) => { - - //encodeURIComponent and encodeURI are not working for all chars. See documentation. Thats why I implemented custom encoding. - username = customURIEncoding(username) - password = customURIEncoding(password) - - var mailInfoJson = new Object() //contains all required info - - //login - fetch("https://msx.tu-dresden.de/owa/auth.owa", { - "headers": { - "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", - "accept-language": "de-DE,de;q=0.9,en-DE;q=0.8,en-GB;q=0.7,en-US;q=0.6,en;q=0.5", - "cache-control": "max-age=0", - "content-type": "application/x-www-form-urlencoded", - "Access-Control-Allow-Origin": "*", - "sec-fetch-dest": "document", - "sec-fetch-mode": "navigate", - "sec-fetch-site": "same-origin", - "sec-fetch-user": "?1", - "upgrade-insecure-requests": "1" - }, - "referrer": "https://msx.tu-dresden.de/owa/auth/logon.aspx?replaceCurrent=1&url=https%3a%2f%2fmsx.tu-dresden.de%2fowa%2f%23authRedirect%3dtrue", - "referrerPolicy": "strict-origin-when-cross-origin", - "Access-Control-Allow-Origin": "*", - "body": "destination=https%3A%2F%2Fmsx.tu-dresden.de%2Fowa%2F%23authRedirect%3Dtrue&flags=4&forcedownlevel=0&username=" + username + "%40msx.tu-dresden.de&password=" + password + "&passwordText=&isUtf8=1", - "method": "POST", - "mode": "no-cors", - "credentials": "include" - }) - .then(() => { - //get clientID and correlationID - fetch("https://msx.tu-dresden.de/owa/", { - "headers": { - "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", - "accept-language": "de-DE,de;q=0.9,en-DE;q=0.8,en-GB;q=0.7,en-US;q=0.6,en;q=0.5", - "cache-control": "max-age=0", - "sec-fetch-dest": "document", - "sec-fetch-mode": "navigate", - "Access-Control-Allow-Origin": "*", - "sec-fetch-site": "same-origin", - "sec-fetch-user": "?1", - "upgrade-insecure-requests": "1" - }, - "referrer": "https://msx.tu-dresden.de/owa/auth/logon.aspx?replaceCurrent=1&url=https%3a%2f%2fmsx.tu-dresden.de%2fowa", - "referrerPolicy": "strict-origin-when-cross-origin", - "body": null, - "method": "GET", - "Access-Control-Allow-Origin": "*", - "mode": "cors", - "credentials": "include" - }) - //extract x-owa-correlationid. correlation id is - .then(resp => resp.text()).then(respText => { - let temp = respText.split("window.clientId = '")[1] - let clientId = temp.split("'")[0] - let corrId = clientId + "_" + (new Date()).getTime() - console.log("corrID: " + corrId) - }) - //getAllInfo - .then(corrId => { - fetch("https://msx.tu-dresden.de/owa/sessiondata.ashx?appcacheclient=0", { - "headers": { - "accept": "*/*", - "accept-language": "de-DE,de;q=0.9,en-DE;q=0.8,en-GB;q=0.7,en-US;q=0.6,en;q=0.5", - "sec-fetch-dest": "empty", - "sec-fetch-mode": "cors", - "Access-Control-Allow-Origin": "*", - "sec-fetch-site": "same-origin", - "x-owa-correlationid": corrId, - "x-owa-smimeinstalled": "1" - }, - "referrer": "https://msx.tu-dresden.de/owa/", - "referrerPolicy": "strict-origin-when-cross-origin", - "Access-Control-Allow-Origin": "*", - "body": null, - "method": "POST", - "mode": "cors", - "credentials": "include" - }) - .then(resp => resp.json()).then(respJson => { - mailInfoJson = respJson - }) - //logout - .then(() => { - //only logout, if user is not using owa in browser session - if (logout) { - console.log("Logging out from owa..") - fetch("https://msx.tu-dresden.de/owa/logoff.owa", { - "headers": { - "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", - "accept-language": "de-DE,de;q=0.9,en-DE;q=0.8,en-GB;q=0.7,en-US;q=0.6,en;q=0.5", - "sec-fetch-dest": "document", - "Access-Control-Allow-Origin": "*", - "sec-fetch-mode": "navigate", - "sec-fetch-site": "same-origin", - "sec-fetch-user": "?1", - "upgrade-insecure-requests": "1" - }, - "referrer": "https://msx.tu-dresden.de/owa/", - "referrerPolicy": "strict-origin-when-cross-origin", - "Access-Control-Allow-Origin": "*", - "body": null, - "method": "GET", - "mode": "cors", - "credentials": "include" - }) - } - }) - .then(() => resolve(mailInfoJson)) - }) - }) - }) +// function to log msx.tu-dresden.de/owa/ and retrieve the .json containing information about EMails +function fetchOWA (username, password, logout) { + return new Promise((resolve, reject) => { + // encodeURIComponent and encodeURI are not working for all chars. See documentation. Thats why I implemented custom encoding. + username = customURIEncoding(username) + password = customURIEncoding(password) + + // eslint-disable-next-line no-new-object + let mailInfoJson = new Object() // contains all required info + + // login + fetch('https://msx.tu-dresden.de/owa/auth.owa', { + headers: { + accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9', + 'accept-language': 'de-DE,de;q=0.9,en-DE;q=0.8,en-GB;q=0.7,en-US;q=0.6,en;q=0.5', + 'cache-control': 'max-age=0', + 'content-type': 'application/x-www-form-urlencoded', + 'Access-Control-Allow-Origin': '*', + 'sec-fetch-dest': 'document', + 'sec-fetch-mode': 'navigate', + 'sec-fetch-site': 'same-origin', + 'sec-fetch-user': '?1', + 'upgrade-insecure-requests': '1' + }, + referrer: 'https://msx.tu-dresden.de/owa/auth/logon.aspx?replaceCurrent=1&url=https%3a%2f%2fmsx.tu-dresden.de%2fowa%2f%23authRedirect%3dtrue', + referrerPolicy: 'strict-origin-when-cross-origin', + 'Access-Control-Allow-Origin': '*', + body: 'destination=https%3A%2F%2Fmsx.tu-dresden.de%2Fowa%2F%23authRedirect%3Dtrue&flags=4&forcedownlevel=0&username=' + username + '%40msx.tu-dresden.de&password=' + password + '&passwordText=&isUtf8=1', + method: 'POST', + mode: 'no-cors', + credentials: 'include' + }) + .then(() => { + // get clientID and correlationID + fetch('https://msx.tu-dresden.de/owa/', { + headers: { + accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9', + 'accept-language': 'de-DE,de;q=0.9,en-DE;q=0.8,en-GB;q=0.7,en-US;q=0.6,en;q=0.5', + 'cache-control': 'max-age=0', + 'sec-fetch-dest': 'document', + 'sec-fetch-mode': 'navigate', + 'Access-Control-Allow-Origin': '*', + 'sec-fetch-site': 'same-origin', + 'sec-fetch-user': '?1', + 'upgrade-insecure-requests': '1' + }, + referrer: 'https://msx.tu-dresden.de/owa/auth/logon.aspx?replaceCurrent=1&url=https%3a%2f%2fmsx.tu-dresden.de%2fowa', + referrerPolicy: 'strict-origin-when-cross-origin', + body: null, + method: 'GET', + 'Access-Control-Allow-Origin': '*', + mode: 'cors', + credentials: 'include' + }) + // extract x-owa-correlationid. correlation id is + .then(resp => resp.text()).then(respText => { + const temp = respText.split("window.clientId = '")[1] + const clientId = temp.split("'")[0] + const corrId = clientId + '_' + (new Date()).getTime() + console.log('corrID: ' + corrId) + }) + // getAllInfo + .then(corrId => { + fetch('https://msx.tu-dresden.de/owa/sessiondata.ashx?appcacheclient=0', { + headers: { + accept: '*/*', + 'accept-language': 'de-DE,de;q=0.9,en-DE;q=0.8,en-GB;q=0.7,en-US;q=0.6,en;q=0.5', + 'sec-fetch-dest': 'empty', + 'sec-fetch-mode': 'cors', + 'Access-Control-Allow-Origin': '*', + 'sec-fetch-site': 'same-origin', + 'x-owa-correlationid': corrId, + 'x-owa-smimeinstalled': '1' + }, + referrer: 'https://msx.tu-dresden.de/owa/', + referrerPolicy: 'strict-origin-when-cross-origin', + 'Access-Control-Allow-Origin': '*', + body: null, + method: 'POST', + mode: 'cors', + credentials: 'include' + }) + .then(resp => resp.json()).then(respJson => { + mailInfoJson = respJson + }) + // logout + .then(() => { + // only logout, if user is not using owa in browser session + if (logout) { + console.log('Logging out from owa..') + fetch('https://msx.tu-dresden.de/owa/logoff.owa', { + headers: { + accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9', + 'accept-language': 'de-DE,de;q=0.9,en-DE;q=0.8,en-GB;q=0.7,en-US;q=0.6,en;q=0.5', + 'sec-fetch-dest': 'document', + 'Access-Control-Allow-Origin': '*', + 'sec-fetch-mode': 'navigate', + 'sec-fetch-site': 'same-origin', + 'sec-fetch-user': '?1', + 'upgrade-insecure-requests': '1' + }, + referrer: 'https://msx.tu-dresden.de/owa/', + referrerPolicy: 'strict-origin-when-cross-origin', + 'Access-Control-Allow-Origin': '*', + body: null, + method: 'GET', + mode: 'cors', + credentials: 'include' + }) + } + }) + .then(() => resolve(mailInfoJson)) + }) + }) + }) } -//extract number of unread messages in owa -function countUnreadMsg(json) { - return new Promise((resolve, reject) => { - json.findFolders.Body.ResponseMessages.Items[0].RootFolder.Folders.forEach(obj => { - if (obj.DisplayName === "Inbox" || obj.DisplayName === "Posteingang") resolve(obj.UnreadCount) - }) - }) +// extract number of unread messages in owa +function countUnreadMsg (json) { + return new Promise((resolve, reject) => { + json.findFolders.Body.ResponseMessages.Items[0].RootFolder.Folders.forEach(obj => { + if (obj.DisplayName === 'Inbox' || obj.DisplayName === 'Posteingang') resolve(obj.UnreadCount) + }) + }) } diff --git a/src/contentScripts/bildungsportal.js b/src/contentScripts/bildungsportal.js index c4e18468..7b17995f 100644 --- a/src/contentScripts/bildungsportal.js +++ b/src/contentScripts/bildungsportal.js @@ -1,30 +1,30 @@ chrome.storage.local.get(['isEnabled', 'loggedOutOpal'], function (result) { - if (/*result.isEnabled &&*/ !(result.loggedOutOpal)) { - //when pop-up shows - document.addEventListener("DOMNodeInserted", function (e) { - //select TU Dresden from selector - if (document.getElementsByName("content:container:login:shibAuthForm:wayfselection")[0]) { - chrome.runtime.sendMessage({ cmd: "save_clicks", click_count: 1 }) - let selectionList = document.getElementsByName("content:container:login:shibAuthForm:wayfselection")[0] - for (let el of selectionList) { - if (el.textContent === "TU Dresden" || el.textContent === "Technsiche Universität Dresden") { - document.getElementsByName("content:container:login:shibAuthForm:wayfselection")[0].value = el.value + if (/* result.isEnabled && */ !(result.loggedOutOpal)) { + // when pop-up shows + document.addEventListener('DOMNodeInserted', function (e) { + // select TU Dresden from selector + if (document.getElementsByName('content:container:login:shibAuthForm:wayfselection')[0]) { + chrome.runtime.sendMessage({ cmd: 'save_clicks', click_count: 1 }) + const selectionList = document.getElementsByName('content:container:login:shibAuthForm:wayfselection')[0] + for (const el of selectionList) { + if (el.textContent === 'TU Dresden' || el.textContent === 'Technsiche Universität Dresden') { + document.getElementsByName('content:container:login:shibAuthForm:wayfselection')[0].value = el.value } } } - //submit selected - if (document.getElementsByName("content:container:login:shibAuthForm:shibLogin")[0]) { - chrome.runtime.sendMessage({ cmd: "save_clicks", click_count: 1 }) - chrome.runtime.sendMessage({ cmd: "show_ok_badge", timeout: 4000 }) - document.getElementsByName("content:container:login:shibAuthForm:shibLogin")[0].click() + // submit selected + if (document.getElementsByName('content:container:login:shibAuthForm:shibLogin')[0]) { + chrome.runtime.sendMessage({ cmd: 'save_clicks', click_count: 1 }) + chrome.runtime.sendMessage({ cmd: 'show_ok_badge', timeout: 4000 }) + document.getElementsByName('content:container:login:shibAuthForm:shibLogin')[0].click() } - }, false); + }, false) - //start login process - window.addEventListener("load", function () { + // start login process + window.addEventListener('load', function () { if (document.getElementsByClassName('btn btn-sm')[1].innerText.includes('Login')) { - chrome.runtime.sendMessage({ cmd: "save_clicks", click_count: 1 }) - chrome.runtime.sendMessage({ cmd: "show_ok_badge", timeout: 4000 }) + chrome.runtime.sendMessage({ cmd: 'save_clicks', click_count: 1 }) + chrome.runtime.sendMessage({ cmd: 'show_ok_badge', timeout: 4000 }) document.getElementsByClassName('btn btn-sm')[1].click() } }, true) diff --git a/src/contentScripts/bildungsportal_insertLogo.js b/src/contentScripts/bildungsportal_insertLogo.js index 7c84940c..78a74cd5 100644 --- a/src/contentScripts/bildungsportal_insertLogo.js +++ b/src/contentScripts/bildungsportal_insertLogo.js @@ -1,320 +1,313 @@ /* { selectedRocketIcon: '{"id": "RI_default", "link": "RocketIcons/default_128px"}' } */ -chrome.storage.local.get(['isEnabled', 'fwdEnabled', 'PRObadge', 'flakeState', "selectedRocketIcon", "foundEasteregg"], function (result) { - if (result.isEnabled || result.fwdEnabled) { - //parse selectedRocketIcon - let selectedRocketIcon = JSON.parse(result.selectedRocketIcon) - - //decide which overlay to show - let christmasTime = false - let d = new Date() - let month = d.getMonth() + 1 //starts at 0 - let day = d.getDate() - if (month === 12 && day > 15 && day < 27) christmasTime = true - - //switch flakeState to false in november - if (month === 11) chrome.storage.local.set({ flakeState: false }, function () { }) - - if (christmasTime) { - //on load - document.addEventListener("DOMNodeInserted", function (e) { - if (!document.getElementById("flake")) insertFlakeSwitch(result.flakeState) - }) - //on document changes - window.addEventListener("load", function () { - if (!document.getElementById("flake")) insertFlakeSwitch(result.flakeState) - if (!document.getElementById("snowflakes") && result.flakeState) insertFlakes() - }, true) - //standard rocket logo - } else { - //on load - document.addEventListener("DOMNodeInserted", function (e) { - if (!document.getElementById("TUFastLogo")) { insertRocket(selectedRocketIcon, result.PRObadge, result.foundEasteregg) } - }) - //on document changes - window.addEventListener("load", function () { - if (!document.getElementById("TUFastLogo")) { insertRocket(selectedRocketIcon, result.PRObadge, result.foundEasteregg) } - }, true) - } +chrome.storage.local.get(['isEnabled', 'fwdEnabled', 'PRObadge', 'flakeState', 'selectedRocketIcon', 'foundEasteregg'], function (result) { + if (result.isEnabled || result.fwdEnabled) { + // parse selectedRocketIcon + const selectedRocketIcon = JSON.parse(result.selectedRocketIcon) + + // decide which overlay to show + let christmasTime = false + const d = new Date() + const month = d.getMonth() + 1 // starts at 0 + const day = d.getDate() + if (month === 12 && day > 15 && day < 27) christmasTime = true + + // switch flakeState to false in november + if (month === 11) chrome.storage.local.set({ flakeState: false }, function () { }) + + if (christmasTime) { + // on load + document.addEventListener('DOMNodeInserted', function (e) { + if (!document.getElementById('flake')) insertFlakeSwitch(result.flakeState) + }) + // on document changes + window.addEventListener('load', function () { + if (!document.getElementById('flake')) insertFlakeSwitch(result.flakeState) + if (!document.getElementById('snowflakes') && result.flakeState) insertFlakes() + }, true) + // standard rocket logo + } else { + // on load + document.addEventListener('DOMNodeInserted', function (e) { + if (!document.getElementById('TUFastLogo')) { insertRocket(selectedRocketIcon, result.PRObadge, result.foundEasteregg) } + }) + // on document changes + window.addEventListener('load', function () { + if (!document.getElementById('TUFastLogo')) { insertRocket(selectedRocketIcon, result.PRObadge, result.foundEasteregg) } + }, true) } + } }) // variables required for rocket logo easteregg -var GLOBAL_counter = 0 //count clicks on icon -var coutdownRemoveScreenOverlay //timer -var timeout = 1000 //timeout for timer -var blocker = false //block icon click -var timeUp = true //true, when time is up -var display_value //number of text, which shows on screen -var typeOfMsg = "" //type of message which is displayed - -function updateRocketLogo(iconPath) { - let timestamp = new Date().getTime(); - chrome.runtime.sendMessage({ cmd: 'update_rocket_logo_easteregg'}, function (result) { }) - document.querySelectorAll("#TUFastLogo img")[0].src = chrome.runtime.getURL("" + iconPath) + "?t =" + timestamp; -} - -function setProBadge() { - document.getElementById("TUFastLogo").parentNode.removeChild(document.getElementById("TUFastLogo")) - chrome.storage.local.set({ PRObadge: "PRO" }, function () { }) - insertRocket("colorful", "PRO") +let globalCounter = 0 // count clicks on icon +let coutdownRemoveScreenOverlay // timer +let timeout = 1000 // timeout for timer +let blocker = false // block icon click +let timeUp = true // true, when time is up +let displayValue // number of text, which shows on screen +let typeOfMsg = '' // type of message which is displayed + +function updateRocketLogo (iconPath) { + const timestamp = new Date().getTime() + chrome.runtime.sendMessage({ cmd: 'update_rocket_logo_easteregg' }, function (result) { }) + document.querySelectorAll('#TUFastLogo img')[0].src = chrome.runtime.getURL('' + iconPath) + '?t =' + timestamp } -function insertScreenOverlay() { - try { - if (!document.getElementById('counter')) { - let body = document.getElementsByTagName("body")[0] - let counter = document.createElement("div") - let container = document.createElement("div") - container.style.position = "relative" - counter.id = "counter" - counter.style.opacity = "1" - counter.style.fontSize = "150px" - counter.style.position = 'absolute' - counter.style.color = "#000000" - counter.style.top = "50%" - counter.style.left = "50%" - counter.style.marginRight = "-50%" - counter.style.transform = "translate(-50%, -50%)" - counter.style.zIndex = "99" - - container.appendChild(counter) - body.prepend(counter) - } - } catch (e) { console.log("cannot insert overlay:" + e) } -} - -async function logoOnClickEasteregg() { - - //block counting up when text is promted - if (blocker && !timeUp) return - - GLOBAL_counter++ +// function setProBadge() { +// document.getElementById('TUFastLogo').parentNode.removeChild(document.getElementById('TUFastLogo')) +// chrome.storage.local.set({ PRObadge: 'PRO' }, function () { }) +// insertRocket('colorful', 'PRO') +// } - //show screen overlay +function insertScreenOverlay () { + try { if (!document.getElementById('counter')) { - //insert overlay - insertScreenOverlay() - } else { - //remove existing timeout - clearTimeout(coutdownRemoveScreenOverlay) + const body = document.getElementsByTagName('body')[0] + const counter = document.createElement('div') + const container = document.createElement('div') + container.style.position = 'relative' + counter.id = 'counter' + counter.style.opacity = '1' + counter.style.fontSize = '150px' + counter.style.position = 'absolute' + counter.style.color = '#000000' + counter.style.top = '50%' + counter.style.left = '50%' + counter.style.marginRight = '-50%' + counter.style.transform = 'translate(-50%, -50%)' + counter.style.zIndex = '99' + + container.appendChild(counter) + body.prepend(counter) } + } catch (e) { console.log('cannot insert overlay:' + e) } +} +async function logoOnClickEasteregg () { + // block counting up when text is promted + if (blocker && !timeUp) return + + globalCounter++ + + // show screen overlay + if (!document.getElementById('counter')) { + // insert overlay + insertScreenOverlay() + } else { + // remove existing timeout + clearTimeout(coutdownRemoveScreenOverlay) + } + + let counter = document.getElementById('counter') + counter.style.color = funnyColor(counter.style.color, 80) + + // trigger actions based on counter + switch (globalCounter) { + case 10: + // easteregg finished + displayValue = '🚀 🚀 🚀' + typeOfMsg = 'text' + // enable rocketIcon, set selected rocketIcon (RI3) + chrome.storage.local.set({ foundEasteregg: true }, function () { }) + chrome.storage.local.set({ selectedRocketIcon: '{"id": "RI3", "link": "../assets/icons/RocketIcons/7_128px.png"}' }, function () { }) + chrome.storage.local.get(['availableRockets'], (resp) => { + const avRockets = resp.availableRockets + avRockets.push('RI3') + chrome.storage.local.set({ availableRockets: avRockets }) + }) + // live-update the logo + updateRocketLogo('../assets/icons/RocketIcons/7_128px.png') + // change the onclick function + document.getElementById('TUFastLogo').onclick = logoOnClick + break + default: + typeOfMsg = 'number' + displayValue = globalCounter + } + + // decide how to show text + switch (typeOfMsg) { + case 'number': + timeout = 1000 + blocker = false + counter.style.fontSize = '150px' + break + case 'text': + counter.style.fontSize = '100px' + timeout = 3000 + blocker = true + break + default: + // same as number + timeout = 1000 + blocker = false + counter.style.fontSize = '150px' + } + + // populate screen overlay with value + counter.innerHTML = displayValue + + timeUp = false + + coutdownRemoveScreenOverlay = setTimeout(function () { counter = document.getElementById('counter') - counter.style.color = funnyColor(counter.style.color, 80) - - //trigger actions based on counter - switch (GLOBAL_counter) { - case 10: - //easteregg finished - display_value = "🚀 🚀 🚀" - typeOfMsg = "text" - //enable rocketIcon, set selected rocketIcon (RI3) - chrome.storage.local.set({ foundEasteregg: true }, function () { }) - chrome.storage.local.set({ selectedRocketIcon: '{"id": "RI3", "link": "../assets/icons/RocketIcons/7_128px.png"}' }, function () { }) - chrome.storage.local.get(["availableRockets"], (resp) => { - let avRockets = resp.availableRockets - avRockets.push("RI3") - chrome.storage.local.set({ "availableRockets": avRockets }) - }) - //live-update the logo - updateRocketLogo("../assets/icons/RocketIcons/7_128px.png") - //change the onclick function - document.getElementById("TUFastLogo").onclick = logoOnClick - break - default: - typeOfMsg = "number" - display_value = GLOBAL_counter - } - - //decide how to show text - switch (typeOfMsg) { - case "number": - timeout = 1000 - blocker = false - counter.style.fontSize = "150px" - break - case "text": - counter.style.fontSize = "100px" - timeout = 3000 - blocker = true - break - default: - //same as number - timeout = 1000 - blocker = false - counter.style.fontSize = "150px" - } - - //populate screen overlay with value - counter.innerHTML = display_value - - timeUp = false - - coutdownRemoveScreenOverlay = setTimeout(function () { - counter = document.getElementById('counter') - counter.parentNode.removeChild(counter) - timeUp = true - }, timeout) + counter.parentNode.removeChild(counter) + timeUp = true + }, timeout) } -function logoOnClick() { - console.log("here") - if (timeUp) chrome.runtime.sendMessage({ cmd: 'open_settings_page', params: 'rocket_icons_settings' }, function (result) { }) +function logoOnClick () { + console.log('here') + if (timeUp) chrome.runtime.sendMessage({ cmd: 'open_settings_page', params: 'rocket_icons_settings' }, function (result) { }) } -function funnyColor(color, step) { - rgb = color.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/) - rgb[1] = parseInt(rgb[1]) - rgb[2] = parseInt(rgb[2]) - rgb[3] = parseInt(rgb[3]) - if (rgb[1] < 255 - step && rgb[2] < 255 - step && rgb[3] < 150 - step) rgb[1] += step - else if (rgb[2] < 255 - step && rgb[3] < 150 - step) rgb[2] += step - else if (rgb[3] < 150 - step) rgb[3] += step - else if (rgb[1] > 0 + step) rgb[1] -= step - else if (rgb[2] > 0 + step) rgb[2] -= step - else if (rgb[3] > 0 + step) rgb[3] -= step - color = "rgb(" + rgb[1] + "," + rgb[2] + "," + rgb[3] + ")" - return color; +function funnyColor (color, step) { + const rgb = color.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/) + rgb[1] = parseInt(rgb[1]) + rgb[2] = parseInt(rgb[2]) + rgb[3] = parseInt(rgb[3]) + if (rgb[1] < 255 - step && rgb[2] < 255 - step && rgb[3] < 150 - step) rgb[1] += step + else if (rgb[2] < 255 - step && rgb[3] < 150 - step) rgb[2] += step + else if (rgb[3] < 150 - step) rgb[3] += step + else if (rgb[1] > 0 + step) rgb[1] -= step + else if (rgb[2] > 0 + step) rgb[2] -= step + else if (rgb[3] > 0 + step) rgb[3] -= step + color = 'rgb(' + rgb[1] + ',' + rgb[2] + ',' + rgb[3] + ')' + return color }; -function insertRocket(selectedRocketIcon, PRObadge = false, foundEasteregg) { - let imgUrl, header, logo_node, logo_link, logo_img, badge - try { - if (document.getElementsByClassName("page-header")[0] != undefined) { - header = document.getElementsByClassName("page-header")[0] - logo_node = document.createElement("h1") - logo_link = document.createElement("a") - logo_img = document.createElement("img") - logo_link.href = "javascript:void(0)" - logo_node.id = "TUFastLogo" - logo_link.title = "powered by TUFast. Enjoy :)" - - //onclick function depends on whether easteregg was already found! - if (foundEasteregg) { - logo_node.onclick = logoOnClick - } else { - logo_node.onclick = logoOnClickEasteregg - } - - //create rocket icon - imgUrl = chrome.runtime.getURL("../" + selectedRocketIcon.link) - logo_img.style.display = "inline-block" - logo_img.style.width = "37px" - logo_img.src = imgUrl - logo_link.appendChild(logo_img) - - //add badge - switch (PRObadge) { - case "PRO": - logo_link.style.position = "relative" - badge = document.createElement("span") - badge.classList.add("badge") - badge.innerHTML = "PRO" - badge.style.fontSize = "0.3em" - badge.style.position = "absolute" - badge.style.bottom = "0px" - badge.style.left = "20px" - logo_link.appendChild(badge) - break - default: - break - } - - //append to header - logo_node.appendChild(logo_link) - header.append(logo_node) - } - } - catch (e) { - console.log("Error inserting logo: " + e) - } -} - +function insertRocket (selectedRocketIcon, PRObadge = false, foundEasteregg) { + let imgUrl, header, logoNode, logoLink, logoImg, badge + try { + if (document.getElementsByClassName('page-header')[0] !== undefined) { + header = document.getElementsByClassName('page-header')[0] + logoNode = document.createElement('h1') + logoLink = document.createElement('a') + logoImg = document.createElement('img') + logoLink.href = 'javascript:void(0)' + logoNode.id = 'TUFastLogo' + logoLink.title = 'powered by TUFast. Enjoy :)' + + // onclick function depends on whether easteregg was already found! + if (foundEasteregg) { + logoNode.onclick = logoOnClick + } else { + logoNode.onclick = logoOnClickEasteregg + } + + // create rocket icon + imgUrl = chrome.runtime.getURL('../' + selectedRocketIcon.link) + logoImg.style.display = 'inline-block' + logoImg.style.width = '37px' + logoImg.src = imgUrl + logoLink.appendChild(logoImg) + + // add badge + switch (PRObadge) { + case 'PRO': + logoLink.style.position = 'relative' + badge = document.createElement('span') + badge.classList.add('badge') + badge.innerHTML = 'PRO' + badge.style.fontSize = '0.3em' + badge.style.position = 'absolute' + badge.style.bottom = '0px' + badge.style.left = '20px' + logoLink.appendChild(badge) + break + default: + break + } -//toggle flake state -function flakesSwitchOnClick() { - chrome.storage.local.get(['flakeState'], function (result) { - chrome.storage.local.set({ flakeState: !(result.flakeState) }, function () { }) - //careful: this has to be negated, as its toggled - if (!result.flakeState) { - document.getElementById("flakeLink").style.color = "black" - insertFlakes() - } - else if (result.flakeState) { - document.getElementById("flakeLink").style.color = "Grey" - removeFlakes() - } - }) + // append to header + logoNode.appendChild(logoLink) + header.append(logoNode) + } + } catch (e) { + console.log('Error inserting logo: ' + e) + } } -function removeFlakes() { - try { - document.getElementById("snowflakes").parentNode.removeChild(document.getElementById("snowflakes")) - } catch (e) { console.log("No snowflakes!: " + e) } +// toggle flake state +function flakesSwitchOnClick () { + chrome.storage.local.get(['flakeState'], function (result) { + chrome.storage.local.set({ flakeState: !(result.flakeState) }, function () { }) + // careful: this has to be negated, as its toggled + if (!result.flakeState) { + document.getElementById('flakeLink').style.color = 'black' + insertFlakes() + } else if (result.flakeState) { + document.getElementById('flakeLink').style.color = 'Grey' + removeFlakes() + } + }) } -function insertFlakes() { - try { - if (!document.getElementById("snowflakes")) { - //create snowflake css - let snowflakeCss = ".snowflake {color: #fff;font-size: 1em;font-family: Arial, sans-serif;text-shadow: 0 0 5px #000;}@-webkit-keyframes snowflakes-fall{0%{top:-10%}100%{top:100%}}@-webkit-keyframes snowflakes-shake{0%,100%{-webkit-transform:translateX(0);transform:translateX(0)}50%{-webkit-transform:translateX(80px);transform:translateX(80px)}}@keyframes snowflakes-fall{0%{top:-10%}100%{top:100%}}@keyframes snowflakes-shake{0%,100%{transform:translateX(0)}50%{transform:translateX(80px)}}.snowflake{position:fixed;top:-10%;z-index:9999;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:default;-webkit-animation-name:snowflakes-fall,snowflakes-shake;-webkit-animation-duration:10s,3s;-webkit-animation-timing-function:linear,ease-in-out;-webkit-animation-iteration-count:infinite,infinite;-webkit-animation-play-state:running,running;animation-name:snowflakes-fall,snowflakes-shake;animation-duration:10s,3s;animation-timing-function:linear,ease-in-out;animation-iteration-count:infinite,infinite;animation-play-state:running,running}.snowflake:nth-of-type(0){left:1%;-webkit-animation-delay:0s,0s;animation-delay:0s,0s}.snowflake:nth-of-type(1){left:10%;-webkit-animation-delay:1s,1s;animation-delay:1s,1s}.snowflake:nth-of-type(2){left:20%;-webkit-animation-delay:6s,.5s;animation-delay:6s,.5s}.snowflake:nth-of-type(3){left:30%;-webkit-animation-delay:4s,2s;animation-delay:4s,2s}.snowflake:nth-of-type(4){left:40%;-webkit-animation-delay:2s,2s;animation-delay:2s,2s}.snowflake:nth-of-type(5){left:50%;-webkit-animation-delay:8s,3s;animation-delay:8s,3s}.snowflake:nth-of-type(6){left:60%;-webkit-animation-delay:6s,2s;animation-delay:6s,2s}.snowflake:nth-of-type(7){left:70%;-webkit-animation-delay:2.5s,1s;animation-delay:2.5s,1s}.snowflake:nth-of-type(8){left:80%;-webkit-animation-delay:1s,0s;animation-delay:1s,0s}.snowflake:nth-of-type(9){left:90%;-webkit-animation-delay:3s,1.5s;animation-delay:3s,1.5s}.snowflake:nth-of-type(10){left:25%;-webkit-animation-delay:2s,0s;animation-delay:2s,0s}.snowflake:nth-of-type(11){left:65%;-webkit-animation-delay:4s,2.5s;animation-delay:4s,2.5s}" - let snowflakeStyle = document.createElement('style') - - //add css to snowflage tag - if (snowflakeStyle.styleSheet) { - snowflakeStyle.styleSheet.cssText = snowflakeCss; - } else { - snowflakeStyle.appendChild(document.createTextNode(snowflakeCss)); - } - - //add snowflage style tag to website head - document.getElementsByTagName("Head")[0].appendChild(snowflakeStyle) - - //create snowflake div - let snowflakes = document.createElement("div") - snowflakes.classList.add("snowflakes") - snowflakes.id = "snowflakes" - snowflakes.setAttribute('aria-hidden', 'true'); - snowflakes.innerHTML = '
Deine Durchschnittnote (nach CP gewichtet): ' + getWeightedAverage(rawGrades) + '
Anzahl Module: ' + rawGrades.filter(x => x.isModule).length + '
Anzahl Prüfungen: ' + rawGrades.filter(x => !x.isModule).length + '
powered by TUfast (entwickelt von Noxdor, Daniel)
Wechsle zur ... nocht nicht für Firefox!
' - ) - var ctx = document.getElementById('myChart').getContext('2d') - ctx.canvas.width = 500 - ctx.canvas.height = 250 - var myChart = new Chart(ctx, { - type: 'bar', - data: { - labels: ['1', '2', '3', '4', 'nicht bestanden'], - datasets: [{ - data: countGrades(rawGrades.filter(x => !x.isModule)), - backgroundColor: [ - '#0b2a51', - '#0b2a51', - '#0b2a51', - '#0b2a51', - '#0b2a51', - ], - borderColor: [ - '#0b2a51', - ], - borderWidth: 1 - }] - }, - options: { - responsive: false, - maintainAspectRatio: false, - legend: { - display: false, - }, - scales: { - yAxes: [{ - ticks: { - beginAtZero: true - } - }], - xAxes: [{ - scaleLabel: { - //display: true, - //labelString: "here" - } - }] - } + // if (result.isEnabled) { + document.addEventListener('DOMContentLoaded', function () { + const imgUrl = chrome.runtime.getURL('../assets/images/tufast48.png') + const rawGrades = parseGrades() + $("table[summary!='Liste der Stammdaten des Studierenden']").parent().eq(2).children().eq(3).after( + 'Deine Durchschnittnote (nach CP gewichtet): ' + getWeightedAverage(rawGrades) + '
Anzahl Module: ' + rawGrades.filter(x => x.isModule).length + '
Anzahl Prüfungen: ' + rawGrades.filter(x => !x.isModule).length + '
powered by TUfast (entwickelt von Noxdor, Daniel)
Wechsle zur ... nocht nicht für Firefox!
' + ) + const ctx = document.getElementById('myChart').getContext('2d') + ctx.canvas.width = 500 + ctx.canvas.height = 250 + // eslint-disable-next-line no-unused-vars, no-undef + const myChart = new Chart(ctx, { + type: 'bar', + data: { + labels: ['1', '2', '3', '4', 'nicht bestanden'], + datasets: [{ + data: countGrades(rawGrades.filter(x => !x.isModule)), + backgroundColor: [ + '#0b2a51', + '#0b2a51', + '#0b2a51', + '#0b2a51', + '#0b2a51' + ], + borderColor: [ + '#0b2a51' + ], + borderWidth: 1 + }] + }, + options: { + responsive: false, + maintainAspectRatio: false, + legend: { + display: false + }, + scales: { + yAxes: [{ + ticks: { + beginAtZero: true } - }) - + }], + xAxes: [{ + scaleLabel: { + // display: true, + // labelString: "here" + } + }] + } + } }) - //} + }) + // } }) -//returns: [{grade: X.X, isModule: true}] -//only count subjects (not modules!) -function parseGrades() { - var grades = [] - var $ = this.$ - - $("table[summary!='Liste der Stammdaten des Studierenden'] > tbody").children().each(function () { +// returns: [{grade: X.X, isModule: true}] +// only count subjects (not modules!) +function parseGrades () { + const grades = [] + const $ = this.$ - //Skip stuff - if ($(this).children().attr('class') === 'Konto' || + $("table[summary!='Liste der Stammdaten des Studierenden'] > tbody").children().each(function () { + // Skip stuff + if ($(this).children().attr('class') === 'Konto' || $(this).children().attr('class') === 'tabelleheader' || $(this).children().eq(1).text().trim() === 'Zurück' || $(this).children().eq(1).attr('bgcolor') === '#ADADAD' - ) { return true } + ) { return true } - let grade = $(this).children().eq(3).text().trim() - let isModule = $(this).children().eq(1).attr('bgcolor') == '#DDDDDD' - let exam = $(this).children().eq(1).text().trim() - let weight = isModule ? parseInt($(this).children().eq(7).text().trim()) : 0 + let grade = $(this).children().eq(3).text().trim() + const isModule = $(this).children().eq(1).attr('bgcolor') === '#DDDDDD' + const exam = $(this).children().eq(1).text().trim() + let weight = isModule ? parseInt($(this).children().eq(7).text().trim()) : 0 + if (isNaN(weight)) { weight = 0 } - //Skip more stuff - if (exam === '') { return true } - if (exam === 'Gesamtnote Zwischenprüfung') { return } - if (grade === "") { return } - else { grade = parseFloat(grade.replace(",", ".")) } + // Skip more stuff + if (exam === '') { return true } + if (exam === 'Gesamtnote Zwischenprüfung') { return } + if (grade === '') { return } else { grade = parseFloat(grade.replace(',', '.')) } - grades.push({ 'grade': grade, 'isModule': isModule, 'weight': weight }) - }) - return grades + grades.push({ grade: grade, isModule: isModule, weight: weight }) + }) + return grades } -//return counted number of rounded grades for display -//also showing failed exams which were passed later -function countGrades(rawGrades) { - var gradesCount = [0, 0, 0, 0, 0] - rawGrades.forEach(function (info) { - let grade = Math.round(info.grade) - switch (grade) { - case 1: - gradesCount[0] = gradesCount[0] + 1 - break - case 2: - gradesCount[1] = gradesCount[1] + 1 - break - case 3: - gradesCount[2] = gradesCount[2] + 1 - break - case 4: - gradesCount[3] = gradesCount[3] + 1 - break - case 5: - gradesCount[4] = gradesCount[4] + 1 - break - default: - break - } - }) - return gradesCount +// return counted number of rounded grades for display +// also showing failed exams which were passed later +function countGrades (rawGrades) { + const gradesCount = [0, 0, 0, 0, 0] + rawGrades.forEach(function (info) { + const grade = Math.round(info.grade) + switch (grade) { + case 1: + gradesCount[0] = gradesCount[0] + 1 + break + case 2: + gradesCount[1] = gradesCount[1] + 1 + break + case 3: + gradesCount[2] = gradesCount[2] + 1 + break + case 4: + gradesCount[3] = gradesCount[3] + 1 + break + case 5: + gradesCount[4] = gradesCount[4] + 1 + break + default: + break + } + }) + return gradesCount } -//return arithmetic grade average -//not counting failed exams! -function getArithAverage(rawGrades) { - //first get all grade-objects that aren't failed, then map it directly as number - let grades = rawGrades.filter(x => x.grade !== 5.0 && !x.isModule).map(x => x.grade) - return grades.length ? (grades.reduce((acc, value) => acc + value, 0) / grades.length).toFixed(1) : 0 -} +// return arithmetic grade average +// not counting failed exams! +// function getArithAverage (rawGrades) { +// // first get all grade-objects that aren't failed, then map it directly as number +// const grades = rawGrades.filter(x => x.grade !== 5.0 && !x.isModule).map(x => x.grade) +// return grades.length ? (grades.reduce((acc, value) => acc + value, 0) / grades.length).toFixed(1) : 0 +// } -//return weighted grade average -//not counting failed modules! -function getWeightedAverage(rawGrades) { - let grades = rawGrades.filter(x => x.grade !== 5.0 && x.isModule) - let totalWeight = grades.reduce((acc, value) => acc + value.weight, 0) - return totalWeight ? (grades.reduce((acc, value) => acc + value.grade * value.weight, 0) / totalWeight).toFixed(1) : 0 +// return weighted grade average +// not counting failed modules! +function getWeightedAverage (rawGrades) { + const grades = rawGrades.filter(x => x.grade !== 5.0 && x.isModule) + const totalWeight = grades.reduce((acc, value) => acc + value.weight, 0) // BUG: + return totalWeight ? (grades.reduce((acc, value) => acc + value.grade * value.weight, 0) / totalWeight).toFixed(1) : 0 } -undefined \ No newline at end of file +// eslint-disable-next-line no-unused-expressions +undefined diff --git a/src/contentScripts/pimpHISQIS_table.js b/src/contentScripts/pimpHISQIS_table.js index 639c55a9..83e5383d 100644 --- a/src/contentScripts/pimpHISQIS_table.js +++ b/src/contentScripts/pimpHISQIS_table.js @@ -1,107 +1,114 @@ -console.log("pimping table ... maybe :)") +console.log('pimping table ... maybe :)') -//this needs to be done first -let oldTable = document.getElementsByTagName('table')[2] -let changeTableLink = document.getElementById("changeTableLink") +// this needs to be done first +const oldTable = document.getElementsByTagName('table')[2] +const changeTableLink = document.getElementById('changeTableLink') -//insert pimped table with style display:none +// insert pimped table with style display:none getGradesFromTable() -let pimpedTable = document.getElementById('pimpedTable') +const pimpedTable = document.getElementById('pimpedTable') -//check if hisqisPimpedTable is activated +// check if hisqisPimpedTable is activated chrome.storage.local.get(['hisqisPimpedTable'], function (result) { - result.hisqisPimpedTable ? setPimpedTable() : setOldTable() + result.hisqisPimpedTable ? setPimpedTable() : setOldTable() }) -//listen for event for switching table +// listen for event for switching table changeTableLink.onclick = function () { - let pimpedTableActivated = pimpedTable.style.display == "none" ? false : true + const pimpedTableActivated = pimpedTable.style.display !== 'none' - //switch table - pimpedTableActivated ? setOldTable() : setPimpedTable() - //store permanently - chrome.storage.local.set({ hisqisPimpedTable: !pimpedTableActivated }, function () { }) + // switch table + pimpedTableActivated ? setOldTable() : setPimpedTable() + // store permanently + chrome.storage.local.set({ hisqisPimpedTable: !pimpedTableActivated }, function () { }) } -function setPimpedTable() { - oldTable.style.display = "none" - pimpedTable.style.display = "block" - changeTableLink.innerHTML = "langweiligen, alten Tabelle." +function setPimpedTable () { + oldTable.style.display = 'none' + pimpedTable.style.display = 'block' + changeTableLink.innerHTML = 'langweiligen, alten Tabelle.' } -function setOldTable() { - oldTable.style.display = "block" - pimpedTable.style.display = "none" - changeTableLink.innerHTML = "neuen, coolen TUfast-Tabelle 🔥 (Beta)." +function setOldTable () { + oldTable.style.display = 'block' + pimpedTable.style.display = 'none' + changeTableLink.innerHTML = 'neuen, coolen TUfast-Tabelle 🔥 (Beta).' } -function getGradesFromTable() { - // create container for vuejs table - const container = document.createElement('div') - container.id = 'container' - const atable = document.getElementsByTagName('table')[2] - // console.log(atable); - atable.insertAdjacentElement('afterend', container) - container.innerHTML = table_html - - let table = [] - // second table is the grade table - // first table row index with useful information: 2 - const tableRows = [...document.getElementsByTagName('tbody')[2].getElementsByTagName('tr')] - - // collect all data from the table - tableRows.forEach((row) => { - let new_row = []; - [...row.cells].forEach((table_data) => { - if (table_data.lastElementChild === null) { - new_row.push(table_data.innerHTML.trim().replace(/&.*;/, '')) - } else { - new_row.push(table_data.lastElementChild.innerHTML.trim().replace(/&.*;/, '')) - } - }) - table.push(new_row) +function getGradesFromTable () { + // create container for vuejs table + const container = document.createElement('div') + container.id = 'container' + const atable = document.getElementsByTagName('table')[2] + // console.log(atable); + atable.insertAdjacentElement('afterend', container) + container.innerHTML = table_html + + const table = [] + // second table is the grade table + // first table row index with useful information: 2 + const tableRows = [...document.getElementsByTagName('tbody')[2].getElementsByTagName('tr')] + + // collect all data from the table + tableRows.forEach((row) => { + const newRow = []; + [...row.cells].forEach((tableData) => { + if (tableData.lastElementChild === null) { + newRow.push(tableData.innerHTML.trim().replace(/&.*;/, '')) + } else { + newRow.push(tableData.lastElementChild.innerHTML.trim().replace(/&.*;/, '')) + } }) - - //if first row is Prüfungsnr., then we need to add dummy row[0] - //this is required, because there are two different views of the hisqis table, dependent on how you navigate there - if (table[0][0] == "Prüfungsnr.") table.unshift(["dummy"]) - - // remove that ugly table from the page - oldTable.style.display = 'none' - - let levels = { - mainLevel: [], - moduleLevel: [], - examLevel: [] - } + table.push(newRow) + }) + + // if first row is Prüfungsnr., then we need to add dummy row[0] + // this is required, because there are two different views of the hisqis table, dependent on how you navigate there + if (table[0][0] === 'Prüfungsnr.') table.unshift(['dummy']) + + // remove that ugly table from the page + oldTable.style.display = 'none' - // Logic to figure out which row is a section, module or exam - table.filter((row, index) => row[0][1] === '0' || parseInt(row[0]) < 1000 ? levels.mainLevel.push(index) : []) - table.filter((row, index) => row[0].slice(-2)[0] === '0' && levels.mainLevel.indexOf(index) < 0 ? levels.moduleLevel.push(index) : []) - table.filter((row, index) => levels.mainLevel.indexOf(index) < 0 && levels.moduleLevel.indexOf(index) < 0 && index > 2 ? levels.examLevel.push(index) : []) + const levels = { + mainLevel: [], + moduleLevel: [], + examLevel: [] + } - runVue(table, levels) + // Logic to figure out which row is a section, module or exam + table.filter((row, index) => row[0][1] === '0' || parseInt(row[0]) < 1000 ? levels.mainLevel.push(index) : []) + table.filter((row, index) => row[0].slice(-2)[0] === '0' && levels.mainLevel.indexOf(index) < 0 ? levels.moduleLevel.push(index) : []) + table.filter((row, index) => levels.mainLevel.indexOf(index) < 0 && levels.moduleLevel.indexOf(index) < 0 && index > 2 ? levels.examLevel.push(index) : []) + + runVue(table, levels) } // Vue.js logic, attaches Vue to the new container under the old table and draws the new table -function runVue(table, levels) { - new Vue({ - el: '#container', - data: { - table, - levels, - }, - methods: { - getColour(row_index, row) { - row_index += 2 - const passedText = row[5] - return this.levels.mainLevel.indexOf(row_index) > -1 ? 'dark' : - this.levels.moduleLevel.indexOf(row_index) > -1 ? 'primary' : - passedText === '' ? 'dark' : passedText === 'bestanden' ? 'success' : - passedText === 'in Bearbeitung' ? 'warn' : 'danger' - } - } - }) +function runVue (table, levels) { + // eslint-disable-next-line no-new, no-undef + new Vue({ + el: '#container', + data: { + table, + levels + }, + methods: { + getColour (rowIndex, row) { + rowIndex += 2 + const passedText = row[5] + return this.levels.mainLevel.indexOf(rowIndex) > -1 + ? 'dark' + : this.levels.moduleLevel.indexOf(rowIndex) > -1 + ? 'primary' + : passedText === '' + ? 'dark' + : passedText === 'bestanden' + ? 'success' + : passedText === 'in Bearbeitung' ? 'warn' : 'danger' + } + } + }) } +// eslint-disable-next-line no-unused-expressions undefined diff --git a/src/contentScripts/qis.js b/src/contentScripts/qis.js index 37f1c2af..37418476 100644 --- a/src/contentScripts/qis.js +++ b/src/contentScripts/qis.js @@ -13,30 +13,28 @@ chrome.storage.local.get(['isEnabled', 'loggedOutQis'], function (result) { } }) -function loginQis(isEnabled) { - if (document.getElementsByTagName('a')[4].innerText === "Ich habe die Nutzungsbedingungen gelesen, verstanden und akzeptiert. >>>") { +function loginQis (isEnabled) { + if (document.getElementsByTagName('a')[4].innerText === 'Ich habe die Nutzungsbedingungen gelesen, verstanden und akzeptiert. >>>') { document.getElementsByTagName('a')[4].click() - chrome.runtime.sendMessage({ cmd: "save_clicks", click_count: 1 }) - } else if (document.getElementById("asdf") && isEnabled) { + chrome.runtime.sendMessage({ cmd: 'save_clicks', click_count: 1 }) + } else if (document.getElementById('asdf') && isEnabled) { chrome.runtime.sendMessage({ cmd: 'get_user_data' }, function (result) { if (!(result.asdf === undefined || result.fdsa === undefined)) { - chrome.runtime.sendMessage({ cmd: "show_ok_badge", timeout: 2000 }) - chrome.runtime.sendMessage({ cmd: "save_clicks", click_count: 1 }) - chrome.runtime.sendMessage({ cmd: "perform_login" }) + chrome.runtime.sendMessage({ cmd: 'show_ok_badge', timeout: 2000 }) + chrome.runtime.sendMessage({ cmd: 'save_clicks', click_count: 1 }) + chrome.runtime.sendMessage({ cmd: 'perform_login' }) document.getElementById('asdf').value = result.asdf document.getElementById('fdsa').value = result.fdsa document.getElementsByName('submit')[0].click() } else { - chrome.runtime.sendMessage({ cmd: "no_login_data" }); + chrome.runtime.sendMessage({ cmd: 'no_login_data' }) } - }); + }) } - //abmelden button + // abmelden button if (document.querySelectorAll('#visual-footer-wrapper :nth-child(5)')[0]) { document.querySelectorAll('#visual-footer-wrapper :nth-child(5)')[0].addEventListener('click', function () { chrome.runtime.sendMessage({ cmd: 'logged_out', portal: 'loggedOutQis' }) }) } } - - diff --git a/src/contentScripts/selma.js b/src/contentScripts/selma.js index 3762c699..a0731ce8 100644 --- a/src/contentScripts/selma.js +++ b/src/contentScripts/selma.js @@ -1,32 +1,32 @@ chrome.storage.local.get(['isEnabled', 'loggedOutSelma'], function (result) { - if (result.isEnabled && !result.loggedOutSelma) { - document.addEventListener('DOMContentLoaded', function () { - if (document.getElementById('field_user')) { - chrome.runtime.sendMessage({ cmd: 'get_user_data' }, function (response) { - if (!(response.asdf === undefined || response.fdsa === undefined)) { - chrome.runtime.sendMessage({ cmd: "show_ok_badge", timeout: 2000 }) - chrome.runtime.sendMessage({ cmd: "save_clicks", click_count: 1 }) - chrome.runtime.sendMessage({ cmd: "perform_login" }) - document.getElementById('field_user').value = response.asdf - document.getElementById('field_pass').value = response.fdsa - document.getElementById('logIn_btn').click() - } else { - chrome.runtime.sendMessage({ cmd: "no_login_data" }); - } - }) - } - //abmelden button - if (document.getElementById('logOut_btn')) { - document.getElementById('logOut_btn').addEventListener('click', function () { - chrome.runtime.sendMessage({ cmd: 'logged_out', portal: 'loggedOutSelma' }) - }) - } + if (result.isEnabled && !result.loggedOutSelma) { + document.addEventListener('DOMContentLoaded', function () { + if (document.getElementById('field_user')) { + chrome.runtime.sendMessage({ cmd: 'get_user_data' }, function (response) { + if (!(response.asdf === undefined || response.fdsa === undefined)) { + chrome.runtime.sendMessage({ cmd: 'show_ok_badge', timeout: 2000 }) + chrome.runtime.sendMessage({ cmd: 'save_clicks', click_count: 1 }) + chrome.runtime.sendMessage({ cmd: 'perform_login' }) + document.getElementById('field_user').value = response.asdf + document.getElementById('field_pass').value = response.fdsa + document.getElementById('logIn_btn').click() + } else { + chrome.runtime.sendMessage({ cmd: 'no_login_data' }) + } }) - console.log('Auto Login to Selma.') - //page is reloaded two times - } else if (result.loggedOutSelma) { - chrome.storage.local.set({ loggedOutSelma: undefined }, function () { }) - } else if (result.loggedOutSelma === undefined) { - chrome.storage.local.set({ loggedOutSelma: false }, function () { }) - } -}) \ No newline at end of file + } + // abmelden button + if (document.getElementById('logOut_btn')) { + document.getElementById('logOut_btn').addEventListener('click', function () { + chrome.runtime.sendMessage({ cmd: 'logged_out', portal: 'loggedOutSelma' }) + }) + } + }) + console.log('Auto Login to Selma.') + // page is reloaded two times + } else if (result.loggedOutSelma) { + chrome.storage.local.set({ loggedOutSelma: undefined }, function () { }) + } else if (result.loggedOutSelma === undefined) { + chrome.storage.local.set({ loggedOutSelma: false }, function () { }) + } +}) diff --git a/src/contentScripts/tex.js b/src/contentScripts/tex.js index f67c32ca..455a86bd 100644 --- a/src/contentScripts/tex.js +++ b/src/contentScripts/tex.js @@ -1,31 +1,30 @@ -chrome.storage.local.get(["loggedOutTex"], function (result) { - if (!result.loggedOutTex) { - //there is only a button and no reason to not click - document.addEventListener('DOMContentLoaded', function () { - document.querySelectorAll("a[href='/saml/login/go']")[0].click(); - console.log("Auto Login to tex."); - }) - } else if (result.loggedOutTex) { - chrome.storage.local.set({ loggedOutTex: false }, function () { }); - } -}); +chrome.storage.local.get(['loggedOutTex'], function (result) { + if (!result.loggedOutTex) { + // there is only a button and no reason to not click + document.addEventListener('DOMContentLoaded', function () { + document.querySelectorAll("a[href='/saml/login/go']")[0].click() + console.log('Auto Login to tex.') + }) + } else if (result.loggedOutTex) { + chrome.storage.local.set({ loggedOutTex: false }, function () { }) + } +}) document.addEventListener('DOMContentLoaded', function () { - //add event listener for log-out button - var buttons = document.querySelectorAll( - "button.btn-link.text-left.dropdown-menu-button" - ); - for (var i = 0; i < buttons.length; i++) { - if (buttons[i].innerHTML.indexOf("Log Out") != -1) { - //listen for click - buttons[i].addEventListener("click", function () { - chrome.runtime.sendMessage({ - cmd: "logged_out", - portal: "loggedOutTex", - }); - console.log("Logged out Tex") - }); - } + // add event listener for log-out button + const buttons = document.querySelectorAll( + 'button.btn-link.text-left.dropdown-menu-button' + ) + for (let i = 0; i < buttons.length; i++) { + if (buttons[i].innerHTML.indexOf('Log Out') !== -1) { + // listen for click + buttons[i].addEventListener('click', function () { + chrome.runtime.sendMessage({ + cmd: 'logged_out', + portal: 'loggedOutTex' + }) + console.log('Logged out Tex') + }) } + } }) - diff --git a/src/contentScripts/tumed.js b/src/contentScripts/tumed.js index c8af80af..ea69a9f0 100644 --- a/src/contentScripts/tumed.js +++ b/src/contentScripts/tumed.js @@ -1,23 +1,24 @@ chrome.storage.local.get(['isEnabled', 'loggedOutTumed'], function (result) { if (result.isEnabled && !result.loggedOutTumed) { document.addEventListener('DOMContentLoaded', function () { - if (document.querySelectorAll(`label[for=__ac_name]`)[0].textContent === "Benutzername" && document.querySelectorAll(`label[for=__ac_password]`)[0].textContent === "Passwort" && document.getElementById("__ac_name") && document.getElementById("__ac_password")) + if (document.querySelectorAll('label[for=__ac_name]')[0].textContent === 'Benutzername' && document.querySelectorAll('label[for=__ac_password]')[0].textContent === 'Passwort' && document.getElementById('__ac_name') && document.getElementById('__ac_password')) { chrome.runtime.sendMessage({ cmd: 'get_user_data' }, function (result) { if (!(result.asdf === undefined || result.fdsa === undefined)) { - chrome.runtime.sendMessage({ cmd: "show_ok_badge", timeout: 2000 }) - chrome.runtime.sendMessage({ cmd: "save_clicks", click_count: 2 }) - chrome.runtime.sendMessage({ cmd: "perform_login" }) - document.getElementById("__ac_name").value = result.asdf - document.getElementById("__ac_password").value = result.fdsa - document.querySelectorAll(`input[value=Anmelden]`)[0].click() + chrome.runtime.sendMessage({ cmd: 'show_ok_badge', timeout: 2000 }) + chrome.runtime.sendMessage({ cmd: 'save_clicks', click_count: 2 }) + chrome.runtime.sendMessage({ cmd: 'perform_login' }) + document.getElementById('__ac_name').value = result.asdf + document.getElementById('__ac_password').value = result.fdsa + document.querySelectorAll('input[value=Anmelden]')[0].click() } else { - chrome.runtime.sendMessage({ cmd: "no_login_data" }); + chrome.runtime.sendMessage({ cmd: 'no_login_data' }) } }) + } - //abmelden button - if (document.getElementById("personaltools-logout")) { - document.getElementById("personaltools-logout").addEventListener('click', function () { + // abmelden button + if (document.getElementById('personaltools-logout')) { + document.getElementById('personaltools-logout').addEventListener('click', function () { chrome.runtime.sendMessage({ cmd: 'logged_out', portal: 'loggedOutTumed' }) }) } @@ -26,4 +27,4 @@ chrome.storage.local.get(['isEnabled', 'loggedOutTumed'], function (result) { } else if (result.loggedOutTumed) { chrome.storage.local.set({ loggedOutTumed: false }, function () { }) } -}) \ No newline at end of file +}) diff --git a/src/freshContent/gradeTable.js b/src/freshContent/gradeTable.js index 06dec4c5..6463be6a 100644 --- a/src/freshContent/gradeTable.js +++ b/src/freshContent/gradeTable.js @@ -1,3 +1,5 @@ +/* eslint-disable */ + const table_html = '