diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..aa8f7a5 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,14 @@ +{ + "env": { + "jest": true + }, + "globals": { + "document": true + }, + "extends": ["airbnb-base", "prettier"], + "plugins": ["prettier"], + "parser": "babel-eslint", + "rules": { + "class-methods-use-this": "off" + } +} diff --git a/CHANGELOG.md b/CHANGELOG.md index a0f80f9..6f1e5af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# 1.3.0 (2019-06-09) + +### Added + +- Ability to show user's gold and exp points on the board bar. Ability to receive notifications when gained/lose exp and gold. ([2633ace](https://github.com/alexktzk/trello-habitica/commit/2633ace)) +- Ability to show to-do difficulty on a card badge with icons from Habitica. ([0467373](https://github.com/alexktzk/trello-habitica/commit/0467373)) + # 1.2.0 (2019-05-16) ### Added diff --git a/README.md b/README.md index 432a9c2..9535117 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,7 @@ Now it's time to fill in the form. 2. To work properly this Power-Up requires some permissions. Make sure you've checked all of the following: - board-buttons -- callback - card-badges -- card-buttons - card-detail-badges - list-actions diff --git a/package-lock.json b/package-lock.json index 1ef57dc..a05049a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1428,6 +1428,12 @@ } } }, + "acorn-jsx": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.1.tgz", + "integrity": "sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==", + "dev": true + }, "acorn-walk": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.1.tgz", @@ -1560,6 +1566,16 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, + "array-includes": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz", + "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.7.0" + } + }, "array-union": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", @@ -1696,6 +1712,91 @@ "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", "dev": true }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "babel-eslint": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.0.1.tgz", + "integrity": "sha512-z7OT1iNV+TjOwHNLLyJk+HN+YVWX+CLE6fPD2SymJZOZQBs+QIexFjhm4keGTm8MW9xr4EC9Q0PbaLB24V5GoQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.0.0", + "@babel/traverse": "^7.0.0", + "@babel/types": "^7.0.0", + "eslint-scope": "3.7.1", + "eslint-visitor-keys": "^1.0.0" + }, + "dependencies": { + "eslint-scope": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", + "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + } + } + }, "babel-jest": { "version": "24.5.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.5.0.tgz", @@ -2155,6 +2256,23 @@ "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=", "dev": true }, + "caller-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", + "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", + "dev": true, + "requires": { + "callsites": "^0.2.0" + }, + "dependencies": { + "callsites": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", + "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", + "dev": true + } + } + }, "callsites": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.0.0.tgz", @@ -2228,6 +2346,12 @@ "supports-color": "^5.3.0" } }, + "chardet": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", + "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", + "dev": true + }, "chokidar": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.2.tgz", @@ -2287,6 +2411,12 @@ "safe-buffer": "^5.0.1" } }, + "circular-json": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", + "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", + "dev": true + }, "class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", @@ -2327,6 +2457,21 @@ } } }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "dev": true + }, "cliui": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", @@ -2551,6 +2696,12 @@ "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", "dev": true }, + "contains-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", + "dev": true + }, "content-disposition": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", @@ -3085,6 +3236,15 @@ "buffer-indexof": "^1.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" + } + }, "dom-converter": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", @@ -3253,80 +3413,491 @@ "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", "dev": true, "requires": { - "es-to-primitive": "^1.2.0", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "is-callable": "^1.1.4", - "is-regex": "^1.0.4", - "object-keys": "^1.0.12" + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-keys": "^1.0.12" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "escodegen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.1.tgz", + "integrity": "sha512-JwiqFD9KdGVVpeuRa68yU3zZnBEOcPs0nKW7wZzXky8Z7tffdYUHbe11bPCV5jYlK6DVdKLWLm0f5I/QlL0Kmw==", + "dev": true, + "requires": { + "esprima": "^3.1.3", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + }, + "dependencies": { + "esprima": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + } + } + }, + "eslint": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.3.0.tgz", + "integrity": "sha512-N/tCqlMKkyNvAvLu+zI9AqDasnSLt00K+Hu8kdsERliC9jYEc8ck12XtjvOXrBKu8fK6RrBcN9bat6Xk++9jAg==", + "dev": true, + "requires": { + "ajv": "^6.5.0", + "babel-code-frame": "^6.26.0", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^3.1.0", + "doctrine": "^2.1.0", + "eslint-scope": "^4.0.0", + "eslint-utils": "^1.3.1", + "eslint-visitor-keys": "^1.0.0", + "espree": "^4.0.0", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^2.0.0", + "functional-red-black-tree": "^1.0.1", + "glob": "^7.1.2", + "globals": "^11.7.0", + "ignore": "^4.0.2", + "imurmurhash": "^0.1.4", + "inquirer": "^5.2.0", + "is-resolvable": "^1.1.0", + "js-yaml": "^3.11.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.5", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.2", + "path-is-inside": "^1.0.2", + "pluralize": "^7.0.0", + "progress": "^2.0.0", + "regexpp": "^2.0.0", + "require-uncached": "^1.0.3", + "semver": "^5.5.0", + "string.prototype.matchall": "^2.0.0", + "strip-ansi": "^4.0.0", + "strip-json-comments": "^2.0.1", + "table": "^4.0.3", + "text-table": "^0.2.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "eslint-config-airbnb": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-17.1.0.tgz", + "integrity": "sha512-R9jw28hFfEQnpPau01NO5K/JWMGLi6aymiF6RsnMURjTk+MqZKllCqGK/0tOvHkPi/NWSSOU2Ced/GX++YxLnw==", + "dev": true, + "requires": { + "eslint-config-airbnb-base": "^13.1.0", + "object.assign": "^4.1.0", + "object.entries": "^1.0.4" + } + }, + "eslint-config-airbnb-base": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-13.1.0.tgz", + "integrity": "sha512-XWwQtf3U3zIoKO1BbHh6aUhJZQweOwSt4c2JrPDg9FP3Ltv3+YfEv7jIDB8275tVnO/qOHbfuYg3kzw6Je7uWw==", + "dev": true, + "requires": { + "eslint-restricted-globals": "^0.1.1", + "object.assign": "^4.1.0", + "object.entries": "^1.0.4" + } + }, + "eslint-config-prettier": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-4.3.0.tgz", + "integrity": "sha512-sZwhSTHVVz78+kYD3t5pCWSYEdVSBR0PXnwjDRsUs8ytIrK8PLXw+6FKp8r3Z7rx4ZszdetWlXYKOHoUrrwPlA==", + "dev": true, + "requires": { + "get-stdin": "^6.0.0" + } + }, + "eslint-import-resolver-node": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz", + "integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "resolve": "^1.5.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" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "eslint-module-utils": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.4.0.tgz", + "integrity": "sha512-14tltLm38Eu3zS+mt0KvILC3q8jyIAH518MlG+HO0p+yK885Lb1UHTY/UgR91eOyGdmxAPb+OLoW4znqIT6Ndw==", + "dev": true, + "requires": { + "debug": "^2.6.8", + "pkg-dir": "^2.0.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" + } + }, + "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" + } + }, + "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" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "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 + }, + "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" + } + } + } + }, + "eslint-plugin-import": { + "version": "2.17.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.17.2.tgz", + "integrity": "sha512-m+cSVxM7oLsIpmwNn2WXTJoReOF9f/CtLMo7qOVmKd1KntBy0hEcuNZ3erTmWjx+DxRO0Zcrm5KwAvI9wHcV5g==", + "dev": true, + "requires": { + "array-includes": "^3.0.3", + "contains-path": "^0.1.0", + "debug": "^2.6.9", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "^0.3.2", + "eslint-module-utils": "^2.4.0", + "has": "^1.0.3", + "lodash": "^4.17.11", + "minimatch": "^3.0.4", + "read-pkg-up": "^2.0.0", + "resolve": "^1.10.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": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "isarray": "^1.0.0" + } + }, + "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" + } + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.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" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "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 + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + } + } + } + }, + "eslint-plugin-prettier": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.0.tgz", + "integrity": "sha512-XWX2yVuwVNLOUhQijAkXz+rMPPoCr7WFiAl8ig6I7Xn+pPVhDhzg4DxHpmbeb0iqjO9UronEA3Tb09ChnFVHHA==", + "dev": true, + "requires": { + "prettier-linter-helpers": "^1.0.0" } }, - "es-to-primitive": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", - "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "eslint-restricted-globals": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz", + "integrity": "sha1-NfDVy8ZMLj7WLpO0saevBbp+1Nc=", + "dev": true + }, + "eslint-scope": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.2.tgz", + "integrity": "sha512-5q1+B/ogmHl8+paxtOKx38Z8LtWkVGuNt3+GQNErqwLl6ViNp/gdJGMCjZNxZ8j/VYjDNZ2Fo+eQc1TAVPIzbg==", "dev": true, "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" } }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + "eslint-utils": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz", + "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==", + "dev": true }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", "dev": true }, - "escodegen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.1.tgz", - "integrity": "sha512-JwiqFD9KdGVVpeuRa68yU3zZnBEOcPs0nKW7wZzXky8Z7tffdYUHbe11bPCV5jYlK6DVdKLWLm0f5I/QlL0Kmw==", + "espree": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-4.1.0.tgz", + "integrity": "sha512-I5BycZW6FCVIub93TeVY1s7vjhP9CY6cXCznIRfiig7nRviKZYdRnj/sHEWC6A7WE9RDWOFq9+7OsWSYz8qv2w==", "dev": true, "requires": { - "esprima": "^3.1.3", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" + "acorn": "^6.0.2", + "acorn-jsx": "^5.0.0", + "eslint-visitor-keys": "^1.0.0" }, "dependencies": { - "esprima": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", + "acorn": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", + "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==", "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true } } }, - "eslint-scope": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.2.tgz", - "integrity": "sha512-5q1+B/ogmHl8+paxtOKx38Z8LtWkVGuNt3+GQNErqwLl6ViNp/gdJGMCjZNxZ8j/VYjDNZ2Fo+eQc1TAVPIzbg==", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, "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.0.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", + "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", + "dev": true, + "requires": { + "estraverse": "^4.0.0" + } + }, "esrecurse": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", @@ -3818,6 +4389,17 @@ } } }, + "external-editor": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", + "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", + "dev": true, + "requires": { + "chardet": "^0.4.0", + "iconv-lite": "^0.4.17", + "tmp": "^0.0.33" + } + }, "extglob": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", @@ -3895,6 +4477,12 @@ "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", "dev": true }, + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, "fast-glob": { "version": "2.2.6", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.6.tgz", @@ -3965,6 +4553,25 @@ "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==", "dev": true }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", + "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", + "dev": true, + "requires": { + "flat-cache": "^1.2.1", + "object-assign": "^4.0.1" + } + }, "fileset": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz", @@ -4041,6 +4648,18 @@ } } }, + "flat-cache": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz", + "integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==", + "dev": true, + "requires": { + "circular-json": "^0.3.1", + "graceful-fs": "^4.1.2", + "rimraf": "~2.6.2", + "write": "^0.2.1" + } + }, "flush-write-stream": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", @@ -4685,12 +5304,24 @@ "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-caller-file": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", "dev": true }, + "get-stdin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", + "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", + "dev": true + }, "get-stream": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", @@ -4873,6 +5504,23 @@ "function-bind": "^1.1.1" } }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + } + } + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -5227,6 +5875,44 @@ "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true }, + "inquirer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz", + "integrity": "sha512-E9BmnJbAKLPGonz0HeWHtbKf+EeSP93paWO3ZYoUpq/aowXvYGjjCSuashhXPpzbArIjBbji39THkxTz9ZeEUQ==", + "dev": true, + "requires": { + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.0", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^2.1.0", + "figures": "^2.0.0", + "lodash": "^4.3.0", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^5.5.2", + "string-width": "^2.1.0", + "strip-ansi": "^4.0.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, "internal-ip": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.2.0.tgz", @@ -5469,6 +6155,12 @@ "isobject": "^3.0.1" } }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true + }, "is-regex": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", @@ -5478,6 +6170,12 @@ "has": "^1.0.1" } }, + "is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", + "dev": true + }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", @@ -6146,6 +6844,12 @@ "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 + }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", @@ -6740,6 +7444,12 @@ "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", "dev": true }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, "nan": { "version": "2.12.1", "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz", @@ -6997,6 +7707,30 @@ "isobject": "^3.0.0" } }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "object.entries": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.0.tgz", + "integrity": "sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.12.0", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, "object.getownpropertydescriptors": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", @@ -7044,6 +7778,15 @@ "wrappy": "1" } }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, "opn": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/opn/-/opn-5.4.0.tgz", @@ -7111,6 +7854,12 @@ "mem": "^4.0.0" } }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, "p-defer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", @@ -7362,6 +8111,12 @@ "find-up": "^3.0.0" } }, + "pluralize": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", + "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", + "dev": true + }, "pn": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", @@ -7414,6 +8169,15 @@ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", "dev": true }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "requires": { + "fast-diff": "^1.1.2" + } + }, "pretty-error": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", @@ -7452,6 +8216,12 @@ "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "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 + }, "promise-inflight": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", @@ -7716,6 +8486,21 @@ "integrity": "sha512-nUmxvfJyAODw+0B13hj8CFVAxhe7fDEAgJgaotBu3nnR+IgGgZq59YedJP5VYTlkEfqjuK6TuRpnymKdatLZfQ==", "dev": true }, + "regexp.prototype.flags": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.2.0.tgz", + "integrity": "sha512-ztaw4M1VqgMwl9HlPpOuiYgItcHlunW0He2fE6eNfT6E/CF2FtYi9ofOYe4mKntstYk0Fyh/rDRBdS3AnxjlrA==", + "dev": true, + "requires": { + "define-properties": "^1.1.2" + } + }, + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "dev": true + }, "regexpu-core": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.5.4.tgz", @@ -7885,6 +8670,24 @@ "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", "dev": true }, + "require-uncached": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", + "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", + "dev": true, + "requires": { + "caller-path": "^0.1.0", + "resolve-from": "^1.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", + "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", + "dev": true + } + } + }, "requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", @@ -7931,6 +8734,16 @@ "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", "dev": true }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, "ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", @@ -7962,6 +8775,15 @@ "integrity": "sha512-OfWGQTb9vnwRjwtA2QwpG2ICclHC3pgXZO5xt8H2EfgDquO0qVdSb5T88L4qJVAEugbS56pAuV4XZM58UX8ulw==", "dev": true }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "dev": true, + "requires": { + "is-promise": "^2.1.0" + } + }, "run-queue": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", @@ -7971,6 +8793,15 @@ "aproba": "^1.1.1" } }, + "rxjs": { + "version": "5.5.12", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.12.tgz", + "integrity": "sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw==", + "dev": true, + "requires": { + "symbol-observable": "1.0.1" + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -8184,6 +9015,15 @@ "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", "dev": true }, + "slice-ansi": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", + "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0" + } + }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -8643,6 +9483,19 @@ } } }, + "string.prototype.matchall": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-2.0.0.tgz", + "integrity": "sha512-WoZ+B2ypng1dp4iFLF2kmZlwwlE19gmjgKuhL1FJfDgCREWb3ye3SDVHSzLH6bxfnvYmkCxbzkmWcQZHA4P//Q==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.10.0", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "regexp.prototype.flags": "^1.2.0" + } + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -8679,6 +9532,12 @@ "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=", "dev": true }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -8688,12 +9547,32 @@ "has-flag": "^3.0.0" } }, + "symbol-observable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", + "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=", + "dev": true + }, "symbol-tree": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz", "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=", "dev": true }, + "table": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/table/-/table-4.0.3.tgz", + "integrity": "sha512-S7rnFITmBH1EnyKcvxBh1LjYeQMmnZtCXSEbHcH6S0NoKit24ZuFO/T1vDcLdYsLQkM188PVVhQmzKIuThNkKg==", + "dev": true, + "requires": { + "ajv": "^6.0.1", + "ajv-keywords": "^3.0.0", + "chalk": "^2.1.0", + "lodash": "^4.17.4", + "slice-ansi": "1.0.0", + "string-width": "^2.1.1" + } + }, "tapable": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.1.tgz", @@ -8755,12 +9634,24 @@ "require-main-filename": "^1.0.1" } }, + "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 + }, "throat": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz", "integrity": "sha1-iQN8vJLFarGJJua6TLsgDhVnKmo=", "dev": true }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, "through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", @@ -8786,6 +9677,15 @@ "setimmediate": "^1.0.4" } }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, "tmpl": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", @@ -9602,6 +10502,15 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, + "write": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", + "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + }, "write-file-atomic": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.1.tgz", diff --git a/package.json b/package.json index 3f6696a..2abcf5c 100644 --- a/package.json +++ b/package.json @@ -37,9 +37,16 @@ "@babel/plugin-transform-runtime": "^7.3.4", "@babel/preset-env": "^7.3.4", "@babel/runtime": "^7.3.4", + "babel-eslint": "^10.0.1", "babel-jest": "^24.5.0", "babel-loader": "^8.0.5", "cpy-cli": "^2.0.0", + "eslint": "^5.3.0", + "eslint-config-airbnb": "^17.1.0", + "eslint-config-airbnb-base": "^13.1.0", + "eslint-config-prettier": "^4.3.0", + "eslint-plugin-import": "^2.17.2", + "eslint-plugin-prettier": "^3.1.0", "fetch-mock": "^7.3.0", "html-webpack-plugin": "^3.2.0", "jest": "^24.1.0", diff --git a/src/js/constants.js b/src/js/constants.js index c323a77..959da7a 100644 --- a/src/js/constants.js +++ b/src/js/constants.js @@ -1,4 +1,4 @@ -export const API = 'https://habitica.com/api/v3'; +export const BASE_URL = 'https://habitica.com/api/v3'; export const LIST_TYPES = { DONE: 'done', @@ -11,15 +11,28 @@ export const CARD_SCOPES = { }; export const ICONS = { - HABITICA: + TRELLO_LOGO: + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYBAMAAAASWSDLAAAAA3NCSVQICAjb4U/gAAAACXBIWXMAAACmAAAApgHdff84AAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAACRQTFRFAGbMA3a9A3e8Ane9A3e9GYPCR5rMa63Ud7PXvNfm4Onu7O/xNljIFwAAAAN0Uk5TBWHLjO1H6wAAAGRJREFUGNNjYFQ2hgIjAQZhYzgwZFBGcIwYjM1X794ZbDp7965iYwZjq927dxebA4nFQI41kG62ABKbacMxAxKTYRyTjI72YBgH4jiiOZZATjKMY1rR0eYM4yABVG+jBAhyUAEAulltP7yEQqgAAAAASUVORK5CYII=', + HABITICA_LOGO: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAC0UExURUdwTAAAAEEjdUEjdUIzeDQvaQAA/0EidUEjdVMiiEQndkEjdUAidUEjdUIidkEjdUEidUAidUEidEEjdUEidUEjdkAjdUMic0EjdUEjdUEjdUEidUEjdUEjdUAjdUIkdEEidEAjdUEjdUEjdUAidUMidEEjd0EidUEidUEjdUEjdUIidkEkdUEjdEEjdT4ndkEjdUIidEIjdkEjdUQlekUlfEMkeEYnfUcngEopg0UnfEwqh08GzoYAAAAzdFJOUwABn9ADBQH9/gIKwq/7QJbLj1VD33ZwD/iKslvZ9UkvUL25pE0iKoPw5Zo2HHFnGMZ8ps6c3PwAAAF2SURBVBgZZcBVluMwEEDR57hsyaCQw8zQMFSSA73/fY3TZ/7mEvEBKf8T3npIxj/SSDKAhOFjCAkQiaR8ywChqn0HMskAqs5o3nkng5R8UIe4DYzbq5m/Bf/oQgTC8L709bYfUTxuxjkXBgkpJBHF3TgfFhWbQe100j+mQArkf5yvbR36FMGpHkgjoL8GqtF5oeqqrndWw5QM2PXyjHIQT9TVZqJWjb0dEBI6BWzu3lk1qlZVXZghJJz9DobeG+eM1YapY4GE3telt2bVre83tapq1bTJEOaxfh2A9Xx6UauqJvxAQDheL2WUkJ9nS7Wq6kKJAJGM096I8eIR1GrDPd8QXoTPIb/vS2O1Ya2vEICEapuzDU5VXcNPyQCE9+4HaeyNWhPCU+s2AqTkv9YIsTdW/W4zWoYVAgibFrlQBOd8D+jfViQRZJy6Y2Drl3rlZTDnW8bnotXpPkO4+SLejafhWhY/ASKOo86+9VLOWqf9fl+Wp78TOCsCfttvbQAAAABJRU5ErkJggg==', - TASK_DOING: - 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAC0UExURUdwTAAAAEEjdUEjdUIzeDQvaQAA/0EidUEjdVMiiEQndkEjdUAidUEjdUIidkEjdUEidUAidUEidEEjdUEidUEjdkAjdUMic0EjdUEjdUEjdUEidUEjdUEjdUAjdUIkdEEidEAjdUEjdUEjdUAidUMidEEjd0EidUEidUEjdUEjdUIidkEkdUEjdEEjdT4ndkEjdUIidEIjdkEjdUQlekUlfEMkeEYnfUcngEopg0UnfEwqh08GzoYAAAAzdFJOUwABn9ADBQH9/gIKwq/7QJbLj1VD33ZwD/iKslvZ9UkvUL25pE0iKoPw5Zo2HHFnGMZ8ps6c3PwAAAF2SURBVBgZZcBVluMwEEDR57hsyaCQw8zQMFSSA73/fY3TZ/7mEvEBKf8T3npIxj/SSDKAhOFjCAkQiaR8ywChqn0HMskAqs5o3nkng5R8UIe4DYzbq5m/Bf/oQgTC8L709bYfUTxuxjkXBgkpJBHF3TgfFhWbQe100j+mQArkf5yvbR36FMGpHkgjoL8GqtF5oeqqrndWw5QM2PXyjHIQT9TVZqJWjb0dEBI6BWzu3lk1qlZVXZghJJz9DobeG+eM1YapY4GE3telt2bVre83tapq1bTJEOaxfh2A9Xx6UauqJvxAQDheL2WUkJ9nS7Wq6kKJAJGM096I8eIR1GrDPd8QXoTPIb/vS2O1Ya2vEICEapuzDU5VXcNPyQCE9+4HaeyNWhPCU+s2AqTkv9YIsTdW/W4zWoYVAgibFrlQBOd8D+jfViQRZJy6Y2Drl3rlZTDnW8bnotXpPkO4+SLejafhWhY/ASKOo86+9VLOWqf9fl+Wp78TOCsCfttvbQAAAABJRU5ErkJggg==', - TASK_DONE: - 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAACZUExURUdwTJnNMpnMM4y6J5nMM2p9AJnMM5bfPpnMM5vPM5nMM5nNMpzQNpnMM5nMM5nLMpjLMpjNM5nNMpfLLpnMM5nNMpjMMpnNM5fKMpnMMpnMMpnMM5nMM5nMM5vMM5jKMprMM5jMM5rMM5nLNJfLNJrLNJjMM5jKM5nMM6TbN6PaNprOM6HXNp/UNaDVNZzQNJ3SNKbeN6jgOA6jikkAAAAodFJOUwD99gb+Af0C+gR5mx7s18sno0YOscVO0VZw5+KB9zstiHKrwUBilW/wrNXJAAABCklEQVQoz2WR2ZqDIAxGU9SAu1Vrp/vsA4ja9v0fbojaWTAX5PNEzw8C4FQQ2IVzWBSHj6yk5lQI73jDHSx5su/YYGLuevhGC4l4rx0OsRJSypOuHdHO4EpKYbJq3CF/8LId0PIru8wCPvEi7awI2+uTfSDoUQ/By22w9IU+Ag+Ir7MCQupjsFCvs+JTqf3ZfhBp9Cl4W5EA4KiR9ayBZmgpuEuLiX91EqXoT9GhH4OHZuKQ3RiJW0Pvr1BHFEx1Tg1FIvlt8Pr3x14OWlg4cp17s4jOkGwVTcbg5IfTwao3JZCC2/IPp0lYK5uB5uUfpwnE2mcqXtyonTz39423vGkLojxxRI+J4/kGZm0YuxibMCYAAAAASUVORK5CYII=', - NOTIFICATIONS: - 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABcAAAAXCAQAAABKIxwrAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QAAKqNIzIAAAAJcEhZcwAADdcAAA3XAUIom3gAAAAHdElNRQfjAhUOEh7JZlf7AAAA5UlEQVQ4y43RvWoCQRSG4ZfFf7ELxCrEQm0kjeg1GKIQMFcg3kRyQwqBXIliGxAbLYUtJGgQ3M9mwcWZnZ3TzDDnGfg4B8zKM+ePGTm86gMhxNhsBRb+GJ91H16kGd+aFLKCjNjFUYTYMnThKVECC3FhkoZfON9hIc507PzHgoX4tuEy/yn8RMmcTINiSsgSzyavOkZQc63JUQE+H/MmenDwJ5N3HbxthtqkjFGI1T1/d2Ah3pK4wG8GXye38pWBhfi85T548AMB5ICIFpXMDR2JbM999oSEhOzp+Wz6lSVHFgzM1hX87JwFnhPN4gAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxOS0wMi0yMVQxMzoxODozMCswMTowMB5daKIAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTktMDItMjFUMTM6MTg6MzArMDE6MDBvANAeAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAABJRU5ErkJggg==' + TRIVIAL: + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAANlBMVEVHcEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAR2LVAAAAEXRSTlMAFKIxCKvcF5kPOYV7ziAEc3trLr0AAACgSURBVBgZ7cFZisMwFEXBq1nykOTsf7ONMaE7E9hPkJ9WlYZhGL5mUp9GU5eZWT0iENVhBhbZRTZRZgubRVaRXZTRjd1NNhfuLjIoiV+p6KTieeSdTnCeV9XpoFZ5rzYdca18Uq86pFXeqU2HOc8z73RKSfyVik4ribtUZLKyW2UU2AVZZTZZZoFNkF0GsjpMQFCPTFaXiUl9Vg3DMPwbP7XEEQha8GZ+AAAAAElFTkSuQmCC', + EASY: + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAARVBMVEVHcEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADZo62CAAAAFnRSTlMAr8sx0UTtxksKeVG4ciDmKls5bOMQ1FHhSgAAAOJJREFUWMPtlcsSgjAQBEEgEMS39v9/qgexRElmrPJkVfq6PVubB6SqCoVCIcn4o3Lm7PJamZhcA6k0QKPzWpmA2g0glAbcCFqpwY0glSMPjvm8Vuq5WrsBMsqJJ6dcXinblhftkIorZdjwzmbVQindZw2g75ZxpXQ9aV4tpLLPFaHfP/JOyfRfrsEpw3qFn7volKF9q6XO0SmLU2636XvklMNcPOSvslYuc/WSb6CVGAAI4ms2yg6AnfofaCUGIMTKjZBXRjeAU2IwA1hlxD5NWonX6BoY5ebfxi+UQqFQ+FPuW6MsGdDOH+AAAAAASUVORK5CYII=', + MEDIUM: + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAKlBMVEVHcEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHrpZrAAAADXRSTlMAIKDgYEC/gBDwkM9Qsgnf8QAAAVxJREFUWMPdldkWgyAMBcWExC78/++2ttiDLBchb/W5mY6omWX5+8t7I0DEKBCCTUFCEKOATUF2gBgFLAryBYhRYFrBPQ7Aw82MryG5VmcaH0aU40OI+vgAojn/JtgUBo6hhhh8EDlidaYXYXVTr6Ie8zr5LWy37/xtm/2a1CZwKMwLRAU1rLRdwSLwUbAI7Ao2gbfCoEDxxm1b9yfpxRcWsKAVS/0F7NGW5wsNELTlqd8Aj0LDFzIkKDTUz5BHreMLJRTUOuqX8KilAAGoIO3cphuQuHGP1NyS+Q6uIdLxDFGrQI7IxxNEq0MpojZ+ILRdQoqSjtq/0TY9uclmr6NlFXE+5ioiuckCUbasQGTHfELUU3hCVB70PT+7CuL3L3dQwsBw44BaxgdKC9x5oJZRgeHWhbnWrkBUUBRjxgCGudauwEdBUYy5B2CYa6V+mQjVcnv2AU9jrv/wegGERSd0TQVf/QAAAABJRU5ErkJggg==', + HARD: + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAIVBMVEVHcEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAt9G3DAAAACnRSTlMAEPBAwIB/IKJwAyvMcQAAAYdJREFUWMPtV8uWhSAME1oE+/8fPL5wUGnKub2LWQzb0CRHoaTT1K6wTOZaAgBTDFZ9iAmBkiyCJEAkiVgWVg1dZAMtC6uGLrKBhoVdQxM5QGxh19BEDhBaODX6IhVEFk6NrkgoUlHS6qlqSHkyhPnC1sVdCuJmS5yDWt6nuJXfKd7lb4pX+S9Fv/xO0S2vFItWvzLkoz6zuiWu1zekaBhQLcR0fIYexfMrdihqeY+i9x8fFG35k4KVk9RQPMt3+Dpl+lG+GAihWSeoIozQgq4zAwMVzYiAgIETLbilMTBwoBkTEDCwo8Vq6wwMbGi2CAgYWNFiP21MsG3bBHn6X399ZecWYudBYiHXUSYRdl0mFrEswOtMIqYF2FBYxLIAWxqJmBZgU2URywJs69fDwh89LG1MGHna5gAi0tjj2lD0QsrI835SaBlnJGBsFO6IoyWcsZC1x4Q+xUjMu1LGm2IkaN5Cyp1iJOq+Mo4zbH8h7vsHDv/I4x66/GOfe/D0j77u4fuT8f8HqE0oB/XJcWkAAAAASUVORK5CYII=', + CHECKED: + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAQAAAAAYLlVAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QAAKqNIzIAAAAJcEhZcwAADdcAAA3XAUIom3gAAAAHdElNRQfjBR4BLB+R2WA8AAACvklEQVRo3u2Yv09TURTHP632vTJojSZ1gKSUSIwO6mSMRhsXjcgkgwwEdyf9AyRgCaQGHFj4MWAcjXGRCGliYmICYWgEHdiMUBSQHwE1WITS61CEdx8FWuAdBt9ZX+79fN9955zvPQ/ccMON/z08B8D0cpKLlLLMCP2HDwBfShV3OcMSA2Tk8WU8YgyFQvGHl7J4DyHqSK7hFatMyuJLqLfgFWmGJPFBony14DPM8kBOQIBmJjT8PF34pPAmLUxZ8IoFujGl8H7amdPwM7RhyOE7mSFjwU/RSkAWv2rBfyNGUKoHm7Tb8OM0USKD9xCghTnt8JM0EpbCB2m2Zf4YUU5J4YuJanWvGOMx5TJ4LyHqGdfwSaKU45XBhzXLyaZeo9Th64b7r/CapFLPS3gTfoqYXOGFqLPhZ2iVajseim1+n2GBNgJydR/VMj/DPN1ylpPL75/JGe4e/N7Lfd7zgRind/2t/HTY/H6ap/nedh4yxwppUvQR2ZUEP13MapYzwROO5rt8hPTashRxbhTcLPfs99MW7Yu84WZBp2DSYcMniRbWdl6xbFn+kx5u5bncw7FNfj9KA6HCPuRlhjQJP3hNRR5bZP3+u5Z6X6inrNA88nGHBEtaAfVwe4dcyO33DbtzPINqBklpp9BLxTYS9t3vDWoY4Ldlu1/0bVkRjvi9SS39moRF4lzPIcExvze5Z5OQ4i1XbNs66vcmtQxoubDCOy6w8RfFcb83qWFQqwhFnPNrliri9wbVJLS+oIhzDkPO7w2qGF73hw0JJ3L4vUMDtkElI5oARS+9tq43T6dztx0fET7ZJChb6jk83/u5to0Ekfm+iKtbSBCa7z0U5TwFwfke/EQsNybh+T4bPioZXu8LDsz3h3Z4nmGUSY5wHMVHnvOCz6j9FJDP2xhc4iwGoySY3F+8G2644Qb8BSHeLEPgAmSrAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE5LTA1LTI5VDIzOjQ0OjMxKzAyOjAwhLix6wAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxOS0wNS0yOVQyMzo0NDozMSswMjowMPXlCVcAAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb7jwaAAAAAElFTkSuQmCC', + NOTIFICATIONS: { + BLACK: + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAPFBMVEVHcEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACHr/7WAAAAE3RSTlMA8KDPPzBgQMC/EJBwgLDgIFB/TeX9tgAAAQ9JREFUWMPtl1GzgiAYRBcIIUCt9v//1x6kshIHPm8vdziP6h7BWQYEADht2IzRDgthopApAEAwFGMCgIkHiIDjIRw0SdIn10jyeQiGJD0EeJI0IEkmiSCRJJFnIsB1waYgniuIOwJfUz7/vwXRVxB7E38r6E38A4Gt2RFsb+KuwJ4qsL2JvxX0Jh4QDBLBsAgUSWqJQJPknI/Kt9AaD7flwJ4H8n2GLrzxkwGYC9e3D6YfzACsqjJsjlRZ1Bp28kAYVflmOa/G1ZdfbwhX9WXIeXXd2B6KFVsZHpO0bR1bfqlW+aG1pdkgyL8ZRHlgzEVJKedH0VJ5oYWL7UD+zSDKP79DYXHWcImGNPGy98wd4OV99UW+D+wAAAAASUVORK5CYII=', + WHITE: + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABAAQMAAACQp+OdAAAABlBMVEVHcEz///+flKJDAAAAAXRSTlMAQObYZgAAAH1JREFUKM/N0bENwyAUBuGzKFwyAqMwGh7NozACJQXiUoDkIlVSRHnV190vPaKqNvLCoCzMB+57MAAgf4tTtX6KP0n8YPwbXBAdkJXihLLeBDrJeh06SHoH7QStp95AarEDEHtqAJwjVwDCLDcAh167s3uUuZH3FFLfiA14AVTO7CnEdrzkAAAAAElFTkSuQmCC' + }, + GOLD: + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAFcUExURUdwTPujIv+mIv+mI/+AAP+nAP2kIv+nI/+aIv+nI/+mIvmjIv+mIv+mIv+hG/+mIv+pIvehIf+mI/+mIv+mIP+lIv+kIv+nI/+mI/+mIv+mIv+lI/+mI/+lIP+dJ/+kIv+mI/+iH/+lIv+nI/+lJP+mI/ehIf+mIv+nI/+mI/+kI/+mIv+mI//Tkd6RH//Ukv+nI/+oI/6lI/+wJeWWH/+qJP/SkN2RH/+tJP7Rjv+nI/WfIfegIe6bIf+zJt2MAN+TJP/WlO2aIOOUHv/Ylt6OAN6SIt+SH+CTH96QGd6PEdyQHuCUIdOKG82HGdOYQP7Qi/O6bf/Ukc6SNOSZLPGdIf+mJv+jAM2FDPS9cOuZIP+9ZP+8Ys2OLO2mQ+WcNu2vWv2jFdWUNs6UOcmKI+SwZM6HHduhTuusVeOvYvG0Yf+nHu+rTd2QH9WMHNWcSduVLu69du6+dvG0ZaaQZ4IAAAAsdFJOUwD9lfICAf/7A/ru/uoeCc8O/oiQH8o+Tdv25eGCGAY49xSzJEfH/cKzoHNyipTLOAAAApZJREFUOMtVU2dz4jAUFBB6CyUQSO/JnZGrYnuMIbaxqckRIL0nF3JpV///zEk2dYfxB3afnt7TLgA2vAEAZrN78YVcbiG+l50FIOAFE5jxgqVkHvESYlkk8SifXALemTGPy7+GeMT4XQlFSbj8DOJDX+y/h/zaOo/8YVVoHWC0BDXsR/z62lARAJk873fpLaveKGM06lZNd/n5fMZRBEA0JAWLhlU3e7f/jo/fbntm3TKKQSmUJgoviM1JQQVa5t1Fu6NVKlqnfdEzIVSC0lwM0wGQLAWL8MC8b2t0YR+jQGvtj4YFi8HSIjki7WF9hmW+afR+YQBZ69w2LMPHeqL4Dm7er1vmvVYY8YUCff2uQlH38268vwgKtxp3Mqmnadr+0NdnFCfAWhhFZkEWMaplXmikXpZl+3N9dsoxChRVBmVJB6Hea5NS+Vf3udN5fnp8OKUoijNEgfTYklyt8g+aNJa/fX+tVF7PDznMU8xls+aStsA8ShyUjyv7tuDm5c/fF0fAMSfNagLNgxyrjAXd7s3Ref/QZ59wIkKFzQHPlODppt+fEnimW3R/q2rzyBaMWuxMXdJAyDganGBfcgekJsZ8PBcoyuj/tKdwxkyBzcGi5IJ29cBhivM5QziL2hyvmr46O2WoEUardh6rfK9hfkxTjD58LBAlzw3hxzvFjarxjBAOnjsAFolhqg2VYwhFfgynw+rQMF6w7FgOCgrH2OAUATqWW8Y0MW2EmFbEh16eYFzihiIxbSQ6tPUKtrVPr4li81MUP5uiWNN92NQr42Ck4yWGBKdWhbBaE1QcrlI8PRmtmHvVjt62omzb0Vt1x8a8ncKNVJ7leYllJZ5nF1IbYJJ34h/LJHfjHk98N5mJjeP/H2u0q0e5TrInAAAAAElFTkSuQmCC', + EXP: + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAD8UExURUdwTP+lHv+/QP+mI/+mIv+mI/+mI/+pAP/yAP+mIv+mIf+mIP+mIv+mIv+mI/+kG/+jDv+mIv+lE/+mIv+lHv+kF/+mI/+nIv+sJP+uJf+rJP+9Wv+sJf+4Tv+sNf+mJP+8WP/UlP/Tkf/Rjf/cqe6cJvekK//ox//erP+qMeCTIv+/Yv+kFP+6U//Hev+uJ/+2TP++XvujIfWfIP/Da//itv/CafymKv20S//Tlv/Wmu+cIP+yRt2PGPfIh//fsffOkO6hLf+uPP+zTP+xRv/HeOObOe6aFuiXIueWHeq4dvGkOu6YAPDJjvbQmO/JkN6MAPvWn/S/d+CWKhATkNUAAAAYdFJOUwD9BL1Kv/0CAUj9/8rBRv79w/xq/P3GaXKR1V0AAAGcSURBVDjLhZPpUsIwFIVTaVNatoJVm6RLTMu+i+DKDu677/8upliUAQL3R2Yy58x3k7sAsBoqkGV+iCMOkkl+CEMFaYzTOxBxELOsmBgRAiDcgYgDBZkmEiJUoNlHs0lGiOCAZm5OcyLEAZBtY+q3yxLW+GWLDlJNo8HaLRMpYMOhcughNqbMJ9TN4BOeUF2T5Sz2ag122qOO7uGsvGJZyCkbSbVb5nevHHKR8JCVWlpCWVO4bLoOOb8ulnqFfOcMQoQVLbKkFYwyuXKLUqcaFIeD8cv9TefM1BGOpbksJ7EFzQltt0nvqlTqfo5GX883+XzhAkILJ7XIMJv7zD/tFovDt1LAqnVCL8t6aJDDFDGMjnLTBmP+dRC8D3p1ShxXX6aIHtk0DG45rwav32PSck0J2X+PjL7ZNGq3pM6ePiqFRIbLG5XI2l7NIdW7/kNirVD/pZZcUq/0O95GqZfNQqZDK49wW7OidksurXTg9nb/Tpx+mQ+7HReNHNbLBZ0DVPHQ6lA8tAvEzrHfuzj7Vy9c3uOd283Xfx3wA4kALaPyZ1lJAAAAAElFTkSuQmCC' }; - -export const TRELLO_ICON = - 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYBAMAAAASWSDLAAAAA3NCSVQICAjb4U/gAAAACXBIWXMAAACmAAAApgHdff84AAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAACRQTFRFAGbMA3a9A3e8Ane9A3e9GYPCR5rMa63Ud7PXvNfm4Onu7O/xNljIFwAAAAN0Uk5TBWHLjO1H6wAAAGRJREFUGNNjYFQ2hgIjAQZhYzgwZFBGcIwYjM1X794ZbDp7965iYwZjq927dxebA4nFQI41kG62ABKbacMxAxKTYRyTjI72YBgH4jiiOZZATjKMY1rR0eYM4yABVG+jBAhyUAEAulltP7yEQqgAAAAASUVORK5CYII='; diff --git a/src/js/edit-task.js b/src/js/edit-task.js index aaab2de..e769d73 100644 --- a/src/js/edit-task.js +++ b/src/js/edit-task.js @@ -1,5 +1,6 @@ import TaskForm from './task-form'; +// eslint-disable-next-line no-undef const trello = TrelloPowerUp.iframe(); const form = new TaskForm(trello); form.initialize(); diff --git a/src/js/habitica-api.js b/src/js/habitica-api.js index 873c707..ad81cf3 100644 --- a/src/js/habitica-api.js +++ b/src/js/habitica-api.js @@ -1,23 +1,22 @@ -import { API } from './constants' -import Storage from './storage' +import { BASE_URL } from './constants'; +import Storage from './storage'; export default class HabiticaApi { - constructor( - trello, - storage = new Storage(trello) - ) { - this.t = trello - this.storage = storage + constructor(trello, storage = new Storage(trello)) { + this.t = trello; + this.storage = storage; } - async request(url, userParams = {}) { - let defaultParams = { + async request(path, userParams = {}) { + const url = BASE_URL + path; + const defaultParams = { headers: await this.authHeaders() - } + }; + + const params = Object.assign({}, defaultParams, userParams); - let params = Object.assign({}, defaultParams, userParams) - return fetch(url, params) - .then(res => this.handleResponse(res)) + // eslint-disable-next-line no-undef + return fetch(url, params).then(res => this.handleResponse(res)); } async authHeaders() { @@ -25,68 +24,74 @@ export default class HabiticaApi { 'x-api-user': await this.t.loadSecret('userId'), 'x-api-key': await this.t.loadSecret('apiToken'), 'Content-Type': 'application/json' - } + }; } handleResponse(res) { - if (res.ok) { - return res.json() - } else { - return this.handleError(res) - } + if (res.ok) return res.json(); + + return this.handleError(res); } async handleError(error) { - if (error.status == 401) { - await this.storage.removeUser().then(() => ( - this.notify( - "Wrong User ID or API Token. Try to login again.", - 'error' - ) - )) - } else if (error.status == 404) { - await this.storage.removeTask() + if (error.status === 401) { + await this.storage + .removeUser() + .then(() => + this.notify( + 'Wrong User ID or API Token. Try to login again.', + 'error' + ) + ); + } else if (error.status === 404) { + await this.storage.removeTask(); } - return error + return error; } - + addTask(params) { - return this.request(API + '/tasks/user', { + return this.request('/tasks/user', { method: 'POST', - body: JSON.stringify(params), - }) + body: JSON.stringify(params) + }); } updateTask(id, params) { - return this.request(API + `/tasks/${id}`, { + return this.request(`/tasks/${id}`, { method: 'PUT', body: JSON.stringify(params) - }) + }); } doTask(id) { - return this.request(API + `/tasks/${id}/score/up`, { - method: 'POST', - }) + return this.request(`/tasks/${id}/score/up`, { + method: 'POST' + }); } undoTask(id) { - return this.request(API + `/tasks/${id}/score/down`, { - method: 'POST', - }) + return this.request(`/tasks/${id}/score/down`, { + method: 'POST' + }); } removeTask(id) { - return this.request(API + `/tasks/${id}`, { - method: 'DELETE', - }) + return this.request(`/tasks/${id}`, { + method: 'DELETE' + }); } getUserProfile() { - return this.request(API + '/user?userFields=profile', { + return this.request('/user?userFields=profile', { + method: 'GET' + }); + } + + getUserStats() { + return this.request('/user?userFields=stats', { method: 'GET' - }) + }); } notify(message, display = 'info') { @@ -94,6 +99,6 @@ export default class HabiticaApi { message, display, duration: 10 - }) + }); } } diff --git a/src/js/index.js b/src/js/index.js index ecf3397..c0475ee 100644 --- a/src/js/index.js +++ b/src/js/index.js @@ -3,10 +3,10 @@ import Storage from './storage'; import Sync from './sync'; import List from './list'; -let syncing = {}; +const syncing = {}; const syncWithHabitica = async t => { - let id = t.getContext().card; + const id = t.getContext().card; // prevent duplicated tries to sync while previous sync in progress if (syncing[id]) return syncing[id]; @@ -18,68 +18,109 @@ const syncWithHabitica = async t => { return syncing[id]; }; +const priorityIcon = { + 0.1: ICONS.TRIVIAL, + 1: ICONS.EASY, + 1.5: ICONS.MEDIUM, + 2: ICONS.HARD +}; + +// eslint-disable-next-line no-undef TrelloPowerUp.initialize({ 'board-buttons': async t => { - let currentUser = await new Storage(t).getUser(); + let buttons; + const storage = new Storage(t); + const currentUser = await storage.getUser(); + const settings = await storage.getSettings(); - let settingsPage = t => - t.popup({ - title: 'Habitica settings', - url: './settings.html', - height: 394 - }); - - let loginPage = t => - t.popup({ - title: 'Log in Habitica', - url: './login.html', - height: 340 - }); + if (currentUser.loggedIn) { + buttons = [ + { + condition: 'always', + icon: { + dark: ICONS.HABITICA_LOGO, + light: ICONS.HABITICA_LOGO + }, + text: currentUser.name, + callback: tt => + tt.popup({ + title: 'Settings', + url: './settings.html', + height: 270 + }) + } + ]; - return [ - { - icon: ICONS.HABITICA, - text: currentUser.loggedIn ? currentUser.name : 'Login', - callback: currentUser.loggedIn ? settingsPage : loginPage - }, - { - icon: ICONS.NOTIFICATIONS, - text: ' ', - callback: t => - t.popup({ - title: "What's new?", - url: './changelog/', - height: 525 - }) + if (settings.showStats) { + buttons.unshift( + { + condition: 'always', + icon: { + dark: ICONS.EXP, + light: ICONS.EXP + }, + text: currentUser.exp + ? `${currentUser.exp} / ${currentUser.expToNextLevel}` + : '?' + }, + { + condition: 'always', + icon: { + dark: ICONS.GOLD, + light: ICONS.GOLD + }, + text: currentUser.gold ? currentUser.gold.toFixed(2) : '?' + } + ); } - ]; + } else { + buttons = [ + { + condition: 'always', + icon: { + dark: ICONS.HABITICA_LOGO, + light: ICONS.HABITICA_LOGO + }, + text: 'Login', + callback: tt => + tt.popup({ + title: 'Log in Habitica', + url: './login.html', + height: 340 + }) + } + ]; + } + + return buttons; }, 'card-badges': async t => { - let storage = new Storage(t); - let currentUser = await storage.getUser(); + const storage = new Storage(t); + const currentUser = await storage.getUser(); if (!currentUser.loggedIn) return []; return syncWithHabitica(t).then(async () => { - let settings = await storage.getSettings(); + const settings = await storage.getSettings(); if (!settings.showBadges) return []; - let taskData = await storage.getTask(); + const taskData = await storage.getTask(); + return [ - { icon: taskData.id ? ICONS.TASK_DOING : null }, - { icon: taskData.done ? ICONS.TASK_DONE : null } + { icon: taskData.id ? priorityIcon[taskData.priority] : null }, + { icon: taskData.done ? ICONS.CHECKED : null } ]; }); }, 'card-detail-badges': async t => { - let taskData = await new Storage(t).getTask(); + const taskData = await new Storage(t).getTask(); if (!taskData.id) return []; return [ { title: 'To-Do', text: 'Edit', - callback: t => - t.popup({ + callback: tt => + tt.popup({ title: 'Edit To-Do', url: './edit-task.html', height: 240 @@ -88,39 +129,40 @@ TrelloPowerUp.initialize({ ]; }, 'list-actions': async t => { - let storage = new Storage(t); - let currentUser = await storage.getUser(); + const storage = new Storage(t); + const currentUser = await storage.getUser(); if (!currentUser.loggedIn) return []; return storage.getLists().then(lists => { return t.list('id').then(list => { const listType = lists[list.id]; - if (listType === LIST_TYPES.DOING) { - return [ - { - text: 'Unmark list as "Doing"', - callback: t => new List(t).unmark() - } - ]; - } else if (listType === LIST_TYPES.DONE) { - return [ - { - text: 'Unmark list as "Done"', - callback: t => new List(t).unmark() - } - ]; - } else { - return [ - { - text: 'Mark list as "Doing"', - callback: t => new List(t).markAsDoing() - }, - { - text: 'Mark list as "Done"', - callback: t => new List(t).markAsDone() - } - ]; + switch (listType) { + case LIST_TYPES.DOING: + return [ + { + text: 'Unmark list as "Doing"', + callback: tt => new List(tt).unmark() + } + ]; + case LIST_TYPES.DONE: + return [ + { + text: 'Unmark list as "Done"', + callback: tt => new List(tt).unmark() + } + ]; + default: + return [ + { + text: 'Mark list as "Doing"', + callback: tt => new List(tt).markAsDoing() + }, + { + text: 'Mark list as "Done"', + callback: tt => new List(tt).markAsDone() + } + ]; } }); }); diff --git a/src/js/list.js b/src/js/list.js index 3f89bea..0472da0 100644 --- a/src/js/list.js +++ b/src/js/list.js @@ -1,50 +1,47 @@ -import Storage from './storage' -import { LIST_TYPES } from './constants' +import Storage from './storage'; +import { LIST_TYPES } from './constants'; export default class List { - constructor( - trello, - storage = new Storage(trello) - ) { - this.t = trello - this.storage = storage + constructor(trello, storage = new Storage(trello)) { + this.t = trello; + this.storage = storage; } markAsDone() { - return this.mark(LIST_TYPES.DONE) + return this.mark(LIST_TYPES.DONE); } markAsDoing() { - return this.mark(LIST_TYPES.DOING) + return this.mark(LIST_TYPES.DOING); } async mark(listType) { - const lists = await this.storage.getLists() - const list = await this.t.list('id', 'name') + const lists = await this.storage.getLists(); + const list = await this.t.list('id', 'name'); - lists[list.id] = listType + lists[list.id] = listType; - this.t.closePopup() - this.notify(`List "${list.name}" was successfully marked`, 'success') - return this.storage.setLists(lists) + this.t.closePopup(); + this.notify(`List "${list.name}" was successfully marked`, 'success'); + return this.storage.setLists(lists); } async unmark() { - const lists = await this.storage.getLists() - const list = await this.t.list('id', 'name') + const lists = await this.storage.getLists(); + const list = await this.t.list('id', 'name'); - delete lists[list.id] + delete lists[list.id]; - this.t.closePopup() - this.notify(`List "${list.name}" was successfully unmarked`, 'success') - return this.storage.setLists(lists) + this.t.closePopup(); + this.notify(`List "${list.name}" was successfully unmarked`, 'success'); + return this.storage.setLists(lists); } notify(message, display = 'info') { this.t.alert({ message, display, - duration: 3 - }) + duration: 5 // min is 5 + }); } } diff --git a/src/js/login.js b/src/js/login.js index ea60377..86db0a3 100644 --- a/src/js/login.js +++ b/src/js/login.js @@ -1,5 +1,6 @@ import LoginForm from './login-form'; +// eslint-disable-next-line no-undef const trello = TrelloPowerUp.iframe(); const form = new LoginForm(trello); form.initialize(); diff --git a/src/js/settings-form.js b/src/js/settings-form.js index 8827de3..ae90d3c 100644 --- a/src/js/settings-form.js +++ b/src/js/settings-form.js @@ -1,3 +1,5 @@ +/* eslint-disable class-methods-use-this */ + import Storage from './storage'; export default class SettingsForm { @@ -7,52 +9,34 @@ export default class SettingsForm { } initialize() { - this.storage.getSettings().then(settings => { - this.initializeElements(); - - this.setScope(settings.scope); - this.setPriority(settings.priority); - this.setShowBadges(settings.showBadges); - this.setPrependIcon(settings.prependIcon); - - this.listenToSubmit(); - this.listenToLogout(); - }); + this.initializeElements(); + this.assignValues(); + this.listenToSubmit(); + this.listenToLogout(); } initializeElements() { this.$scope = document.getElementById('scope'); this.$priority = document.getElementById('priority'); + this.$prependIcon = document.getElementById('prepend-icon'); + this.$showBadges = document.getElementById('show-badges'); + this.$showStats = document.getElementById('show-stats'); this.$submitButton = document.getElementById('submit-btn'); this.$logoutButton = document.getElementById('logout-btn'); + this.$showStatsNotifications = document.getElementById( + 'show-stats-notifications' + ); } - setScope(val) { - this.$scope.value = val; - } - - setPriority(val) { - this.$priority.value = val; - } - - setShowBadges(val) { - const el = document.querySelector(`#show-badges-${val}`); - if (el) el.checked = true; - } - - getShowBadges() { - const el = document.querySelector('input[name="show-badges"]:checked'); - return el ? JSON.parse(el.value) : true; - } - - setPrependIcon(val) { - const el = document.querySelector(`#prepend-icon-${val}`); - if (el) el.checked = true; - } - - getPrependIcon() { - const el = document.querySelector('input[name="prepend-icon"]:checked'); - return el ? JSON.parse(el.value) : false; + assignValues() { + return this.storage.getSettings().then(async settings => { + this.$scope.value = settings.scope; + this.$priority.value = settings.priority; + this.$prependIcon.checked = settings.prependIcon; + this.$showBadges.checked = settings.showBadges; + this.$showStats.checked = settings.showStats; + this.$showStatsNotifications.checked = settings.showStatsNotifications; + }); } listenToSubmit() { @@ -70,8 +54,10 @@ export default class SettingsForm { .setSettings({ scope: this.$scope.value, priority: this.$priority.value, - showBadges: this.getShowBadges(), - prependIcon: this.getPrependIcon() + prependIcon: this.$prependIcon.checked, + showBadges: this.$showBadges.checked, + showStats: this.$showStats.checked, + showStatsNotifications: this.$showStatsNotifications.checked }) .then(() => this.t.closePopup()); } diff --git a/src/js/settings.js b/src/js/settings.js index a008d89..5567f63 100644 --- a/src/js/settings.js +++ b/src/js/settings.js @@ -1,5 +1,6 @@ import SettingsForm from './settings-form'; +// eslint-disable-next-line no-undef const trello = TrelloPowerUp.iframe(); const form = new SettingsForm(trello); form.initialize(); diff --git a/src/js/storage.js b/src/js/storage.js index 9999de5..99d142f 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -1,8 +1,8 @@ export default class Storage { constructor(trello) { - this.t = trello + this.t = trello; } - + getAll() { return this.t.getAll(); } @@ -21,7 +21,12 @@ export default class Storage { setUser(obj) { return this.t.get('board', 'private', 'user', {}).then(current => { - return this.t.set('board', 'private', 'user', Object.assign({}, current, obj)); + return this.t.set( + 'board', + 'private', + 'user', + Object.assign({}, current, obj) + ); }); } @@ -35,7 +40,12 @@ export default class Storage { setTask(obj) { return this.t.get('card', 'private', 'task', {}).then(current => { - return this.t.set('card', 'private', 'task', Object.assign({}, current, obj)); + return this.t.set( + 'card', + 'private', + 'task', + Object.assign({}, current, obj) + ); }); } @@ -44,20 +54,27 @@ export default class Storage { } getSettings() { - let defaultSettings = { + const defaultSettings = { scope: 'me', - priority: '1', + priority: 1, + prependIcon: true, showBadges: true, - prependIcon: false + showStats: true, + showStatsNotifications: true }; return this.t.get('board', 'private', 'settings', {}).then(settings => { - return Object.assign({}, defaultSettings, settings) + return Object.assign({}, defaultSettings, settings); }); } setSettings(obj) { return this.t.get('board', 'private', 'settings', {}).then(current => { - return this.t.set('board', 'private', 'settings', Object.assign({}, current, obj)); + return this.t.set( + 'board', + 'private', + 'settings', + Object.assign({}, current, obj) + ); }); } diff --git a/src/js/sync.js b/src/js/sync.js index d429b6b..4add706 100644 --- a/src/js/sync.js +++ b/src/js/sync.js @@ -1,75 +1,82 @@ -import { CARD_SCOPES, LIST_TYPES } from './constants' -import Storage from './storage' -import Task from './task' +/* eslint-disable consistent-return */ +/* eslint-disable no-else-return */ +/* eslint-disable no-lonely-if */ + +import { CARD_SCOPES, LIST_TYPES } from './constants'; +import Storage from './storage'; +import Task from './task'; export default class Sync { - constructor( - trello, - storage = new Storage(trello) - ) { - this.t = trello - this.storage = storage - this.task = undefined + constructor(trello, storage = new Storage(trello)) { + this.t = trello; + this.storage = storage; + this.task = undefined; } - getTask() { - this.task = this.task || new Task(this.t, this.storage) - return this.task + currentTask() { + this.task = this.task || new Task(this.t, this.storage); + return this.task; } async start() { - let card = await this.t.card('id', 'idList', 'members') - let settings = await this.storage.getSettings() - let taskData = await this.storage.getTask() + const card = await this.t.card('id', 'idList', 'members'); + const settings = await this.storage.getSettings(); + const taskData = await this.storage.getTask(); // Skips the cards that are not assigned to user. - // Remove the badge if card was already synced. - if (settings.scope == CARD_SCOPES.ASSIGNED_TO_ME) { - let me = this.t.getContext().member - if (!card.members.some(member => member.id == me)) { - return this.handleScoped(taskData) + // Removes the badge if card was already synced. + if (settings.scope === CARD_SCOPES.ASSIGNED_TO_ME) { + const me = this.t.getContext().member; + if (!card.members.some(member => member.id === me)) { + return this.handleScoped(taskData); } } - let lists = await this.storage.getLists() - let listType = lists[card.idList] + const lists = await this.storage.getLists(); + const listType = lists[card.idList]; - return this.handle(taskData, listType) + return this.handle(taskData, listType); } handleScoped(taskData) { if (taskData.id) { if (taskData.done) { - return this.getTask().handleUndo().then(() => this.getTask().handleRemove()) + return this.currentTask() + .handleUndo() + .then(() => this.currentTask().handleRemove()); } else { - return this.getTask().handleRemove() + return this.currentTask().handleRemove(); } } } handle(taskData, listType) { - if (listType == LIST_TYPES.DOING) { + if (listType === LIST_TYPES.DOING) { if (taskData.id) { if (taskData.done) { - return this.getTask().handleUndo() + return this.currentTask().handleUndo(); } } else { - return this.getTask().handleAdd() + return this.currentTask().handleAdd(); } - } else if (listType == LIST_TYPES.DONE) { + } else if (listType === LIST_TYPES.DONE) { if (taskData.id) { if (!taskData.done) { - return this.getTask().handleDo() + return this.currentTask().handleDo(); } } else { - return this.getTask().handleAdd().then(() => this.getTask().handleDo()) + return this.currentTask() + .handleAdd() + .then(() => this.currentTask().handleDo()); } } else { if (taskData.id) { if (taskData.done) { - return this.getTask().handleUndo().then(() => this.getTask().handleRemove()) + return this.currentTask() + .handleUndo() + .then(() => this.currentTask().handleRemove()); } else { - return this.getTask().handleRemove() + return this.currentTask().handleRemove(); } } } diff --git a/src/js/task-form.js b/src/js/task-form.js index 09e7d22..f6a9886 100644 --- a/src/js/task-form.js +++ b/src/js/task-form.js @@ -1,49 +1,46 @@ -import Storage from './storage' -import Task from './task' +import Storage from './storage'; +import Task from './task'; export default class TaskForm { - constructor( - trello, - storage = new Storage(trello) - ) { - this.t = trello - this.storage = storage + constructor(trello, storage = new Storage(trello)) { + this.t = trello; + this.storage = storage; } initialize() { this.storage.getTask().then(task => { - this.initializeElements() + this.initializeElements(); - this.setPriority(task.priority) - - this.listenToSubmit() - }) + this.setPriority(task.priority); + + this.listenToSubmit(); + }); } initializeElements() { - this.$priority = document.getElementById('priority') - this.$submitButton = document.getElementById('submit-btn') + this.$priority = document.getElementById('priority'); + this.$submitButton = document.getElementById('submit-btn'); } setPriority(val) { - this.$priority.value = val + this.$priority.value = val; } listenToSubmit() { - this.$submitButton.addEventListener('click', () => this.handleSubmit()) + this.$submitButton.addEventListener('click', () => this.handleSubmit()); } - + updatePriority(val) { - return new Task(this.t).handleUpdate({ priority: val }) + return new Task(this.t).handleUpdate({ priority: val }); } handleSubmit() { - this.$submitButton.disabled = true - - this.storage.getTask().then(task => { - if (task.priority == this.$priority.value) return + this.$submitButton.disabled = true; + const priority = Number(this.$priority.value); - return this.updatePriority(this.$priority.value) - }).then(() => this.t.closePopup()) + this.storage + .getTask() + .then(task => task.priority !== priority && this.updatePriority(priority)) + .then(() => this.t.closePopup()); } } diff --git a/src/js/task.js b/src/js/task.js index 112888d..b3bd0c6 100644 --- a/src/js/task.js +++ b/src/js/task.js @@ -1,75 +1,81 @@ -import { TRELLO_ICON } from './constants' -import Storage from './storage' -import HabiticaApi from './habitica-api' +import { ICONS } from './constants'; +import Storage from './storage'; +import HabiticaApi from './habitica-api'; +import User from './user'; export default class Task { constructor( trello, storage = new Storage(trello), - API = new HabiticaApi(trello, storage) + API = new HabiticaApi(trello, storage), + user = new User(trello, storage) ) { - this.t = trello - this.storage = storage - this.API = API + this.t = trello; + this.storage = storage; + this.API = API; + this.currentUser = user; } async getTemplate(card) { - let cardUrl = `https://trello.com/c/${card.shortLink}` - let settings = await this.storage.getSettings() - let icon = settings.prependIcon ? `![](${TRELLO_ICON}) ` : '' + const cardUrl = `https://trello.com/c/${card.shortLink}`; + const settings = await this.storage.getSettings(); + const icon = settings.prependIcon ? `![](${ICONS.TRELLO_LOGO}) ` : ''; return { type: 'todo', priority: settings.priority, text: `### ${icon}${card.name}`, notes: `[Open in Trello](${cardUrl})` - } + }; } async handleAdd() { - let card = await this.t.card('name', 'shortLink') - let params = await this.getTemplate(card) + const card = await this.t.card('name', 'shortLink'); + const params = await this.getTemplate(card); - return this.API.addTask(params) - .then(res => ( - this.storage.setTask({ - id: res.data.id, - text: res.data.text, - notes: res.data.notes, - priority: res.data.priority - }) - )) - } + return this.API.addTask(params).then(res => + this.storage.setTask({ + id: res.data.id, + text: res.data.text, + notes: res.data.notes, + priority: res.data.priority + }) + ); + } async handleRemove() { - let task = await this.storage.getTask() + const task = await this.storage.getTask(); - return this.API.removeTask(task.id) - .then(_ => this.storage.removeTask()) + return this.API.removeTask(task.id).then(() => this.storage.removeTask()); } async handleDo() { - let task = await this.storage.getTask() + const task = await this.storage.getTask(); - return this.API.doTask(task.id) - .then(_ => this.storage.setTask({ done: true })) + return this.API.doTask(task.id).then(res => + this.storage + .setTask({ done: true }) + .then(() => this.currentUser.updateStats(res.data)) + ); } async handleUndo() { - let task = await this.storage.getTask() + const task = await this.storage.getTask(); - return this.API.undoTask(task.id) - .then(_ => this.storage.setTask({ done: false })) + return this.API.undoTask(task.id).then(res => + this.storage + .setTask({ done: false }) + .then(() => this.currentUser.updateStats(res.data)) + ); } - async handleUpdate(params) { - let task = await this.storage.getTask() + async handleUpdate({ priority }) { + const task = await this.storage.getTask(); - return this.API.updateTask(task.id, params) - .then(res => ( - this.storage.setTask({ - priority: res.data.priority - }) - )) + return this.API.updateTask(task.id, { priority }).then(res => + this.storage.setTask({ + priority: res.data.priority + }) + ); } } diff --git a/src/js/user.js b/src/js/user.js new file mode 100644 index 0000000..aa33d7b --- /dev/null +++ b/src/js/user.js @@ -0,0 +1,48 @@ +import Storage from './storage'; + +export default class Task { + constructor(trello, storage = new Storage(trello)) { + this.t = trello; + this.storage = storage; + } + + // In some cases user stats response is missing the toNextLevel field + // so it's better to calculate it manually with this formula: + // https://habitica.fandom.com/wiki/Experience_Level_Chart + async calculateExpToNextLevel(lvl) { + const currentUser = await this.storage.getUser(); + + if (currentUser.lvl === lvl && currentUser.expToNextLevel) + return currentUser.expToNextLevel; + + const exp = 0.25 * lvl ** 2 + 10 * lvl + 139.75; + const remainder = exp % 10; + return exp - remainder + Math.round(remainder / 10) * 10; // rounded to the closest 10 + } + + async updateStats({ gp, exp, lvl }) { + const gold = gp; + + this.notifyAboutStats({ gold, exp }); + const expToNextLevel = await this.calculateExpToNextLevel(lvl); + return this.storage.setUser({ lvl, gold, exp, expToNextLevel }); + } + + async notifyAboutStats({ exp, gold }) { + const settings = await this.storage.getSettings(); + if (!settings.showStatsNotifications) return undefined; + + const currentUser = await this.storage.getUser(); + const expDiff = currentUser.exp ? exp - currentUser.exp : exp; + const goldDiff = currentUser.gold ? gold - currentUser.gold : gold; + + const outcome = goldDiff > 0 ? 'gained' : 'lost'; + const display = goldDiff > 0 ? 'success' : 'error'; + + return this.t.alert({ + message: `You ${outcome} ${expDiff} exp and ${goldDiff.toFixed(2)} gold.`, + display, + duration: 5 // min is 5 + }); + } +} diff --git a/src/settings.html b/src/settings.html index 6431a54..6d14bef 100644 --- a/src/settings.html +++ b/src/settings.html @@ -3,33 +3,54 @@
+ -