diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index fc695dab0e6..b35701771a9 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,4 +1,4 @@ # These are supported funding model platforms -github: [asturur] +github: [asturur, ShaMan123, melchiar] patreon: fabricJS diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 144f752174a..a20dbfcd314 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -14,7 +14,8 @@ about: Create a report with a reproducible test case in a JSFIDDLE, For anything ## Test Case -http://jsfiddle.net/fabricjs/Da7SP/ +- [ ] [Browser Issue Template](https://jsfiddle.net/Lcp2h3nv/) +- [ ] [Node Issue Template](https://codesandbox.io/s/exciting-browser-ytb701) ## Information about environment Nodejs or browser? diff --git a/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md new file mode 100644 index 00000000000..d334c3a32fb --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md @@ -0,0 +1,52 @@ +--- +name: Pull Request +about: Creating a PR + +--- + + + + + + + + +## Motivation + + + +## Description + + +## Changes + + +## Gist + + +## In Action + + diff --git a/.github/workflows/node-unit-tests.yml b/.github/workflows/node-unit-tests.yml index 895f16497d0..22755859711 100644 --- a/.github/workflows/node-unit-tests.yml +++ b/.github/workflows/node-unit-tests.yml @@ -26,4 +26,4 @@ jobs: node-version: ${{ matrix.node-version }} - run: npm ci - run: npm run build:fast - - run: npm run test + - run: npm run test -- --suite unit diff --git a/.github/workflows/npmpublish.yml b/.github/workflows/npmpublish.yml index d97740e8ac2..02a061310ce 100644 --- a/.github/workflows/npmpublish.yml +++ b/.github/workflows/npmpublish.yml @@ -4,7 +4,7 @@ on: release: branches: - master - - v3.x + - 5.x types: [published] jobs: diff --git a/.github/workflows/visual-node.yml b/.github/workflows/visual-node.yml index 82080df9c8f..b63b6bee37c 100644 --- a/.github/workflows/visual-node.yml +++ b/.github/workflows/visual-node.yml @@ -26,4 +26,4 @@ jobs: node-version: ${{ matrix.node-version }} - run: npm ci - run: npm run build:fast - - run: npm run test:visual + - run: npm run test -- --suite visual diff --git a/.gitignore b/.gitignore index 4c9697cdb8a..b4fee0d2bce 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ before_commit .idea/ /dist/fabric.require.js /dist/fabric.min.js.gz +/scripts/cli_cache.json diff --git a/CHANGELOG.md b/CHANGELOG.md index bec59fe104d..ad41dceb182 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,31 @@ # Changelog +# Changelog + +## [next] + +- MAJOR feat(fabric.Point): divide, scalarDivide, scalarDivideEquals [`#7716`](https://github.com/fabricjs/fabric.js/pull/7716) +- MAJOR feat(): Reuse fabric.Point logic for scaling and naming consistency [`#7710`](https://github.com/fabricjs/fabric.js/pull/7710) +- feat(Canvas#getCenter): migrate to `getCenterPoint` [`#7699`](https://github.com/fabricjs/fabric.js/pull/7699) +- MAJOR feat(fabric) remove callbacks in for Promise support [`#7657`](https://github.com/fabricjs/fabric.js/pull/7657) +- chore(): BREAKING Cleanup fabric.Point for v6 (#7709) [`7e563c7`](https://github.com/fabricjs/fabric.js/commit/7e563c72164070aafb03043643e85d06d0dee32c) + +## [5.2.1] + +- fix(): add `eraser` to Object state/cache props [`#7720`](https://github.com/fabricjs/fabric.js/pull/7720) + +## [5.2.0] + +- feat(fabric.Object): isType accepts multiple `type` [`#7715`](https://github.com/fabricjs/fabric.js/pull/7715) +- chore(): Replace deprecated String.prototype.substr() with Array.prototype.slice() [`#7696`](https://github.com/fabricjs/fabric.js/pull/7696) +- chore(): use Array.isArray instead of ie6+ workarounds [`#7718`](https://github.com/fabricjs/fabric.js/pull/7718) +- MINOR: feat(fabric.Canvas): add `getTopContext` method to expose the internal contextTop [`#7697`](https://github.com/fabricjs/fabric.js/pull/7697) +- fix(fabric.Object) Add cacheContext checks before trying to render on cache [`#7694`](https://github.com/fabricjs/fabric.js/pull/7694) +- tests(): node test suite enhancement [`#7691`](https://github.com/fabricjs/fabric.js/pull/7691) +- feat(Canvas#getCenter): migrate to `getCenterPoint` [`#7699`](https://github.com/fabricjs/fabric.js/pull/7699) +- updated package.json [`803ce95`](https://github.com/fabricjs/fabric.js/commit/803ce95878150fba9e4195804bccae9bcfe45c6d) +- tests(fabric.animation): fix test reliability [`4be0fb9`](https://github.com/fabricjs/fabric.js/commit/4be0fb9903e15db294b89030feb645e5da766740) + ## [5.1.0] - build(deps): bump node-fetch from 2.6.6 to 2.6.7 [`#7684`](https://github.com/fabricjs/fabric.js/pull/7684) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 02ff9da1031..1a9fdc65564 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,11 +1,5 @@ # Contributing to Fabric -1. [Questions?!?](#questions) -2. [Issue tracker](#issue-tracker) -3. [Issue tracker guidelines](#issue-tracker-guidelines) -4. [Pull requests](#pull-request) -5. [Pull request guidelines](#pull-request-guidelines) - ## Questions?!? To get your questions answered, please ask/search on [Fabric's google group], [StackOverflow] or on Fabric's IRC channel (irc://irc.freenode.net/#fabric.js). @@ -24,21 +18,28 @@ If you are sure that it's a bug in Fabric.js or a suggestion, open a new [issue] ### Issue tracker guidelines -- **Search:** Before opening a new issue please [search](https://github.com/fabricjs/fabric.js/search?q=&ref=cmdform&type=Issues) Fabric's existing issues. +If you think you found a bug in Fabric file an [issue](https://github.com/fabricjs/fabric.js/issues). + +- **Gotchas**: Make sure you didn't fall into a known [fabric-gotcha](http://fabricjs.com/fabric-gotchas) + +- **Search:** Before opening a new issue please take the time to [search](https://github.com/fabricjs/fabric.js/search?q=&ref=cmdform&type=Issues) Fabric's existing issues. This is vital to prevent spamming and to keep the community in a good state. - **Title:** Choose an informative title. - **Description:** Use the questions above to describe the issue. Add logs, screenshots or videos if that makes sense. -- **Test case:** Please post code to replicate the bug - best on [jsfiddle](http://jsfiddle.net). Ideally a failing test would be -perfect, but even a simple script demonstrating the error would suffice. You could use [this jsfiddle template](http://jsfiddle.net/fabricjs/Da7SP/) as a -starting point. +- **Test case:** Make sure you create a minimal and immediate test case, demonstrating the bug, with relevant explanations. It should be extremely **easy** and **fast** for someone to understand your bug and reproduce it. Bug templates can be found within a [bug report](https://github.com/fabricjs/fabric.js/issues/new?assignees=&labels=&template=bug_report.md) -- **Fabric.js version:** Make sure to specify which version of Fabric.js you are using. The version can be found in [fabric.js file](https://github.com/fabricjs/fabric.js/blob/master/dist/fabric.js#L5) or just by executing `fabric.version` in the browser console. +- **Fabric.js version:** Make sure to specify which version of Fabric.js you are using. The version can be found in [fabric.js file](https://github.com/fabricjs/fabric.js/blob/master/dist/fabric.js#L5) or just by executing `fabric.version` in the browser console. It is advised you upgrade to the latest version before proceeding. + +**Without these requirements issues shall be closed.** + +If you're unsure about something that is not a bug, it's best to start a [discussion](https://github.com/fabricjs/fabric.js/discussions) or create a post on [Fabric's google group](groups.google.com/forum/?fromgroups#!forum/fabricjs) where someone might clarify some of the things. ## Pull requests We are very grateful for your pull requests! This is your chance to improve Fabric for everyone else. +Before you begin read this through and take a look at [fabric-gotchas](http://fabricjs.com/fabric-gotchas) ### Online one-click setup for making PRs @@ -48,18 +49,46 @@ Contribute to fabricjs using a fully featured online development environment tha ### Setting up a local environment -Coming Soon! +1. Clone your fork of `fabric.js` to your machine +1. Install dependencies: `npm i` + +You can decide how you prefer to work: -### Working on fabricjs.com +#### `fabricjs.com` +You can start the dev server that runs fabric's website and test live changes. +After setting up `fabricjs.com` on your machine run `npm start` from the `fabric.js` folder. +This will start the dev server and watch for changes in both repositories. +While working, you might need to refresh the page for changes to take place. +See [Working on fabricjs.com](#working-on-fabricjscom). +To customize the dev server take a look at [`./scripts`](./scripts). + +#### symlinking +You can symlink `fabric.js` and test your changes in a separate project. +1. From `fabric.js` folder run `npm link` **OR** `yarn link`. +1. From your project's folder run `npm link fabric` **OR** `yarn link fabric`. + +See [npm link](https://docs.npmjs.com/cli/v8/commands/npm-link) **OR** [yarn link](https://yarnpkg.com/cli/link).\ +Don't forget to unlink the package once you're done. + +### Working on `fabricjs.com` To develop fabric's site you need to clone [`fabricjs.com`](https://github.com/fabricjs/fabricjs.com) in the same parent folder of [`fabric.js`](https://github.com/fabricjs/fabric.js), so that `fabric.js` and `fabricjs.com` are siblings. -To start the dev server run `npm start` inside the `fabricjs.com` directory (after installing dependecies). +To start the dev server run `npm start:dev` inside the `fabricjs.com` directory (after installing dependecies). If you are working on windows, check out [`jekyll` docs](https://jekyllrb.com/docs/installation/) for futher instructions. **Adding a DEMO**: Take a look at an existing [demo file](https://github.com/fabricjs/fabricjs.com/blob/gh-pages/posts/demos/_posts/2020-2-15-custom-control-render.md). Create a new file in the same directory (`posts/demos/_posts`) and you're good to go. +### Tests +Fabric has 2 test suites: +- `unit` testing logic and state +- `visual` testing visual outcome against image refs + +#### Running Tests +- **Node.js**: run `npm test -- -a -d` to run **a**ll tests in **d**ebug mode (pass `--help` to see more options). +- **Browser**: start `fabricjs.com` (`npm start`) and navigate to the `tests` tab where you will find the test interface. + ### Pull request guidelines Here are a few notes you should take into account: @@ -68,7 +97,7 @@ Here are a few notes you should take into account: - **Distribution files:** Do your changes only in the [source files](https://github.com/fabricjs/fabric.js/tree/master/src). Don't include the [distribution files](https://github.com/fabricjs/fabric.js/tree/master/dist) in your commit. -- **Add tests**: Tests are always a great addition - invest a little time and expand the [unit tests suite](https://github.com/fabricjs/fabric.js/tree/master/test/unit). More informations about [QUnit](http://qunitjs.com/) tests can be found in the [wiki](https://github.com/fabricjs/fabric.js/wiki/How-to-contribute-to-Fabric#testing-fabric). +- **Add tests**: Tests are vital - invest time to extend the [tests suites](https://github.com/fabricjs/fabric.js/tree/master/test). More information about [QUnit](http://qunitjs.com/) tests can be found in the [wiki](https://github.com/fabricjs/fabric.js/wiki/How-to-contribute-to-Fabric#testing-fabric). - **Add documentation:** Fabric uses [JSDoc 3] for documentation. The generated documentation can be found at [fabricjs.com](http://fabricjs.com/docs). @@ -89,4 +118,4 @@ Here are a few notes you should take into account: [fabricjs.com]: http://fabricjs.com/demos [fabricjs.com/docs]: http://fabricjs.com/docs [JSDoc 3]: http://usejsdoc.org/ -[issue]: https://github.com/kangax/fabric.js/issues +[issue]: https://github.com/fabric/fabric.js/issues diff --git a/build.js b/build.js index 38f3b45ab05..91fe3924799 100644 --- a/build.js +++ b/build.js @@ -178,6 +178,7 @@ var filesToInclude = [ 'src/shapes/object.class.js', 'src/mixins/object_origin.mixin.js', 'src/mixins/object_geometry.mixin.js', + 'src/mixins/object_ancestry.mixin.js', 'src/mixins/object_stacking.mixin.js', 'src/mixins/object.svg_export.js', 'src/mixins/stateful.mixin.js', diff --git a/lib/event.js b/lib/event.js index b754bc10982..93f12c2deb6 100644 --- a/lib/event.js +++ b/lib/event.js @@ -237,7 +237,7 @@ var eventManager = function(target, type, listener, configure, trigger, fromOver } return createBatchCommands(events); } else if (type.indexOf("on") === 0) { // to support things like "onclick" instead of "click" - type = type.substr(2); + type = type.slice(2); } // Ensure listener is a function. @@ -1368,7 +1368,7 @@ root.gesture = function(conf) { var dx = touch.move.x - self.x; var dy = touch.move.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); - // If touch start.distance from centroid is 0, scale should not be updated. + // If touch start.distance from centroid is 0, scale should not be updated. // This prevents dividing by 0 in cases where start.distance is oddly 0. if (start.distance !== 0) { scale += distance / start.distance; diff --git a/old-travis-reference.yml b/old-travis-reference.yml index cacd58bd2da..ec2bb767705 100644 --- a/old-travis-reference.yml +++ b/old-travis-reference.yml @@ -68,7 +68,7 @@ jobs: - stage: Visual Tests env: LAUNCHER=Node node_js: "12" - script: npm run build:fast && npm run test:visual + script: npm run build:fast && npm run test -- --suite visual - stage: Visual Tests env: LAUNCHER=Chrome script: npm run build:fast && testem ci --port 8080 -f testem-visual.json -l $LAUNCHER @@ -84,13 +84,13 @@ jobs: # - stage: Extra OS # env: CANFAIL=TRUE # node_js: "10" - # script: npm run build:fast && npm run test:visual + # script: npm run build:fast && npm run test -- --suite visual # before_install: brew upgrade giflib && brew install pkg-config cairo pango libpng jpeg librsvg # os: osx # - stage: Extra OS # env: CANFAIL=TRUE # node_js: "10" - # script: npm run build:fast && npm run test:visual + # script: npm run build:fast && npm run test -- --suite visual # os: windows script: npm run build:fast && npm run test diff --git a/package-lock.json b/package-lock.json index 4387e331a72..a27329f1b92 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,8 +1,6352 @@ { "name": "fabric", - "version": "5.0.0", - "lockfileVersion": 1, + "version": "5.1.0", + "lockfileVersion": 2, "requires": true, + "packages": { + "": { + "name": "fabric", + "version": "5.1.0", + "license": "MIT", + "devDependencies": { + "@types/fs-extra": "^9.0.13", + "@types/lodash": "^4.14.180", + "@types/node": "^17.0.21", + "ansi-escape": "^1.1.0", + "auto-changelog": "^2.3.0", + "chalk": "^2.4.1", + "commander": "^9.1.0", + "deep-object-diff": "^1.1.7", + "eslint": "4.18.x", + "fs-extra": "^10.0.1", + "fuzzy": "^0.1.3", + "inquirer": "^8.2.1", + "inquirer-checkbox-plus-prompt": "^1.0.1", + "moment": "^2.29.1", + "nyc": "^15.1.0", + "pixelmatch": "^4.0.2", + "qunit": "^2.17.2", + "testem": "^3.2.0", + "uglify-js": "^3.3.28" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "canvas": "^2.8.0", + "jsdom": "^19.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", + "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.16.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.4.tgz", + "integrity": "sha512-1o/jo7D+kC9ZjHX5v+EHrdjl3PhxMrLSOTGsOdHJ+KL8HCaEK6ehrVL2RS6oHDZp+L7xLirLrPmQtEng769J/Q==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.7.tgz", + "integrity": "sha512-aeLaqcqThRNZYmbMqtulsetOQZ/5gbR/dWruUCJcpas4Qoyy+QeagfDsPdMrqwsPRDNxJvBlRiZxxX7THO7qtA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.16.7", + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helpers": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.16.7", + "@babel/types": "^7.16.7", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/core/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@babel/generator": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.7.tgz", + "integrity": "sha512-/ST3Sg8MLGY5HVYmrjOgL60ENux/HfO/CsUh7y4MalThufhE/Ff/6EibFDHi4jiDCaWfJKoqbE6oTh21c5hrRg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz", + "integrity": "sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.16.4", + "@babel/helper-validator-option": "^7.16.7", + "browserslist": "^4.17.5", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", + "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", + "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", + "dev": true, + "dependencies": { + "@babel/helper-get-function-arity": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-get-function-arity": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", + "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", + "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", + "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz", + "integrity": "sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-simple-access": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/helper-validator-identifier": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz", + "integrity": "sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", + "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", + "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", + "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.7.tgz", + "integrity": "sha512-9ZDoqtfY7AuEOt3cxchfii6C7GDyyMBffktR5B2jvWv8u2+efwvpnVKXMWzNehqy68tKgAfSwfdw/lWpthS2bw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.7.tgz", + "integrity": "sha512-aKpPMfLvGO3Q97V0qhw/V2SWNWlwfJknuwAunU7wZLSfrM4xTBvg7E5opUVi1kJTBKihE38CPg4nBiqX83PWYw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.16.7", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/@babel/parser": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.7.tgz", + "integrity": "sha512-sR4eaSrnM7BV7QPzGfEX5paG/6wrZM3I0HDzfIAK06ESvo9oy3xBuVBxE3MbQaKNhvg8g/ixjMWo2CGpzpHsDA==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", + "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.7.tgz", + "integrity": "sha512-8KWJPIb8c2VvY8AJrydh6+fVRo2ODx1wYBU2398xJVq0JomuLBZmVQzLPBblJgHIGYG4znCpUZUZ0Pt2vdmVYQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.16.7", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/types": "^7.16.7", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.7.tgz", + "integrity": "sha512-E8HuV7FO9qLpx6OtoGfUQ2cjIYnbFwvZWYBS+87EwtdMvmUPJSwykpovFB+8insbpF0uJcpr8KMUi64XZntZcg==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.16.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.8.tgz", + "integrity": "sha512-CMGKi28CF+qlbXh26hDe6NxCd7amqeAzEqnS6IHeO6LoaKyM/n+Xw3HT1COdq8cuioOdlKdqn/hCmqPUOMOywg==", + "optional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.5", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "optional": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@types/component-emitter": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.11.tgz", + "integrity": "sha512-SRXjM+tfsSlA9VuG8hGO2nft2p8zjXCK1VcC6N4NXbBbYbSia9kzCChYQajIjzIqOOOuh5Ock6MmV2oux4jDZQ==", + "dev": true + }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", + "dev": true + }, + "node_modules/@types/cors": { + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", + "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==", + "dev": true + }, + "node_modules/@types/fs-extra": { + "version": "9.0.13", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", + "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/lodash": { + "version": "4.14.180", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.180.tgz", + "integrity": "sha512-XOKXa1KIxtNXgASAnwj7cnttJxS4fksBRywK/9LzRV5YxrF80BXZIGeQSuoESQ/VkUj30Ae0+YcuHc15wJCB2g==", + "dev": true + }, + "node_modules/@types/node": { + "version": "17.0.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", + "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==", + "dev": true + }, + "node_modules/@xmldom/xmldom": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.7.5.tgz", + "integrity": "sha512-V3BIhmY36fXZ1OtVcI9W+FxQqxVLsPKcNjWigIaa81dLC9IolJl5Mt4Cvhmr0flUnjSpTdrbMTSbXqYqV5dT6A==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/abab": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", + "optional": true + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "optional": true + }, + "node_modules/accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "dev": true, + "dependencies": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "optional": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "optional": true, + "dependencies": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + } + }, + "node_modules/acorn-globals/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "optional": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", + "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", + "dev": true, + "dependencies": { + "acorn": "^3.0.4" + } + }, + "node_modules/acorn-jsx/node_modules/acorn": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "optional": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "optional": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "dev": true, + "dependencies": { + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" + } + }, + "node_modules/ajv-keywords": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz", + "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=", + "dev": true, + "peerDependencies": { + "ajv": "^5.0.0" + } + }, + "node_modules/ansi-escape": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-escape/-/ansi-escape-1.1.0.tgz", + "integrity": "sha1-ithZ6Epp4P+Rd5aUeTqS5OjAXpk=", + "dev": true + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "devOptional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/append-transform": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", + "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", + "dev": true, + "dependencies": { + "default-require-extensions": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "optional": true + }, + "node_modules/archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", + "dev": true + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "optional": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, + "node_modules/async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=", + "dev": true + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "optional": true + }, + "node_modules/auto-changelog": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/auto-changelog/-/auto-changelog-2.3.0.tgz", + "integrity": "sha512-S2B+RtTgytsa7l5iFGBoWT9W9ylITT5JJ8OaMJ7nrwvnlRm1dSS2tghaYueDeInZZafOE+1llH3tUQjMDRVS1g==", + "dev": true, + "dependencies": { + "commander": "^5.0.0", + "handlebars": "^4.7.3", + "node-fetch": "^2.6.0", + "parse-github-url": "^1.0.2", + "semver": "^6.3.0" + }, + "bin": { + "auto-changelog": "src/index.js" + }, + "engines": { + "node": ">=8.3" + } + }, + "node_modules/auto-changelog/node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/auto-changelog/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/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, + "dependencies": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + } + }, + "node_modules/babel-code-frame/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/babel-code-frame/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/babel-code-frame/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/babel-code-frame/node_modules/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, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/babel-code-frame/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/backbone": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/backbone/-/backbone-1.4.0.tgz", + "integrity": "sha512-RLmDrRXkVdouTg38jcgHhyQ/2zjg7a8E6sz2zxfz21Hh17xDJYUHBZimVIt5fUyS8vbfpeSmTL3gUjTEvUV3qQ==", + "dev": true, + "dependencies": { + "underscore": ">=1.8.3" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "devOptional": true + }, + "node_modules/base64-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.1.tgz", + "integrity": "sha512-vFIUq7FdLtjZMhATwDul5RZWv2jpXQ09Pd6jcVEOvIsqCWTRFD/ONHNfyOS8dA/Ippi5dsIgpyKWKZaAKZltbA==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "dev": true, + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "node_modules/body-parser": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.1.tgz", + "integrity": "sha512-8ljfQi5eBk8EJfECMrgqNGWPEY5jWP+1IzkzkGdFFEwFQZZyaZ21UqdaHktgiMlH0xLHqIFtE/u2OYE5dOtViA==", + "dev": true, + "dependencies": { + "bytes": "3.1.1", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.9.6", + "raw-body": "2.4.2", + "type-is": "~1.6.18" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/bytes": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", + "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "devOptional": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", + "optional": true + }, + "node_modules/browserslist": { + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz", + "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==", + "dev": true, + "dependencies": { + "caniuse-lite": "^1.0.30001286", + "electron-to-chromium": "^1.4.17", + "escalade": "^3.1.1", + "node-releases": "^2.0.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/caching-transform": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", + "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", + "dev": true, + "dependencies": { + "hasha": "^5.0.0", + "make-dir": "^3.0.0", + "package-hash": "^4.0.0", + "write-file-atomic": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/caller-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", + "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", + "dev": true, + "dependencies": { + "callsites": "^0.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/callsites": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", + "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001298", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001298.tgz", + "integrity": "sha512-AcKqikjMLlvghZL/vfTHorlQsLDhGRalYf1+GmWCf5SCMziSGjRYQW/JEksj14NaYHIR6KIhrFAy0HV5C25UzQ==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } + }, + "node_modules/canvas": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.8.0.tgz", + "integrity": "sha512-gLTi17X8WY9Cf5GZ2Yns8T5lfBOcGgFehDFb+JQwDqdOoBOcECS9ZWMEAqMSVcMYwXD659J8NyzjRY/2aE+C2Q==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.0", + "nan": "^2.14.0", + "simple-get": "^3.0.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "node_modules/charm": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/charm/-/charm-1.0.2.tgz", + "integrity": "sha1-it02cVOm2aWBMxBSxAkJkdqZXjU=", + "dev": true, + "dependencies": { + "inherits": "^2.0.1" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "optional": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/circular-json": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", + "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", + "deprecated": "CircularJSON is in maintenance only, flatted is its successor.", + "dev": true + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", + "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "optional": true, + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "optional": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.1.0.tgz", + "integrity": "sha512-i0/MaqBtdbnJ4XQs4Pmyb+oFQl+q0lsAmokVUH92SlSw4fkeAcG3bVon+Qt7hmtF+u3Het6o4VgrcY3qAoEB6w==", + "dev": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "node_modules/component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "dependencies": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/compression/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "devOptional": true + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/concat-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/concat-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "devOptional": true + }, + "node_modules/consolidate": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.15.1.tgz", + "integrity": "sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==", + "dev": true, + "dependencies": { + "bluebird": "^3.1.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/convert-source-map/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "dependencies": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "node_modules/cross-spawn/node_modules/lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "dependencies": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "node_modules/cross-spawn/node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + }, + "node_modules/cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "optional": true + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "optional": true, + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "optional": true + }, + "node_modules/data-urls": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.1.tgz", + "integrity": "sha512-Ds554NeT5Gennfoo9KN50Vh6tpgtvYEwraYjejXnyTpu1C7oXKxdFk75REooENHE8ndTVOJuv+BEs4/J/xcozw==", + "optional": true, + "dependencies": { + "abab": "^2.0.3", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^10.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/data-urls/node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "optional": true, + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/data-urls/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "optional": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/data-urls/node_modules/whatwg-url": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-10.0.0.tgz", + "integrity": "sha512-CLxxCmdUby142H5FZzn4D8ikO1cmypvXVQktsgosNy4a4BHrDHeciBBGZhb0bNoR5/MltoCatso+vFjjGx8t0w==", + "optional": true, + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "devOptional": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decimal.js": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", + "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==", + "optional": true + }, + "node_modules/decompress-response": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", + "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", + "optional": true, + "dependencies": { + "mimic-response": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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==", + "devOptional": true + }, + "node_modules/deep-object-diff": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/deep-object-diff/-/deep-object-diff-1.1.7.tgz", + "integrity": "sha512-QkgBca0mL08P6HiOjoqvmm6xOAl2W6CT2+34Ljhg0OeFan8cwlcdq8jrLKsBBuUFAZLsN5b6y491KdKEoSo9lg==", + "dev": true + }, + "node_modules/default-require-extensions": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", + "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", + "dev": true, + "dependencies": { + "strip-bom": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "dev": true, + "dependencies": { + "clone": "^1.0.2" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "optional": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "devOptional": true + }, + "node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "optional": true, + "dependencies": { + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/domexception/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "optional": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "node_modules/electron-to-chromium": { + "version": "1.4.38", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.38.tgz", + "integrity": "sha512-WhHt3sZazKj0KK/UpgsbGQnUUoFeAHVishzHFExMxagpZgjiGYSC9S0ZlbhCfSH2L2i+2A1yyqOIliTctMx7KQ==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "devOptional": true + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/engine.io": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.1.2.tgz", + "integrity": "sha512-v/7eGHxPvO2AWsksyx2PUsQvBafuvqs0jJJQ0FdmJG1b9qIvgSbqDRGwNhfk2XHaTTbTXiC4quRE8Q9nRjsrQQ==", + "dev": true, + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.0", + "ws": "~8.2.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.2.tgz", + "integrity": "sha512-wuiO7qO/OEkPJSFueuATIXtrxF7/6GTbAO9QLv7nnbjwZ5tYhLm9zxvLwxstRs0dcT0KUlWTjtIOs1T86jt12g==", + "dev": true, + "dependencies": { + "base64-arraybuffer": "~1.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/ws": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/escodegen": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "optional": true, + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/eslint": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.18.2.tgz", + "integrity": "sha512-qy4i3wODqKMYfz9LUI8N2qYDkHkoieTbiHpMrYUI/WbjhXJQr7lI4VngixTgaG+yHX+NBCv7nW4hA0ShbvaNKw==", + "dev": true, + "dependencies": { + "ajv": "^5.3.0", + "babel-code-frame": "^6.22.0", + "chalk": "^2.1.0", + "concat-stream": "^1.6.0", + "cross-spawn": "^5.1.0", + "debug": "^3.1.0", + "doctrine": "^2.1.0", + "eslint-scope": "^3.7.1", + "eslint-visitor-keys": "^1.0.0", + "espree": "^3.5.2", + "esquery": "^1.0.0", + "esutils": "^2.0.2", + "file-entry-cache": "^2.0.0", + "functional-red-black-tree": "^1.0.1", + "glob": "^7.1.2", + "globals": "^11.0.1", + "ignore": "^3.3.3", + "imurmurhash": "^0.1.4", + "inquirer": "^3.0.6", + "is-resolvable": "^1.0.0", + "js-yaml": "^3.9.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.4", + "minimatch": "^3.0.2", + "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", + "require-uncached": "^1.0.3", + "semver": "^5.3.0", + "strip-ansi": "^4.0.0", + "strip-json-comments": "~2.0.1", + "table": "4.0.2", + "text-table": "~0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-scope": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.3.tgz", + "integrity": "sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA==", + "dev": true, + "dependencies": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/eslint-scope/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint/node_modules/ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint/node_modules/ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint/node_modules/chardet": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", + "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", + "dev": true + }, + "node_modules/eslint/node_modules/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, + "dependencies": { + "restore-cursor": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint/node_modules/cli-width": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", + "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", + "dev": true + }, + "node_modules/eslint/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint/node_modules/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, + "dependencies": { + "chardet": "^0.4.0", + "iconv-lite": "^0.4.17", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/eslint/node_modules/figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint/node_modules/inquirer": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", + "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", + "dev": true, + "dependencies": { + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.0", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^2.0.4", + "figures": "^2.0.0", + "lodash": "^4.3.0", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rx-lite": "^4.0.8", + "rx-lite-aggregates": "^4.0.8", + "string-width": "^2.1.0", + "strip-ansi": "^4.0.0", + "through": "^2.3.6" + } + }, + "node_modules/eslint/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint/node_modules/mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint/node_modules/mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/eslint/node_modules/mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, + "node_modules/eslint/node_modules/onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "dependencies": { + "mimic-fn": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint/node_modules/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, + "dependencies": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/eslint/node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/espree": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", + "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", + "dev": true, + "dependencies": { + "acorn": "^5.5.0", + "acorn-jsx": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/espree/node_modules/acorn": { + "version": "5.7.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", + "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "devOptional": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/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, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "devOptional": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "devOptional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "node_modules/events-to-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/events-to-array/-/events-to-array-1.1.2.tgz", + "integrity": "sha1-LUH1Y+H+QA7Uli/hpNXGp1Od9/Y=", + "dev": true + }, + "node_modules/execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "dependencies": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/execa/node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/execa/node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/execa/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/express": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.2.tgz", + "integrity": "sha512-oxlxJxcQlYwqPWKVJJtvQiwHgosH/LrLSPA+H4UxpyvSS6jC5aH+5MoHFM+KABgTOt0APue4w66Ha8jCUo9QGg==", + "dev": true, + "dependencies": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.4.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.9.6", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.17.2", + "serve-static": "1.14.2", + "setprototypeof": "1.2.0", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/external-editor/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", + "dev": true + }, + "node_modules/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 + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "devOptional": true + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/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, + "dependencies": { + "flat-cache": "^1.2.1", + "object-assign": "^4.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fireworm": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/fireworm/-/fireworm-0.7.1.tgz", + "integrity": "sha1-zPIPeUHxCIg/zduZOD2+bhhhx1g=", + "dev": true, + "dependencies": { + "async": "~0.2.9", + "is-type": "0.0.1", + "lodash.debounce": "^3.1.1", + "lodash.flatten": "^3.0.2", + "minimatch": "^3.0.2" + } + }, + "node_modules/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, + "dependencies": { + "circular-json": "^0.3.1", + "graceful-fs": "^4.1.2", + "rimraf": "~2.6.2", + "write": "^0.2.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/flat-cache/node_modules/rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/follow-redirects": { + "version": "1.14.8", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz", + "integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/foreground-child/node_modules/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, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/foreground-child/node_modules/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, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/foreground-child/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/foreground-child/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "optional": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fromentries": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", + "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/fs-extra": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.1.tgz", + "integrity": "sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs-extra/node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "devOptional": true + }, + "node_modules/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 + }, + "node_modules/fuzzy": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/fuzzy/-/fuzzy-0.1.3.tgz", + "integrity": "sha1-THbsL/CsGjap3M+aAN+GIweNTtg=", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "optional": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "devOptional": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/globalyzer": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz", + "integrity": "sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==", + "dev": true + }, + "node_modules/globrex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", + "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", + "dev": true + }, + "node_modules/graceful-fs": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", + "dev": true + }, + "node_modules/growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", + "dev": true + }, + "node_modules/handlebars": { + "version": "4.7.7", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", + "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/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, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-ansi/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "devOptional": true + }, + "node_modules/hasha": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", + "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", + "dev": true, + "dependencies": { + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "optional": true, + "dependencies": { + "whatwg-encoding": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "optional": true, + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "optional": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", + "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", + "dev": true + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "devOptional": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "devOptional": true + }, + "node_modules/inquirer": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.1.tgz", + "integrity": "sha512-pxhBaw9cyTFMjwKtkjePWDhvwzvrNGAw7En4hottzlPvz80GZaMZthdDU35aA6/f5FRZf3uhE057q8w1DE3V2g==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/inquirer-checkbox-plus-prompt": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/inquirer-checkbox-plus-prompt/-/inquirer-checkbox-plus-prompt-1.0.1.tgz", + "integrity": "sha1-VP8e0Jd3oQNThWIna1z0Uhox0W0=", + "dev": true, + "dependencies": { + "cli-cursor": "^2.1.0", + "figures": "^2.0.0", + "inquirer": "^5.1.0", + "lodash": "^4.17.5" + } + }, + "node_modules/inquirer-checkbox-plus-prompt/node_modules/ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer-checkbox-plus-prompt/node_modules/ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer-checkbox-plus-prompt/node_modules/chardet": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", + "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", + "dev": true + }, + "node_modules/inquirer-checkbox-plus-prompt/node_modules/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, + "dependencies": { + "restore-cursor": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer-checkbox-plus-prompt/node_modules/cli-width": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", + "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", + "dev": true + }, + "node_modules/inquirer-checkbox-plus-prompt/node_modules/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, + "dependencies": { + "chardet": "^0.4.0", + "iconv-lite": "^0.4.17", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/inquirer-checkbox-plus-prompt/node_modules/figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer-checkbox-plus-prompt/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inquirer-checkbox-plus-prompt/node_modules/inquirer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz", + "integrity": "sha512-E9BmnJbAKLPGonz0HeWHtbKf+EeSP93paWO3ZYoUpq/aowXvYGjjCSuashhXPpzbArIjBbji39THkxTz9ZeEUQ==", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/inquirer-checkbox-plus-prompt/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer-checkbox-plus-prompt/node_modules/mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer-checkbox-plus-prompt/node_modules/mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, + "node_modules/inquirer-checkbox-plus-prompt/node_modules/onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "dependencies": { + "mimic-fn": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer-checkbox-plus-prompt/node_modules/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, + "dependencies": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer-checkbox-plus-prompt/node_modules/rxjs": { + "version": "5.5.12", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.12.tgz", + "integrity": "sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw==", + "dev": true, + "dependencies": { + "symbol-observable": "1.0.1" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/inquirer-checkbox-plus-prompt/node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer-checkbox-plus-prompt/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/inquirer/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/inquirer/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/inquirer/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/inquirer/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inquirer/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "devOptional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "optional": true + }, + "node_modules/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 + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-type": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/is-type/-/is-type-0.0.1.tgz", + "integrity": "sha1-9lHYXDZdRJVdFKUdjXBh8/a0d5w=", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-hook": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", + "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", + "dev": true, + "dependencies": { + "append-transform": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/istanbul-lib-processinfo": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", + "integrity": "sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw==", + "dev": true, + "dependencies": { + "archy": "^1.0.0", + "cross-spawn": "^7.0.0", + "istanbul-lib-coverage": "^3.0.0-alpha.1", + "make-dir": "^3.0.0", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "uuid": "^3.3.3" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-processinfo/node_modules/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, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/istanbul-lib-processinfo/node_modules/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, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-processinfo/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-processinfo/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.3.tgz", + "integrity": "sha512-x9LtDVtfm/t1GFiLl3NffC7hz+I1ragvgX1P/Lg1NlIagifZDKUkuuaAxH/qpwj2IuEfD8G2Bs/UKp+sZ/pKkg==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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 + }, + "node_modules/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, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdom": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-19.0.0.tgz", + "integrity": "sha512-RYAyjCbxy/vri/CfnjUWJQQtZ3LKlLnDqj+9XLNnJPgEGeirZs3hllKR20re8LUZ6o1b1X4Jat+Qd26zmP41+A==", + "optional": true, + "dependencies": { + "abab": "^2.0.5", + "acorn": "^8.5.0", + "acorn-globals": "^6.0.0", + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "data-urls": "^3.0.1", + "decimal.js": "^10.3.1", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^3.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^10.0.0", + "ws": "^8.2.3", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "optional": true, + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/jsdom/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "optional": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/jsdom/node_modules/whatwg-url": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-10.0.0.tgz", + "integrity": "sha512-CLxxCmdUby142H5FZzn4D8ikO1cmypvXVQktsgosNy4a4BHrDHeciBBGZhb0bNoR5/MltoCatso+vFjjGx8t0w==", + "optional": true, + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true + }, + "node_modules/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 + }, + "node_modules/json5": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonfile/node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "devOptional": true, + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash._baseflatten": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/lodash._baseflatten/-/lodash._baseflatten-3.1.4.tgz", + "integrity": "sha1-B3D/gBMa9uNPO1EXlqe6UhTmX/c=", + "dev": true, + "dependencies": { + "lodash.isarguments": "^3.0.0", + "lodash.isarray": "^3.0.0" + } + }, + "node_modules/lodash._getnative": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", + "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", + "dev": true + }, + "node_modules/lodash._isiterateecall": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", + "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=", + "dev": true + }, + "node_modules/lodash.assignin": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz", + "integrity": "sha1-uo31+4QesKPoBEIysOJjqNxqKKI=", + "dev": true + }, + "node_modules/lodash.castarray": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz", + "integrity": "sha1-wCUTUV4wna3dTCTGDP3c9ZdtkRU=", + "dev": true + }, + "node_modules/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 + }, + "node_modules/lodash.debounce": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-3.1.1.tgz", + "integrity": "sha1-gSIRw3ipTMKdWqTjNGzwv846ffU=", + "dev": true, + "dependencies": { + "lodash._getnative": "^3.0.0" + } + }, + "node_modules/lodash.find": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.find/-/lodash.find-4.6.0.tgz", + "integrity": "sha1-ywcE1Hq3F4n/oN6Ll92Sb7iLE7E=", + "dev": true + }, + "node_modules/lodash.flatten": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-3.0.2.tgz", + "integrity": "sha1-3hz1d1j49EeTGdNcPpzGDEUBk4w=", + "dev": true, + "dependencies": { + "lodash._baseflatten": "^3.0.0", + "lodash._isiterateecall": "^3.0.0" + } + }, + "node_modules/lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", + "dev": true + }, + "node_modules/lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=", + "dev": true + }, + "node_modules/lodash.isarray": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", + "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", + "dev": true + }, + "node_modules/lodash.uniqby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", + "integrity": "sha1-2ZwHpmnp5tJOE2Lf4mbGdhavEwI=", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/log-symbols/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/log-symbols/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-symbols/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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==", + "devOptional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "devOptional": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "devOptional": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", + "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", + "devOptional": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.34", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", + "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", + "devOptional": true, + "dependencies": { + "mime-db": "1.51.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-response": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", + "optional": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "devOptional": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "node_modules/minipass": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", + "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "optional": true, + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "optional": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "devOptional": true + }, + "node_modules/mustache": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-3.2.1.tgz", + "integrity": "sha512-RERvMFdLpaFfSRIEe632yDm5nsd0SDKn8hGmcUwswnyiE5mtdZLDybtHAz6hjJhawokF0hXvGLtx9mrQfm6FkA==", + "dev": true, + "bin": { + "mustache": "bin/mustache" + }, + "engines": { + "npm": ">=1.4.0" + } + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "node_modules/nan": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", + "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", + "optional": true + }, + "node_modules/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 + }, + "node_modules/negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "devOptional": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-notifier": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-9.0.1.tgz", + "integrity": "sha512-fPNFIp2hF/Dq7qLDzSg4vZ0J4e9v60gJR+Qx7RbjbWqzPDdEqeVpEx5CFeDAELIl+A/woaaNn1fQ5nEVerMxJg==", + "dev": true, + "dependencies": { + "growly": "^1.3.0", + "is-wsl": "^2.2.0", + "semver": "^7.3.2", + "shellwords": "^0.1.1", + "uuid": "^8.3.0", + "which": "^2.0.2" + } + }, + "node_modules/node-notifier/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/node-notifier/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/node-preload": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", + "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", + "dev": true, + "dependencies": { + "process-on-spawn": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/node-releases": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", + "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==", + "dev": true + }, + "node_modules/node-watch": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/node-watch/-/node-watch-0.7.3.tgz", + "integrity": "sha512-3l4E8uMPY1HdMMryPRUAl+oIHtXtyiTlIiESNSVSNxcPfzAFzeTbXFQkZfAwBbo0B1qMSG8nUABx+Gd+YrbKrQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "optional": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "dependencies": { + "path-key": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "optional": true, + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "node_modules/number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nwsapi": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", + "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", + "optional": true + }, + "node_modules/nyc": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", + "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", + "dev": true, + "dependencies": { + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "caching-transform": "^4.0.0", + "convert-source-map": "^1.7.0", + "decamelize": "^1.2.0", + "find-cache-dir": "^3.2.0", + "find-up": "^4.1.0", + "foreground-child": "^2.0.0", + "get-package-type": "^0.1.0", + "glob": "^7.1.6", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-hook": "^3.0.0", + "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-processinfo": "^2.0.2", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "make-dir": "^3.0.0", + "node-preload": "^0.2.1", + "p-map": "^3.0.0", + "process-on-spawn": "^1.0.0", + "resolve-from": "^5.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "spawn-wrap": "^2.0.0", + "test-exclude": "^6.0.0", + "yargs": "^15.0.2" + }, + "bin": { + "nyc": "bin/nyc.js" + }, + "engines": { + "node": ">=8.9" + } + }, + "node_modules/nyc/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "devOptional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "devOptional": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "devOptional": true, + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ora/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/ora/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/ora/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/package-hash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", + "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.15", + "hasha": "^5.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/parse-github-url": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-github-url/-/parse-github-url-1.0.2.tgz", + "integrity": "sha512-kgBf6avCbO3Cn6+RnzRGLkUsv4ZVqv/VfAYkRsyBcgkshNvVBkRn1FEZcW0Jb+npXQWm2vHPnnOqFteZxRRGNw==", + "dev": true, + "bin": { + "parse-github-url": "cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "optional": true + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "devOptional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/pixelmatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-4.0.2.tgz", + "integrity": "sha1-j0fc7FARtHe2fbA8JDvB8wheiFQ=", + "dev": true, + "dependencies": { + "pngjs": "^3.0.0" + }, + "bin": { + "pixelmatch": "bin/pixelmatch" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/pngjs": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", + "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "devOptional": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/printf": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/printf/-/printf-0.6.1.tgz", + "integrity": "sha512-is0ctgGdPJ5951KulgfzvHGwJtZ5ck8l042vRkV6jrkpBzTmb/lueTqguWHy2JfVA+RY6gFVlaZgUS0j7S/dsw==", + "dev": true, + "engines": { + "node": ">= 0.9.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/process-on-spawn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", + "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", + "dev": true, + "dependencies": { + "fromentries": "^1.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "node_modules/psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "optional": true + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", + "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==", + "dev": true, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/qunit": { + "version": "2.18.1", + "resolved": "https://registry.npmjs.org/qunit/-/qunit-2.18.1.tgz", + "integrity": "sha512-A2Adgr/DeMQOJZFVllyQi2wiGJVVXGSRRwMe39fNfuuftUYHHpGRTWUhBa8wNblunCAOUCt+1uFcg1L7NaxQTA==", + "dev": true, + "dependencies": { + "commander": "7.2.0", + "node-watch": "0.7.3", + "tiny-glob": "0.2.9" + }, + "bin": { + "qunit": "bin/qunit.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/qunit/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.2.tgz", + "integrity": "sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ==", + "dev": true, + "dependencies": { + "bytes": "3.1.1", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/bytes": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", + "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "devOptional": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/release-zalgo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", + "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", + "dev": true, + "dependencies": { + "es6-error": "^4.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "node_modules/require-uncached": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", + "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", + "dev": true, + "dependencies": { + "caller-path": "^0.1.0", + "resolve-from": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "devOptional": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/rx-lite": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", + "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=", + "dev": true + }, + "node_modules/rx-lite-aggregates": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", + "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", + "dev": true, + "dependencies": { + "rx-lite": "*" + } + }, + "node_modules/rxjs": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz", + "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "devOptional": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "devOptional": true + }, + "node_modules/saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "optional": true, + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "devOptional": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", + "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "1.8.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/serve-static": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", + "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", + "dev": true, + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "devOptional": true + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", + "dev": true + }, + "node_modules/signal-exit": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", + "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==", + "devOptional": true + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "optional": true + }, + "node_modules/simple-get": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", + "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", + "optional": true, + "dependencies": { + "decompress-response": "^4.2.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/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, + "dependencies": { + "is-fullwidth-code-point": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/socket.io": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.4.1.tgz", + "integrity": "sha512-s04vrBswdQBUmuWJuuNTmXUVJhP0cVky8bBDhdkf8y0Ptsu7fKU2LuLbts9g+pdmAdyMMn8F/9Mf1/wbtUN0fg==", + "dev": true, + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "debug": "~4.3.2", + "engine.io": "~6.1.0", + "socket.io-adapter": "~2.3.3", + "socket.io-parser": "~4.0.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.3.3.tgz", + "integrity": "sha512-Qd/iwn3VskrpNO60BeRyCyr8ZWw9CPZyitW4AQwmRZ8zCiyDiL+znRnWX6tDHXnWn1sJrM1+b6Mn6wEDJJ4aYQ==", + "dev": true + }, + "node_modules/socket.io-parser": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz", + "integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==", + "dev": true, + "dependencies": { + "@types/component-emitter": "^1.2.10", + "component-emitter": "~1.3.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "devOptional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spawn-args": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/spawn-args/-/spawn-args-0.2.0.tgz", + "integrity": "sha1-+30L0dcP1DFr2ePew4nmX51jYbs=", + "dev": true + }, + "node_modules/spawn-wrap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", + "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", + "dev": true, + "dependencies": { + "foreground-child": "^2.0.0", + "is-windows": "^1.0.2", + "make-dir": "^3.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "which": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/spawn-wrap/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "devOptional": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "devOptional": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "devOptional": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/styled_string": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/styled_string/-/styled_string-0.0.1.tgz", + "integrity": "sha1-0ieCvYEpVFm8Tx3xjEutjpTdEko=", + "dev": true + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "optional": true + }, + "node_modules/table": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz", + "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==", + "dev": true, + "dependencies": { + "ajv": "^5.2.3", + "ajv-keywords": "^2.1.0", + "chalk": "^2.1.0", + "lodash": "^4.17.4", + "slice-ansi": "1.0.0", + "string-width": "^2.1.1" + } + }, + "node_modules/table/node_modules/ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/table/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/table/node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/table/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/tap-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/tap-parser/-/tap-parser-7.0.0.tgz", + "integrity": "sha512-05G8/LrzqOOFvZhhAk32wsGiPZ1lfUrl+iV7+OkKgfofZxiceZWMHkKmow71YsyVQ8IvGBP2EjcIjE5gL4l5lA==", + "dev": true, + "dependencies": { + "events-to-array": "^1.0.1", + "js-yaml": "^3.2.7", + "minipass": "^2.2.0" + }, + "bin": { + "tap-parser": "bin/cmd.js" + } + }, + "node_modules/tap-parser/node_modules/minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "node_modules/tap-parser/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/tar": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "optional": true, + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/testem": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/testem/-/testem-3.6.0.tgz", + "integrity": "sha512-sXwx2IlOadOhrKf0hsV1Yt/yuYhdfrtJ4dpp7T6pFN62GjMyKifjAv2SFm+4zYHee1JwxheO7JUL0+3iN0rlHw==", + "dev": true, + "dependencies": { + "@xmldom/xmldom": "^0.7.1", + "backbone": "^1.1.2", + "bluebird": "^3.4.6", + "charm": "^1.0.0", + "commander": "^2.6.0", + "compression": "^1.7.4", + "consolidate": "^0.15.1", + "execa": "^1.0.0", + "express": "^4.10.7", + "fireworm": "^0.7.0", + "glob": "^7.0.4", + "http-proxy": "^1.13.1", + "js-yaml": "^3.2.5", + "lodash.assignin": "^4.1.0", + "lodash.castarray": "^4.4.0", + "lodash.clonedeep": "^4.4.1", + "lodash.find": "^4.5.1", + "lodash.uniqby": "^4.7.0", + "mkdirp": "^0.5.1", + "mustache": "^3.0.0", + "node-notifier": "^9.0.1", + "npmlog": "^4.0.0", + "printf": "^0.6.1", + "rimraf": "^2.4.4", + "socket.io": "^4.1.2", + "spawn-args": "^0.2.0", + "styled_string": "0.0.1", + "tap-parser": "^7.0.0", + "tmp": "0.0.33" + }, + "bin": { + "testem": "testem.js" + }, + "engines": { + "node": ">= 7.*" + } + }, + "node_modules/testem/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/testem/node_modules/aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true + }, + "node_modules/testem/node_modules/are-we-there-yet": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz", + "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==", + "dev": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "node_modules/testem/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/testem/node_modules/gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "dev": true, + "dependencies": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "node_modules/testem/node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/testem/node_modules/mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/testem/node_modules/npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "dev": true, + "dependencies": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "node_modules/testem/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/testem/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/testem/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/testem/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/testem/node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/testem/node_modules/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, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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 + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "node_modules/tiny-glob": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz", + "integrity": "sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==", + "dev": true, + "dependencies": { + "globalyzer": "0.1.0", + "globrex": "^0.1.2" + } + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tough-cookie": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", + "optional": true, + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", + "devOptional": true + }, + "node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "devOptional": true, + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/uglify-js": { + "version": "3.3.28", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.3.28.tgz", + "integrity": "sha512-68Rc/aA6cswiaQ5SrE979UJcXX+ADA1z33/ZsPd+fbAiVdjZ16OXdbtGO+rJUUBgK6qdf3SOPhQf3K/ybF5Miw==", + "dev": true, + "dependencies": { + "commander": "~2.15.0", + "source-map": "~0.6.1" + }, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/uglify-js/node_modules/commander": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "dev": true + }, + "node_modules/underscore": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.2.tgz", + "integrity": "sha512-ekY1NhRzq0B08g4bGuX4wd2jZx5GnKz6mKSqFL4nqBlfyMGiG10gDFhDTMEfYmDL6Jy0FUIZp7wiRB+0BP7J2g==", + "dev": true + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "optional": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "devOptional": true + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "dev": true, + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "optional": true, + "dependencies": { + "browser-process-hrtime": "^1.0.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz", + "integrity": "sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg==", + "optional": true, + "dependencies": { + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "dev": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", + "devOptional": true + }, + "node_modules/whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "optional": true, + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "optional": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "devOptional": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "devOptional": true, + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/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==", + "devOptional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "devOptional": true + }, + "node_modules/write": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", + "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", + "dev": true, + "dependencies": { + "mkdirp": "^0.5.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/write/node_modules/mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/ws": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.4.0.tgz", + "integrity": "sha512-IHVsKe2pjajSUIl4KYMQOdlyliovpEPquKkqbwswulszzI7r0SfQrxnXdWAEqOlDCLrVSJzo+O1hAwdog2sKSQ==", + "optional": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "optional": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "optional": true + }, + "node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "devOptional": true + }, + "node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + } + }, "dependencies": { "@babel/code-frame": { "version": "7.16.7", @@ -263,18 +6607,6 @@ "to-fast-properties": "^2.0.0" } }, - "@blakeembrey/deque": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@blakeembrey/deque/-/deque-1.0.5.tgz", - "integrity": "sha512-6xnwtvp9DY1EINIKdTfvfeAtCYw4OqBZJhtiqkT3ivjnEfa25VQ3TsKvaFfKm8MyGIEfE95qLe+bNEt3nB0Ylg==", - "dev": true - }, - "@blakeembrey/template": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@blakeembrey/template/-/template-1.0.0.tgz", - "integrity": "sha512-J6WGZqCLdRMHUkyRG6fBSIFJ0rL60/nsQNh5rQvsYZ5u0PsKw6XQcJcA3DWvd9cN3j/IQx5yB1fexhCafwwUUw==", - "dev": true - }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -343,10 +6675,25 @@ "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==", "dev": true }, + "@types/fs-extra": { + "version": "9.0.13", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", + "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/lodash": { + "version": "4.14.180", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.180.tgz", + "integrity": "sha512-XOKXa1KIxtNXgASAnwj7cnttJxS4fksBRywK/9LzRV5YxrF80BXZIGeQSuoESQ/VkUj30Ae0+YcuHc15wJCB2g==", + "dev": true + }, "@types/node": { - "version": "17.0.8", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.8.tgz", - "integrity": "sha512-YofkM6fGv4gDJq78g4j0mMuGMkZVxZDgtU0JRdx6FgiJDG+0fY0GKVolOV8WqVmEhLCXkQRjwDdKyPxJp/uucg==", + "version": "17.0.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", + "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==", "dev": true }, "@xmldom/xmldom": { @@ -459,18 +6806,37 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz", "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=", + "dev": true, + "requires": {} + }, + "ansi-escape": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-escape/-/ansi-escape-1.1.0.tgz", + "integrity": "sha1-ithZ6Epp4P+Rd5aUeTqS5OjAXpk=", "dev": true }, "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "requires": { + "type-fest": "^0.21.3" + }, + "dependencies": { + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "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==" + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "devOptional": true }, "ansi-styles": { "version": "3.2.1", @@ -481,16 +6847,6 @@ "color-convert": "^1.9.0" } }, - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, "append-transform": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", @@ -522,12 +6878,6 @@ "readable-stream": "^3.6.0" } }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -647,7 +6997,8 @@ "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "devOptional": true }, "base64-arraybuffer": { "version": "1.0.1", @@ -655,17 +7006,28 @@ "integrity": "sha512-vFIUq7FdLtjZMhATwDul5RZWv2jpXQ09Pd6jcVEOvIsqCWTRFD/ONHNfyOS8dA/Ippi5dsIgpyKWKZaAKZltbA==", "dev": true }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true + }, "base64id": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", "dev": true }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } }, "bluebird": { "version": "3.7.2", @@ -727,20 +7089,12 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "devOptional": 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" - } - }, "browser-process-hrtime": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", @@ -760,6 +7114,16 @@ "picocolors": "^1.0.0" } }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -834,9 +7198,9 @@ } }, "chardet": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", - "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, "charm": { @@ -848,22 +7212,6 @@ "inherits": "^2.0.1" } }, - "chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - } - }, "chownr": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", @@ -883,18 +7231,24 @@ "dev": true }, "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=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "dev": true, "requires": { - "restore-cursor": "^2.0.0" + "restore-cursor": "^3.1.0" } }, + "cli-spinners": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", + "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", + "dev": true + }, "cli-width": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", - "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", "dev": true }, "cliui": { @@ -908,6 +7262,12 @@ "wrap-ansi": "^6.2.0" } }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "dev": true + }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -951,9 +7311,9 @@ } }, "commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.1.0.tgz", + "integrity": "sha512-i0/MaqBtdbnJ4XQs4Pmyb+oFQl+q0lsAmokVUH92SlSw4fkeAcG3bVon+Qt7hmtF+u3Het6o4VgrcY3qAoEB6w==", "dev": true }, "commondir": { @@ -1018,7 +7378,8 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "devOptional": true }, "concat-stream": { "version": "1.6.2", @@ -1067,7 +7428,8 @@ "console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "devOptional": true }, "consolidate": { "version": "0.15.1", @@ -1232,6 +7594,7 @@ "version": "4.3.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "devOptional": true, "requires": { "ms": "2.1.2" } @@ -1260,7 +7623,14 @@ "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==" + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "devOptional": true + }, + "deep-object-diff": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/deep-object-diff/-/deep-object-diff-1.1.7.tgz", + "integrity": "sha512-QkgBca0mL08P6HiOjoqvmm6xOAl2W6CT2+34Ljhg0OeFan8cwlcdq8jrLKsBBuUFAZLsN5b6y491KdKEoSo9lg==", + "dev": true }, "default-require-extensions": { "version": "3.0.0", @@ -1271,6 +7641,15 @@ "strip-bom": "^4.0.0" } }, + "defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "dev": true, + "requires": { + "clone": "^1.0.2" + } + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -1280,7 +7659,8 @@ "delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "devOptional": true }, "depd": { "version": "1.1.2", @@ -1341,7 +7721,8 @@ "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==" + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "devOptional": true }, "encodeurl": { "version": "1.0.2", @@ -1380,7 +7761,8 @@ "version": "8.2.3", "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", - "dev": true + "dev": true, + "requires": {} } } }, @@ -1475,12 +7857,39 @@ "text-table": "~0.2.0" }, "dependencies": { + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true + }, "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "dev": true }, + "chardet": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", + "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", + "dev": true + }, + "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.1", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", + "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", + "dev": true + }, "debug": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", @@ -1490,6 +7899,69 @@ "ms": "^2.1.1" } }, + "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" + } + }, + "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" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inquirer": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", + "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", + "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.0.4", + "figures": "^2.0.0", + "lodash": "^4.3.0", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rx-lite": "^4.0.8", + "rx-lite-aggregates": "^4.0.8", + "string-width": "^2.1.0", + "strip-ansi": "^4.0.0", + "through": "^2.3.6" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, "mkdirp": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", @@ -1499,12 +7971,47 @@ "minimist": "^1.2.5" } }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, + "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" + } + }, + "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" + } + }, "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", @@ -1561,7 +8068,8 @@ "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "devOptional": true }, "esquery": { "version": "1.4.0", @@ -1584,12 +8092,14 @@ "estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "devOptional": true }, "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "devOptional": true }, "etag": { "version": "1.8.1", @@ -1713,13 +8223,13 @@ } }, "external-editor": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", - "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", "dev": true, "requires": { - "chardet": "^0.4.0", - "iconv-lite": "^0.4.17", + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", "tmp": "^0.0.33" }, "dependencies": { @@ -1749,12 +8259,13 @@ "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "devOptional": true }, "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", "dev": true, "requires": { "escape-string-regexp": "^1.0.5" @@ -1770,15 +8281,6 @@ "object-assign": "^4.0.1" } }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, "finalhandler": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", @@ -1950,6 +8452,25 @@ "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", "dev": true }, + "fs-extra": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.1.tgz", + "integrity": "sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "dependencies": { + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true + } + } + }, "fs-minipass": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", @@ -1962,14 +8483,8 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "devOptional": true }, "functional-red-black-tree": { "version": "1.0.1", @@ -1977,6 +8492,12 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "fuzzy": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/fuzzy/-/fuzzy-0.1.3.tgz", + "integrity": "sha1-THbsL/CsGjap3M+aAN+GIweNTtg=", + "dev": true + }, "gauge": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", @@ -2025,6 +8546,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "devOptional": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -2034,15 +8556,6 @@ "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": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -2112,7 +8625,8 @@ "has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "devOptional": true }, "hasha": { "version": "5.2.2", @@ -2193,6 +8707,12 @@ "safer-buffer": ">= 2.1.2 < 3.0.0" } }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true + }, "ignore": { "version": "3.3.10", "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", @@ -2215,6 +8735,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "devOptional": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -2223,42 +8744,223 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "devOptional": true }, "inquirer": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", - "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.1.tgz", + "integrity": "sha512-pxhBaw9cyTFMjwKtkjePWDhvwzvrNGAw7En4hottzlPvz80GZaMZthdDU35aA6/f5FRZf3uhE057q8w1DE3V2g==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "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" + } + }, + "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" + } + }, + "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" + } + } + } + }, + "inquirer-checkbox-plus-prompt": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/inquirer-checkbox-plus-prompt/-/inquirer-checkbox-plus-prompt-1.0.1.tgz", + "integrity": "sha1-VP8e0Jd3oQNThWIna1z0Uhox0W0=", "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.0.4", "figures": "^2.0.0", - "lodash": "^4.3.0", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rx-lite": "^4.0.8", - "rx-lite-aggregates": "^4.0.8", - "string-width": "^2.1.0", - "strip-ansi": "^4.0.0", - "through": "^2.3.6" + "inquirer": "^5.1.0", + "lodash": "^4.17.5" }, "dependencies": { + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true + }, "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "dev": true }, + "chardet": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", + "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", + "dev": true + }, + "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.1", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", + "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", + "dev": true + }, + "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" + } + }, + "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" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "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" + } + }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "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 + }, + "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" + } + }, + "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" + } + }, + "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" + } + }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", @@ -2286,45 +8988,22 @@ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "dev": true }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, "is-docker": { "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", "dev": true }, "is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "devOptional": true }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", "dev": true }, "is-potential-custom-element-name": { @@ -2360,6 +9039,12 @@ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", "dev": true }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", @@ -2628,10 +9313,29 @@ "minimist": "^1.2.5" } }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + }, + "dependencies": { + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true + } + } + }, "levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "devOptional": true, "requires": { "prelude-ls": "~1.1.2", "type-check": "~0.3.2" @@ -2741,10 +9445,72 @@ "integrity": "sha1-2ZwHpmnp5tJOE2Lf4mbGdhavEwI=", "dev": true }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "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" + } + }, + "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" + } + }, + "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" + } + } + } + }, "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==", + "devOptional": true, "requires": { "yallist": "^4.0.0" } @@ -2753,6 +9519,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "devOptional": true, "requires": { "semver": "^6.0.0" }, @@ -2760,7 +9527,8 @@ "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "devOptional": true } } }, @@ -2791,20 +9559,22 @@ "mime-db": { "version": "1.51.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", - "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==" + "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", + "devOptional": true }, "mime-types": { "version": "2.1.34", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", + "devOptional": true, "requires": { "mime-db": "1.51.0" } }, "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true }, "mimic-response": { @@ -2817,6 +9587,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "devOptional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -2852,10 +9623,17 @@ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "optional": true }, + "moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==", + "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==" + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "devOptional": true }, "mustache": { "version": "3.2.1", @@ -2864,9 +9642,9 @@ "dev": true }, "mute-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, "nan": { @@ -2903,6 +9681,7 @@ "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "devOptional": true, "requires": { "whatwg-url": "^5.0.0" } @@ -2954,9 +9733,9 @@ "dev": true }, "node-watch": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/node-watch/-/node-watch-0.7.2.tgz", - "integrity": "sha512-g53VjSARRv1JdST0LZRIg8RiuLr1TaBbVPsVvxh0/0Ymvi0xYUjDuoqQQAWtHJQUXhiShowPT/aXKNeHBcyQsw==", + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/node-watch/-/node-watch-0.7.3.tgz", + "integrity": "sha512-3l4E8uMPY1HdMMryPRUAl+oIHtXtyiTlIiESNSVSNxcPfzAFzeTbXFQkZfAwBbo0B1qMSG8nUABx+Gd+YrbKrQ==", "dev": true }, "nopt": { @@ -2968,12 +9747,6 @@ "abbrev": "1" } }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, "npm-run-path": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", @@ -3061,7 +9834,8 @@ "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "devOptional": true }, "on-finished": { "version": "2.3.0", @@ -3082,90 +9856,102 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "devOptional": true, "requires": { "wrappy": "1" } }, - "onchange": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/onchange/-/onchange-7.1.0.tgz", - "integrity": "sha512-ZJcqsPiWUAUpvmnJri5TPBooqJOPmC0ttN65juhN15Q8xA+Nbg3BaxBHXQ45EistKKlKElb0edmbPWnKSBkvMg==", + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "devOptional": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", "dev": true, "requires": { - "@blakeembrey/deque": "^1.0.5", - "@blakeembrey/template": "^1.0.0", - "arg": "^4.1.3", - "chokidar": "^3.3.1", - "cross-spawn": "^7.0.1", - "ignore": "^5.1.4", - "tree-kill": "^1.2.2" + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" }, "dependencies": { - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "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": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "color-convert": "^2.0.1" } }, - "ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", - "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" + } }, - "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==", + "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": { - "shebang-regex": "^3.0.0" + "color-name": "~1.1.4" } }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "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 }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "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": { - "isexe": "^2.0.0" + "has-flag": "^4.0.0" } } } }, - "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" - } - }, - "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - } - }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", @@ -3250,7 +10036,8 @@ "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=" + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "devOptional": true }, "path-is-inside": { "version": "1.0.2", @@ -3276,12 +10063,6 @@ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "dev": true }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true - }, "pixelmatch": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-4.0.2.tgz", @@ -3315,7 +10096,8 @@ "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "devOptional": true }, "printf": { "version": "0.6.1", @@ -3389,14 +10171,22 @@ "dev": true }, "qunit": { - "version": "2.17.2", - "resolved": "https://registry.npmjs.org/qunit/-/qunit-2.17.2.tgz", - "integrity": "sha512-17isVvuOmALzsPjiV7wFg/6O5vJYXBrQZPwocfQSSh0I/rXvfX7bKMFJ4GMVW3U4P8r2mBeUy8EAngti4QD2Vw==", + "version": "2.18.1", + "resolved": "https://registry.npmjs.org/qunit/-/qunit-2.18.1.tgz", + "integrity": "sha512-A2Adgr/DeMQOJZFVllyQi2wiGJVVXGSRRwMe39fNfuuftUYHHpGRTWUhBa8wNblunCAOUCt+1uFcg1L7NaxQTA==", "dev": true, "requires": { "commander": "7.2.0", - "node-watch": "0.7.2", + "node-watch": "0.7.3", "tiny-glob": "0.2.9" + }, + "dependencies": { + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true + } } }, "range-parser": { @@ -3438,22 +10228,13 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "optional": true, + "devOptional": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, "release-zalgo": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", @@ -3498,12 +10279,12 @@ "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=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "dev": true, "requires": { - "onetime": "^2.0.0", + "onetime": "^5.1.0", "signal-exit": "^3.0.2" } }, @@ -3511,6 +10292,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "devOptional": true, "requires": { "glob": "^7.1.3" } @@ -3536,15 +10318,26 @@ "rx-lite": "*" } }, + "rxjs": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz", + "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==", + "dev": true, + "requires": { + "tslib": "^2.1.0" + } + }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "devOptional": true }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "devOptional": true }, "saxes": { "version": "5.0.1", @@ -3559,6 +10352,7 @@ "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "devOptional": true, "requires": { "lru-cache": "^6.0.0" } @@ -3624,7 +10418,8 @@ "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "devOptional": true }, "setprototypeof": { "version": "1.2.0", @@ -3656,7 +10451,8 @@ "signal-exit": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", - "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==" + "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==", + "devOptional": true }, "simple-concat": { "version": "1.0.1", @@ -3726,7 +10522,8 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "devOptional": true }, "spawn-args": { "version": "0.2.0", @@ -3771,29 +10568,31 @@ "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", "dev": true }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "devOptional": true, + "requires": { + "safe-buffer": "~5.2.0" + } + }, "string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "devOptional": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "optional": true, - "requires": { - "safe-buffer": "~5.2.0" - } - }, "strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "devOptional": true, "requires": { "ansi-regex": "^5.0.1" } @@ -3831,6 +10630,12 @@ "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.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", @@ -4079,6 +10884,15 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, "string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", @@ -4090,15 +10904,6 @@ "strip-ansi": "^3.0.0" } }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", @@ -4147,15 +10952,6 @@ "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", "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" - } - }, "toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -4176,18 +10972,20 @@ "tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", + "devOptional": true }, - "tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", "dev": true }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "devOptional": true, "requires": { "prelude-ls": "~1.1.2" } @@ -4262,7 +11060,8 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "devOptional": true }, "utils-merge": { "version": "1.0.1", @@ -4300,10 +11099,20 @@ "xml-name-validator": "^4.0.0" } }, + "wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "dev": true, + "requires": { + "defaults": "^1.0.3" + } + }, "webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", + "devOptional": true }, "whatwg-encoding": { "version": "2.0.0", @@ -4324,6 +11133,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "devOptional": true, "requires": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -4348,6 +11158,7 @@ "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "devOptional": true, "requires": { "string-width": "^1.0.2 || 2 || 3 || 4" } @@ -4355,7 +11166,8 @@ "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==" + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "devOptional": true }, "wordwrap": { "version": "1.0.0", @@ -4403,7 +11215,8 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "devOptional": true }, "write": { "version": "0.2.1", @@ -4441,7 +11254,8 @@ "version": "8.4.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.4.0.tgz", "integrity": "sha512-IHVsKe2pjajSUIl4KYMQOdlyliovpEPquKkqbwswulszzI7r0SfQrxnXdWAEqOlDCLrVSJzo+O1hAwdog2sKSQ==", - "optional": true + "optional": true, + "requires": {} }, "xml-name-validator": { "version": "4.0.0", @@ -4464,7 +11278,8 @@ "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "devOptional": true }, "yargs": { "version": "15.4.1", diff --git a/package.json b/package.json index 1c5f54f13f3..f75251bda63 100644 --- a/package.json +++ b/package.json @@ -45,26 +45,18 @@ "license": "MIT", "scripts": { "changelog": "auto-changelog -o change-output.md --unreleased-only", - "build": "node build.js modules=ALL requirejs exclude=gestures,accessors,erasing", - "build:fast": "node build.js modules=ALL requirejs fast exclude=gestures,accessors,erasing", - "build:watch": "onchange 'src/**/**' 'HEADER.js' 'lib/**/**' -- npm run build_export", - "link:watch": "onchange 'src/**/**' 'HEADER.js' 'lib/**/**' -- npm link", - "build_with_gestures": "node build.js modules=ALL exclude=accessors", - "build_export": "npm run build:fast && npm run export_dist_to_site", - "test:single": "qunit test/node_test_setup.js test/lib", + "build": "node ./scripts build", + "build:fast": "npm run build -- --fast", + "dev": "node ./scripts", + "start": "node ./scripts start", + "export": "node ./scripts website export", + "test": "node ./scripts test", "test:coverage": "nyc --silent qunit test/node_test_setup.js test/lib test/unit", "test:visual:coverage": "nyc --silent --no-clean qunit test/node_test_setup.js test/lib test/visual", "coverage:report": "nyc report --reporter=lcov --reporter=text", - "test": "qunit test/node_test_setup.js test/lib test/unit", - "test:visual": "qunit test/node_test_setup.js test/lib test/visual", - "test:visual:single": "qunit test/node_test_setup.js test/lib", - "test:all": "npm run test && npm run test:visual", "lint": "eslint --config .eslintrc.json src", "lint_tests": "eslint test/unit --config .eslintrc_tests && eslint test/visual --config .eslintrc_tests", - "export_gesture_to_site": "cp dist/fabric.js ../fabricjs.com/lib/fabric_with_gestures.js", - "export_dist_to_site": "cp dist/fabric.js ../fabricjs.com/lib/fabric.js && cp package.json ../fabricjs.com/lib/package.json && cp -r src HEADER.js lib ../fabricjs.com/build/files/", - "export_tests_to_site": "cp test/unit/*.js ../fabricjs.com/test/unit && cp -r test/visual/* ../fabricjs.com/test/visual && cp -r test/fixtures/* ../fabricjs.com/test/fixtures && cp -r test/lib/* ../fabricjs.com/test/lib", - "all": "npm run build && npm run test && npm run test:visual && npm run lint && npm run lint_tests && npm run export_dist_to_site && npm run export_tests_to_site", + "all": "npm run build && npm run test -- --all && npm run lint && npm run lint_tests && npm run export", "testem": "testem .", "testem:ci": "testem ci" }, @@ -73,19 +65,28 @@ "jsdom": "^19.0.0" }, "devDependencies": { + "@types/fs-extra": "^9.0.13", + "@types/lodash": "^4.14.180", + "@types/node": "^17.0.21", + "ansi-escape": "^1.1.0", "auto-changelog": "^2.3.0", "chalk": "^2.4.1", + "commander": "^9.1.0", + "deep-object-diff": "^1.1.7", "eslint": "4.18.x", + "fs-extra": "^10.0.1", + "fuzzy": "^0.1.3", + "inquirer": "^8.2.1", + "inquirer-checkbox-plus-prompt": "^1.0.1", + "moment": "^2.29.1", "nyc": "^15.1.0", - "onchange": "^7.1.0", "pixelmatch": "^4.0.2", - "qunit": "^2.13.0", + "qunit": "^2.17.2", "testem": "^3.2.0", - "uglify-js": "3.3.x" + "uglify-js": "^3.3.28" }, "engines": { "node": ">=14.0.0" }, - "main": "./dist/fabric.js", - "dependencies": {} + "main": "./dist/fabric.js" } diff --git a/scripts/index.js b/scripts/index.js new file mode 100644 index 00000000000..67894b24708 --- /dev/null +++ b/scripts/index.js @@ -0,0 +1,366 @@ +#!/usr/bin/env node +const fs = require('fs-extra'); +const _ = require('lodash'); +const path = require('path'); +const cp = require('child_process'); +const inquirer = require('inquirer'); +const ansiEscapes = require('ansi-escapes'); +const fuzzy = require('fuzzy'); +const chalk = require('chalk'); +const moment = require('moment'); +const Checkbox = require('inquirer-checkbox-plus-prompt'); +const commander = require('commander'); +const program = new commander.Command(); + +const CLI_CACHE = path.resolve(__dirname, 'cli_cache.json'); +const wd = path.resolve(__dirname, '..'); +const websiteDir = path.resolve(wd, '../fabricjs.com'); + +class ICheckbox extends Checkbox { + constructor(questions, rl, answers) { + super(questions, rl, answers); + this.opt.source = this.opt.source.bind(this); + } + getCurrentValue() { + const current = super.getCurrentValue(); + return current.concat(this.firstSourceLoading ? this.default : []); + } + onSpaceKey() { + const choice = this.choices.getChoice(this.pointer); + if (!choice) { + return; + } + + this.toggleChoice(choice); + if (choice.value && !choice.value.file) { + delete this.lastQuery; + // Remove the choices from the checked values with the same type + _.remove(this.value, v => v.type === choice.value.type && v.file); + _.remove(this.checkedChoices, checkedChoice => { + if (!checkedChoice.value.file) { + return false; + } + checkedChoice.checked = false; + return checkedChoice.value.type === choice.value.type; + }); + + this.executeSource(); + } + + this.render(); + } +} +inquirer.registerPrompt('test-selection', ICheckbox); + +function build(options = {}) { + _.defaultsDeep(options, { exclude: ['gestures', 'accessors', 'erasing'] }); + const args = [ + `node`, + `build.js`, + `modules=${options.modules && options.modules.length > 0 ? options.modules.join(',') : 'ALL'}`, + `requirejs`, + `${options.fast ? 'fast' : ''}`, + `exclude=${options.exclude.join(',')}` + ] + cp.execSync(args.join(' '), { stdio: 'inherit', cwd: wd }); +} + +function startWebsite() { + if (require(path.resolve(websiteDir, 'package.json')).name !== 'fabricjs.com') { + console.log(chalk.red('Could not locate fabricjs.com directory')); + } + cp.spawn('npm', ['run', 'start:dev'], { + stdio: 'inherit', + cwd: websiteDir, + shell: true, + }); +} + +function watch(path, callback, debounce = 500) { + fs.watch(path, { recursive: true }, _.debounce((type, location) => { + try { + callback(type, location); + } catch (error) { + console.error(error); + } + }, debounce, { trailing: true })); +} + +function copy(from ,to) { + try { + fs.copySync(from, to); + const containingFolder = path.resolve(wd, '..'); + console.log(`copied ${path.relative(containingFolder, from)} to ${path.relative(containingFolder, to)}`); + } catch (error) { + console.error(error); + } +} + +const BUILD_SOURCE = ['src', 'lib', 'HEADER.js']; + +function exportBuildToWebsite(options = {}) { + _.defaultsDeep(options, { gestures: true }); + if (options.gestures) { + build({ exclude: ['accessors'] }); + copy(path.resolve(wd, './dist/fabric.js'), path.resolve(websiteDir, './lib/fabric_with_gestures.js')); + } + build(); + copy(path.resolve(wd, './dist/fabric.js'), path.resolve(websiteDir, './lib/fabric.js')); + copy(path.resolve(wd, './package.json'), path.resolve(websiteDir, './lib/package.json')); + BUILD_SOURCE.forEach(p => copy(path.resolve(wd, p), path.resolve(websiteDir, './build/files', p))); + console.log(chalk.bold(`[${moment().format('HH:MM')}] exported build to fabricjs.com`)); +} + +function exportTestsToWebsite() { + const paths = [ + './test/unit', + './test/visual', + './test/fixtures', + './test/lib', + ] + paths.forEach(location => copy(path.resolve(wd, location), path.resolve(websiteDir, location))); + console.log(chalk.bold(`[${moment().format('HH:MM')}] exported tests to fabricjs.com`)); +} + +function exportToWebsite(options) { + if (!options.include || options.include.length === 0) { + options.include = ['build', 'tests']; + } + options.include .forEach(x => { + if (x === 'build') { + exportBuildToWebsite(); + options.watch && BUILD_SOURCE.forEach(p => { + watch(path.resolve(wd, p), exportBuildToWebsite); + }); + } + else if (x === 'tests') { + exportTestsToWebsite(); + options.watch && watch(path.resolve(wd, './test'), exportTestsToWebsite); + } + }) +} + +/** + * + * @param {string[]} tests file paths + * @param {{debug?:boolean,recreate?:boolean,show?:boolean,filter?:boolean}} [options] + */ +function test(tests, options) { + options = options || {}; + const args = ['qunit', 'test/node_test_setup.js', 'test/lib'].concat(tests); + process.env.QUNIT_DEBUG_VISUAL_TESTS = options.debug; + process.env.QUNIT_RECREATE_VISUAL_REFS = options.recreate; + if (options.filter) { + process.env.QUNIT_FILTER = options.filter; + } + return new Promise((resolve, reject) => { + try { + var p = cp.spawn(args.join(' '), { cwd: wd, env: process.env, shell: true, stdio: 'pipe' }); + let clearLines = 0; + process.stdout.write(ansiEscapes.cursorHide); + p.stdout.on('data', function (data) { + data = _.compact(data.toString().trim().split(/\n/)); + data.forEach(line => { + if (clearLines > 0 && !options.show) { + process.stdout.write(ansiEscapes.cursorUp(1)); + process.stdout.write(ansiEscapes.eraseDown); + } + if (line.startsWith('ok')) { + clearLines = 1; + console.log(chalk.green(line)); + } + else if (line.startsWith('not ok')) { + clearLines = 0; + console.log(chalk.bold(chalk.red('not ok') + line.slice(6))); + } + else { + clearLines = 0; + console.log(line); + } + }); + }); + p.stdout.once('end', () => { + process.stdout.write(ansiEscapes.cursorDown()); + process.stdout.write(ansiEscapes.cursorShow); + resolve(); + }); + } catch (error) { + process.stdout.write(ansiEscapes.cursorDown()); + process.stdout.write(ansiEscapes.cursorShow); + reject(error); + } + }); +} + +/** + * + * @param {'unit'|'visual'} type correspondes to the test directories + * @returns + */ +function listTestFiles(type) { + return fs.readdirSync(path.resolve(wd, './test', type)).filter(p => { + const ext = path.parse(p).ext.slice(1); + return ext === 'js' || ext === 'ts'; + }); +} + +function writeCLIFile(tests) { + fs.writeFileSync(CLI_CACHE, JSON.stringify(tests, null, '\t')); +} + +function readCLIFile() { + return fs.existsSync(CLI_CACHE) ? require(CLI_CACHE) : []; +} + +function createChoiceData(type, file) { + return { + name: `${type}/${file}`, + short: `${type}/${file}`, + value: { + type, + file + } + } +} + +async function selectTestFile() { + const selected = readCLIFile(); + const unitTests = listTestFiles('unit').map(file => createChoiceData('unit', file)); + const visualTests = listTestFiles('visual').map(file => createChoiceData('visual', file)); + const { tests: filteredTests } = await inquirer.prompt([ + { + type: 'test-selection', + name: 'tests', + message: 'Select test files', + highlight: true, + searchable: true, + default: selected, + pageSize: Math.max(10, selected.length), + source(answersSoFar, input = '') { + return new Promise(resolve => { + const tests = _.concat(unitTests, visualTests); + const value = _.map(this.getCurrentValue(), value => createChoiceData(value.type, value.file)); + if (value.length > 0) { + if (value.find(v => v.value && v.value.type === 'unit' && !v.value.file)) { + _.pullAll(tests, unitTests); + } + if (value.find(v => v.value && v.value.type === 'visual' && !v.value.file)) { + _.pullAll(tests, visualTests); + } + } + const unitChoice = createChoiceData('unit', ''); + const visualChoice = createChoiceData('visual', ''); + !value.find(v => _.isEqual(v, unitChoice)) && value.push(unitChoice); + !value.find(v => _.isEqual(v, visualChoice)) && value.push(visualChoice); + if (value.length > 0) { + value.unshift(new inquirer.Separator()); + value.push(new inquirer.Separator()); + } + const res = fuzzy.filter(input, tests, { + extract: (item) => item.name + }).map((element) => element.original); + resolve(value.concat(_.differenceBy(res, value, 'name'))); + }); + } + } + ]); + writeCLIFile(filteredTests); + return filteredTests; +} + +async function runIntreactiveTestSuite(options) { + // some tests fail because of some pollution when run from the same context + // test(_.map(await selectTestFile(), curr => `test/${curr.type}/${curr.file}`)) + const tests = _.reduce(await selectTestFile(), (acc, curr) => { + acc[curr.type].push(`test/${curr.type}/${curr.file}`); + return acc; + }, { unit: [], visual: [] }); + _.reduce(tests, async (queue, files, suite) => { + await queue; + if (files.length > 0) { + console.log(chalk.bold(chalk.blue(`running ${suite} test suite`))); + return test(files, options); + } + }, Promise.resolve()); +} + +program + .name('fabric.js') + .description('fabric.js DEV CLI tools') + .showSuggestionAfterError(); + +program + .command('start') + .description('start fabricjs.com dev server and watch for changes') + .action((options) => { + exportToWebsite({ + watch: true + }); + startWebsite(); + }); + +program + .command('build') + .description('build dist') + .option('-f, --fast') + .option('-w, --watch') + .option('-x, --exclude [exclude...]') + .option('-m, --modules [modules...]') + .action((options) => { + const { watch: w, ...rest } = options || {}; + build(rest); + w && watch(path.resolve(wd, 'src'), () => build(rest)); + }); + +program + .command('test') + .description('run test suite') + .addOption(new commander.Option('-s, --suite [suite...]', 'test suite to run').choices(['unit', 'visual'])) + .option('-f, --file [file]', 'run a specific test file') + .option('--filter [filter]', 'filter tests by name') + .option('-a, --all', 'run all tests', false) + .option('-d, --debug', 'debug visual tests by overriding refs (golden images) in case of visual changes', false) + .option('-r, --recreate', 'recreate visual refs (golden images)', false) + .option('--show', 'show passing tests', false) + .option('-cc, --clear-cache', 'clear CLI test cache', false) + .action((options) => { + if (options.clearCache) { + fs.removeSync(CLI_CACHE); + } + if (options.all) { + options.suite = ['unit', 'visual']; + } + if (options.suite) { + _.reduce(options.suite, async (queue, suite) => { + await queue; + console.log(chalk.bold(chalk.blue(`running ${suite} test suite`))); + return test(`test/${suite}`, options); + }, Promise.resolve()); + } + else if (options.file) { + test(`test/${options.file}`, options); + } + else { + runIntreactiveTestSuite(options); + } + }); + +const website = program + .command('website') + .description('fabricjs.com commands'); + +website + .command('start') + .description('start fabricjs.com dev server') + .allowExcessArguments() + .allowUnknownOption() + .action(startWebsite); + +website + .command('export') + .description('export files to fabricjs.com directory') + .addOption(new commander.Option('-i, --include [what...]').choices(['build', 'tests']).default(['build', 'tests'], 'export all')) + .option('-w, --watch') + .action(exportToWebsite); + +program.parse(process.argv); \ No newline at end of file diff --git a/src/canvas.class.js b/src/canvas.class.js index 2b3a2c1857a..4fe6fa45601 100644 --- a/src/canvas.class.js +++ b/src/canvas.class.js @@ -42,6 +42,28 @@ * @fires after:render at the end of the render process, receives the context in the callback * @fires before:render at start the render process, receives the context in the callback * + * @fires contextmenu:before + * @fires contextmenu + * @example + * let handler; + * targets.forEach(target => { + * target.on('contextmenu:before', opt => { + * // decide which target should handle the event before canvas hijacks it + * if (someCaseHappens && opt.targets.includes(target)) { + * handler = target; + * } + * }); + * target.on('contextmenu', opt => { + * // do something fantastic + * }); + * }); + * canvas.on('contextmenu', opt => { + * if (!handler) { + * // no one takes responsibility, it's always left to me + * // let's show them how it's done! + * } + * }); + * */ fabric.Canvas = fabric.util.createClass(fabric.StaticCanvas, /** @lends fabric.Canvas.prototype */ { @@ -352,6 +374,13 @@ */ _hoveredTargets: [], + /** + * hold the list of objects to render + * @type fabric.Object[] + * @private + */ + _objectsToRender: undefined, + /** * @private */ @@ -369,6 +398,23 @@ this.calcOffset(); }, + /** + * @private + * @param {fabric.Object} obj Object that was added + */ + _onObjectAdded: function (obj) { + this._objectsToRender = undefined; + this.callSuper('_onObjectAdded', obj); + }, + + /** + * @private + * @param {fabric.Object} obj Object that was removed + */ + _onObjectRemoved: function (obj) { + this._objectsToRender = undefined; + this.callSuper('_onObjectRemoved', obj); + }, /** * Divides objects in two groups, one to render immediately * and one to render as activeGroup. @@ -378,7 +424,7 @@ var activeObjects = this.getActiveObjects(), object, objsToRender, activeGroupObjects; - if (activeObjects.length > 0 && !this.preserveObjectStacking) { + if (!this.preserveObjectStacking && activeObjects.length > 1) { objsToRender = []; activeGroupObjects = []; for (var i = 0, length = this._objects.length; i < length; i++) { @@ -395,6 +441,15 @@ } objsToRender.push.apply(objsToRender, activeGroupObjects); } + // in case a single object is selected render it's entire above the other objects + else if (!this.preserveObjectStacking && activeObjects.length === 1) { + var target = activeObjects[0], ancestors = target.getAncestors(true); + var topAncestor = ancestors.length === 0 ? target : ancestors.pop(); + objsToRender = this._objects.slice(); + var index = objsToRender.indexOf(topAncestor); + index > -1 && objsToRender.splice(objsToRender.indexOf(topAncestor), 1); + objsToRender.push(topAncestor); + } else { objsToRender = this._objects; } @@ -416,7 +471,8 @@ this.hasLostContext = false; } var canvasToDrawOn = this.contextContainer; - this.renderCanvas(canvasToDrawOn, this._chooseObjectsToRender()); + !this._objectsToRender && (this._objectsToRender = this._chooseObjectsToRender()); + this.renderCanvas(canvasToDrawOn, this._objectsToRender); return this; }, @@ -507,7 +563,7 @@ _isSelectionKeyPressed: function(e) { var selectionKeyPressed = false; - if (Object.prototype.toString.call(this.selectionKey) === '[object Array]') { + if (Array.isArray(this.selectionKey)) { selectionKeyPressed = !!this.selectionKey.find(function(key) { return e[key] === true; }); } else { @@ -622,14 +678,22 @@ if (!target) { return; } - - var pointer = this.getPointer(e), corner = target.__corner, + var pointer = this.getPointer(e); + if (target.group) { + // transform pointer to target's containing coordinate plane + pointer = fabric.util.transformPoint(pointer, fabric.util.invertTransform(target.group.calcTransformMatrix())); + } + var corner = target.__corner, control = target.controls[corner], actionHandler = (alreadySelected && corner) ? control.getActionHandler(e, target, control) : fabric.controlsUtils.dragHandler, action = this._getActionFromCorner(alreadySelected, corner, e, target), origin = this._getOriginFromCorner(target, corner), altKey = e[this.centeredKey], + /** + * relative to target's containing coordinate plane + * both agree on every point + **/ transform = { target: target, action: action, @@ -639,7 +703,6 @@ scaleY: target.scaleY, skewX: target.skewX, skewY: target.skewY, - // used by transation offsetX: pointer.x - target.left, offsetY: pointer.y - target.top, originX: origin.x, @@ -648,11 +711,7 @@ ey: pointer.y, lastX: pointer.x, lastY: pointer.y, - // unsure they are useful anymore. - // left: target.left, - // top: target.top, theta: degreesToRadians(target.angle), - // end of unsure width: target.width * target.scaleX, shiftKey: e.shiftKey, altKey: altKey, @@ -745,11 +804,12 @@ if (shouldLookForActive && activeObject._findTargetCorner(pointer, isTouch)) { return activeObject; } - if (aObjects.length > 1 && !skipGroup && activeObject === this._searchPossibleTargets([activeObject], pointer)) { + if (aObjects.length > 1 && activeObject.type === 'activeSelection' + && !skipGroup && this.searchPossibleTargets([activeObject], pointer)) { return activeObject; } if (aObjects.length === 1 && - activeObject === this._searchPossibleTargets([activeObject], pointer)) { + activeObject === this.searchPossibleTargets([activeObject], pointer)) { if (!this.preserveObjectStacking) { return activeObject; } @@ -759,7 +819,7 @@ this.targets = []; } } - var target = this._searchPossibleTargets(this._objects, pointer); + var target = this.searchPossibleTargets(this._objects, pointer); if (e[this.altSelectionKey] && target && activeTarget && target !== activeTarget) { target = activeTarget; this.targets = activeTargetSubs; @@ -796,10 +856,10 @@ }, /** - * Function used to search inside objects an object that contains pointer in bounding box or that contains pointerOnCanvas when painted + * Internal Function used to search inside objects an object that contains pointer in bounding box or that contains pointerOnCanvas when painted * @param {Array} [objects] objects array to look into * @param {Object} [pointer] x,y object of point coordinates we want to check. - * @return {fabric.Object} object that contains pointer + * @return {fabric.Object} **top most object from given `objects`** that contains pointer * @private */ _searchPossibleTargets: function(objects, pointer) { @@ -813,7 +873,7 @@ this._normalizePointer(objToCheck.group, pointer) : pointer; if (this._checkTarget(pointerToUse, objToCheck, pointer)) { target = objects[i]; - if (target.subTargetCheck && target instanceof fabric.Group) { + if (target.subTargetCheck && Array.isArray(target._objects)) { subTarget = this._searchPossibleTargets(target._objects, pointer); subTarget && this.targets.push(subTarget); } @@ -823,6 +883,18 @@ return target; }, + /** + * Function used to search inside objects an object that contains pointer in bounding box or that contains pointerOnCanvas when painted + * @see {@link fabric.Canvas#_searchPossibleTargets} + * @param {Array} [objects] objects array to look into + * @param {Object} [pointer] x,y object of point coordinates we want to check. + * @return {fabric.Object} **top most object on screen** that contains pointer + */ + searchPossibleTargets: function (objects, pointer) { + var target = this._searchPossibleTargets(objects, pointer); + return target; + }, + /** * Returns pointer coordinates without the effect of the viewport * @param {Object} pointer with "x" and "y" number values @@ -838,27 +910,27 @@ /** * Returns pointer coordinates relative to canvas. * Can return coordinates with or without viewportTransform. - * ignoreZoom false gives back coordinates that represent + * ignoreVpt false gives back coordinates that represent * the point clicked on canvas element. - * ignoreZoom true gives back coordinates after being processed + * ignoreVpt true gives back coordinates after being processed * by the viewportTransform ( sort of coordinates of what is displayed * on the canvas where you are clicking. - * ignoreZoom true = HTMLElement coordinates relative to top,left - * ignoreZoom false, default = fabric space coordinates, the same used for shape position - * To interact with your shapes top and left you want to use ignoreZoom true - * most of the time, while ignoreZoom false will give you coordinates + * ignoreVpt true = HTMLElement coordinates relative to top,left + * ignoreVpt false, default = fabric space coordinates, the same used for shape position + * To interact with your shapes top and left you want to use ignoreVpt true + * most of the time, while ignoreVpt false will give you coordinates * compatible with the object.oCoords system. * of the time. * @param {Event} e - * @param {Boolean} ignoreZoom + * @param {Boolean} ignoreVpt * @return {Object} object with "x" and "y" number values */ - getPointer: function (e, ignoreZoom) { + getPointer: function (e, ignoreVpt) { // return cached values if we are in the event processing chain - if (this._absolutePointer && !ignoreZoom) { + if (this._absolutePointer && !ignoreVpt) { return this._absolutePointer; } - if (this._pointer && ignoreZoom) { + if (this._pointer && ignoreVpt) { return this._pointer; } @@ -881,7 +953,7 @@ this.calcOffset(); pointer.x = pointer.x - this._offset.left; pointer.y = pointer.y - this._offset.top; - if (!ignoreZoom) { + if (!ignoreVpt) { pointer = this.restorePointerVpt(pointer); } @@ -925,7 +997,7 @@ this.upperCanvasEl = upperCanvasEl; } fabric.util.addClass(upperCanvasEl, 'upper-canvas ' + lowerCanvasClass); - + this.upperCanvasEl.setAttribute('data-fabric', 'top'); this.wrapperEl.appendChild(upperCanvasEl); this._copyCanvasStyle(lowerCanvasEl, upperCanvasEl); @@ -947,9 +1019,13 @@ * @private */ _initWrapperElement: function () { + if (this.wrapperEl) { + return; + } this.wrapperEl = fabric.util.wrapElement(this.lowerCanvasEl, 'div', { 'class': this.containerClass }); + this.wrapperEl.setAttribute('data-fabric', 'wrapper'); fabric.util.setStyle(this.wrapperEl, { width: this.width + 'px', height: this.height + 'px', @@ -990,8 +1066,17 @@ toEl.style.cssText = fromEl.style.cssText; }, + /** + * Returns context of top canvas where interactions are drawn + * @returns {CanvasRenderingContext2D} + */ + getTopContext: function () { + return this.contextTop; + }, + /** * Returns context of canvas where object selection is drawn + * @alias * @return {CanvasRenderingContext2D} */ getSelectionContext: function() { @@ -1057,7 +1142,7 @@ */ _fireSelectionEvents: function(oldObjects, e) { var somethingChanged = false, objects = this.getActiveObjects(), - added = [], removed = []; + added = [], removed = [], invalidate = false; oldObjects.forEach(function(oldObject) { if (objects.indexOf(oldObject) === -1) { somethingChanged = true; @@ -1079,6 +1164,7 @@ } }); if (oldObjects.length > 0 && objects.length > 0) { + invalidate = true; somethingChanged && this.fire('selection:updated', { e: e, selected: added, @@ -1086,17 +1172,20 @@ }); } else if (objects.length > 0) { + invalidate = true; this.fire('selection:created', { e: e, selected: added, }); } else if (oldObjects.length > 0) { + invalidate = true; this.fire('selection:cleared', { e: e, deselected: removed, }); } + invalidate && (this._objectsToRender = undefined); }, /** @@ -1184,21 +1273,24 @@ * @chainable */ dispose: function () { - var wrapper = this.wrapperEl; + var wrapperEl = this.wrapperEl, + lowerCanvasEl = this.lowerCanvasEl, + upperCanvasEl = this.upperCanvasEl, + cacheCanvasEl = this.cacheCanvasEl; this.removeListeners(); - wrapper.removeChild(this.upperCanvasEl); - wrapper.removeChild(this.lowerCanvasEl); + this.callSuper('dispose'); + wrapperEl.removeChild(upperCanvasEl); + wrapperEl.removeChild(lowerCanvasEl); this.contextCache = null; this.contextTop = null; - ['upperCanvasEl', 'cacheCanvasEl'].forEach((function(element) { - fabric.util.cleanUpJsdomNode(this[element]); - this[element] = undefined; - }).bind(this)); - if (wrapper.parentNode) { - wrapper.parentNode.replaceChild(this.lowerCanvasEl, this.wrapperEl); + fabric.util.cleanUpJsdomNode(upperCanvasEl); + this.upperCanvasEl = undefined; + fabric.util.cleanUpJsdomNode(cacheCanvasEl); + this.cacheCanvasEl = undefined; + if (wrapperEl.parentNode) { + wrapperEl.parentNode.replaceChild(lowerCanvasEl, wrapperEl); } delete this.wrapperEl; - fabric.StaticCanvas.prototype.dispose.call(this); return this; }, @@ -1237,7 +1329,7 @@ var originalProperties = this._realizeGroupTransformOnObject(instance), object = this.callSuper('_toObject', instance, methodName, propertiesToInclude); //Undo the damage we did by changing all of its properties - this._unwindGroupTransformOnObject(instance, originalProperties); + originalProperties && instance.set(originalProperties); return object; }, @@ -1263,18 +1355,6 @@ } }, - /** - * Restores the changed properties of instance - * @private - * @param {fabric.Object} [instance] the object to un-transform (gets mutated) - * @param {Object} [originalValues] the original values of instance, as returned by _realizeGroupTransformOnObject - */ - _unwindGroupTransformOnObject: function(instance, originalValues) { - if (originalValues) { - instance.set(originalValues); - } - }, - /** * @private */ @@ -1283,7 +1363,7 @@ //object when the group is deselected var originalProperties = this._realizeGroupTransformOnObject(instance); this.callSuper('_setSVGObject', markup, instance, reviver); - this._unwindGroupTransformOnObject(instance, originalProperties); + originalProperties && instance.set(originalProperties); }, setViewportTransform: function (vpt) { diff --git a/src/controls.actions.js b/src/controls.actions.js index f51b7814030..0883853e34d 100644 --- a/src/controls.actions.js +++ b/src/controls.actions.js @@ -24,7 +24,9 @@ * @return {Number} 0 - 7 a quadrant number */ function findCornerQuadrant(fabricObject, control) { - var cornerAngle = fabricObject.angle + radiansToDegrees(Math.atan2(control.y, control.x)) + 360; + // angle is relative to canvas plane + var angle = fabricObject.getTotalAngle(); + var cornerAngle = angle + radiansToDegrees(Math.atan2(control.y, control.x)) + 360; return Math.round((cornerAngle % 360) / 45); } @@ -193,7 +195,7 @@ */ function wrapWithFixedAnchor(actionHandler) { return function(eventData, transform, x, y) { - var target = transform.target, centerPoint = target.getCenterPoint(), + var target = transform.target, centerPoint = target.getRelativeCenterPoint(), constraint = target.translateToOriginPoint(centerPoint, transform.originX, transform.originY), actionPerformed = actionHandler(eventData, transform, x, y); target.setPositionByOrigin(constraint, transform.originX, transform.originY); @@ -231,7 +233,7 @@ control = target.controls[transform.corner], zoom = target.canvas.getZoom(), padding = target.padding / zoom, - localPoint = target.toLocalPoint(new fabric.Point(x, y), originX, originY); + localPoint = target.normalizePoint(new fabric.Point(x, y), originX, originY); if (localPoint.x >= padding) { localPoint.x -= padding; } @@ -277,7 +279,7 @@ function skewObjectX(eventData, transform, x, y) { var target = transform.target, // find how big the object would be, if there was no skewX. takes in account scaling - dimNoSkew = target._getTransformedDimensions(0, target.skewY), + dimNoSkew = target._getTransformedDimensions({ skewX: 0, skewY: target.skewY }), localPoint = getLocalPoint(transform, transform.originX, transform.originY, x, y), // the mouse is in the center of the object, and we want it to stay there. // so the object will grow twice as much as the mouse. @@ -320,7 +322,7 @@ function skewObjectY(eventData, transform, x, y) { var target = transform.target, // find how big the object would be, if there was no skewX. takes in account scaling - dimNoSkew = target._getTransformedDimensions(target.skewX, 0), + dimNoSkew = target._getTransformedDimensions({ skewX: target.skewX, skewY: 0 }), localPoint = getLocalPoint(transform, transform.originX, transform.originY, x, y), // the mouse is in the center of the object, and we want it to stay there. // so the object will grow twice as much as the mouse. @@ -469,7 +471,7 @@ function rotationWithSnapping(eventData, transform, x, y) { var t = transform, target = t.target, - pivotPoint = target.translateToOriginPoint(target.getCenterPoint(), t.originX, t.originY); + pivotPoint = target.translateToOriginPoint(target.getRelativeCenterPoint(), t.originX, t.originY); if (target.lockRotation) { return false; @@ -688,9 +690,10 @@ strokePadding = target.strokeWidth / (target.strokeUniform ? target.scaleX : 1), multiplier = isTransformCentered(transform) ? 2 : 1, oldWidth = target.width, - newWidth = Math.abs(localPoint.x * multiplier / target.scaleX) - strokePadding; + newWidth = Math.ceil(Math.abs(localPoint.x * multiplier / target.scaleX) - strokePadding); target.set('width', Math.max(newWidth, 0)); - return oldWidth !== newWidth; + // check against actual target width in case `newWidth` was rejected + return oldWidth !== target.width; } /** diff --git a/src/controls.render.js b/src/controls.render.js index 1c3d339eef4..4c1ca95f57b 100644 --- a/src/controls.render.js +++ b/src/controls.render.js @@ -82,7 +82,9 @@ // this is still wrong ctx.lineWidth = 1; ctx.translate(left, top); - ctx.rotate(degreesToRadians(fabricObject.angle)); + // angle is relative to canvas plane + var angle = fabricObject.getTotalAngle(); + ctx.rotate(degreesToRadians(angle)); // this does not work, and fixed with ( && ) does not make sense. // to have real transparent corners we need the controls on upperCanvas // transparentCorners || ctx.clearRect(-xSizeBy2, -ySizeBy2, xSize, ySize); diff --git a/src/mixins/animation.mixin.js b/src/mixins/animation.mixin.js index 02d19ba0ad9..28d84661f36 100644 --- a/src/mixins/animation.mixin.js +++ b/src/mixins/animation.mixin.js @@ -26,7 +26,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati return fabric.util.animate({ target: this, startValue: object.left, - endValue: this.getCenter().left, + endValue: this.getCenterPoint().x, duration: this.FX_DURATION, onChange: function(value) { object.set('left', value); @@ -59,7 +59,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati return fabric.util.animate({ target: this, startValue: object.top, - endValue: this.getCenter().top, + endValue: this.getCenterPoint().y, duration: this.FX_DURATION, onChange: function(value) { object.set('top', value); diff --git a/src/mixins/canvas_dataurl_exporter.mixin.js b/src/mixins/canvas_dataurl_exporter.mixin.js index b23ffab9f4a..75d7322ca8e 100644 --- a/src/mixins/canvas_dataurl_exporter.mixin.js +++ b/src/mixins/canvas_dataurl_exporter.mixin.js @@ -12,8 +12,9 @@ * @param {Number} [options.width] Cropping width. Introduced in v1.2.14 * @param {Number} [options.height] Cropping height. Introduced in v1.2.14 * @param {Boolean} [options.enableRetinaScaling] Enable retina scaling for clone image. Introduce in 2.0.0 + * @param {(object: fabric.Object) => boolean} [options.filter] Function to filter objects. * @return {String} Returns a data: URL containing a representation of the object in the format specified by options.format - * @see {@link http://jsfiddle.net/fabricjs/NfZVb/|jsFiddle demo} + * @see {@link https://jsfiddle.net/xsjua1rd/ demo} * @example Generate jpeg dataURL with lower quality * var dataURL = canvas.toDataURL({ * format: 'jpeg', @@ -32,6 +33,11 @@ * format: 'png', * multiplier: 2 * }); + * @example Generate dataURL with objects that overlap a specified object + * var myObject; + * var dataURL = canvas.toDataURL({ + * filter: (object) => object.isContainedWithinObject(myObject) || object.intersectsWithObject(myObject) + * }); */ toDataURL: function (options) { options || (options = { }); @@ -50,29 +56,31 @@ * This is an intermediary step used to get to a dataUrl but also it is useful to * create quick image copies of a canvas without passing for the dataUrl string * @param {Number} [multiplier] a zoom factor. - * @param {Object} [cropping] Cropping informations - * @param {Number} [cropping.left] Cropping left offset. - * @param {Number} [cropping.top] Cropping top offset. - * @param {Number} [cropping.width] Cropping width. - * @param {Number} [cropping.height] Cropping height. + * @param {Object} [options] Cropping informations + * @param {Number} [options.left] Cropping left offset. + * @param {Number} [options.top] Cropping top offset. + * @param {Number} [options.width] Cropping width. + * @param {Number} [options.height] Cropping height. + * @param {(object: fabric.Object) => boolean} [options.filter] Function to filter objects. */ - toCanvasElement: function(multiplier, cropping) { + toCanvasElement: function (multiplier, options) { multiplier = multiplier || 1; - cropping = cropping || { }; - var scaledWidth = (cropping.width || this.width) * multiplier, - scaledHeight = (cropping.height || this.height) * multiplier, + options = options || { }; + var scaledWidth = (options.width || this.width) * multiplier, + scaledHeight = (options.height || this.height) * multiplier, zoom = this.getZoom(), originalWidth = this.width, originalHeight = this.height, newZoom = zoom * multiplier, vp = this.viewportTransform, - translateX = (vp[4] - (cropping.left || 0)) * multiplier, - translateY = (vp[5] - (cropping.top || 0)) * multiplier, + translateX = (vp[4] - (options.left || 0)) * multiplier, + translateY = (vp[5] - (options.top || 0)) * multiplier, originalInteractive = this.interactive, newVp = [newZoom, 0, 0, newZoom, translateX, translateY], originalRetina = this.enableRetinaScaling, canvasEl = fabric.util.createCanvasElement(), - originalContextTop = this.contextTop; + originalContextTop = this.contextTop, + objectsToRender = options.filter ? this._objects.filter(options.filter) : this._objects; canvasEl.width = scaledWidth; canvasEl.height = scaledHeight; this.contextTop = null; @@ -82,7 +90,7 @@ this.width = scaledWidth; this.height = scaledHeight; this.calcViewportBoundaries(); - this.renderCanvas(canvasEl.getContext('2d'), this._objects); + this.renderCanvas(canvasEl.getContext('2d'), objectsToRender); this.viewportTransform = vp; this.width = originalWidth; this.height = originalHeight; diff --git a/src/mixins/canvas_events.mixin.js b/src/mixins/canvas_events.mixin.js index 0dbb920fb3f..fb7c63f2b74 100644 --- a/src/mixins/canvas_events.mixin.js +++ b/src/mixins/canvas_events.mixin.js @@ -235,10 +235,12 @@ * @param {Event} e Event object fired on mousedown */ _onContextMenu: function (e) { + this._simpleEventHandler('contextmenu:before', e); if (this.stopContextMenu) { e.stopPropagation(); e.preventDefault(); } + this._simpleEventHandler('contextmenu', e); return false; }, diff --git a/src/mixins/canvas_grouping.mixin.js b/src/mixins/canvas_grouping.mixin.js index b3775557277..b59661a38ab 100644 --- a/src/mixins/canvas_grouping.mixin.js +++ b/src/mixins/canvas_grouping.mixin.js @@ -51,7 +51,7 @@ var activeSelection = this._activeObject, currentActiveObjects = activeSelection._objects.slice(0); if (activeSelection.contains(target)) { - activeSelection.removeWithUpdate(target); + activeSelection.remove(target); this._hoveredTarget = target; this._hoveredTargets = this.targets.concat(); if (activeSelection.size() === 1) { @@ -60,7 +60,7 @@ } } else { - activeSelection.addWithUpdate(target); + activeSelection.add(target); this._hoveredTarget = activeSelection; this._hoveredTargets = this.targets.concat(); } diff --git a/src/mixins/collection.mixin.js b/src/mixins/collection.mixin.js index 49af03f1960..9966d8986c2 100644 --- a/src/mixins/collection.mixin.js +++ b/src/mixins/collection.mixin.js @@ -3,79 +3,69 @@ */ fabric.Collection = { + /** + * @type {fabric.Object[]} + */ _objects: [], /** * Adds objects to collection, Canvas or Group, then renders canvas * (if `renderOnAddRemove` is not `false`). - * in case of Group no changes to bounding box are made. * Objects should be instances of (or inherit from) fabric.Object - * Use of this function is highly discouraged for groups. - * you can add a bunch of objects with the add method but then you NEED - * to run a addWithUpdate call for the Group class or position/bbox will be wrong. - * @param {...fabric.Object} object Zero or more fabric instances - * @return {Self} thisArg - * @chainable + * @private + * @param {fabric.Object[]} objects to add + * @param {(object:fabric.Object) => any} [callback] + * @returns {number} new array length */ - add: function () { - this._objects.push.apply(this._objects, arguments); - if (this._onObjectAdded) { - for (var i = 0, length = arguments.length; i < length; i++) { - this._onObjectAdded(arguments[i]); + add: function (objects, callback) { + var size = this._objects.push.apply(this._objects, objects); + if (callback) { + for (var i = 0, length = objects.length; i < length; i++) { + callback.call(this, objects[i]); } } - this.renderOnAddRemove && this.requestRenderAll(); - return this; + return size; }, /** * Inserts an object into collection at specified index, then renders canvas (if `renderOnAddRemove` is not `false`) * An object should be an instance of (or inherit from) fabric.Object - * Use of this function is highly discouraged for groups. - * you can add a bunch of objects with the insertAt method but then you NEED - * to run a addWithUpdate call for the Group class or position/bbox will be wrong. - * @param {Object} object Object to insert + * @private + * @param {fabric.Object|fabric.Object[]} objects Object(s) to insert * @param {Number} index Index to insert object at - * @param {Boolean} nonSplicing When `true`, no splicing (shifting) of objects occurs - * @return {Self} thisArg - * @chainable + * @param {(object:fabric.Object) => any} [callback] */ - insertAt: function (object, index, nonSplicing) { - var objects = this._objects; - if (nonSplicing) { - objects[index] = object; - } - else { - objects.splice(index, 0, object); + insertAt: function (objects, index, callback) { + var args = [index, 0].concat(objects); + this._objects.splice.apply(this._objects, args); + if (callback) { + for (var i = 2, length = args.length; i < length; i++) { + callback.call(this, args[i]); + } } - this._onObjectAdded && this._onObjectAdded(object); - this.renderOnAddRemove && this.requestRenderAll(); - return this; }, /** * Removes objects from a collection, then renders canvas (if `renderOnAddRemove` is not `false`) - * @param {...fabric.Object} object Zero or more fabric instances - * @return {Self} thisArg - * @chainable + * @private + * @param {fabric.Object[]} objectsToRemove objects to remove + * @param {(object:fabric.Object) => any} [callback] function to call for each object removed + * @returns {boolean} true if objects were removed */ - remove: function() { + remove: function(objectsToRemove, callback) { var objects = this._objects, index, somethingRemoved = false; - for (var i = 0, length = arguments.length; i < length; i++) { - index = objects.indexOf(arguments[i]); - + for (var i = 0, length = objectsToRemove.length; i < length; i++) { + index = objects.indexOf(objectsToRemove[i]); // only call onObjectRemoved if an object was actually removed if (index !== -1) { somethingRemoved = true; objects.splice(index, 1); - this._onObjectRemoved && this._onObjectRemoved(arguments[i]); + callback && callback.call(this, objectsToRemove[i]); } } - - this.renderOnAddRemove && somethingRemoved && this.requestRenderAll(); - return this; + return somethingRemoved; }, /** @@ -100,17 +90,16 @@ fabric.Collection = { /** * Returns an array of children objects of this instance - * Type parameter introduced in 1.3.10 - * since 2.3.5 this method return always a COPY of the array; - * @param {String} [type] When specified, only objects of this type are returned + * @param {...String} [types] When specified, only objects of these types are returned * @return {Array} */ - getObjects: function(type) { - if (typeof type === 'undefined') { + getObjects: function() { + if (arguments.length === 0) { return this._objects.concat(); } - return this._objects.filter(function(o) { - return o.type === type; + var types = Array.from(arguments); + return this._objects.filter(function (o) { + return types.indexOf(o.type) > -1; }); }, @@ -140,7 +129,8 @@ fabric.Collection = { }, /** - * Returns true if collection contains an object + * Returns true if collection contains an object.\ + * **Prefer using {@link `fabric.Object#isDescendantOf`} for performance reasons** * @param {Object} object Object to check against * @param {Boolean} [deep=false] `true` to check all descendants, `false` to check only `_objects` * @return {Boolean} `true` if collection contains an object diff --git a/src/mixins/eraser_brush.mixin.js b/src/mixins/eraser_brush.mixin.js index 376c4fa8647..4400e5aca41 100644 --- a/src/mixins/eraser_brush.mixin.js +++ b/src/mixins/eraser_brush.mixin.js @@ -7,6 +7,10 @@ var _getSvgCommons = fabric.Object.prototype.getSvgCommons; var __createBaseClipPathSVGMarkup = fabric.Object.prototype._createBaseClipPathSVGMarkup; var __createBaseSVGMarkup = fabric.Object.prototype._createBaseSVGMarkup; + + fabric.Object.prototype.cacheProperties.push('eraser'); + fabric.Object.prototype.stateProperties.push('eraser'); + /** * @fires erasing:end */ @@ -305,9 +309,7 @@ */ _renderOverlay: function (ctx) { __renderOverlay.call(this, ctx); - if (this.isErasing() && !this.freeDrawingBrush.inverted) { - this.freeDrawingBrush._render(); - } + this.isErasing() && this.freeDrawingBrush._render(); } }); @@ -357,12 +359,12 @@ /** * @private - * This is designed to support erasing a collection with both erasable and non-erasable objects. - * Iterates over collections to allow nested selective erasing. - * Prepares the pattern brush that will draw on the top context to achieve the desired visual effect. - * If brush is **NOT** inverted render all non-erasable objects. - * If brush is inverted render all erasable objects that have been erased with their clip path inverted. - * This will render the erased parts as if they were not erased. + * This is designed to support erasing a collection with both erasable and non-erasable objects while maintaining object stacking.\ + * Iterates over collections to allow nested selective erasing.\ + * Prepares objects before rendering the pattern brush.\ + * If brush is **NOT** inverted render all non-erasable objects.\ + * If brush is inverted render all objects, erasable objects without their eraser. + * This will render the erased parts as if they were not erased in the first place, achieving an undo effect. * * @param {fabric.Collection} collection * @param {CanvasRenderingContext2D} ctx @@ -381,29 +383,22 @@ restorationContext.visibility.push(obj); restorationContext.collection.push(collection); } - else if (this.inverted && obj.visible) { - // render only erasable objects that were erased - if (obj.erasable && obj.eraser) { - obj.eraser.inverted = true; - obj.dirty = true; - collection.dirty = true; - restorationContext.eraser.push(obj); - restorationContext.collection.push(collection); - } - else { - obj.visible = false; - collection.dirty = true; - restorationContext.visibility.push(obj); - restorationContext.collection.push(collection); - } + else if (this.inverted && obj.erasable && obj.eraser && obj.visible) { + // render all objects without eraser + var eraser = obj.eraser; + obj.eraser = undefined; + obj.dirty = true; + collection.dirty = true; + restorationContext.eraser.push([obj, eraser]); + restorationContext.collection.push(collection); } }, this); }, /** * Prepare the pattern for the erasing brush - * This pattern will be drawn on the top context, achieving a visual effect of erasing only erasable objects - * @todo decide how overlay color should behave when `inverted === true`, currently draws over it which is undesirable + * This pattern will be drawn on the top context after clipping the main context, + * achieving a visual effect of erasing only erasable objects * @private */ preparePattern: function () { @@ -427,11 +422,17 @@ this.canvas._renderBackground(patternCtx); if (bgErasable) { this.canvas.backgroundImage = backgroundImage; } } - else if (this.inverted && (backgroundImage && bgErasable)) { - var color = this.canvas.backgroundColor; - this.canvas.backgroundColor = undefined; + else if (this.inverted) { + var eraser = backgroundImage && backgroundImage.eraser; + if (eraser) { + backgroundImage.eraser = undefined; + backgroundImage.dirty = true; + } this.canvas._renderBackground(patternCtx); - this.canvas.backgroundColor = color; + if (eraser) { + backgroundImage.eraser = eraser; + backgroundImage.dirty = true; + } } patternCtx.save(); patternCtx.transform.apply(patternCtx, this.canvas.viewportTransform); @@ -439,8 +440,9 @@ this._prepareCollectionTraversal(this.canvas, patternCtx, restorationContext); this.canvas._renderObjects(patternCtx, this.canvas._objects); restorationContext.visibility.forEach(function (obj) { obj.visible = true; }); - restorationContext.eraser.forEach(function (obj) { - obj.eraser.inverted = false; + restorationContext.eraser.forEach(function (entry) { + var obj = entry[0], eraser = entry[1]; + obj.eraser = eraser; obj.dirty = true; }); restorationContext.collection.forEach(function (obj) { obj.dirty = true; }); @@ -450,11 +452,17 @@ __renderOverlay.call(this.canvas, patternCtx); if (overlayErasable) { this.canvas.overlayImage = overlayImage; } } - else if (this.inverted && (overlayImage && overlayErasable)) { - var color = this.canvas.overlayColor; - this.canvas.overlayColor = undefined; + else if (this.inverted) { + var eraser = overlayImage && overlayImage.eraser; + if (eraser) { + overlayImage.eraser = undefined; + overlayImage.dirty = true; + } __renderOverlay.call(this.canvas, patternCtx); - this.canvas.overlayColor = color; + if (eraser) { + overlayImage.eraser = eraser; + overlayImage.dirty = true; + } } }, @@ -526,12 +534,10 @@ */ _render: function () { var ctx; - if (!this.inverted) { - // clip canvas - ctx = this.canvas.getContext(); - this.callSuper('_render', ctx); - } - // render brush and mask it with image of non erasables + // clip canvas + ctx = this.canvas.getContext(); + this.callSuper('_render', ctx); + // render brush and mask it with pattern ctx = this.canvas.contextTop; this.canvas.clearContext(ctx); this.callSuper('_render', ctx); diff --git a/src/mixins/itext.svg_export.js b/src/mixins/itext.svg_export.js index 5d8f7a63c25..326b03c7a53 100644 --- a/src/mixins/itext.svg_export.js +++ b/src/mixins/itext.svg_export.js @@ -53,6 +53,7 @@ (this.fontStyle ? 'font-style="' + this.fontStyle + '" ' : ''), (this.fontWeight ? 'font-weight="' + this.fontWeight + '" ' : ''), (textDecoration ? 'text-decoration="' + textDecoration + '" ' : ''), + (this.direction === 'rtl' ? 'direction="' + this.direction + '" ' : ''), 'style="', this.getSvgStyles(noShadow), '"', this.addPaintOrder(), ' >', textAndBg.textSpans.join(''), '\n' @@ -75,6 +76,9 @@ // text and text-background for (var i = 0, len = this._textLines.length; i < len; i++) { lineOffset = this._getLineLeftOffset(i); + if (this.direction === 'rtl') { + lineOffset += this.width; + } if (this.textBackgroundColor || this.styleHas('textBackgroundColor', i)) { this._setSVGTextLineBg(textBgRects, i, textLeftOffset + lineOffset, height); } @@ -149,7 +153,12 @@ textSpans.push(this._createTextCharSpan(charsToRender, style, textLeftOffset, textTopOffset)); charsToRender = ''; actualStyle = nextStyle; - textLeftOffset += boxWidth; + if (this.direction === 'rtl') { + textLeftOffset -= boxWidth; + } + else { + textLeftOffset += boxWidth; + } boxWidth = 0; } } diff --git a/src/mixins/itext_behavior.mixin.js b/src/mixins/itext_behavior.mixin.js index 4d075b4f4a4..8c58cf68d5d 100644 --- a/src/mixins/itext_behavior.mixin.js +++ b/src/mixins/itext_behavior.mixin.js @@ -141,9 +141,14 @@ this.abortCursorAnimation(); this._currentCursorOpacity = 1; - this._cursorTimeout2 = setTimeout(function() { - _this._tick(); - }, delay); + if (delay) { + this._cursorTimeout2 = setTimeout(function () { + _this._tick(); + }, delay); + } + else { + this._tick(); + } }, /** @@ -432,12 +437,12 @@ */ fromStringToGraphemeSelection: function(start, end, text) { var smallerTextStart = text.slice(0, start), - graphemeStart = fabric.util.string.graphemeSplit(smallerTextStart).length; + graphemeStart = this.graphemeSplit(smallerTextStart).length; if (start === end) { return { selectionStart: graphemeStart, selectionEnd: graphemeStart }; } var smallerTextEnd = text.slice(start, end), - graphemeEnd = fabric.util.string.graphemeSplit(smallerTextEnd).length; + graphemeEnd = this.graphemeSplit(smallerTextEnd).length; return { selectionStart: graphemeStart, selectionEnd: graphemeStart + graphemeEnd }; }, @@ -592,6 +597,8 @@ this.canvas.defaultCursor = this._savedProps.defaultCursor; this.canvas.moveCursor = this._savedProps.moveCursor; } + + delete this._savedProps; }, /** diff --git a/src/mixins/itext_click_behavior.mixin.js b/src/mixins/itext_click_behavior.mixin.js index eab105f4e88..69b14ed9a13 100644 --- a/src/mixins/itext_click_behavior.mixin.js +++ b/src/mixins/itext_click_behavior.mixin.js @@ -230,7 +230,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot break; } } - lineLeftOffset = this._getLineLeftOffset(lineIndex); + lineLeftOffset = Math.abs(this._getLineLeftOffset(lineIndex)); width = lineLeftOffset * this.scaleX; line = this._textLines[lineIndex]; // handling of RTL: in order to get things work correctly, @@ -238,7 +238,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot // so in position detection we mirror the X offset, and when is time // of rendering it, we mirror it again. if (this.direction === 'rtl') { - mouseOffset.x = this.width * this.scaleX - mouseOffset.x + width; + mouseOffset.x = this.width * this.scaleX - mouseOffset.x; } for (var j = 0, jlen = line.length; j < jlen; j++) { prevWidth = width; diff --git a/src/mixins/itext_key_behavior.mixin.js b/src/mixins/itext_key_behavior.mixin.js index d45217fd7c2..8372dbd44f0 100644 --- a/src/mixins/itext_key_behavior.mixin.js +++ b/src/mixins/itext_key_behavior.mixin.js @@ -16,7 +16,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot // https://bugs.chromium.org/p/chromium/issues/detail?id=870966 this.hiddenTextarea.style.cssText = 'position: absolute; top: ' + style.top + '; left: ' + style.left + '; z-index: -999; opacity: 0; width: 1px; height: 1px; font-size: 1px;' + - ' paddingï½°top: ' + style.fontSize + ';'; + ' padding-top: ' + style.fontSize + ';'; if (this.hiddenTextareaContainer) { this.hiddenTextareaContainer.appendChild(this.hiddenTextarea); @@ -25,6 +25,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot fabric.document.body.appendChild(this.hiddenTextarea); } + fabric.util.addListener(this.hiddenTextarea, 'blur', this.blur.bind(this)); fabric.util.addListener(this.hiddenTextarea, 'keydown', this.onKeyDown.bind(this)); fabric.util.addListener(this.hiddenTextarea, 'keyup', this.onKeyUp.bind(this)); fabric.util.addListener(this.hiddenTextarea, 'input', this.onInput.bind(this)); @@ -98,6 +99,13 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot this.hiddenTextarea && this.hiddenTextarea.focus(); }, + /** + * Override this method to customize cursor behavior on textbox blur + */ + blur: function () { + this.abortCursorAnimation(); + }, + /** * Handles keydown event * only used for arrows and combination of modifier keys. @@ -679,7 +687,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot if (end > start) { this.removeStyleFromTo(start, end); } - var graphemes = fabric.util.string.graphemeSplit(text); + var graphemes = this.graphemeSplit(text); this.insertNewStyleBlock(graphemes, start, style); this._text = [].concat(this._text.slice(0, start), graphemes, this._text.slice(end)); this.text = this._text.join(''); diff --git a/src/mixins/object_ancestry.mixin.js b/src/mixins/object_ancestry.mixin.js new file mode 100644 index 00000000000..594da24a03d --- /dev/null +++ b/src/mixins/object_ancestry.mixin.js @@ -0,0 +1,17 @@ +fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ { + + /** + * + * @param {boolean} [strict] returns only ancestors that are objects (without canvas) + * @returns {(fabric.Object | fabric.StaticCanvas)[]} ancestors from bottom to top + */ + getAncestors: function (strict) { + var ancestors = []; + var parent = this.group || (!strict ? this.canvas : undefined); + while (parent) { + ancestors.push(parent); + parent = parent.group || (!strict ? parent.canvas : undefined); + } + return ancestors; + } +}); diff --git a/src/mixins/object_geometry.mixin.js b/src/mixins/object_geometry.mixin.js index 38f2cf2ef8c..43cce0216fa 100644 --- a/src/mixins/object_geometry.mixin.js +++ b/src/mixins/object_geometry.mixin.js @@ -431,7 +431,7 @@ calcLineCoords: function() { var vpt = this.getViewportTransform(), - padding = this.padding, angle = degreesToRadians(this.angle), + padding = this.padding, angle = degreesToRadians(this.getTotalAngle()), cos = util.cos(angle), sin = util.sin(angle), cosP = cos * padding, sinP = sin * padding, cosPSinP = cosP + sinP, cosPMinusSinP = cosP - sinP, aCoords = this.calcACoords(); @@ -461,7 +461,8 @@ var rotateMatrix = this._calcRotateMatrix(), translateMatrix = this._calcTranslateMatrix(), vpt = this.getViewportTransform(), - startMatrix = multiplyMatrices(vpt, translateMatrix), + startMatrix = this.group ? multiplyMatrices(vpt, this.group.calcTransformMatrix()) : vpt, + startMatrix = multiplyMatrices(startMatrix, translateMatrix), finalMatrix = multiplyMatrices(startMatrix, rotateMatrix), finalMatrix = multiplyMatrices(finalMatrix, [1 / vpt[0], 0, 0, 1 / vpt[3], 0, 0]), dim = this._calculateCurrentDimensions(), @@ -471,15 +472,18 @@ }); // debug code - // var canvas = this.canvas; - // setTimeout(function() { - // canvas.contextTop.clearRect(0, 0, 700, 700); - // canvas.contextTop.fillStyle = 'green'; - // Object.keys(coords).forEach(function(key) { - // var control = coords[key]; - // canvas.contextTop.fillRect(control.x, control.y, 3, 3); - // }); - // }, 50); + /* + var canvas = this.canvas; + setTimeout(function () { + if (!canvas) return; + canvas.contextTop.clearRect(0, 0, 700, 700); + canvas.contextTop.fillStyle = 'green'; + Object.keys(coords).forEach(function(key) { + var control = coords[key]; + canvas.contextTop.fillRect(control.x, control.y, 3, 3); + }); + }, 50); + */ return coords; }, @@ -536,7 +540,7 @@ * @return {Array} rotation matrix for the object */ _calcTranslateMatrix: function() { - var center = this.getCenterPoint(); + var center = this.getRelativeCenterPoint(); return [1, 0, 0, 1, center.x, center.y]; }, @@ -601,77 +605,62 @@ return cache.value; }, - /* + /** * Calculate object dimensions from its properties * @private - * @return {Object} .x width dimension - * @return {Object} .y height dimension + * @returns {fabric.Point} dimensions */ _getNonTransformedDimensions: function() { - var strokeWidth = this.strokeWidth, - w = this.width + strokeWidth, - h = this.height + strokeWidth; - return { x: w, y: h }; + return new fabric.Point(this.width, this.height).scalarAddEquals(this.strokeWidth); }, - /* + /** * Calculate object bounding box dimensions from its properties scale, skew. - * @param {Number} skewX, a value to override current skewX - * @param {Number} skewY, a value to override current skewY + * @param {Object} [options] + * @param {Number} [options.scaleX] + * @param {Number} [options.scaleY] + * @param {Number} [options.skewX] + * @param {Number} [options.skewY] * @private - * @return {Object} .x width dimension - * @return {Object} .y height dimension + * @returns {fabric.Point} dimensions */ - _getTransformedDimensions: function(skewX, skewY) { - if (typeof skewX === 'undefined') { - skewX = this.skewX; - } - if (typeof skewY === 'undefined') { - skewY = this.skewY; - } - var dimensions, dimX, dimY, - noSkew = skewX === 0 && skewY === 0; - + _getTransformedDimensions: function (options) { + options = Object.assign({ + scaleX: this.scaleX, + scaleY: this.scaleY, + skewX: this.skewX, + skewY: this.skewY, + }, options || {}); + // stroke is applied before/after transformations are applied according to `strokeUniform` + var preScalingStrokeValue, postScalingStrokeValue, strokeWidth = this.strokeWidth; if (this.strokeUniform) { - dimX = this.width; - dimY = this.height; + preScalingStrokeValue = 0; + postScalingStrokeValue = strokeWidth; } else { - dimensions = this._getNonTransformedDimensions(); - dimX = dimensions.x; - dimY = dimensions.y; + preScalingStrokeValue = strokeWidth; + postScalingStrokeValue = 0; } + var dimX = this.width + preScalingStrokeValue, + dimY = this.height + preScalingStrokeValue, + finalDimensions, + noSkew = options.skewX === 0 && options.skewY === 0; if (noSkew) { - return this._finalizeDimensions(dimX * this.scaleX, dimY * this.scaleY); + finalDimensions = new fabric.Point(dimX * options.scaleX, dimY * options.scaleY); + } + else { + var bbox = util.sizeAfterTransform(dimX, dimY, options); + finalDimensions = new fabric.Point(bbox.x, bbox.y); } - var bbox = util.sizeAfterTransform(dimX, dimY, { - scaleX: this.scaleX, - scaleY: this.scaleY, - skewX: skewX, - skewY: skewY, - }); - return this._finalizeDimensions(bbox.x, bbox.y); - }, - /* - * Calculate object bounding box dimensions from its properties scale, skew. - * @param Number width width of the bbox - * @param Number height height of the bbox - * @private - * @return {Object} .x finalized width dimension - * @return {Object} .y finalized height dimension - */ - _finalizeDimensions: function(width, height) { - return this.strokeUniform ? - { x: width + this.strokeWidth, y: height + this.strokeWidth } - : - { x: width, y: height }; + return finalDimensions.scalarAddEquals(postScalingStrokeValue); }, - /* + /** * Calculate object dimensions for controls box, including padding and canvas zoom. * and active selection - * private + * @private + * @returns {fabric.Point} dimensions */ _calculateCurrentDimensions: function() { var vpt = this.getViewportTransform(), diff --git a/src/mixins/object_interactivity.mixin.js b/src/mixins/object_interactivity.mixin.js index 4aea3954d05..62335fa8996 100644 --- a/src/mixins/object_interactivity.mixin.js +++ b/src/mixins/object_interactivity.mixin.js @@ -10,15 +10,10 @@ * @return {String|Boolean} corner code (tl, tr, bl, br, etc.), or false if nothing is found */ _findTargetCorner: function(pointer, forTouch) { - // objects in group, anykind, are not self modificable, - // must not return an hovered corner. - if (!this.hasControls || this.group || (!this.canvas || this.canvas._activeObject !== this)) { + if (!this.hasControls || (!this.canvas || this.canvas._activeObject !== this)) { return false; } - - var ex = pointer.x, - ey = pointer.y, - xPoints, + var xPoints, lines, keys = Object.keys(this.oCoords), j = keys.length - 1, i; this.__corner = 0; @@ -45,7 +40,7 @@ // this.canvas.contextTop.fillRect(lines.rightline.d.x, lines.rightline.d.y, 2, 2); // this.canvas.contextTop.fillRect(lines.rightline.o.x, lines.rightline.o.y, 2, 2); - xPoints = this._findCrossPoints({ x: ex, y: ey }, lines); + xPoints = this._findCrossPoints(pointer, lines); if (xPoints !== 0 && xPoints % 2 === 1) { this.__corner = i; return i; @@ -101,7 +96,7 @@ return this; } ctx.save(); - var center = this.getCenterPoint(), wh = this._calculateCurrentDimensions(), + var center = this.getRelativeCenterPoint(), wh = this._calculateCurrentDimensions(), vpt = this.canvas.viewportTransform; ctx.translate(center.x, center.y); ctx.scale(1 / vpt[0], 1 / vpt[3]); @@ -128,8 +123,7 @@ width = wh.x + strokeWidth, height = wh.y + strokeWidth, hasControls = typeof styleOverride.hasControls !== 'undefined' ? - styleOverride.hasControls : this.hasControls, - shouldStroke = false; + styleOverride.hasControls : this.hasControls; ctx.save(); ctx.strokeStyle = styleOverride.borderColor || this.borderColor; @@ -141,26 +135,8 @@ width, height ); + hasControls && this.drawControlsConnectingLines(ctx); - if (hasControls) { - ctx.beginPath(); - this.forEachControl(function(control, key, fabricObject) { - // in this moment, the ctx is centered on the object. - // width and height of the above function are the size of the bbox. - if (control.withConnection && control.getVisibility(fabricObject, key)) { - // reset movement for each control - shouldStroke = true; - ctx.moveTo(control.x * width, control.y * height); - ctx.lineTo( - control.x * width + control.offsetX, - control.y * height + control.offsetY - ); - } - }); - if (shouldStroke) { - ctx.stroke(); - } - } ctx.restore(); return this; }, @@ -184,7 +160,9 @@ width = bbox.x + strokeWidth * (strokeUniform ? this.canvas.getZoom() : options.scaleX) + borderScaleFactor, height = - bbox.y + strokeWidth * (strokeUniform ? this.canvas.getZoom() : options.scaleY) + borderScaleFactor; + bbox.y + strokeWidth * (strokeUniform ? this.canvas.getZoom() : options.scaleY) + borderScaleFactor, + hasControls = typeof styleOverride.hasControls !== 'undefined' ? + styleOverride.hasControls : this.hasControls; ctx.save(); this._setLineDash(ctx, styleOverride.borderDashArray || this.borderDashArray); ctx.strokeStyle = styleOverride.borderColor || this.borderColor; @@ -194,11 +172,46 @@ width, height ); + hasControls && this.drawControlsConnectingLines(ctx); ctx.restore(); return this; }, + /** + * Draws lines from a borders of an object's bounding box to controls that have `withConnection` property set. + * Requires public properties: width, height + * Requires public options: padding, borderColor + * @param {CanvasRenderingContext2D} ctx Context to draw on + * @return {fabric.Object} thisArg + * @chainable + */ + drawControlsConnectingLines: function (ctx) { + var wh = this._calculateCurrentDimensions(), + strokeWidth = this.borderScaleFactor, + width = wh.x + strokeWidth, + height = wh.y + strokeWidth, + shouldStroke = false; + + ctx.beginPath(); + this.forEachControl(function (control, key, fabricObject) { + // in this moment, the ctx is centered on the object. + // width and height of the above function are the size of the bbox. + if (control.withConnection && control.getVisibility(fabricObject, key)) { + // reset movement for each control + shouldStroke = true; + ctx.moveTo(control.x * width, control.y * height); + ctx.lineTo( + control.x * width + control.offsetX, + control.y * height + control.offsetY + ); + } + }); + shouldStroke && ctx.stroke(); + + return this; + }, + /** * Draws corners of an object's bounding box. * Requires public properties: width, height @@ -211,7 +224,7 @@ drawControls: function(ctx, styleOverride) { styleOverride = styleOverride || {}; ctx.save(); - var retinaScaling = this.canvas.getRetinaScaling(), matrix, p; + var retinaScaling = this.canvas.getRetinaScaling(), p; ctx.setTransform(retinaScaling, 0, 0, retinaScaling, 0, 0); ctx.strokeStyle = ctx.fillStyle = styleOverride.cornerColor || this.cornerColor; if (!this.transparentCorners) { @@ -219,20 +232,9 @@ } this._setLineDash(ctx, styleOverride.cornerDashArray || this.cornerDashArray); this.setCoords(); - if (this.group) { - // fabricJS does not really support drawing controls inside groups, - // this piece of code here helps having at least the control in places. - // If an application needs to show some objects as selected because of some UI state - // can still call Object._renderControls() on any object they desire, independently of groups. - // using no padding, circular controls and hiding the rotating cursor is higly suggested, - matrix = this.group.calcTransformMatrix(); - } this.forEachControl(function(control, key, fabricObject) { - p = fabricObject.oCoords[key]; if (control.getVisibility(fabricObject, key)) { - if (matrix) { - p = fabric.util.transformPoint(p, matrix); - } + p = fabricObject.oCoords[key]; control.render(ctx, p.x, p.y, styleOverride, fabricObject); } }); diff --git a/src/mixins/object_origin.mixin.js b/src/mixins/object_origin.mixin.js index 7cd8f10df70..34c205a9883 100644 --- a/src/mixins/object_origin.mixin.js +++ b/src/mixins/object_origin.mixin.js @@ -12,53 +12,52 @@ bottom: 0.5 }; + /** + * @typedef {number | 'left' | 'center' | 'right'} OriginX + * @typedef {number | 'top' | 'center' | 'bottom'} OriginY + */ + fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ { + /** + * Resolves origin value relative to center + * @private + * @param {OriginX} originX + * @returns number + */ + resolveOriginX: function (originX) { + return typeof originX === 'string' ? + originXOffset[originX] : + originX - 0.5; + }, + + /** + * Resolves origin value relative to center + * @private + * @param {OriginY} originY + * @returns number + */ + resolveOriginY: function (originY) { + return typeof originY === 'string' ? + originYOffset[originY] : + originY - 0.5; + }, + /** * Translates the coordinates from a set of origin to another (based on the object's dimensions) * @param {fabric.Point} point The point which corresponds to the originX and originY params - * @param {String} fromOriginX Horizontal origin: 'left', 'center' or 'right' - * @param {String} fromOriginY Vertical origin: 'top', 'center' or 'bottom' - * @param {String} toOriginX Horizontal origin: 'left', 'center' or 'right' - * @param {String} toOriginY Vertical origin: 'top', 'center' or 'bottom' + * @param {OriginX} fromOriginX Horizontal origin: 'left', 'center' or 'right' + * @param {OriginY} fromOriginY Vertical origin: 'top', 'center' or 'bottom' + * @param {OriginX} toOriginX Horizontal origin: 'left', 'center' or 'right' + * @param {OriginY} toOriginY Vertical origin: 'top', 'center' or 'bottom' * @return {fabric.Point} */ translateToGivenOrigin: function(point, fromOriginX, fromOriginY, toOriginX, toOriginY) { var x = point.x, y = point.y, - offsetX, offsetY, dim; - - if (typeof fromOriginX === 'string') { - fromOriginX = originXOffset[fromOriginX]; - } - else { - fromOriginX -= 0.5; - } - - if (typeof toOriginX === 'string') { - toOriginX = originXOffset[toOriginX]; - } - else { - toOriginX -= 0.5; - } - - offsetX = toOriginX - fromOriginX; - - if (typeof fromOriginY === 'string') { - fromOriginY = originYOffset[fromOriginY]; - } - else { - fromOriginY -= 0.5; - } - - if (typeof toOriginY === 'string') { - toOriginY = originYOffset[toOriginY]; - } - else { - toOriginY -= 0.5; - } - - offsetY = toOriginY - fromOriginY; + dim, + offsetX = this.resolveOriginX(toOriginX) - this.resolveOriginX(fromOriginX), + offsetY = this.resolveOriginY(toOriginY) - this.resolveOriginY(fromOriginY); if (offsetX || offsetY) { dim = this._getTransformedDimensions(); @@ -72,8 +71,8 @@ /** * Translates the coordinates from origin to center coordinates (based on the object's dimensions) * @param {fabric.Point} point The point which corresponds to the originX and originY params - * @param {String} originX Horizontal origin: 'left', 'center' or 'right' - * @param {String} originY Vertical origin: 'top', 'center' or 'bottom' + * @param {OriginX} originX Horizontal origin: 'left', 'center' or 'right' + * @param {OriginY} originY Vertical origin: 'top', 'center' or 'bottom' * @return {fabric.Point} */ translateToCenterPoint: function(point, originX, originY) { @@ -87,8 +86,8 @@ /** * Translates the coordinates from center to origin coordinates (based on the object's dimensions) * @param {fabric.Point} center The point which corresponds to center of the object - * @param {String} originX Horizontal origin: 'left', 'center' or 'right' - * @param {String} originY Vertical origin: 'top', 'center' or 'bottom' + * @param {OriginX} originX Horizontal origin: 'left', 'center' or 'right' + * @param {OriginY} originY Vertical origin: 'top', 'center' or 'bottom' * @return {fabric.Point} */ translateToOriginPoint: function(center, originX, originY) { @@ -100,12 +99,30 @@ }, /** - * Returns the real center coordinates of the object + * Returns the center coordinates of the object relative to canvas * @return {fabric.Point} */ getCenterPoint: function() { - var leftTop = new fabric.Point(this.left, this.top); - return this.translateToCenterPoint(leftTop, this.originX, this.originY); + var relCenter = this.getRelativeCenterPoint(); + return this.group ? + fabric.util.transformPoint(relCenter, this.group.calcTransformMatrix()) : + relCenter; + }, + + /** + * Returns the center coordinates of the object relative to it's containing group or null + * @return {fabric.Point|null} point or null of object has no parent group + */ + getCenterPointRelativeToParent: function () { + return this.group ? this.getRelativeCenterPoint() : null; + }, + + /** + * Returns the center coordinates of the object relative to it's parent + * @return {fabric.Point} + */ + getRelativeCenterPoint: function () { + return this.translateToCenterPoint(new fabric.Point(this.left, this.top), this.originX, this.originY); }, /** @@ -119,26 +136,24 @@ /** * Returns the coordinates of the object as if it has a different origin - * @param {String} originX Horizontal origin: 'left', 'center' or 'right' - * @param {String} originY Vertical origin: 'top', 'center' or 'bottom' + * @param {OriginX} originX Horizontal origin: 'left', 'center' or 'right' + * @param {OriginY} originY Vertical origin: 'top', 'center' or 'bottom' * @return {fabric.Point} */ getPointByOrigin: function(originX, originY) { - var center = this.getCenterPoint(); + var center = this.getRelativeCenterPoint(); return this.translateToOriginPoint(center, originX, originY); }, /** - * Returns the point in local coordinates - * @param {fabric.Point} point The point relative to the global coordinate system - * @param {String} originX Horizontal origin: 'left', 'center' or 'right' - * @param {String} originY Vertical origin: 'top', 'center' or 'bottom' + * Returns the normalized point (rotated relative to center) in local coordinates + * @param {fabric.Point} point The point relative to instance coordinate system + * @param {OriginX} originX Horizontal origin: 'left', 'center' or 'right' + * @param {OriginY} originY Vertical origin: 'top', 'center' or 'bottom' * @return {fabric.Point} */ - toLocalPoint: function(point, originX, originY) { - var center = this.getCenterPoint(), - p, p2; - + normalizePoint: function(point, originX, originY) { + var center = this.getRelativeCenterPoint(), p, p2; if (typeof originX !== 'undefined' && typeof originY !== 'undefined' ) { p = this.translateToGivenOrigin(center, 'center', 'center', originX, originY); } @@ -153,6 +168,20 @@ return p2.subtractEquals(p); }, + /** + * Returns coordinates of a pointer relative to object's top left corner in object's plane + * @param {Event} e Event to operate upon + * @param {Object} [pointer] Pointer to operate upon (instead of event) + * @return {Object} Coordinates of a pointer (x, y) + */ + getLocalPointer: function (e, pointer) { + pointer = pointer || this.canvas.getPointer(e); + return fabric.util.transformPoint( + new fabric.Point(pointer.x, pointer.y), + fabric.util.invertTransform(this.calcTransformMatrix()) + ).addEquals(new fabric.Point(this.width / 2, this.height / 2)); + }, + /** * Returns the point in global coordinates * @param {fabric.Point} The point relative to the local coordinate system @@ -165,8 +194,8 @@ /** * Sets the position of the object taking into consideration the object's origin * @param {fabric.Point} pos The new position of the object - * @param {String} originX Horizontal origin: 'left', 'center' or 'right' - * @param {String} originY Vertical origin: 'top', 'center' or 'bottom' + * @param {OriginX} originX Horizontal origin: 'left', 'center' or 'right' + * @param {OriginY} originY Vertical origin: 'top', 'center' or 'bottom' * @return {void} */ setPositionByOrigin: function(pos, originX, originY) { @@ -214,7 +243,7 @@ this._originalOriginX = this.originX; this._originalOriginY = this.originY; - var center = this.getCenterPoint(); + var center = this.getRelativeCenterPoint(); this.originX = 'center'; this.originY = 'center'; @@ -230,7 +259,7 @@ */ _resetOrigin: function() { var originPoint = this.translateToOriginPoint( - this.getCenterPoint(), + this.getRelativeCenterPoint(), this._originalOriginX, this._originalOriginY); @@ -248,7 +277,7 @@ * @private */ _getLeftTopCoords: function() { - return this.translateToOriginPoint(this.getCenterPoint(), 'left', 'top'); + return this.translateToOriginPoint(this.getRelativeCenterPoint(), 'left', 'top'); }, }); diff --git a/src/parser.js b/src/parser.js index a4592642a65..cd729a08b7b 100644 --- a/src/parser.js +++ b/src/parser.js @@ -78,8 +78,7 @@ } function normalizeValue(attr, value, parentAttributes, fontSize) { - var isArray = Object.prototype.toString.call(value) === '[object Array]', - parsed; + var isArray = Array.isArray(value), parsed; if ((attr === 'fill' || attr === 'stroke') && value === 'none') { value = ''; @@ -477,7 +476,7 @@ return; } - var xlink = xlinkAttribute.substr(1), + var xlink = xlinkAttribute.slice(1), x = el.getAttribute('x') || 0, y = el.getAttribute('y') || 0, el2 = elementById(doc, xlink).cloneNode(true), @@ -749,7 +748,7 @@ function recursivelyParseGradientsXlink(doc, gradient) { var gradientsAttrs = ['gradientTransform', 'x1', 'x2', 'y1', 'y2', 'gradientUnits', 'cx', 'cy', 'r', 'fx', 'fy'], xlinkAttr = 'xlink:href', - xLink = gradient.getAttribute(xlinkAttr).substr(1), + xLink = gradient.getAttribute(xlinkAttr).slice(1), referencedGradient = elementById(doc, xLink); if (referencedGradient && referencedGradient.getAttribute(xlinkAttr)) { recursivelyParseGradientsXlink(doc, referencedGradient); diff --git a/src/point.class.js b/src/point.class.js index ca7b84f3ef2..d538835ceee 100644 --- a/src/point.class.js +++ b/src/point.class.js @@ -117,47 +117,61 @@ return this; }, + /** + * Multiplies this point by another value and returns a new one + * @param {fabric.Point} that + * @return {fabric.Point} + */ + multiply: function (that) { + return new Point(this.x * that.x, this.y * that.y); + }, + /** * Multiplies this point by a value and returns a new one - * TODO: rename in scalarMultiply in 2.0 * @param {Number} scalar * @return {fabric.Point} */ - multiply: function (scalar) { + scalarMultiply: function (scalar) { return new Point(this.x * scalar, this.y * scalar); }, /** * Multiplies this point by a value - * TODO: rename in scalarMultiplyEquals in 2.0 * @param {Number} scalar * @return {fabric.Point} thisArg * @chainable */ - multiplyEquals: function (scalar) { + scalarMultiplyEquals: function (scalar) { this.x *= scalar; this.y *= scalar; return this; }, + /** + * Divides this point by another and returns a new one + * @param {fabric.Point} that + * @return {fabric.Point} + */ + divide: function (that) { + return new Point(this.x / that.x, this.y / that.y); + }, + /** * Divides this point by a value and returns a new one - * TODO: rename in scalarDivide in 2.0 * @param {Number} scalar * @return {fabric.Point} */ - divide: function (scalar) { + scalarDivide: function (scalar) { return new Point(this.x / scalar, this.y / scalar); }, /** * Divides this point by a value - * TODO: rename in scalarDivideEquals in 2.0 * @param {Number} scalar * @return {fabric.Point} thisArg * @chainable */ - divideEquals: function (scalar) { + scalarDivideEquals: function (scalar) { this.x /= scalar; this.y /= scalar; return this; diff --git a/src/shapes/active_selection.class.js b/src/shapes/active_selection.class.js index 36b17c6b280..a2b648752d0 100644 --- a/src/shapes/active_selection.class.js +++ b/src/shapes/active_selection.class.js @@ -24,58 +24,42 @@ */ type: 'activeSelection', + /** + * @override + */ + layout: 'fit-content', + + /** + * @override + */ + subTargetCheck: false, + + /** + * @override + */ + interactive: false, + /** * Constructor - * @param {Object} objects ActiveSelection objects + * + * @param {fabric.Object[]} [objects] instance objects * @param {Object} [options] Options object - * @return {Object} thisArg + * @param {boolean} [objectsRelativeToGroup] true if objects exist in group coordinate plane + * @return {fabric.ActiveSelection} thisArg */ - initialize: function(objects, options) { - options = options || {}; - this._objects = objects || []; - for (var i = this._objects.length; i--; ) { - this._objects[i].group = this; - } - - if (options.originX) { - this.originX = options.originX; - } - if (options.originY) { - this.originY = options.originY; - } - this._calcBounds(); - this._updateObjectsCoords(); - fabric.Object.prototype.initialize.call(this, options); + initialize: function (objects, options, objectsRelativeToGroup) { + this.callSuper('initialize', objects, options, objectsRelativeToGroup); this.setCoords(); }, /** - * Change te activeSelection to a normal group, - * High level function that automatically adds it to canvas as - * active object. no events fired. - * @since 2.0.0 - * @return {fabric.Group} + * we want objects to retain their canvas ref when exiting instance + * @private + * @param {fabric.Object} object + * @param {boolean} [removeParentTransform] true if object should exit group without applying group's transform to it */ - toGroup: function() { - var objects = this._objects.concat(); - this._objects = []; - var options = fabric.Object.prototype.toObject.call(this); - var newGroup = new fabric.Group([]); - delete options.type; - newGroup.set(options); - objects.forEach(function(object) { - object.canvas.remove(object); - object.group = newGroup; - }); - newGroup._objects = objects; - if (!this.canvas) { - return newGroup; - } - var canvas = this.canvas; - canvas.add(newGroup); - canvas._activeObject = newGroup; - newGroup.setCoords(); - return newGroup; + exitGroup: function (object, removeParentTransform) { + this._exitGroup(object, removeParentTransform); }, /** @@ -84,7 +68,7 @@ * @return {Boolean} [cancel] */ onDeselect: function() { - this.destroy(); + this.removeAll(); return false; }, diff --git a/src/shapes/group.class.js b/src/shapes/group.class.js index 1bf2f6d3a6d..9e9c47905d6 100644 --- a/src/shapes/group.class.js +++ b/src/shapes/group.class.js @@ -1,12 +1,18 @@ -(function(global) { +(function (global) { 'use strict'; - var fabric = global.fabric || (global.fabric = { }), - min = fabric.util.array.min, - max = fabric.util.array.max; + var fabric = global.fabric || (global.fabric = {}), + multiplyTransformMatrices = fabric.util.multiplyTransformMatrices, + invertTransform = fabric.util.invertTransform, + transformPoint = fabric.util.transformPoint, + applyTransformToObject = fabric.util.applyTransformToObject, + degreesToRadians = fabric.util.degreesToRadians, + clone = fabric.util.object.clone, + extend = fabric.util.object.extend; if (fabric.Group) { + fabric.warn('fabric.Group is already defined'); return; } @@ -15,280 +21,319 @@ * @class fabric.Group * @extends fabric.Object * @mixes fabric.Collection - * @tutorial {@link http://fabricjs.com/fabric-intro-part-3#groups} + * @fires layout once layout completes * @see {@link fabric.Group#initialize} for constructor definition */ fabric.Group = fabric.util.createClass(fabric.Object, fabric.Collection, /** @lends fabric.Group.prototype */ { /** * Type of an object - * @type String + * @type string * @default */ type: 'group', + /** + * Specifies the **layout strategy** for instance + * Used by `getLayoutStrategyResult` to calculate layout + * `fit-content`, `fit-content-lazy`, `fixed`, `clip-path` are supported out of the box + * @type string + * @default + */ + layout: 'fit-content', + /** * Width of stroke * @type Number - * @default */ strokeWidth: 0, /** - * Indicates if click, mouseover, mouseout events & hoverCursor should also check for subtargets - * @type Boolean + * List of properties to consider when checking if state + * of an object is changed (fabric.Object#hasStateChanged) + * as well as for history (undo/redo) purposes + * @type string[] + */ + stateProperties: fabric.Object.prototype.stateProperties.concat('layout'), + + /** + * Used to optimize performance + * set to `false` if you don't need caontained objects to be target of events * @default + * @type boolean */ subTargetCheck: false, /** - * Groups are container, do not render anything on theyr own, ence no cache properties - * @type Array + * Used to allow targeting of object inside groups. + * set to true if you want to select an object inside a group. + * REQUIRES subTargetCheck set to true * @default + * @type boolean */ - cacheProperties: [], + interactive: false, /** - * setOnGroup is a method used for TextBox that is no more used since 2.0.0 The behavior is still - * available setting this boolean to true. - * @type Boolean - * @since 2.0.0 - * @default + * Used internally to optimize performance + * Once an object is selected, instance is rendered without the selected object. + * This way instance is cached only once for the entire interaction with the selected object. + * @private */ - useSetOnGroup: false, + _activeObjects: undefined, /** * Constructor - * @param {Object} objects Group objects + * + * @param {fabric.Object[]} [objects] instance objects * @param {Object} [options] Options object - * @param {Boolean} [isAlreadyGrouped] if true, objects have been grouped already. - * @return {Object} thisArg - */ - initialize: function(objects, options, isAlreadyGrouped) { - options = options || {}; - this._objects = []; - // if objects enclosed in a group have been grouped already, - // we cannot change properties of objects. - // Thus we need to set options to group without objects, - isAlreadyGrouped && this.callSuper('initialize', options); + * @param {boolean} [objectsRelativeToGroup] true if objects exist in group coordinate plane + * @return {fabric.Group} thisArg + */ + initialize: function (objects, options, objectsRelativeToGroup) { this._objects = objects || []; - for (var i = this._objects.length; i--; ) { - this._objects[i].group = this; - } - - if (!isAlreadyGrouped) { - var center = options && options.centerPoint; - // we want to set origins before calculating the bounding box. - // so that the topleft can be set with that in mind. - // if specific top and left are passed, are overwritten later - // with the callSuper('initialize', options) - if (options.originX !== undefined) { - this.originX = options.originX; - } - if (options.originY !== undefined) { - this.originY = options.originY; - } - // if coming from svg i do not want to calc bounds. - // i assume width and height are passed along options - center || this._calcBounds(); - this._updateObjectsCoords(center); - delete options.centerPoint; - this.callSuper('initialize', options); + this._activeObjects = []; + this.__objectMonitor = this.__objectMonitor.bind(this); + this.__objectSelectionTracker = this.__objectSelectionMonitor.bind(this, true); + this.__objectSelectionDisposer = this.__objectSelectionMonitor.bind(this, false); + this._firstLayoutDone = false; + this.callSuper('initialize', options); + if (objectsRelativeToGroup) { + this.forEachObject(function (object) { + this.enterGroup(object, false); + }, this); } else { - this._updateObjectsACoords(); + // we need to preserve object's center point in relation to canvas and apply group's transform to it + var inv = invertTransform(this.calcTransformMatrix()); + this.forEachObject(function (object) { + this.enterGroup(object, false); + var center = transformPoint(object.getCenterPoint(), inv); + object.setPositionByOrigin(center, 'center', 'center'); + }, this); } - - this.setCoords(); + this._applyLayoutStrategy({ + type: 'initialization', + options: options, + objectsRelativeToGroup: objectsRelativeToGroup + }); }, /** * @private - */ - _updateObjectsACoords: function() { - var skipControls = true; - for (var i = this._objects.length; i--; ){ - this._objects[i].setCoords(skipControls); + * @param {string} key + * @param {*} value + */ + _set: function (key, value) { + var prev = this[key]; + this.callSuper('_set', key, value); + if (key === 'canvas' && prev !== value) { + this.forEachObject(function (object) { + object._set(key, value); + }); + } + if (key === 'layout' && prev !== value) { + this._applyLayoutStrategy({ type: 'layout_change', layout: value, prevLayout: prev }); } + if (key === 'interactive') { + this.forEachObject(this._watchObject.bind(this, value)); + } + return this; }, /** - * @private - * @param {Boolean} [skipCoordsChange] if true, coordinates of objects enclosed in a group do not change + * Add objects + * @param {...fabric.Object} objects */ - _updateObjectsCoords: function(center) { - var center = center || this.getCenterPoint(); - for (var i = this._objects.length; i--; ){ - this._updateObjectCoords(this._objects[i], center); - } + add: function () { + fabric.Collection.add.call(this, arguments, this._onObjectAdded); + this._onAfterObjectsChange('added', Array.from(arguments)); }, /** - * @private - * @param {Object} object - * @param {fabric.Point} center, current center of group. + * Inserts an object into collection at specified index + * @param {fabric.Object} objects Object to insert + * @param {Number} index Index to insert object at */ - _updateObjectCoords: function(object, center) { - var objectLeft = object.left, - objectTop = object.top, - skipControls = true; + insertAt: function (objects, index) { + fabric.Collection.insertAt.call(this, objects, index, this._onObjectAdded); + this._onAfterObjectsChange('added', Array.isArray(objects) ? objects : [objects]); + }, - object.set({ - left: objectLeft - center.x, - top: objectTop - center.y - }); - object.group = this; - object.setCoords(skipControls); + /** + * Remove objects + * @param {...fabric.Object} objects + */ + remove: function () { + fabric.Collection.remove.call(this, arguments, this._onObjectRemoved); + this._onAfterObjectsChange('removed', Array.from(arguments)); }, /** - * Returns string represenation of a group - * @return {String} + * Remove all objects + * @returns {fabric.Object[]} removed objects */ - toString: function() { - return '#'; + removeAll: function () { + this._activeObjects = []; + var remove = this._objects.slice(); + this.remove.apply(this, remove); + return remove; }, /** - * Adds an object to a group; Then recalculates group's dimension, position. - * @param {Object} object - * @return {fabric.Group} thisArg - * @chainable - */ - addWithUpdate: function(object) { - var nested = !!this.group; - this._restoreObjectsState(); - fabric.util.resetObjectTransform(this); - if (object) { - if (nested) { - // if this group is inside another group, we need to pre transform the object - fabric.util.removeTransformFromObject(object, this.group.calcTransformMatrix()); - } - this._objects.push(object); - object.group = this; - object._set('canvas', this.canvas); - } - this._calcBounds(); - this._updateObjectsCoords(); - this.dirty = true; - if (nested) { - this.group.addWithUpdate(); - } - else { - this.setCoords(); - } - return this; + * invalidates layout on object modified + * @private + */ + __objectMonitor: function (opt) { + this._applyLayoutStrategy(extend(clone(opt), { + type: 'object_modified' + })); + this._set('dirty', true); }, /** - * Removes an object from a group; Then recalculates group's dimension, position. - * @param {Object} object - * @return {fabric.Group} thisArg - * @chainable + * keeps track of the selected objects + * @private */ - removeWithUpdate: function(object) { - this._restoreObjectsState(); - fabric.util.resetObjectTransform(this); + __objectSelectionMonitor: function (selected, opt) { + var object = opt.target; + if (selected) { + this._activeObjects.push(object); + this._set('dirty', true); + } + else if (this._activeObjects.length > 0) { + var index = this._activeObjects.indexOf(object); + if (index > -1) { + this._activeObjects.splice(index, 1); + this._set('dirty', true); + } + } + }, - this.remove(object); - this._calcBounds(); - this._updateObjectsCoords(); - this.setCoords(); - this.dirty = true; - return this; + /** + * @private + * @param {boolean} watch + * @param {fabric.Object} object + */ + _watchObject: function (watch, object) { + var directive = watch ? 'on' : 'off'; + // make sure we listen only once + watch && this._watchObject(false, object); + object[directive]('changed', this.__objectMonitor); + object[directive]('modified', this.__objectMonitor); + object[directive]('selected', this.__objectSelectionTracker); + object[directive]('deselected', this.__objectSelectionDisposer); }, /** * @private - */ - _onObjectAdded: function(object) { - this.dirty = true; - object.group = this; + * @param {fabric.Object} object + * @param {boolean} [removeParentTransform] true if object is in canvas coordinate plane + */ + enterGroup: function (object, removeParentTransform) { + if (object.group) { + if (object.group === this) { + /* _DEV_MODE_START_ */ + console.warn('fabric.Group: duplicate objects are not supported inside group, this call has no effect'); + /* _DEV_MODE_END_ */ + return; + } + object.group.remove(object); + } + // can be this converted to utils? + if (removeParentTransform) { + applyTransformToObject( + object, + multiplyTransformMatrices( + invertTransform(this.calcTransformMatrix()), + object.calcTransformMatrix() + ) + ); + } + object.setCoords(); + object._set('group', this); object._set('canvas', this.canvas); + this.interactive && this._watchObject(true, object); + var activeObject = this.canvas && this.canvas.getActiveObject && this.canvas.getActiveObject(); + // if we are adding the activeObject in a group + // TODO migrate back to isDescendantOf + if (activeObject && (activeObject === object || (object.contains && object.contains(activeObject)))) { + this._activeObjects.push(object); + } }, /** * @private + * @param {fabric.Object} object + * @param {boolean} [removeParentTransform] true if object should exit group without applying group's transform to it */ - _onObjectRemoved: function(object) { - this.dirty = true; - delete object.group; + exitGroup: function (object, removeParentTransform) { + this._exitGroup(object, removeParentTransform); + object._set('canvas', undefined); }, /** * @private - */ - _set: function(key, value) { - var i = this._objects.length; - if (this.useSetOnGroup) { - while (i--) { - this._objects[i].setOnGroup(key, value); - } + * @param {fabric.Object} object + * @param {boolean} [removeParentTransform] true if object should exit group without applying group's transform to it + */ + _exitGroup: function (object, removeParentTransform) { + object._set('group', undefined); + if (!removeParentTransform) { + applyTransformToObject( + object, + multiplyTransformMatrices( + this.calcTransformMatrix(), + object.calcTransformMatrix() + ) + ); + object.setCoords(); } - if (key === 'canvas') { - while (i--) { - this._objects[i]._set(key, value); - } + this._watchObject(false, object); + var index = this._activeObjects.length > 0 ? this._activeObjects.indexOf(object) : -1; + if (index > -1) { + this._activeObjects.splice(index, 1); } - fabric.Object.prototype._set.call(this, key, value); }, /** - * Returns object representation of an instance - * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output - * @return {Object} object representation of an instance + * @private + * @param {'added'|'removed'} type + * @param {fabric.Object[]} targets */ - toObject: function(propertiesToInclude) { - var _includeDefaultValues = this.includeDefaultValues; - var objsToObject = this._objects - .filter(function (obj) { - return !obj.excludeFromExport; - }) - .map(function (obj) { - var originalDefaults = obj.includeDefaultValues; - obj.includeDefaultValues = _includeDefaultValues; - var _obj = obj.toObject(propertiesToInclude); - obj.includeDefaultValues = originalDefaults; - return _obj; - }); - var obj = fabric.Object.prototype.toObject.call(this, propertiesToInclude); - obj.objects = objsToObject; - return obj; + _onAfterObjectsChange: function (type, targets) { + this._applyLayoutStrategy({ + type: type, + targets: targets + }); + this._set('dirty', true); }, /** - * Returns object representation of an instance, in dataless mode. - * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output - * @return {Object} object representation of an instance + * @private + * @param {fabric.Object} object */ - toDatalessObject: function(propertiesToInclude) { - var objsToObject, sourcePath = this.sourcePath; - if (sourcePath) { - objsToObject = sourcePath; - } - else { - var _includeDefaultValues = this.includeDefaultValues; - objsToObject = this._objects.map(function(obj) { - var originalDefaults = obj.includeDefaultValues; - obj.includeDefaultValues = _includeDefaultValues; - var _obj = obj.toDatalessObject(propertiesToInclude); - obj.includeDefaultValues = originalDefaults; - return _obj; - }); - } - var obj = fabric.Object.prototype.toDatalessObject.call(this, propertiesToInclude); - obj.objects = objsToObject; - return obj; + _onObjectAdded: function (object) { + this.enterGroup(object, true); + object.fire('added', { target: this }); }, /** - * Renders instance on a given context - * @param {CanvasRenderingContext2D} ctx context to render instance on + * @private + * @param {fabric.Object} object */ - render: function(ctx) { - this._transformDone = true; - this.callSuper('render', ctx); - this._transformDone = false; + _onRelativeObjectAdded: function (object) { + this.enterGroup(object, false); + object.fire('added', { target: this }); + }, + + /** + * @private + * @param {fabric.Object} object + * @param {boolean} [removeParentTransform] true if object should exit group without applying group's transform to it + */ + _onObjectRemoved: function (object, removeParentTransform) { + this.exitGroup(object, removeParentTransform); + object.fire('removed', { target: this }); }, /** @@ -328,11 +373,11 @@ }, /** - * Check if this group or its parent group are caching, recursively up + * Check if instance or its group are caching, recursively up * @return {Boolean} */ - isOnACache: function() { - return this.ownCaching || (this.group && this.group.isOnACache()); + isOnACache: function () { + return this.ownCaching || (!!this.group && this.group.isOnACache()); }, /** @@ -370,151 +415,448 @@ }, /** - * Restores original state of each of group objects (original state is that which was before group was created). - * if the nested boolean is true, the original state will be restored just for the - * first group and not for all the group chain - * @private - * @param {Boolean} nested tell the function to restore object state up to the parent group and not more - * @return {fabric.Group} thisArg - * @chainable - */ - _restoreObjectsState: function() { - var groupMatrix = this.calcOwnMatrix(); - this._objects.forEach(function(object) { - // instead of using _this = this; - fabric.util.addTransformToObject(object, groupMatrix); - delete object.group; + * @override + * @return {Boolean} + */ + setCoords: function () { + this.callSuper('setCoords'); + (this.subTargetCheck || this.type === 'activeSelection') && this.forEachObject(function (object) { object.setCoords(); }); - return this; }, /** - * Destroys a group (restoring state of its objects) - * @return {fabric.Group} thisArg - * @chainable + * Renders instance on a given context + * @param {CanvasRenderingContext2D} ctx context to render instance on */ - destroy: function() { - // when group is destroyed objects needs to get a repaint to be eventually - // displayed on canvas. - this._objects.forEach(function(object) { - object.set('dirty', true); - }); - return this._restoreObjectsState(); + render: function (ctx) { + // used to inform objects not to double opacity + this._transformDone = true; + this.callSuper('render', ctx); + this._transformDone = false; }, - dispose: function () { - this.callSuper('dispose'); - this.forEachObject(function (object) { - object.dispose && object.dispose(); + /** + * @public + * @param {Partial & { layout?: string }} [context] pass values to use for layout calculations + */ + triggerLayout: function (context) { + if (context && context.layout) { + context.prevLayout = this.layout; + this.layout = context.layout; + } + this._applyLayoutStrategy({ type: 'imperative', context: context }); + }, + + /** + * @private + * @param {fabric.Object} object + * @param {fabric.Point} diff + */ + _adjustObjectPosition: function (object, diff) { + object.set({ + left: object.left + diff.x, + top: object.top + diff.y, }); - this._objects = []; }, /** - * make a group an active selection, remove the group from canvas - * the group has to be on canvas for this to work. - * @return {fabric.ActiveSelection} thisArg - * @chainable + * initial layout logic: + * calculate bbox of objects (if necessary) and translate it according to options recieved from the constructor (left, top, width, height) + * so it is placed in the center of the bbox received from the constructor + * + * @private + * @param {LayoutContext} context */ - toActiveSelection: function() { - if (!this.canvas) { + _applyLayoutStrategy: function (context) { + var isFirstLayout = context.type === 'initialization'; + if (!isFirstLayout && !this._firstLayoutDone) { + // reject layout requests before initialization layout + return; + } + var center = this.getRelativeCenterPoint(); + var result = this.getLayoutStrategyResult(this.layout, this._objects.concat(), context); + if (result) { + // handle positioning + var newCenter = new fabric.Point(result.centerX, result.centerY); + var vector = center.subtract(newCenter).add(new fabric.Point(result.correctionX || 0, result.correctionY || 0)); + var diff = transformPoint(vector, invertTransform(this.calcOwnMatrix()), true); + // set dimensions + this.set({ width: result.width, height: result.height }); + // adjust objects to account for new center + !context.objectsRelativeToGroup && this.forEachObject(function (object) { + this._adjustObjectPosition(object, diff); + }, this); + // clip path as well + !isFirstLayout && this.layout !== 'clip-path' && this.clipPath && !this.clipPath.absolutePositioned + && this._adjustObjectPosition(this.clipPath, diff); + if (!newCenter.eq(center)) { + // set position + this.setPositionByOrigin(newCenter, 'center', 'center'); + this.setCoords(); + } + } + else if (isFirstLayout) { + // fill `result` with initial values for the layout hook + result = { + centerX: center.x, + centerY: center.y, + width: this.width, + height: this.height, + }; + } + else { + // no `result` so we return return; } - var objects = this._objects, canvas = this.canvas; - this._objects = []; - var options = this.toObject(); - delete options.objects; - var activeSelection = new fabric.ActiveSelection([]); - activeSelection.set(options); - activeSelection.type = 'activeSelection'; - canvas.remove(this); - objects.forEach(function(object) { - object.group = activeSelection; - object.dirty = true; - canvas.add(object); + // flag for next layouts + this._firstLayoutDone = true; + // fire layout hook and event (event will fire only for layouts after initialization layout) + this.onLayout(context, result); + this.fire('layout', { + context: context, + result: result, + diff: diff }); - activeSelection.canvas = canvas; - activeSelection._objects = objects; - canvas._activeObject = activeSelection; - activeSelection.setCoords(); - return activeSelection; + // recursive up + if (this.group && this.group._applyLayoutStrategy) { + // append the path recursion to context + if (!context.path) { + context.path = []; + } + context.path.push(this); + // all parents should invalidate their layout + this.group._applyLayoutStrategy(context); + } }, + /** - * Destroys a group (restoring state of its objects) - * @return {fabric.Group} thisArg - * @chainable - */ - ungroupOnCanvas: function() { - return this._restoreObjectsState(); + * Override this method to customize layout. + * If you need to run logic once layout completes use `onLayout` + * @public + * + * @typedef {'initialization'|'object_modified'|'added'|'removed'|'layout_change'|'imperative'} LayoutContextType + * + * @typedef LayoutContext context object with data regarding what triggered the call + * @property {LayoutContextType} type + * @property {fabric.Object[]} [path] array of objects starting from the object that triggered the call to the current one + * + * @typedef LayoutResult positioning and layout data **relative** to instance's parent + * @property {number} centerX new centerX as measured by the containing plane (same as `left` with `originX` set to `center`) + * @property {number} centerY new centerY as measured by the containing plane (same as `top` with `originY` set to `center`) + * @property {number} [correctionX] correctionX to translate objects by, measured as `centerX` + * @property {number} [correctionY] correctionY to translate objects by, measured as `centerY` + * @property {number} width + * @property {number} height + * + * @param {string} layoutDirective + * @param {fabric.Object[]} objects + * @param {LayoutContext} context + * @returns {LayoutResult | undefined} + */ + getLayoutStrategyResult: function (layoutDirective, objects, context) { // eslint-disable-line no-unused-vars + // `fit-content-lazy` performance enhancement + // skip if instance had no objects before the `added` event because it may have kept layout after removing all previous objects + if (layoutDirective === 'fit-content-lazy' + && context.type === 'added' && objects.length > context.targets.length) { + // calculate added objects' bbox with existing bbox + var addedObjects = context.targets.concat(this); + return this.prepareBoundingBox(layoutDirective, addedObjects, context); + } + else if (layoutDirective === 'fit-content' || layoutDirective === 'fit-content-lazy' + || (layoutDirective === 'fixed' && context.type === 'initialization')) { + return this.prepareBoundingBox(layoutDirective, objects, context); + } + else if (layoutDirective === 'clip-path' && this.clipPath) { + var clipPath = this.clipPath; + var clipPathSizeAfter = clipPath._getTransformedDimensions(); + if (clipPath.absolutePositioned && (context.type === 'initialization' || context.type === 'layout_change')) { + // we want the center point to exist in group's containing plane + var clipPathCenter = clipPath.getCenterPoint(); + if (this.group) { + // send point from canvas plane to group's containing plane + var inv = invertTransform(this.group.calcTransformMatrix()); + clipPathCenter = transformPoint(clipPathCenter, inv); + } + return { + centerX: clipPathCenter.x, + centerY: clipPathCenter.y, + width: clipPathSizeAfter.x, + height: clipPathSizeAfter.y, + }; + } + else if (!clipPath.absolutePositioned) { + var center; + var clipPathRelativeCenter = clipPath.getRelativeCenterPoint(), + // we want the center point to exist in group's containing plane, so we send it upwards + clipPathCenter = transformPoint(clipPathRelativeCenter, this.calcOwnMatrix(), true); + if (context.type === 'initialization' || context.type === 'layout_change') { + var bbox = this.prepareBoundingBox(layoutDirective, objects, context) || {}; + center = new fabric.Point(bbox.centerX || 0, bbox.centerY || 0); + return { + centerX: center.x + clipPathCenter.x, + centerY: center.y + clipPathCenter.y, + correctionX: bbox.correctionX - clipPathCenter.x, + correctionY: bbox.correctionY - clipPathCenter.y, + width: clipPath.width, + height: clipPath.height, + }; + } + else { + center = this.getRelativeCenterPoint(); + return { + centerX: center.x + clipPathCenter.x, + centerY: center.y + clipPathCenter.y, + width: clipPathSizeAfter.x, + height: clipPathSizeAfter.y, + }; + } + } + } + else if (layoutDirective === 'svg' && context.type === 'initialization') { + var bbox = this.getObjectsBoundingBox(objects, true) || {}; + return Object.assign(bbox, { + correctionX: -bbox.offsetX || 0, + correctionY: -bbox.offsetY || 0, + }); + } }, /** - * Sets coordinates of all objects inside group - * @return {fabric.Group} thisArg - * @chainable + * Override this method to customize layout. + * A wrapper around {@link fabric.Group#getObjectsBoundingBox} + * @public + * @param {string} layoutDirective + * @param {fabric.Object[]} objects + * @param {LayoutContext} context + * @returns {LayoutResult | undefined} */ - setObjectsCoords: function() { - var skipControls = true; - this.forEachObject(function(object) { - object.setCoords(skipControls); - }); - return this; + prepareBoundingBox: function (layoutDirective, objects, context) { + if (context.type === 'initialization') { + return this.prepareInitialBoundingBox(layoutDirective, objects, context); + } + else if (context.type === 'imperative' && context.context) { + return Object.assign( + this.getObjectsBoundingBox(objects) || {}, + context.context + ); + } + else { + return this.getObjectsBoundingBox(objects); + } }, /** - * @private + * Calculates center taking into account originX, originY while not being sure that width/height are initialized + * @public + * @param {string} layoutDirective + * @param {fabric.Object[]} objects + * @param {LayoutContext} context + * @returns {LayoutResult | undefined} + */ + prepareInitialBoundingBox: function (layoutDirective, objects, context) { + var options = context.options || {}, + hasX = typeof options.left === 'number', + hasY = typeof options.top === 'number', + hasWidth = typeof options.width === 'number', + hasHeight = typeof options.height === 'number'; + + // performance enhancement + // skip layout calculation if bbox is defined + if ((hasX && hasY && hasWidth && hasHeight && context.objectsRelativeToGroup) || objects.length === 0) { + // return nothing to skip layout + return; + } + + var bbox = this.getObjectsBoundingBox(objects) || {}; + var width = hasWidth ? this.width : (bbox.width || 0), + height = hasHeight ? this.height : (bbox.height || 0), + calculatedCenter = new fabric.Point(bbox.centerX || 0, bbox.centerY || 0), + origin = new fabric.Point(this.resolveOriginX(this.originX), this.resolveOriginY(this.originY)), + size = new fabric.Point(width, height), + strokeWidthVector = this._getTransformedDimensions({ width: 0, height: 0 }), + sizeAfter = this._getTransformedDimensions({ + width: width, + height: height, + strokeWidth: 0 + }), + bboxSizeAfter = this._getTransformedDimensions({ + width: bbox.width, + height: bbox.height, + strokeWidth: 0 + }), + rotationCorrection = new fabric.Point(0, 0); + + if (this.angle) { + var rad = degreesToRadians(this.angle), + sin = Math.abs(fabric.util.sin(rad)), + cos = Math.abs(fabric.util.cos(rad)); + sizeAfter.setXY( + sizeAfter.x * cos + sizeAfter.y * sin, + sizeAfter.x * sin + sizeAfter.y * cos + ); + bboxSizeAfter.setXY( + bboxSizeAfter.x * cos + bboxSizeAfter.y * sin, + bboxSizeAfter.x * sin + bboxSizeAfter.y * cos + ); + strokeWidthVector = fabric.util.rotateVector(strokeWidthVector, rad); + // correct center after rotating + var strokeCorrection = strokeWidthVector.multiply(origin.scalarAdd(-0.5).scalarDivide(-2)); + rotationCorrection = sizeAfter.subtract(size).scalarDivide(2).add(strokeCorrection); + calculatedCenter.addEquals(rotationCorrection); + } + // calculate center and correction + var originT = origin.scalarAdd(0.5); + var originCorrection = sizeAfter.multiply(originT); + var centerCorrection = new fabric.Point( + hasWidth ? bboxSizeAfter.x / 2 : originCorrection.x, + hasHeight ? bboxSizeAfter.y / 2 : originCorrection.y + ); + var center = new fabric.Point( + hasX ? this.left - (sizeAfter.x + strokeWidthVector.x) * origin.x : calculatedCenter.x - centerCorrection.x, + hasY ? this.top - (sizeAfter.y + strokeWidthVector.y) * origin.y : calculatedCenter.y - centerCorrection.y + ); + var offsetCorrection = new fabric.Point( + hasX ? + center.x - calculatedCenter.x + bboxSizeAfter.x * (hasWidth ? 0.5 : 0) : + -(hasWidth ? (sizeAfter.x - strokeWidthVector.x) * 0.5 : sizeAfter.x * originT.x), + hasY ? + center.y - calculatedCenter.y + bboxSizeAfter.y * (hasHeight ? 0.5 : 0) : + -(hasHeight ? (sizeAfter.y - strokeWidthVector.y) * 0.5 : sizeAfter.y * originT.y) + ).add(rotationCorrection); + var correction = new fabric.Point( + hasWidth ? -sizeAfter.x / 2 : 0, + hasHeight ? -sizeAfter.y / 2 : 0 + ).add(offsetCorrection); + + return { + centerX: center.x, + centerY: center.y, + correctionX: correction.x, + correctionY: correction.y, + width: size.x, + height: size.y, + }; + }, + + /** + * Calculate the bbox of objects relative to instance's containing plane + * @public + * @param {fabric.Object[]} objects + * @returns {LayoutResult | null} bounding box */ - _calcBounds: function(onlyWidthHeight) { - var aX = [], - aY = [], - o, prop, coords, - props = ['tr', 'br', 'bl', 'tl'], - i = 0, iLen = this._objects.length, - j, jLen = props.length; - - for ( ; i < iLen; ++i) { - o = this._objects[i]; - coords = o.calcACoords(); - for (j = 0; j < jLen; j++) { - prop = props[j]; - aX.push(coords[prop].x); - aY.push(coords[prop].y); - } - o.aCoords = coords; + getObjectsBoundingBox: function (objects, ignoreOffset) { + if (objects.length === 0) { + return null; } + var objCenter, sizeVector, min, max, a, b; + objects.forEach(function (object, i) { + objCenter = object.getRelativeCenterPoint(); + sizeVector = object._getTransformedDimensions().scalarDivideEquals(2); + if (object.angle) { + var rad = degreesToRadians(object.angle), + sin = Math.abs(fabric.util.sin(rad)), + cos = Math.abs(fabric.util.cos(rad)), + rx = sizeVector.x * cos + sizeVector.y * sin, + ry = sizeVector.x * sin + sizeVector.y * cos; + sizeVector = new fabric.Point(rx, ry); + } + a = objCenter.subtract(sizeVector); + b = objCenter.add(sizeVector); + if (i === 0) { + min = new fabric.Point(Math.min(a.x, b.x), Math.min(a.y, b.y)); + max = new fabric.Point(Math.max(a.x, b.x), Math.max(a.y, b.y)); + } + else { + min.setXY(Math.min(min.x, a.x, b.x), Math.min(min.y, a.y, b.y)); + max.setXY(Math.max(max.x, a.x, b.x), Math.max(max.y, a.y, b.y)); + } + }); + + var size = max.subtract(min), + relativeCenter = ignoreOffset ? size.scalarDivide(2) : min.midPointFrom(max), + // we send `relativeCenter` up to group's containing plane + offset = transformPoint(min, this.calcOwnMatrix()), + center = transformPoint(relativeCenter, this.calcOwnMatrix()); + + return { + offsetX: offset.x, + offsetY: offset.y, + centerX: center.x, + centerY: center.y, + width: size.x, + height: size.y, + }; + }, - this._getBounds(aX, aY, onlyWidthHeight); + /** + * Hook that is called once layout has completed. + * Provided for layout customization, override if necessary. + * Complements `getLayoutStrategyResult`, which is called at the beginning of layout. + * @public + * @param {LayoutContext} context layout context + * @param {LayoutResult} result layout result + */ + onLayout: function (/* context, result */) { + // override by subclass }, /** + * * @private + * @param {'toObject'|'toDatalessObject'} [method] + * @param {string[]} [propertiesToInclude] Any properties that you might want to additionally include in the output + * @returns {fabric.Object[]} serialized objects */ - _getBounds: function(aX, aY, onlyWidthHeight) { - var minXY = new fabric.Point(min(aX), min(aY)), - maxXY = new fabric.Point(max(aX), max(aY)), - top = minXY.y || 0, left = minXY.x || 0, - width = (maxXY.x - minXY.x) || 0, - height = (maxXY.y - minXY.y) || 0; - this.width = width; - this.height = height; - if (!onlyWidthHeight) { - // the bounding box always finds the topleft most corner. - // whatever is the group origin, we set up here the left/top position. - this.setPositionByOrigin({ x: left, y: top }, 'left', 'top'); - } + __serializeObjects: function (method, propertiesToInclude) { + var _includeDefaultValues = this.includeDefaultValues; + return this._objects + .filter(function (obj) { + return !obj.excludeFromExport; + }) + .map(function (obj) { + var originalDefaults = obj.includeDefaultValues; + obj.includeDefaultValues = _includeDefaultValues; + var data = obj[method || 'toObject'](propertiesToInclude); + obj.includeDefaultValues = originalDefaults; + //delete data.version; + return data; + }); + }, + + /** + * Returns object representation of an instance + * @param {string[]} [propertiesToInclude] Any properties that you might want to additionally include in the output + * @return {Object} object representation of an instance + */ + toObject: function (propertiesToInclude) { + var obj = this.callSuper('toObject', ['layout', 'subTargetCheck', 'interactive'].concat(propertiesToInclude)); + obj.objects = this.__serializeObjects('toObject', propertiesToInclude); + return obj; + }, + + toString: function () { + return '#'; + }, + + dispose: function () { + this._activeObjects = []; + this.forEachObject(function (object) { + this._watchObject(false, object); + object.dispose && object.dispose(); + }, this); }, /* _TO_SVG_START_ */ + /** * Returns svg representation of an instance * @param {Function} [reviver] Method for further parsing of svg representation. * @return {String} svg representation of an instance */ - _toSVG: function(reviver) { + _toSVG: function (reviver) { var svgString = ['\n']; - for (var i = 0, len = this._objects.length; i < len; i++) { svgString.push('\t\t', this._objects[i].toSVG(reviver)); } @@ -542,20 +884,19 @@ * @param {Function} [reviver] Method for further parsing of svg representation. * @return {String} svg representation of an instance */ - toClipPathSVG: function(reviver) { + toClipPathSVG: function (reviver) { var svgString = []; - for (var i = 0, len = this._objects.length; i < len; i++) { svgString.push('\t', this._objects[i].toClipPathSVG(reviver)); } - return this._createBaseClipPathSVGMarkup(svgString, { reviver: reviver }); }, /* _TO_SVG_END_ */ }); /** - * Returns {@link fabric.Group} instance from an object representation + * @todo support loading from svg + * @private * @static * @memberOf fabric.Group * @param {Object} object Object to create a group from @@ -563,12 +904,13 @@ */ fabric.Group.fromObject = function(object) { var objects = object.objects || [], - options = fabric.util.object.clone(object, true); + options = clone(object, true); delete options.objects; - return fabric.util.enlivenObjects(objects).then(function (enlivenedObjects) { - return fabric.util.enlivenObjectEnlivables(options).then(function(enlivedProps) { - return new fabric.Group(enlivenedObjects, Object.assign(options, enlivedProps), true); - }); + return Promise.all([ + fabric.util.enlivenObjects(objects), + fabric.util.enlivenObjectEnlivables(options) + ]).then(function (enlivened) { + return new fabric.Group(enlivened[0], Object.assign(options, enlivened[1]), true); }); }; diff --git a/src/shapes/image.class.js b/src/shapes/image.class.js index 7a5964012b5..f4a1fc78ace 100644 --- a/src/shapes/image.class.js +++ b/src/shapes/image.class.js @@ -395,8 +395,8 @@ var filter = this.resizeFilter, minimumScale = this.minimumScaleTrigger, objectScale = this.getTotalObjectScaling(), - scaleX = objectScale.scaleX, - scaleY = objectScale.scaleY, + scaleX = objectScale.x, + scaleY = objectScale.y, elementToFilter = this._filteredEl || this._originalElement; if (this.group) { this.set('dirty', true); @@ -552,7 +552,7 @@ */ _needsResize: function() { var scale = this.getTotalObjectScaling(); - return (scale.scaleX !== this._lastScaleX || scale.scaleY !== this._lastScaleY); + return (scale.x !== this._lastScaleX || scale.y !== this._lastScaleY); }, /** diff --git a/src/shapes/itext.class.js b/src/shapes/itext.class.js index b4e66b3ad63..8040e65f15b 100644 --- a/src/shapes/itext.class.js +++ b/src/shapes/itext.class.js @@ -186,6 +186,21 @@ this.initBehavior(); }, + /** + * While editing handle differently + * @private + * @param {string} key + * @param {*} value + */ + _set: function (key, value) { + if (this.isEditing && this._savedProps && key in this._savedProps) { + this._savedProps[key] = value; + } + else { + this.callSuper('_set', key, value); + } + }, + /** * Sets selection start (left boundary of a selection) * @param {Number} index Index to set selection start to @@ -356,7 +371,15 @@ left: lineLeftOffset + (leftOffset > 0 ? leftOffset : 0), }; if (this.direction === 'rtl') { - boundaries.left *= -1; + if (this.textAlign === 'right' || this.textAlign === 'justify' || this.textAlign === 'justify-right') { + boundaries.left *= -1; + } + else if (this.textAlign === 'left' || this.textAlign === 'justify-left') { + boundaries.left = lineLeftOffset - (leftOffset > 0 ? leftOffset : 0); + } + else if (this.textAlign === 'center' || this.textAlign === 'justify-center') { + boundaries.left = lineLeftOffset - (leftOffset > 0 ? leftOffset : 0); + } } this.cursorOffsetCache = boundaries; return this.cursorOffsetCache; @@ -445,7 +468,15 @@ ctx.fillStyle = this.selectionColor; } if (this.direction === 'rtl') { - drawStart = this.width - drawStart - drawWidth; + if (this.textAlign === 'right' || this.textAlign === 'justify' || this.textAlign === 'justify-right') { + drawStart = this.width - drawStart - drawWidth; + } + else if (this.textAlign === 'left' || this.textAlign === 'justify-left') { + drawStart = boundaries.left + lineOffset - boxEnd; + } + else if (this.textAlign === 'center' || this.textAlign === 'justify-center') { + drawStart = boundaries.left + lineOffset - boxEnd; + } } ctx.fillRect( drawStart, diff --git a/src/shapes/object.class.js b/src/shapes/object.class.js index 1001b897255..c91e9adc063 100644 --- a/src/shapes/object.class.js +++ b/src/shapes/object.class.js @@ -727,17 +727,17 @@ _getCacheCanvasDimensions: function() { var objectScale = this.getTotalObjectScaling(), // caculate dimensions without skewing - dim = this._getTransformedDimensions(0, 0), - neededX = dim.x * objectScale.scaleX / this.scaleX, - neededY = dim.y * objectScale.scaleY / this.scaleY; + dim = this._getTransformedDimensions({ skewX: 0, skewY: 0 }), + neededX = dim.x * objectScale.x / this.scaleX, + neededY = dim.y * objectScale.y / this.scaleY; return { // for sure this ALIASING_LIMIT is slightly creating problem // in situation in which the cache canvas gets an upper limit // also objectScale contains already scaleX and scaleY width: neededX + ALIASING_LIMIT, height: neededY + ALIASING_LIMIT, - zoomX: objectScale.scaleX, - zoomY: objectScale.scaleY, + zoomX: objectScale.x, + zoomY: objectScale.y, x: neededX, y: neededY }; @@ -899,20 +899,17 @@ * @param {Object} object */ _removeDefaultValues: function(object) { - var prototype = fabric.util.getKlass(object.type).prototype, - stateProperties = prototype.stateProperties; - stateProperties.forEach(function(prop) { - if (prop === 'left' || prop === 'top') { + var prototype = fabric.util.getKlass(object.type).prototype; + Object.keys(object).forEach(function(prop) { + if (prop === 'left' || prop === 'top' || prop === 'type') { return; } if (object[prop] === prototype[prop]) { delete object[prop]; } - var isArray = Object.prototype.toString.call(object[prop]) === '[object Array]' && - Object.prototype.toString.call(prototype[prop]) === '[object Array]'; - // basically a check for [] === [] - if (isArray && object[prop].length === 0 && prototype[prop].length === 0) { + if (Array.isArray(object[prop]) && Array.isArray(prototype[prop]) + && object[prop].length === 0 && prototype[prop].length === 0) { delete object[prop]; } }); @@ -930,7 +927,7 @@ /** * Return the object scale factor counting also the group scaling - * @return {Object} object with scaleX and scaleY properties + * @return {fabric.Point} */ getObjectScaling: function() { // if the object is a top level one, on the canvas, we go for simple aritmetic @@ -938,14 +935,11 @@ // and will likely kill the cache when not needed // https://github.com/fabricjs/fabric.js/issues/7157 if (!this.group) { - return { - scaleX: this.scaleX, - scaleY: this.scaleY, - }; + return new fabric.Point(Math.abs(this.scaleX), Math.abs(this.scaleY)); } // if we are inside a group total zoom calculation is complex, we defer to generic matrices var options = fabric.util.qrDecompose(this.calcTransformMatrix()); - return { scaleX: Math.abs(options.scaleX), scaleY: Math.abs(options.scaleY) }; + return new fabric.Point(Math.abs(options.scaleX), Math.abs(options.scaleY)); }, /** @@ -953,14 +947,13 @@ * @return {Object} object with scaleX and scaleY properties */ getTotalObjectScaling: function() { - var scale = this.getObjectScaling(), scaleX = scale.scaleX, scaleY = scale.scaleY; + var scale = this.getObjectScaling(); if (this.canvas) { var zoom = this.canvas.getZoom(); var retina = this.canvas.getRetinaScaling(); - scaleX *= zoom * retina; - scaleY *= zoom * retina; + scale.scalarMultiplyEquals(zoom * retina); } - return { scaleX: scaleX, scaleY: scaleY }; + return scale; }, /** @@ -975,6 +968,16 @@ return opacity; }, + /** + * Returns the object angle relative to canvas counting also the group property + * @returns {number} + */ + getTotalAngle: function () { + return this.group ? + fabric.util.qrDecompose(this.calcTransformMatrix()).angle : + this.angle; + }, + /** * @private * @param {String} key @@ -1018,16 +1021,6 @@ return this; }, - /** - * This callback function is called by the parent group of an object every - * time a non-delegated property changes on the group. It is passed the key - * and value as parameters. Not adding in this function's signature to avoid - * Travis build error about unused variables. - */ - setOnGroup: function() { - // implemented by sub-classes, as needed. - }, - /** * Retrieves viewportTransform from Object's canvas if possible * @method getViewportTransform @@ -1088,7 +1081,7 @@ renderCache: function(options) { options = options || {}; - if (!this._cacheCanvas) { + if (!this._cacheCanvas || !this._cacheContext) { this._createCacheCanvas(); } if (this.isCacheDirty()) { @@ -1103,6 +1096,7 @@ */ _removeCacheCanvas: function() { this._cacheCanvas = null; + this._cacheContext = null; this.cacheWidth = 0; this.cacheHeight = 0; }, @@ -1175,6 +1169,7 @@ * Check if this object or a child object will cast a shadow * used by Group.shouldCache to know if child has a shadow recursively * @return {Boolean} + * @deprecated */ willDrawShadow: function() { return !!this.shadow && (this.shadow.offsetX !== 0 || this.shadow.offsetY !== 0); @@ -1261,7 +1256,7 @@ if (this.isNotVisible()) { return false; } - if (this._cacheCanvas && !skipCanvas && this._updateCacheCanvas()) { + if (this._cacheCanvas && this._cacheContext && !skipCanvas && this._updateCacheCanvas()) { // in this case the context is already cleared. return true; } @@ -1270,7 +1265,7 @@ (this.clipPath && this.clipPath.absolutePositioned) || (this.statefullCache && this.hasStateChanged('cacheProperties')) ) { - if (this._cacheCanvas && !skipCanvas) { + if (this._cacheCanvas && this._cacheContext && !skipCanvas) { var width = this.cacheWidth / this.zoomX; var height = this.cacheHeight / this.zoomY; this._cacheContext.clearRect(-width / 2, -height / 2, width, height); @@ -1426,24 +1421,19 @@ return; } - var shadow = this.shadow, canvas = this.canvas, scaling, + var shadow = this.shadow, canvas = this.canvas, multX = (canvas && canvas.viewportTransform[0]) || 1, - multY = (canvas && canvas.viewportTransform[3]) || 1; - if (shadow.nonScaling) { - scaling = { scaleX: 1, scaleY: 1 }; - } - else { - scaling = this.getObjectScaling(); - } + multY = (canvas && canvas.viewportTransform[3]) || 1, + scaling = shadow.nonScaling ? new fabric.Point(1, 1) : this.getObjectScaling(); if (canvas && canvas._isRetinaScaling()) { multX *= fabric.devicePixelRatio; multY *= fabric.devicePixelRatio; } ctx.shadowColor = shadow.color; ctx.shadowBlur = shadow.blur * fabric.browserShadowBlurConstant * - (multX + multY) * (scaling.scaleX + scaling.scaleY) / 4; - ctx.shadowOffsetX = shadow.offsetX * multX * scaling.scaleX; - ctx.shadowOffsetY = shadow.offsetY * multY * scaling.scaleY; + (multX + multY) * (scaling.x + scaling.y) / 4; + ctx.shadowOffsetX = shadow.offsetX * multX * scaling.x; + ctx.shadowOffsetY = shadow.offsetY * multY * scaling.y; }, /** @@ -1546,12 +1536,9 @@ } ctx.save(); - if (this.strokeUniform && this.group) { + if (this.strokeUniform) { var scaling = this.getObjectScaling(); - ctx.scale(1 / scaling.scaleX, 1 / scaling.scaleY); - } - else if (this.strokeUniform) { - ctx.scale(1 / this.scaleX, 1 / this.scaleY); + ctx.scale(1 / scaling.x, 1 / scaling.y); } this._setLineDash(ctx, this.strokeDashArray); this._setStrokeStyles(ctx, this); @@ -1704,7 +1691,8 @@ var utils = fabric.util, origParams = utils.saveObjectTransform(this), originalGroup = this.group, originalShadow = this.shadow, abs = Math.abs, - multiplier = (options.multiplier || 1) * (options.enableRetinaScaling ? fabric.devicePixelRatio : 1); + retinaScaling = options.enableRetinaScaling ? Math.max(fabric.devicePixelRatio, 1) : 1, + multiplier = (options.multiplier || 1) * retinaScaling; delete this.group; if (options.withoutTransform) { utils.resetObjectTransform(this); @@ -1716,21 +1704,15 @@ var el = fabric.util.createCanvasElement(), // skip canvas zoom and calculate with setCoords now. boundingRect = this.getBoundingRect(true, true), - shadow = this.shadow, scaling, - shadowOffset = { x: 0, y: 0 }, shadowBlur, + shadow = this.shadow, shadowOffset = { x: 0, y: 0 }, width, height; if (shadow) { - shadowBlur = shadow.blur; - if (shadow.nonScaling) { - scaling = { scaleX: 1, scaleY: 1 }; - } - else { - scaling = this.getObjectScaling(); - } + var shadowBlur = shadow.blur; + var scaling = shadow.nonScaling ? new fabric.Point(1, 1) : this.getObjectScaling(); // consider non scaling shadow. - shadowOffset.x = 2 * Math.round(abs(shadow.offsetX) + shadowBlur) * (abs(scaling.scaleX)); - shadowOffset.y = 2 * Math.round(abs(shadow.offsetY) + shadowBlur) * (abs(scaling.scaleY)); + shadowOffset.x = 2 * Math.round(abs(shadow.offsetX) + shadowBlur) * (abs(scaling.x)); + shadowOffset.y = 2 * Math.round(abs(shadow.offsetY) + shadowBlur) * (abs(scaling.y)); } width = boundingRect.width + shadowOffset.x; height = boundingRect.height + shadowOffset.y; @@ -1793,7 +1775,7 @@ * @return {Boolean} */ isType: function(type) { - return this.type === type; + return arguments.length > 1 ? Array.from(arguments).includes(this.type) : this.type === type; }, /** @@ -1903,23 +1885,13 @@ }, /** - * Returns coordinates of a pointer relative to an object - * @param {Event} e Event to operate upon - * @param {Object} [pointer] Pointer to operate upon (instead of event) - * @return {Object} Coordinates of a pointer (x, y) + * This callback function is called by the parent group of an object every + * time a non-delegated property changes on the group. It is passed the key + * and value as parameters. Not adding in this function's signature to avoid + * Travis build error about unused variables. */ - getLocalPointer: function(e, pointer) { - pointer = pointer || this.canvas.getPointer(e); - var pClicked = new fabric.Point(pointer.x, pointer.y), - objectLeftTop = this._getLeftTopCoords(); - if (this.angle) { - pClicked = fabric.util.rotatePoint( - pClicked, objectLeftTop, degreesToRadians(-this.angle)); - } - return { - x: pClicked.x - objectLeftTop.x, - y: pClicked.y - objectLeftTop.y - }; + setOnGroup: function() { + // implemented by sub-classes, as needed. }, /** diff --git a/src/shapes/path.class.js b/src/shapes/path.class.js index f4b6898b98e..bb7b7382f3b 100644 --- a/src/shapes/path.class.js +++ b/src/shapes/path.class.js @@ -7,7 +7,6 @@ max = fabric.util.array.max, extend = fabric.util.object.extend, clone = fabric.util.object.clone, - _toString = Object.prototype.toString, toFixed = fabric.util.toFixed; if (fabric.Path) { @@ -61,10 +60,8 @@ * @param {Object} [options] Options object */ _setPath: function (path, options) { - var fromArray = _toString.call(path) === '[object Array]'; - this.path = fabric.util.makePathSimpler( - fromArray ? path : fabric.util.parsePath(path) + Array.isArray(path) ? path : fabric.util.parsePath(path) ); fabric.Polyline.prototype._setPositionDimensions.call(this, options || {}); diff --git a/src/shapes/polyline.class.js b/src/shapes/polyline.class.js index 5d0b7b85b36..fbf9301cd5b 100644 --- a/src/shapes/polyline.class.js +++ b/src/shapes/polyline.class.js @@ -83,6 +83,7 @@ }, _setPositionDimensions: function(options) { + options || (options = {}); var calcDim = this._calcDimensions(options), correctLeftTop, correctSize = this.exactBoundingBox ? this.strokeWidth : 0; this.width = calcDim.width - correctSize; diff --git a/src/shapes/text.class.js b/src/shapes/text.class.js index 2bb6bc2e5f7..1d99be796cf 100644 --- a/src/shapes/text.class.js +++ b/src/shapes/text.class.js @@ -882,11 +882,20 @@ /** * Measure and return the info of a single grapheme. * needs the the info of previous graphemes already filled - * @private + * Override to customize measuring + * + * @typedef {object} GraphemeBBox + * @property {number} width + * @property {number} height + * @property {number} kernedWidth + * @property {number} left + * @property {number} deltaY + * * @param {String} grapheme to be measured * @param {Number} lineIndex index of the line where the char is * @param {Number} charIndex position in the line * @param {String} [prevGrapheme] character preceding the one to be measured + * @returns {GraphemeBBox} grapheme bbox */ _getGraphemeBox: function(grapheme, lineIndex, charIndex, prevGrapheme, skipLeft) { var style = this.getCompleteStyleDeclaration(lineIndex, charIndex), @@ -1044,7 +1053,9 @@ path = this.path, shortCut = !isJustify && this.charSpacing === 0 && this.isEmptyStyles(lineIndex) && !path, isLtr = this.direction === 'ltr', sign = this.direction === 'ltr' ? 1 : -1, - drawingLeft, currentDirection = ctx.canvas.getAttribute('dir'); + // this was changed in the PR #7674 + // currentDirection = ctx.canvas.getAttribute('dir'); + drawingLeft, currentDirection = ctx.direction; ctx.save(); if (currentDirection !== this.direction) { ctx.canvas.setAttribute('dir', isLtr ? 'ltr' : 'rtl'); @@ -1306,7 +1317,15 @@ leftOffset = lineDiff; } if (direction === 'rtl') { - leftOffset -= lineDiff; + if (textAlign === 'right' || textAlign === 'justify' || textAlign === 'justify-right') { + leftOffset = 0; + } + else if (textAlign === 'left' || textAlign === 'justify-left') { + leftOffset = -lineDiff; + } + else if (textAlign === 'center' || textAlign === 'justify-center') { + leftOffset = -lineDiff / 2; + } } return leftOffset; }, @@ -1512,6 +1531,15 @@ this.callSuper('render', ctx); }, + /** + * Override this method to customize grapheme splitting + * @param {string} value + * @returns {string[]} array of graphemes + */ + graphemeSplit: function (value) { + return fabric.util.string.graphemeSplit(value); + }, + /** * Returns the text as an array of lines. * @param {String} text text to split @@ -1523,7 +1551,7 @@ newLine = ['\n'], newText = []; for (var i = 0; i < lines.length; i++) { - newLines[i] = fabric.util.string.graphemeSplit(lines[i]); + newLines[i] = this.graphemeSplit(lines[i]); newText = newText.concat(newLines[i], newLine); } newText.pop(); diff --git a/src/shapes/textbox.class.js b/src/shapes/textbox.class.js index 06cf7982cfc..3faff27b7b0 100644 --- a/src/shapes/textbox.class.js +++ b/src/shapes/textbox.class.js @@ -271,7 +271,7 @@ var wrapped = [], i; this.isWrapping = true; for (i = 0; i < lines.length; i++) { - wrapped = wrapped.concat(this._wrapLine(lines[i], i, desiredWidth)); + wrapped.push.apply(wrapped, this._wrapLine(lines[i], i, desiredWidth)); } this.isWrapping = false; return wrapped; @@ -279,13 +279,15 @@ /** * Helper function to measure a string of text, given its lineIndex and charIndex offset - * it gets called when charBounds are not available yet. + * It gets called when charBounds are not available yet. + * Override if necessary + * Use with {@link fabric.Textbox#wordSplit} + * * @param {CanvasRenderingContext2D} ctx * @param {String} text * @param {number} lineIndex * @param {number} charOffset * @returns {number} - * @private */ _measureWord: function(word, lineIndex, charOffset) { var width = 0, prevGrapheme, skipLeft = true; @@ -298,6 +300,16 @@ return width; }, + /** + * Override this method to customize word splitting + * Use with {@link fabric.Textbox#_measureWord} + * @param {string} value + * @returns {string[]} array of words + */ + wordSplit: function (value) { + return value.split(this._wordJoiners); + }, + /** * Wraps a line of text using the width of the Textbox and a context. * @param {Array} line The grapheme array that represent the line @@ -313,7 +325,7 @@ graphemeLines = [], line = [], // spaces in different languages? - words = splitByGrapheme ? fabric.util.string.graphemeSplit(_line) : _line.split(this._wordJoiners), + words = splitByGrapheme ? this.graphemeSplit(_line) : this.wordSplit(_line), word = '', offset = 0, infix = splitByGrapheme ? '' : ' ', @@ -328,14 +340,25 @@ words.push([]); } desiredWidth -= reservedSpace; - for (var i = 0; i < words.length; i++) { + // measure words + var data = words.map(function (word) { // if using splitByGrapheme words are already in graphemes. - word = splitByGrapheme ? words[i] : fabric.util.string.graphemeSplit(words[i]); - wordWidth = this._measureWord(word, lineIndex, offset); + word = splitByGrapheme ? word : this.graphemeSplit(word); + var width = this._measureWord(word, lineIndex, offset); + largestWordWidth = Math.max(width, largestWordWidth); + offset += word.length + 1; + return { word: word, width: width }; + }.bind(this)); + var maxWidth = Math.max(desiredWidth, largestWordWidth, this.dynamicMinWidth); + // layout words + offset = 0; + for (var i = 0; i < words.length; i++) { + word = data[i].word; + wordWidth = data[i].width; offset += word.length; lineWidth += infixWidth + wordWidth - additionalSpace; - if (lineWidth > desiredWidth && !lineJustStarted) { + if (lineWidth > maxWidth && !lineJustStarted) { graphemeLines.push(line); line = []; lineWidth = wordWidth; @@ -353,10 +376,6 @@ infixWidth = splitByGrapheme ? 0 : this._measureWord([infix], lineIndex, offset); offset++; lineJustStarted = false; - // keep track of largest word - if (wordWidth > largestWordWidth) { - largestWordWidth = wordWidth; - } } i && graphemeLines.push(line); diff --git a/src/static_canvas.class.js b/src/static_canvas.class.js index 9e46d6d1fbb..614afcd3df8 100644 --- a/src/static_canvas.class.js +++ b/src/static_canvas.class.js @@ -32,7 +32,8 @@ * @fires object:added * @fires object:removed */ - fabric.StaticCanvas = fabric.util.createClass(fabric.CommonMethods, /** @lends fabric.StaticCanvas.prototype */ { + // eslint-disable-next-line max-len + fabric.StaticCanvas = fabric.util.createClass(fabric.CommonMethods, fabric.Collection, /** @lends fabric.StaticCanvas.prototype */ { /** * Constructor @@ -313,10 +314,15 @@ else { this.lowerCanvasEl = fabric.util.getById(canvasEl) || this._createCanvasElement(); } - + if (this.lowerCanvasEl.hasAttribute('data-fabric')) { + /* _DEV_MODE_START_ */ + throw new Error('fabric.js: trying to initialize a canvas that has already been initialized'); + /* _DEV_MODE_END_ */ + } fabric.util.addClass(this.lowerCanvasEl, 'lower-canvas'); - this._originalCanvasStyle = this.lowerCanvasEl.style; + this.lowerCanvasEl.setAttribute('data-fabric', 'main'); if (this.interactive) { + this._originalCanvasStyle = this.lowerCanvasEl.style.cssText; this._applyCanvasStyle(this.lowerCanvasEl); } @@ -558,12 +564,56 @@ return this.lowerCanvasEl; }, + /** + * @param {...fabric.Object} objects to add + * @return {Self} thisArg + * @chainable + */ + add: function () { + fabric.Collection.add.call(this, arguments, this._onObjectAdded); + arguments.length > 0 && this.renderOnAddRemove && this.requestRenderAll(); + return this; + }, + + /** + * Inserts an object into collection at specified index, then renders canvas (if `renderOnAddRemove` is not `false`) + * An object should be an instance of (or inherit from) fabric.Object + * @param {fabric.Object|fabric.Object[]} objects Object(s) to insert + * @param {Number} index Index to insert object at + * @param {Boolean} nonSplicing When `true`, no splicing (shifting) of objects occurs + * @return {Self} thisArg + * @chainable + */ + insertAt: function (objects, index) { + fabric.Collection.insertAt.call(this, objects, index, this._onObjectAdded); + this.renderOnAddRemove && this.requestRenderAll(); + return this; + }, + + /** + * @param {...fabric.Object} objects to remove + * @return {Self} thisArg + * @chainable + */ + remove: function () { + var didRemove = fabric.Collection.remove.call(this, arguments, this._onObjectRemoved); + didRemove && this.renderOnAddRemove && this.requestRenderAll(); + return this; + }, + /** * @private * @param {fabric.Object} obj Object that was added */ _onObjectAdded: function(obj) { this.stateful && obj.setupState(); + if (obj.canvas && obj.canvas !== this) { + /* _DEV_MODE_START_ */ + console.warn('fabric.Canvas: trying to add an object that belongs to a different canvas.\n' + + 'Resulting to default behavior: removing object from previous canvas and adding to new canvas'); + /* _DEV_MODE_END_ */ + obj.canvas.remove(obj); + } obj._set('canvas', this); obj.setCoords(); this.fire('object:added', { target: obj }); @@ -815,6 +865,7 @@ * Returns coordinates of a center of canvas. * Returned value is an object with top and left properties * @return {Object} object with "top" and "left" number values + * @deprecated migrate to `getCenterPoint` */ getCenter: function () { return { @@ -823,13 +874,21 @@ }; }, + /** + * Returns coordinates of a center of canvas. + * @return {fabric.Point} + */ + getCenterPoint: function () { + return new fabric.Point(this.width / 2, this.height / 2); + }, + /** * Centers object horizontally in the canvas * @param {fabric.Object} object Object to center horizontally * @return {fabric.Canvas} thisArg */ centerObjectH: function (object) { - return this._centerObject(object, new fabric.Point(this.getCenter().left, object.getCenterPoint().y)); + return this._centerObject(object, new fabric.Point(this.getCenterPoint().x, object.getCenterPoint().y)); }, /** @@ -839,7 +898,7 @@ * @chainable */ centerObjectV: function (object) { - return this._centerObject(object, new fabric.Point(object.getCenterPoint().x, this.getCenter().top)); + return this._centerObject(object, new fabric.Point(object.getCenterPoint().x, this.getCenterPoint().y)); }, /** @@ -849,9 +908,8 @@ * @chainable */ centerObject: function(object) { - var center = this.getCenter(); - - return this._centerObject(object, new fabric.Point(center.left, center.top)); + var center = this.getCenterPoint(); + return this._centerObject(object, center); }, /** @@ -862,7 +920,6 @@ */ viewportCenterObject: function(object) { var vpCenter = this.getVpCenter(); - return this._centerObject(object, vpCenter); }, @@ -896,9 +953,9 @@ * @chainable */ getVpCenter: function() { - var center = this.getCenter(), + var center = this.getCenterPoint(), iVpt = invertTransform(this.viewportTransform); - return transformPoint({ x: center.left, y: center.top }, iVpt); + return transformPoint(center, iVpt); }, /** @@ -1562,10 +1619,13 @@ this.overlayImage = null; this._iTextInstances = null; this.contextContainer = null; - // restore canvas style + // restore canvas style and attributes this.lowerCanvasEl.classList.remove('lower-canvas'); - fabric.util.setStyle(this.lowerCanvasEl, this._originalCanvasStyle); - delete this._originalCanvasStyle; + this.lowerCanvasEl.removeAttribute('data-fabric'); + if (this.interactive) { + this.lowerCanvasEl.style.cssText = this._originalCanvasStyle; + delete this._originalCanvasStyle; + } // restore canvas size to original size in case retina scaling was applied this.lowerCanvasEl.setAttribute('width', this.width); this.lowerCanvasEl.setAttribute('height', this.height); @@ -1585,7 +1645,6 @@ }); extend(fabric.StaticCanvas.prototype, fabric.Observable); - extend(fabric.StaticCanvas.prototype, fabric.Collection); extend(fabric.StaticCanvas.prototype, fabric.DataURLExporter); extend(fabric.StaticCanvas, /** @lends fabric.StaticCanvas */ { diff --git a/src/util/animate.js b/src/util/animate.js index 48e9abe0d49..bc795adb929 100644 --- a/src/util/animate.js +++ b/src/util/animate.js @@ -4,30 +4,18 @@ clone = fabric.util.object.clone; /** + * * @typedef {Object} AnimationOptions * Animation of a value or list of values. - * When using lists, think of something like this: - * fabric.util.animate({ - * startValue: [1, 2, 3], - * endValue: [2, 4, 6], - * onChange: function([a, b, c]) { - * canvas.zoomToPoint({x: b, y: c}, a) - * canvas.renderAll() - * } - * }); - * @example * @property {Function} [onChange] Callback; invoked on every value change * @property {Function} [onComplete] Callback; invoked when value change is completed - * @example - * // Note: startValue, endValue, and byValue must match the type - * var animationOptions = { startValue: 0, endValue: 1, byValue: 0.25 } - * var animationOptions = { startValue: [0, 1], endValue: [1, 2], byValue: [0.25, 0.25] } * @property {number | number[]} [startValue=0] Starting value * @property {number | number[]} [endValue=100] Ending value * @property {number | number[]} [byValue=100] Value to modify the property by * @property {Function} [easing] Easing function - * @property {Number} [duration=500] Duration of change (in ms) + * @property {number} [duration=500] Duration of change (in ms) * @property {Function} [abort] Additional function with logic. If returns true, animation aborts. + * @property {number} [delay] Delay of animation start (in ms) * * @typedef {() => void} CancelFunction * @@ -137,10 +125,27 @@ * Changes value from one to another within certain period of time, invoking callbacks as value is being changed. * @memberOf fabric.util * @param {AnimationOptions} [options] Animation options + * When using lists, think of something like this: + * @example + * fabric.util.animate({ + * startValue: [1, 2, 3], + * endValue: [2, 4, 6], + * onChange: function([x, y, zoom]) { + * canvas.zoomToPoint(new fabric.Point(x, y), zoom); + * canvas.requestRenderAll(); + * } + * }); + * * @example - * // Note: startValue, endValue, and byValue must match the type - * fabric.util.animate({ startValue: 0, endValue: 1, byValue: 0.25 }) - * fabric.util.animate({ startValue: [0, 1], endValue: [1, 2], byValue: [0.25, 0.25] }) + * fabric.util.animate({ + * startValue: 1, + * endValue: 0, + * onChange: function(v) { + * obj.set('opacity', v); + * canvas.requestRenderAll(); + * } + * }); + * * @returns {CancelFunction} cancel function */ function animate(options) { @@ -163,7 +168,7 @@ }); fabric.runningAnimations.push(context); - requestAnimFrame(function(timestamp) { + var runner = function (timestamp) { var start = timestamp || +new Date(), duration = options.duration || 500, finish = start + duration, time, @@ -216,7 +221,16 @@ requestAnimFrame(tick); } })(start); - }); + }; + + if (options.delay) { + setTimeout(function () { + requestAnimFrame(runner); + }, options.delay); + } + else { + requestAnimFrame(runner); + } return context.cancel; } diff --git a/src/util/misc.js b/src/util/misc.js index 5362d86b359..6c9c122d0ef 100644 --- a/src/util/misc.js +++ b/src/util/misc.js @@ -6,6 +6,10 @@ PiBy180 = Math.PI / 180, PiBy2 = Math.PI / 2; + /** + * @typedef {[number,number,number,number,number,number]} Matrix + */ + /** * @namespace fabric.util */ @@ -117,7 +121,7 @@ rotatePoint: function(point, origin, radians) { var newPoint = new fabric.Point(point.x - origin.x, point.y - origin.y), v = fabric.util.rotateVector(newPoint, radians); - return new fabric.Point(v.x, v.y).addEquals(origin); + return v.addEquals(origin); }, /** @@ -126,17 +130,14 @@ * @memberOf fabric.util * @param {Object} vector The vector to rotate (x and y) * @param {Number} radians The radians of the angle for the rotation - * @return {Object} The new rotated point + * @return {fabric.Point} The new rotated point */ rotateVector: function(vector, radians) { var sin = fabric.util.sin(radians), cos = fabric.util.cos(radians), rx = vector.x * cos - vector.y * sin, ry = vector.x * sin + vector.y * cos; - return { - x: rx, - y: ry - }; + return new fabric.Point(rx, ry); }, /** @@ -175,7 +176,7 @@ * @returns {Point} vector representing the unit vector of pointing to the direction of `v` */ getHatVector: function (v) { - return new fabric.Point(v.x, v.y).multiply(1 / Math.hypot(v.x, v.y)); + return new fabric.Point(v.x, v.y).scalarMultiply(1 / Math.hypot(v.x, v.y)); }, /** @@ -290,8 +291,70 @@ ); }, + /** + * Sends a point from the source coordinate plane to the destination coordinate plane.\ + * From the canvas/viewer's perspective the point remains unchanged. + * + * @example Send point from canvas plane to group plane + * var obj = new fabric.Rect({ left: 20, top: 20, width: 60, height: 60, strokeWidth: 0 }); + * var group = new fabric.Group([obj], { strokeWidth: 0 }); + * var sentPoint1 = fabric.util.sendPointToPlane(new fabric.Point(50, 50), null, group.calcTransformMatrix()); + * var sentPoint2 = fabric.util.sendPointToPlane(new fabric.Point(50, 50), fabric.iMatrix, group.calcTransformMatrix()); + * console.log(sentPoint1, sentPoint2) // both points print (0,0) which is the center of group + * + * @static + * @memberOf fabric.util + * @see {fabric.util.transformPointRelativeToCanvas} for transforming relative to canvas + * @param {fabric.Point} point + * @param {Matrix} [from] plane matrix containing object. Passing `null` is equivalent to passing the identity matrix, which means `point` exists in the canvas coordinate plane. + * @param {Matrix} [to] destination plane matrix to contain object. Passing `null` means `point` should be sent to the canvas coordinate plane. + * @returns {fabric.Point} transformed point + */ + sendPointToPlane: function (point, from, to) { + // we are actually looking for the transformation from the destination plane to the source plane (which is a linear mapping) + // the object will exist on the destination plane and we want it to seem unchanged by it so we reverse the destination matrix (to) and then apply the source matrix (from) + var inv = fabric.util.invertTransform(to || fabric.iMatrix); + var t = fabric.util.multiplyTransformMatrices(inv, from || fabric.iMatrix); + return fabric.util.transformPoint(point, t); + }, + + /** + * Transform point relative to canvas. + * From the viewport/viewer's perspective the point remains unchanged. + * + * `child` relation means `point` exists in the coordinate plane created by `canvas`. + * In other words point is measured acoording to canvas' top left corner + * meaning that if `point` is equal to (0,0) it is positioned at canvas' top left corner. + * + * `sibling` relation means `point` exists in the same coordinate plane as canvas. + * In other words they both relate to the same (0,0) and agree on every point, which is how an event relates to canvas. + * + * @static + * @memberOf fabric.util + * @param {fabric.Point} point + * @param {fabric.StaticCanvas} canvas + * @param {'sibling'|'child'} relationBefore current relation of point to canvas + * @param {'sibling'|'child'} relationAfter desired relation of point to canvas + * @returns {fabric.Point} transformed point + */ + transformPointRelativeToCanvas: function (point, canvas, relationBefore, relationAfter) { + if (relationBefore !== 'child' && relationBefore !== 'sibling') { + throw new Error('fabric.js: recieved bad argument ' + relationBefore); + } + if (relationAfter !== 'child' && relationAfter !== 'sibling') { + throw new Error('fabric.js: recieved bad argument ' + relationAfter); + } + if (relationBefore === relationAfter) { + return point; + } + var t = canvas.viewportTransform; + return fabric.util.transformPoint(point, relationAfter === 'child' ? fabric.util.invertTransform(t) : t); + }, + /** * Returns coordinates of points's bounding rectangle (left, top, width, height) + * @static + * @memberOf fabric.util * @param {Array} points 4 points array * @param {Array} [transform] an array of 6 numbers representing a 2x3 transform matrix * @return {Object} Object with left, top, width, height properties @@ -543,32 +606,13 @@ * @static * @memberOf fabric.util * @param {Array} elements SVG elements to group - * @param {Object} [options] Options object - * @param {String} path Value to set sourcePath to * @return {fabric.Object|fabric.Group} */ - groupSVGElements: function(elements, options, path) { - var object; + groupSVGElements: function(elements) { if (elements && elements.length === 1) { return elements[0]; } - if (options) { - if (options.width && options.height) { - options.centerPoint = { - x: options.width / 2, - y: options.height / 2 - }; - } - else { - delete options.width; - delete options.height; - } - } - object = new fabric.Group(elements, options); - if (typeof path !== 'undefined') { - object.sourcePath = path; - } - return object; + return new fabric.Group(elements); }, /** @@ -580,7 +624,7 @@ * @return {Array} properties Properties names to include */ populateWithProperties: function(source, destination, properties) { - if (properties && Object.prototype.toString.call(properties) === '[object Array]') { + if (properties && Array.isArray(properties)) { for (var i = 0, len = properties.length; i < len; i++) { if (properties[i] in source) { destination[properties[i]] = source[properties[i]]; @@ -981,7 +1025,7 @@ * this is equivalent to remove from that object that transformation, so that * added in a space with the removed transform, the object will be the same as before. * Removing from an object a transform that scale by 2 is like scaling it by 1/2. - * Removing from an object a transfrom that rotate by 30deg is like rotating by 30deg + * Removing from an object a transform that rotate by 30deg is like rotating by 30deg * in the opposite direction. * This util is used to add objects inside transformed groups or nested groups. * @memberOf fabric.util @@ -1029,6 +1073,50 @@ object.setPositionByOrigin(center, 'center', 'center'); }, + /** + * + * A util that abstracts applying transform to objects.\ + * Sends `object` to the destination coordinate plane by applying the relevant transformations.\ + * Changes the space/plane where `object` is drawn.\ + * From the canvas/viewer's perspective `object` remains unchanged. + * + * @example Move clip path from one object to another while preserving it's appearance as viewed by canvas/viewer + * let obj, obj2; + * let clipPath = new fabric.Circle({ radius: 50 }); + * obj.clipPath = clipPath; + * // render + * fabric.util.sendObjectToPlane(clipPath, obj.calcTransformMatrix(), obj2.calcTransformMatrix()); + * obj.clipPath = undefined; + * obj2.clipPath = clipPath; + * // render, clipPath now clips obj2 but seems unchanged from the eyes of the viewer + * + * @example Clip an object's clip path with an existing object + * let obj, existingObj; + * let clipPath = new fabric.Circle({ radius: 50 }); + * obj.clipPath = clipPath; + * let transformTo = fabric.util.multiplyTransformMatrices(obj.calcTransformMatrix(), clipPath.calcTransformMatrix()); + * fabric.util.sendObjectToPlane(existingObj, existingObj.group?.calcTransformMatrix(), transformTo); + * clipPath.clipPath = existingObj; + * + * @static + * @memberof fabric.util + * @param {fabric.Object} object + * @param {Matrix} [from] plane matrix containing object. Passing `null` is equivalent to passing the identity matrix, which means `object` is a direct child of canvas. + * @param {Matrix} [to] destination plane matrix to contain object. Passing `null` means `object` should be sent to the canvas coordinate plane. + * @returns {Matrix} the transform matrix that was applied to `object` + */ + sendObjectToPlane: function (object, from, to) { + // we are actually looking for the transformation from the destination plane to the source plane (which is a linear mapping) + // the object will exist on the destination plane and we want it to seem unchanged by it so we reverse the destination matrix (to) and then apply the source matrix (from) + var inv = fabric.util.invertTransform(to || fabric.iMatrix); + var t = fabric.util.multiplyTransformMatrices(inv, from || fabric.iMatrix); + fabric.util.applyTransformToObject( + object, + fabric.util.multiplyTransformMatrices(t, object.calcOwnMatrix()) + ); + return t; + }, + /** * given a width and height, return the size of the bounding box * that can contains the box with width/height with applied transform diff --git a/test/lib/visualTestLoop.js b/test/lib/visualTestLoop.js index ff9473d4641..582e4ef0197 100644 --- a/test/lib/visualTestLoop.js +++ b/test/lib/visualTestLoop.js @@ -61,16 +61,20 @@ return fabric.isLikelyNode ? localPath('/..', finalName) : getAbsolutePath('/test' + finalName); } + function generateGolden(filename, original) { + if (fabric.isLikelyNode && original) { + var plainFileName = filename.replace('file://', ''); + var dataUrl = original.toDataURL().split(',')[1]; + console.log('creating original for ', filename); + fs.writeFileSync(plainFileName, dataUrl, { encoding: 'base64' }); + } + } + function getImage(filename, original, callback) { if (fabric.isLikelyNode && original) { var plainFileName = filename.replace('file://', ''); - try { - fs.statSync(plainFileName); - } - catch (err) { - var dataUrl = original.toDataURL().split(',')[1]; - console.log('creating original for ', filename); - fs.writeFileSync(plainFileName, dataUrl, { encoding: 'base64' }); + if (!fs.existsSync(plainFileName)) { + generateGolden(filename, original); } } var img = fabric.document.createElement('img'); @@ -132,16 +136,20 @@ var totalPixels = width * height; var imageDataCanvas = renderedCanvas.getContext('2d').getImageData(0, 0, width, height).data; var canvas = fabric.document.createElement('canvas'); + var canvas2 = fabric.document.createElement('canvas'); canvas.width = width; canvas.height = height; + canvas2.width = width; + canvas2.height = height; var ctx = canvas.getContext('2d'); var output = ctx.getImageData(0, 0, width, height); + canvas2.getContext('2d').drawImage(renderedCanvas, 0, 0, width, height); getImage(getGoldeName(golden), renderedCanvas, function(goldenImage) { ctx.drawImage(goldenImage, 0, 0); visualCallback.addArguments({ enabled: true, golden: canvas, - fabric: renderedCanvas, + fabric: canvas2, diff: output }); var imageDataGolden = ctx.getImageData(0, 0, width, height).data; @@ -157,8 +165,11 @@ var stringa = imageDataToChalk(output); console.log(stringa); } - done(); + if ((!isOK && QUnit.debugVisual) || QUnit.recreateVisualRefs) { + generateGolden(getGoldeName(golden), renderedCanvas); + } fabricCanvas.dispose(); + done(); }); }); }); diff --git a/test/node_test_setup.js b/test/node_test_setup.js index 09155a0514a..1cda27ef9fa 100644 --- a/test/node_test_setup.js +++ b/test/node_test_setup.js @@ -1,5 +1,26 @@ // set the fabric framework as a global for tests var chalk = require('chalk'); +var diff = require('deep-object-diff').diff; +var commander = require('commander'); + +commander.program + .option('-d, --debug', 'debug visual tests by overriding refs (golden images) in case of visual changes', false) + .option('-r, --recreate', 'recreate visual refs (golden images)', false) + .action(options => { + QUnit.debug = QUnit.debugVisual = options.debug; + QUnit.recreateVisualRefs = options.recreate; + }).parse(process.argv); +// for now accept an env variable because qunit doesn't allow passing unknown options +if (process.env.QUNIT_DEBUG_VISUAL_TESTS === 'true') { + QUnit.debugVisual = true; +} +if (process.env.QUNIT_RECREATE_VISUAL_REFS === 'true') { + QUnit.recreateVisualRefs = true; +} +if (process.env.QUNIT_FILTER) { + QUnit.config.filter = process.env.QUNIT_FILTER; +} + global.fabric = require('../dist/fabric').fabric; global.pixelmatch = require('pixelmatch'); global.fs = require('fs'); @@ -31,7 +52,7 @@ global.imageDataToChalk = function(imageData) { }; QUnit.config.testTimeout = 15000; QUnit.config.noglobals = true; -QUnit.config.hidePassed = true; +QUnit.config.hidepassed = true; var jsdom = require('jsdom'); @@ -59,3 +80,55 @@ fabric.jsdomImplForWrapper = require('jsdom/lib/jsdom/living/generated/utils').i fabric.nodeCanvas = require('jsdom/lib/jsdom/utils').Canvas; fabric.window = virtualWindow; DOMParser = fabric.window.DOMParser; + + +// QUnit Logging + +// testID +var objectInit = fabric.Object.prototype.initialize; +var canvasInit = fabric.StaticCanvas.prototype.initialize; +var testID = 0; +fabric.Object.prototype.initialize = function () { + objectInit.apply(this, arguments); + this.testID = `${this.type}#${++testID}`; +} +fabric.StaticCanvas.prototype.initialize = function () { + canvasInit.apply(this, arguments); + this.testID = `Canvas#${++testID}`; +} + +function getLoggingRepresentation(input) { + return typeof input === 'object' && input && input.testID ? + input.testID : + input; +} + +// https://api.qunitjs.com/extension/QUnit.dump.parse/ +QUnit.dump.maxDepth = 1; +// https://github.com/qunitjs/qunit/blob/main/src/assert.js +QUnit.assert.deepEqual = function (actual, expected, message) { + actual = QUnit.dump.parse(actual); + expected = QUnit.dump.parse(expected); + this.pushResult({ + result: QUnit.equiv(actual, expected), + message: `${message}\ndiff:\n${diff(actual, expected)}`, + actual, + expected + }); +}; +QUnit.assert.equal = function (actual, expected, message) { + this.pushResult({ + result: actual == expected, + actual: getLoggingRepresentation(actual), + expected: getLoggingRepresentation(expected), + message + }); +}; +QUnit.assert.strictEqual = function (actual, expected, message) { + this.pushResult({ + result: actual === expected, + actual: getLoggingRepresentation(actual), + expected: getLoggingRepresentation(expected), + message + }); +}; \ No newline at end of file diff --git a/test/unit/activeselection.js b/test/unit/activeselection.js index b2f52f175a6..534b47a36d0 100644 --- a/test/unit/activeselection.js +++ b/test/unit/activeselection.js @@ -62,6 +62,7 @@ width: 80, height: 60, fill: 'rgb(0,0,0)', + layout: 'fit-content', stroke: null, strokeWidth: 0, strokeDashArray: null, @@ -72,6 +73,8 @@ scaleX: 1, scaleY: 1, shadow: null, + subTargetCheck: false, + interactive: false, visible: true, backgroundColor: '', angle: 0, @@ -99,7 +102,7 @@ group.includeDefaultValues = false; var clone = group.toObject(); var objects = [{ - version: fabric.version, + version: fabric.version, type: 'rect', left: 10, top: -30, @@ -107,7 +110,7 @@ height: 10, strokeWidth: 0 }, { - version: fabric.version, + version: fabric.version, type: 'rect', left: -40, top: -10, @@ -122,7 +125,7 @@ top: 100, width: 80, height: 60, - objects: objects + objects: objects, }; assert.deepEqual(clone, expectedObject); }); @@ -181,42 +184,11 @@ // assert.equal(group.get('lockRotation'), true); }); - QUnit.test('insertAt', function(assert) { - var rect1 = new fabric.Rect(), - rect2 = new fabric.Rect(), - group = new fabric.Group(); - - group.add(rect1, rect2); - - assert.ok(typeof group.insertAt === 'function', 'should respond to `insertAt` method'); - - group.insertAt(rect1, 1); - assert.equal(group.item(1), rect1); - group.insertAt(rect2, 2); - assert.equal(group.item(2), rect2); - assert.equal(group.insertAt(rect1, 2), group, 'should be chainable'); - }); - - QUnit.test('group addWithUpdate', function(assert) { - var rect1 = new fabric.Rect({ top: 1, left: 1, width: 2, height: 2, strokeWidth: 0, fill: 'red', opacity: 1, objectCaching: false}), - rect2 = new fabric.Rect({ top: 5, left: 5, width: 2, height: 2, strokeWidth: 0, fill: 'red', opacity: 1, objectCaching: false}), - group = new fabric.Group([rect1]); - - var coords = group.oCoords; - group.addWithUpdate(rect2); - var newCoords = group.oCoords; - assert.notEqual(coords, newCoords, 'object coords have been recalculated - add'); - }); - - QUnit.test('group removeWithUpdate', function(assert) { - var rect1 = new fabric.Rect({ top: 1, left: 1, width: 2, height: 2, strokeWidth: 0, fill: 'red', opacity: 1, objectCaching: false}), - rect2 = new fabric.Rect({ top: 5, left: 5, width: 2, height: 2, strokeWidth: 0, fill: 'red', opacity: 1, objectCaching: false}), - group = new fabric.Group([rect1, rect2]); - - var coords = group.oCoords; - group.removeWithUpdate(rect2); - var newCoords = group.oCoords; - assert.notEqual(coords, newCoords, 'object coords have been recalculated - remove'); + QUnit.test('inherited methods', function (assert) { + var methods = ['add', 'insertAt', 'remove', 'removeAll']; + methods.forEach(method => { + assert.strictEqual(fabric.ActiveSelection.prototype[method], fabric.Group.prototype[method]); + }); }); QUnit.test('ActiveSelection shouldCache', function(assert) { diff --git a/test/unit/animation.js b/test/unit/animation.js index 023398ca8ad..8ddb39f1e7a 100644 --- a/test/unit/animation.js +++ b/test/unit/animation.js @@ -324,7 +324,7 @@ assert.equal(Math.round(object.get('left')), 1); assert.equal(Math.round(object.get('top')), 1); - assert.equal(changedInvocations, 2); + assert.ok(changedInvocations > 0); assert.equal(completeInvocations, 1); done(); @@ -404,6 +404,23 @@ }, 100); }); + QUnit.test('animate with delay', function (assert) { + var done = assert.async(); + var object = new fabric.Object({ left: 123, top: 124 }); + var started = false; + var t = new Date(); + var abort = object._animate('left', 223, { + onStart: function () { + started = true; + assert.gte(new Date() - t, 500, 'animation delay'); + return false; + }, + onComplete: done, + delay: 500 + }); + assert.ok(started === false); + }); + QUnit.test('animate easing easeInQuad', function(assert) { var done = assert.async(); assert.ok(typeof fabric.util.ease.easeInQuad === 'function'); diff --git a/test/unit/canvas.js b/test/unit/canvas.js index b618c32dbff..7f3724a186d 100644 --- a/test/unit/canvas.js +++ b/test/unit/canvas.js @@ -136,6 +136,12 @@ } }); + QUnit.test('prevent multiple canvas initialization', function (assert) { + var canvas = new fabric.Canvas(); + assert.ok(canvas.lowerCanvasEl); + assert.throws(() => new fabric.Canvas(canvas.lowerCanvasEl)); + }); + QUnit.test('initialProperties', function(assert) { assert.ok('backgroundColor' in canvas); assert.equal(canvas.includeDefaultValues, true); @@ -207,6 +213,9 @@ QUnit.test('_initInteractive', function(assert) { assert.ok(typeof canvas._initInteractive === 'function'); + assert.equal(canvas.lowerCanvasEl.getAttribute('data-fabric'), 'main', 'el should be marked by canvas init'); + assert.equal(canvas.upperCanvasEl.getAttribute('data-fabric'), 'top', 'el should be marked by canvas init'); + assert.equal(canvas.wrapperEl.getAttribute('data-fabric'), 'wrapper', 'el should be marked by canvas init'); }); QUnit.test('renderTop', function(assert) { @@ -739,8 +748,8 @@ rect3Selected = true; }); var currentObjects = canvas.getActiveObjects(); - activeSelection.removeWithUpdate(rect1); - activeSelection.addWithUpdate(rect3); + activeSelection.remove(rect1); + activeSelection.add(rect3); canvas._fireSelectionEvents(currentObjects, {}); assert.ok(rect3Selected, 'rect 3 selected'); assert.ok(rect1Deselected, 'rect 1 deselected'); @@ -829,26 +838,31 @@ group = new fabric.Group([rect, rect2]); canvas.add(group); + target = canvas.findTarget({ clientX: 5, clientY: 5 }, true); assert.equal(target, group, 'Should return the group'); assert.equal(canvas.targets[0], undefined, 'no subtarget should return'); + target = canvas.findTarget({ clientX: 30, clientY: 30 }); assert.equal(target, group, 'Should return the group'); group.subTargetCheck = true; + group.setCoords(); target = canvas.findTarget({ clientX: 5, clientY: 5 }); assert.equal(target, group, 'Should return the group'); assert.equal(canvas.targets[0], rect, 'should return the rect'); + target = canvas.findTarget({ clientX: 15, clientY: 15 }); assert.equal(target, group, 'Should return the group'); assert.equal(canvas.targets[0], undefined, 'no subtarget should return'); + target = canvas.findTarget({ clientX: 32, clientY: 32 }); @@ -874,7 +888,7 @@ [rect3, rect4], { scaleX: 0.5, scaleY: 0.5, top: 100, left: 0 }); group3.subTargetCheck = true; - + group3.setCoords(); var rect1 = new fabric.Rect({ width: 100, height: 100, @@ -933,7 +947,6 @@ assert.equal(target, g, 'Should return the group 106'); assert.equal(canvas.targets[0], rect2, 'should find the target rect2 106'); canvas.targets = []; - }); QUnit.test('findTarget with subTargetCheck on activeObject', function(assert) { @@ -941,9 +954,10 @@ rect2 = makeRect({ left: 30, top: 30}), target, group = new fabric.Group([rect, rect2]); + + group.subTargetCheck = true; canvas.add(group); canvas.setActiveObject(group); - group.subTargetCheck = true; target = canvas.findTarget({ clientX: 9, clientY: 9 }); @@ -972,9 +986,9 @@ rect2 = makeRect({ left: 30, top: 30}), target, group = new fabric.Group([rect, rect2]); canvas.preserveObjectStacking = true; + group.subTargetCheck = true; canvas.add(group); canvas.setActiveObject(group); - group.subTargetCheck = true; target = canvas.findTarget({ clientX: 9, clientY: 9 }); @@ -1110,7 +1124,7 @@ canvas.add(rect1); canvas.add(rect2); canvas.add(rect3); - var group = new fabric.ActiveSelection([rect1, rect2]); + var group = new fabric.ActiveSelection([rect1, rect2], { subTargetCheck: true }); canvas.setActiveObject(group); target = canvas.findTarget({ clientX: 5, clientY: 5 @@ -1293,6 +1307,13 @@ assert.equal(center.top, upperCanvasEl.height / 2); }); + QUnit.test('getCenterPoint', function(assert) { + assert.ok(typeof canvas.getCenterPoint === 'function'); + var center = canvas.getCenterPoint(); + assert.equal(center.x, upperCanvasEl.width / 2); + assert.equal(center.y, upperCanvasEl.height / 2); + }); + QUnit.test('centerObjectH', function(assert) { assert.ok(typeof canvas.centerObjectH === 'function'); var rect = makeRect({ left: 102, top: 202 }); @@ -2056,10 +2077,17 @@ parentEl.className = 'rootNode'; parentEl.appendChild(el); + var originalDevicePixelRatio = fabric.devicePixelRatio; + fabric.devicePixelRatio = 1.25; + assert.equal(parentEl.firstChild, el, 'canvas should be appended at partentEl'); assert.equal(parentEl.childNodes.length, 1, 'parentEl has 1 child only'); - var canvas = new fabric.Canvas(el, {enableRetinaScaling: false, renderOnAddRemove: false }); + el.style.position = 'relative'; + var elStyle = el.style.cssText; + assert.equal(elStyle, 'position: relative;', 'el style should not be empty'); + + var canvas = new fabric.Canvas(el, { enableRetinaScaling: true, renderOnAddRemove: false }); wrapperEl = canvas.wrapperEl; lowerCanvasEl = canvas.lowerCanvasEl; upperCanvasEl = canvas.upperCanvasEl; @@ -2069,6 +2097,8 @@ assert.equal(wrapperEl.className, canvas.containerClass, 'DIV class should be set'); assert.equal(wrapperEl.childNodes[0], lowerCanvasEl, 'First child should be lowerCanvas'); assert.equal(wrapperEl.childNodes[1], upperCanvasEl, 'Second child should be upperCanvas'); + assert.equal(canvas._originalCanvasStyle, elStyle, 'saved original canvas style for disposal'); + assert.notEqual(el.style.cssText, canvas._originalCanvasStyle, 'canvas el style has been changed'); if (!fabric.isLikelyNode) { assert.equal(parentEl.childNodes[0], wrapperEl, 'wrapperEl is appendend to rootNode'); } @@ -2092,9 +2122,50 @@ } assert.equal(canvas.wrapperEl, null, 'wrapperEl should be deleted'); assert.equal(canvas.upperCanvasEl, null, 'upperCanvas should be deleted'); + assert.equal(canvas.lowerCanvasEl, null, 'lowerCanvasEl should be deleted'); assert.equal(canvas.cacheCanvasEl, null, 'cacheCanvasEl should be deleted'); assert.equal(canvas.contextTop, null, 'contextTop should be deleted'); assert.equal(canvas.contextCache, null, 'contextCache should be deleted'); + assert.equal(canvas._originalCanvasStyle, undefined, 'removed original canvas style'); + assert.equal(el.style.cssText, elStyle, 'restored original canvas style'); + assert.equal(el.width, 200, 'restored width'); + assert.equal(el.height, 200, 'restored height'); + + fabric.devicePixelRatio = originalDevicePixelRatio; + }); + + QUnit.test('dispose + set dimensions', function (assert) { + //made local vars to do not dispose the external canvas + var el = fabric.document.createElement('canvas'), + parentEl = fabric.document.createElement('div'); + el.width = 200; el.height = 200; + parentEl.className = 'rootNode'; + parentEl.appendChild(el); + + var originalDevicePixelRatio = fabric.devicePixelRatio; + fabric.devicePixelRatio = 1.25; + + assert.equal(parentEl.firstChild, el, 'canvas should be appended at partentEl'); + assert.equal(parentEl.childNodes.length, 1, 'parentEl has 1 child only'); + + el.style.position = 'relative'; + var elStyle = el.style.cssText; + assert.equal(elStyle, 'position: relative;', 'el style should not be empty'); + + var canvas = new fabric.Canvas(el, { enableRetinaScaling: true, renderOnAddRemove: false }); + + canvas.setDimensions({ width: 500, height: 500 }); + assert.equal(canvas._originalCanvasStyle, elStyle, 'saved original canvas style for disposal'); + assert.notEqual(el.style.cssText, canvas._originalCanvasStyle, 'canvas el style has been changed'); + + canvas.dispose(); + assert.equal(canvas._originalCanvasStyle, undefined, 'removed original canvas style'); + assert.equal(el.style.cssText, elStyle, 'restored original canvas style'); + assert.equal(el.width, 500, 'restored width'); + assert.equal(el.height, 500, 'restored height'); + + fabric.devicePixelRatio = originalDevicePixelRatio; + }); // QUnit.test('dispose', function(assert) { @@ -2521,6 +2592,11 @@ assert.ok(typeof InheritedCanvasClass === 'function'); }); + QUnit.test('canvas getTopContext', function(assert) { + assert.ok(typeof canvas.getTopContext === 'function'); + assert.equal(canvas.getTopContext(), canvas.contextTop, 'it jsut returns contextTop'); + }); + QUnit.test('_shouldCenterTransform', function(assert) { assert.equal( canvas._shouldCenterTransform({}, 'someAction', false), false, 'a non standard action does not center scale'); diff --git a/test/unit/canvas_events.js b/test/unit/canvas_events.js index c407a807627..6b89d6b56f4 100644 --- a/test/unit/canvas_events.js +++ b/test/unit/canvas_events.js @@ -488,9 +488,9 @@ c._onDragLeave, c._onDrop, ]; - // initialize canvas more than once - c.initialize(); - c.initialize(); + // _initEventListeners canvas more than once + c._initEventListeners(); + c._initEventListeners(); var eventsArray2 = [ c._onMouseDown, c._onMouseMove, @@ -510,7 +510,7 @@ c._onDragLeave, c._onDrop, ]; - assert.deepEqual(eventsArray, eventsArray2, 'after first initialize, functions do not change.'); + assert.deepEqual(eventsArray, eventsArray2, 'after first _initEventListeners, functions do not change.'); }); ['DragEnter', 'DragLeave', 'DragOver', 'Drop'].forEach(function(eventType) { @@ -522,9 +522,9 @@ c[funcName] = function() { counter++; }; - // initialize canvas more than once - c.initialize(c.lowerCanvasEl); - c.initialize(c.lowerCanvasEl); + // _initEventListeners canvas more than once + c._initEventListeners(c.lowerCanvasEl); + c._initEventListeners(c.lowerCanvasEl); var event = fabric.document.createEvent('HTMLEvents'); event.initEvent(eventName, true, true); c.upperCanvasEl.dispatchEvent(event); @@ -769,9 +769,9 @@ c[funcName] = function() { counter++; }; - // initialize canvas more than once - c.initialize(c.lowerCanvasEl); - c.initialize(c.lowerCanvasEl); + // _initEventListeners canvas more than once + c._initEventListeners(c.lowerCanvasEl); + c._initEventListeners(c.lowerCanvasEl); var event = fabric.document.createEvent('MouseEvent'); event.initEvent(eventName, true, true); c.upperCanvasEl.dispatchEvent(event); @@ -787,9 +787,9 @@ counter++; }; var c = new fabric.Canvas(); - // initialize canvas more than once - c.initialize(c.lowerCanvasEl); - c.initialize(c.lowerCanvasEl); + // _initEventListeners canvas more than once + c._initEventListeners(c.lowerCanvasEl); + c._initEventListeners(c.lowerCanvasEl); // a mouse down is necessary to register mouse up. var _event = fabric.document.createEvent('MouseEvent'); @@ -857,9 +857,9 @@ counter++; }; var c = new fabric.Canvas(); - // initialize canvas more than once - c.initialize(c.lowerCanvasEl); - c.initialize(c.lowerCanvasEl); + // _initEventListeners canvas more than once + c._initEventListeners(c.lowerCanvasEl); + c._initEventListeners(c.lowerCanvasEl); var event = fabric.document.createEvent('UIEvents'); event.initUIEvent('resize', true, false, fabric.window, 0); fabric.window.dispatchEvent(event); diff --git a/test/unit/canvas_static.js b/test/unit/canvas_static.js index bbf050f209c..b3fcf0cf2c3 100644 --- a/test/unit/canvas_static.js +++ b/test/unit/canvas_static.js @@ -188,8 +188,11 @@ canvas.overlayColor = fabric.StaticCanvas.prototype.overlayColor; canvas.viewportTransform = [1, 0, 0, 1, 0, 0]; canvas.calcOffset(); + canvas.requestRenderAll = fabric.StaticCanvas.prototype.requestRenderAll; canvas.cancelRequestedRender(); canvas2.cancelRequestedRender(); + canvas.renderOnAddRemove = false; + canvas2.renderOnAddRemove = false; }, afterEach: function() { canvas.cancelRequestedRender(); @@ -197,6 +200,12 @@ } }); + QUnit.test('prevent multiple canvas initialization', function (assert) { + var canvas = new fabric.StaticCanvas(); + assert.ok(canvas.lowerCanvasEl); + assert.throws(() => new fabric.StaticCanvas(canvas.lowerCanvasEl)); + }); + QUnit.test('initialProperties', function(assert) { var canvas = new fabric.StaticCanvas(); assert.ok('backgroundColor' in canvas); @@ -241,6 +250,7 @@ assert.deepEqual(canvas.getObjects('rect'), [rect], 'should return rect only'); assert.deepEqual(canvas.getObjects('circle'), [circle], 'should return circle only'); + assert.deepEqual(canvas.getObjects('circle', 'rect'), [rect, circle], 'should return rect and circle'); }); QUnit.test('getElement', function(assert) { @@ -265,33 +275,79 @@ var rect1 = makeRect(), rect2 = makeRect(), rect3 = makeRect(), - rect4 = makeRect(); + rect4 = makeRect(), + renderAllCount = 0; + function countRenderAll() { + renderAllCount++; + } + canvas.renderOnAddRemove = true; + canvas.requestRenderAll = countRenderAll; assert.ok(typeof canvas.add === 'function'); assert.equal(canvas.add(rect1), canvas, 'should be chainable'); assert.strictEqual(canvas.item(0), rect1); + assert.equal(renderAllCount, 1); canvas.add(rect2, rect3, rect4); assert.equal(canvas.getObjects().length, 4, 'should support multiple arguments'); + assert.equal(renderAllCount, 2); + + canvas.add(); + assert.equal(renderAllCount, 2); assert.strictEqual(canvas.item(1), rect2); assert.strictEqual(canvas.item(2), rect3); assert.strictEqual(canvas.item(3), rect4); }); + QUnit.test('add an object that belongs to a different canvas', function (assert) { + var rect1 = makeRect(); + var control = []; + canvas.on('object:added', (opt) => { + control.push({ + action: 'added', + canvas: canvas, + target: opt.target + }); + }); + canvas.on('object:removed', (opt) => { + control.push({ + action: 'removed', + canvas: canvas, + target: opt.target + }); + }); + canvas2.on('object:added', (opt) => { + control.push({ + action: 'added', + canvas: canvas2, + target: opt.target + }); + }); + canvas.add(rect1); + assert.strictEqual(canvas.item(0), rect1); + canvas2.add(rect1); + assert.equal(canvas.item(0), undefined); + assert.equal(canvas.size(), 0); + assert.strictEqual(canvas2.item(0), rect1); + var expected = [ + { action: 'added', target: rect1, canvas: canvas }, + { action: 'removed', target: rect1, canvas: canvas }, + { action: 'added', target: rect1, canvas: canvas2 }, + ] + assert.deepEqual(control, expected); + }); + QUnit.test('add renderOnAddRemove disabled', function(assert) { var rect = makeRect(), - originalRenderOnAddition, renderAllCount = 0; function countRenderAll() { renderAllCount++; } - originalRenderOnAddition = canvas.renderOnAddRemove; canvas.renderOnAddRemove = false; - - canvas.on('after:render', countRenderAll); + canvas.requestRenderAll = countRenderAll; assert.equal(canvas.add(rect), canvas, 'should be chainable'); assert.equal(renderAllCount, 0); @@ -301,12 +357,6 @@ canvas.add(makeRect(), makeRect(), makeRect()); assert.equal(canvas.getObjects().length, 4, 'should support multiple arguments'); assert.equal(renderAllCount, 0); - - canvas.renderAll(); - assert.equal(renderAllCount, 1); - - canvas.off('after:render', countRenderAll); - canvas.renderOnAddRemove = originalRenderOnAddition; }); QUnit.test('object:added', function(assert) { @@ -337,34 +387,41 @@ QUnit.test('insertAt', function(assert) { var rect1 = makeRect(), - rect2 = makeRect(); + rect2 = makeRect(), + renderAllCount = 0; canvas.add(rect1, rect2); assert.ok(typeof canvas.insertAt === 'function', 'should respond to `insertAt` method'); + function countRenderAll() { + renderAllCount++; + } + canvas.requestRenderAll = countRenderAll; + canvas.renderOnAddRemove = true; + assert.equal(renderAllCount, 0); var rect = makeRect(); canvas.insertAt(rect, 1); + assert.equal(renderAllCount, 1); assert.strictEqual(canvas.item(1), rect); canvas.insertAt(rect, 2); + assert.equal(renderAllCount, 2); assert.strictEqual(canvas.item(2), rect); assert.equal(canvas.insertAt(rect, 2), canvas, 'should be chainable'); + assert.equal(renderAllCount, 3); }); QUnit.test('insertAt renderOnAddRemove disabled', function(assert) { var rect1 = makeRect(), rect2 = makeRect(), - originalRenderOnAddition, renderAllCount = 0; function countRenderAll() { renderAllCount++; } - originalRenderOnAddition = canvas.renderOnAddRemove; canvas.renderOnAddRemove = false; - - canvas.on('after:render', countRenderAll); + canvas.requestRenderAll = countRenderAll; canvas.add(rect1, rect2); assert.equal(renderAllCount, 0); @@ -377,60 +434,53 @@ assert.strictEqual(canvas.item(1), rect); canvas.insertAt(rect, 2); assert.equal(renderAllCount, 0); - - canvas.renderAll(); - assert.equal(renderAllCount, 1); - - canvas.off('after:render', countRenderAll); - canvas.renderOnAddRemove = originalRenderOnAddition; }); QUnit.test('remove', function(assert) { var rect1 = makeRect(), rect2 = makeRect(), rect3 = makeRect(), - rect4 = makeRect(); + rect4 = makeRect(), + renderAllCount = 0; - canvas.add(rect1, rect2, rect3, rect4); + function countRenderAll() { + renderAllCount++; + } + canvas.add(rect1, rect2, rect3, rect4); + canvas.requestRenderAll = countRenderAll; + canvas.renderOnAddRemove = true; assert.ok(typeof canvas.remove === 'function'); + assert.equal(renderAllCount, 0); assert.equal(canvas.remove(rect1), canvas, 'should be chainable'); assert.strictEqual(canvas.item(0), rect2, 'should be second object'); canvas.remove(rect2, rect3); + assert.equal(renderAllCount, 2); assert.strictEqual(canvas.item(0), rect4); canvas.remove(rect4); + assert.equal(renderAllCount, 3); assert.equal(canvas.isEmpty(), true, 'canvas should be empty'); }); QUnit.test('remove renderOnAddRemove disabled', function(assert) { var rect1 = makeRect(), rect2 = makeRect(), - originalRenderOnAddition, renderAllCount = 0; function countRenderAll() { renderAllCount++; } - - originalRenderOnAddition = canvas.renderOnAddRemove; + canvas.requestRenderAll = countRenderAll; canvas.renderOnAddRemove = false; - canvas.on('after:render', countRenderAll); - canvas.add(rect1, rect2); assert.equal(renderAllCount, 0); assert.equal(canvas.remove(rect1), canvas, 'should be chainable'); assert.equal(renderAllCount, 0); assert.strictEqual(canvas.item(0), rect2, 'only second object should be left'); - - canvas.renderAll(); - assert.equal(renderAllCount, 1); - - canvas.off('after:render', countRenderAll); - canvas.renderOnAddRemove = originalRenderOnAddition; }); QUnit.test('object:removed', function(assert) { @@ -1555,9 +1605,12 @@ var canvas2 = new fabric.StaticCanvas(null, { renderOnAddRemove: false }); assert.ok(typeof canvas2.dispose === 'function'); canvas2.add(makeRect(), makeRect(), makeRect()); + var lowerCanvas = canvas2.lowerCanvasEl; + assert.equal(lowerCanvas.getAttribute('data-fabric'), 'main', 'lowerCanvasEl should be marked by fabric'); canvas2.dispose(); assert.equal(canvas2.getObjects().length, 0, 'dispose should clear canvas'); assert.equal(canvas2.lowerCanvasEl, null, 'dispose should clear lowerCanvasEl'); + assert.equal(lowerCanvas.hasAttribute('data-fabric'), false, 'dispose should clear lowerCanvasEl data-fabric attr'); assert.equal(canvas2.contextContainer, null, 'dispose should clear contextContainer'); }); diff --git a/test/unit/collection.js b/test/unit/collection.js index 6a92b83b6be..064fae5d4d7 100644 --- a/test/unit/collection.js +++ b/test/unit/collection.js @@ -5,121 +5,127 @@ QUnit.module('fabric.Collection', { beforeEach: function() { - collection.rendered = 0; collection._objects = []; - delete collection.renderOnAddRemove; - delete collection._onObjectAdded; - delete collection._onObjectRemoved; collection2._objects = []; } }); - collection.requestRenderAll = function() { - this.rendered++; - }; - QUnit.test('add', function(assert) { var obj = { prop: 4 }, fired = 0; assert.ok(typeof collection.add === 'function', 'has add method'); assert.deepEqual(collection._objects, [], 'start with empty array of items'); - var returned = collection.add(obj); - assert.equal(returned, collection, 'is chainable'); + var returned = collection.add([obj], cb); assert.equal(collection._objects[0], obj, 'add object in the array'); assert.equal(fired, 0, 'fired is 0'); - - collection._onObjectAdded = function() { + var cb = function () { fired++; }; - collection.add(obj); + collection.add([obj], cb); assert.equal(collection._objects[1], obj, 'add object in the array'); - assert.equal(fired, 1, 'fired is incremented if there is a _onObjectAdded'); - collection.renderOnAddRemove = true; - assert.equal(collection.rendered, 0, 'this.renderAll has not been called'); - collection.add(obj); - assert.equal(collection.rendered, 1, 'this.renderAll has been called'); + assert.equal(fired, 1, 'fired is incremented due to callback'); + collection.add([obj], cb); assert.equal(collection._objects.length, 3, 'we have 3 objects in collection'); fired = 0; - collection.add(obj, obj, obj, obj); + collection.add([obj, obj, obj, obj], cb); assert.equal(fired, 4, 'fired is incremented for every object added'); assert.equal(collection._objects.length, 7, 'all objects have been added'); - assert.equal(collection.rendered, 2, 'this.renderAll has been called just once more'); }); - QUnit.test('insertAt', function(assert) { - var obj = { prop: 4 }, fired = 0, index = 1, nonSplicing = false; - collection._objects = [{ prop: 0 }, {prop: 1}]; - assert.ok(typeof collection.insertAt === 'function', 'has insertAdd method'); - var previousObject = collection._objects[index]; - var previousLength = collection._objects.length; - collection.insertAt(obj, index, nonSplicing); - assert.equal(collection._objects[index], obj, 'add object in the array at specified index'); - assert.equal(collection._objects[index + 1], previousObject, 'add old object in the array at next index'); - assert.equal(collection._objects.length, previousLength + 1, 'length is incremented'); + QUnit.test('insertAt', function (assert) { + var rect1 = new fabric.Rect({ id: 1 }), + rect2 = new fabric.Rect({ id: 2 }), + rect3 = new fabric.Rect({ id: 3 }), + rect4 = new fabric.Rect({ id: 4 }), + rect5 = new fabric.Rect({ id: 5 }), + rect6 = new fabric.Rect({ id: 6 }), + rect7 = new fabric.Rect({ id: 7 }), + rect8 = new fabric.Rect({ id: 8 }), + control = [], + fired = [], + firingControl = []; + + collection.add([rect1, rect2]); + control.push(rect1, rect2); + + assert.ok(typeof collection.insertAt === 'function', 'should respond to `insertAt` method'); + + const equalsControl = () => { + assert.deepEqual(collection.getObjects().map(o => o.id), control.map(o => o.id), 'should equal control array'); + assert.deepEqual(collection.getObjects(), control, 'should equal control array'); + assert.deepEqual(fired.map(o => o.id), firingControl.map(o => o.id), 'fired events should equal control array'); + assert.deepEqual(fired, firingControl, 'fired events should equal control array'); + } - nonSplicing = true; - previousLength = collection._objects.length; - var newObject = { prop: 5 }; - previousObject = collection._objects[index]; - var returned = collection.insertAt(newObject, index, nonSplicing); - assert.equal(returned, collection, 'is chainable'); - assert.equal(collection._objects[index], newObject, 'add newobject in the array at specified index'); - assert.notEqual(collection._objects[index + 1], previousObject, 'old object is not in the array at next index'); - assert.equal(collection._objects.indexOf(previousObject), -1, 'old object is no more in array'); - assert.equal(collection._objects.length, previousLength, 'length is not incremented'); assert.ok(typeof collection._onObjectAdded === 'undefined', 'do not have a standard _onObjectAdded method'); - assert.equal(fired, 0, 'fired is 0'); - collection._onObjectAdded = function() { - fired++; + var cb = function (object) { + fired.push(object); }; - collection.insertAt(obj, 1); - assert.equal(fired, 1, 'fired is incremented if there is a _onObjectAdded'); - collection.renderOnAddRemove = true; - collection.insertAt(obj, 1); - assert.equal(collection.rendered, 1, 'this.renderAll has been called'); + + collection.insertAt(rect3, 1, cb); + control.splice(1, 0, rect3); + firingControl.push(rect3); + equalsControl(); + collection.insertAt(rect4, 0, cb); + control.splice(0, 0, rect4); + firingControl.push(rect4); + equalsControl(); + collection.insertAt(rect5, 2, cb); + control.splice(2, 0, rect5); + firingControl.push(rect5); + equalsControl(); + collection.insertAt([rect6], 2, cb); + control.splice(2, 0, rect6); + firingControl.push(rect6); + equalsControl(); + collection.insertAt([rect7, rect8], 3, cb); + control.splice(3, 0, rect7, rect8); + firingControl.push(rect7, rect8); + equalsControl(); + // insert duplicates + collection.insertAt([rect1, rect2], 2, cb); + control.splice(2, 0, rect1, rect2); + firingControl.push(rect1, rect2); + equalsControl(); }); QUnit.test('remove', function(assert) { var obj = { prop: 4 }, obj2 = { prop: 2 }, obj3 = { prop: 3 }, fired = 0; - collection.add({ prop: 0 }, {prop: 1}, obj2, obj, obj3); + collection.add([{ prop: 0 }, {prop: 1}, obj2, obj, obj3]); var previousLength = collection._objects.length; assert.ok(typeof collection.remove === 'function', 'has remove method'); - var returned = collection.remove(obj); - assert.equal(returned, collection, 'is chainable'); + var returned = collection.remove([obj]); + assert.ok(returned, 'removed obj'); + assert.ok(!collection.remove([{ prop: 'foo' }]), 'nothing removed'); assert.equal(collection._objects.indexOf(obj), -1, 'obj is no more in array'); assert.equal(collection._objects.length, previousLength - 1, 'length has changed'); assert.equal(fired, 0, 'fired is 0'); - collection._onObjectRemoved = function() { + var callback = function() { fired++; }; - collection.remove(obj2); - assert.equal(fired, 1, 'fired is incremented if there is a _onObjectAdded'); - collection.remove(obj2); + collection.remove([obj2], callback); + assert.equal(fired, 1, 'fired is incremented if there is a callback'); + collection.remove([obj2], callback); assert.equal(fired, 1, 'fired is not incremented again if there is no object to remove'); - collection.add(obj2); - collection.add(obj); - collection.renderOnAddRemove = true; - assert.equal(collection.rendered, 0, 'this.renderAll has not been called'); - collection.remove(obj2); - assert.equal(collection.rendered, 1, 'this.renderAll has been called'); + collection.add([obj2]); + collection.add([obj]); + collection.remove([obj2], callback); previousLength = collection._objects.length; fired = 0; - collection.remove(obj, obj3); + collection.remove([obj, obj3], callback); assert.equal(collection._objects.length, previousLength - 2, 'we have 2 objects less'); assert.equal(fired, 2, 'fired is incremented for every object removed'); - assert.equal(collection.rendered, 2, 'this.renderAll has been called just once more'); }); QUnit.test('forEachObject', function(assert) { var obj = { prop: false }, obj2 = { prop: false }, obj3 = { prop: false }, fired = 0; - collection.add(obj2, obj, obj3); + collection.add([obj2, obj, obj3]); assert.ok(typeof collection.forEachObject === 'function', 'has forEachObject method'); var callback = function(_obj) { _obj.prop = true; fired++; }; var returned = collection.forEachObject(callback); - assert.equal(returned, collection, 'is chainable'); assert.equal(fired, collection._objects.length, 'fired once for every object'); assert.equal(obj.prop, true, 'fired for obj'); assert.equal(obj2.prop, true, 'fired for obj2'); @@ -127,8 +133,8 @@ }); QUnit.test('getObjects', function(assert) { - var obj = { type: 'a' }, obj2 = { type: 'b' }; - collection.add(obj2, obj); + var obj = { type: 'a' }, obj2 = { type: 'b' }, obj3 = { type: 'c' }; + collection.add([obj2, obj, obj3]); assert.ok(typeof collection.getObjects === 'function', 'has getObjects method'); var returned = collection.getObjects(); assert.notEqual(returned, collection._objects, 'does not return a reference to _objects'); @@ -136,11 +142,16 @@ assert.notEqual(returned, collection._objects, 'return a new array'); assert.equal(returned.indexOf(obj2), -1, 'object of type B is not included'); assert.equal(returned.indexOf(obj), 0, 'object of type A is included'); + returned = collection.getObjects('a', 'b'); + assert.ok(returned.indexOf(obj2) > -1, 'object of type B is not included'); + assert.ok(returned.indexOf(obj) > -1, 'object of type A is included'); + assert.ok(returned.indexOf(obj3) === -1, 'object of type c is included'); + assert.equal(returned.length, 2, 'returned only a, b types'); }); QUnit.test('item', function(assert) { var obj = { type: 'a' }, obj2 = { type: 'b' }, index = 1; - collection.add(obj2, obj); + collection.add([obj2, obj]); assert.ok(typeof collection.item === 'function', 'has item method'); var returned = collection.item(index); assert.equal(returned, collection._objects[index], 'return the object at index'); @@ -151,7 +162,7 @@ assert.ok(typeof collection.isEmpty === 'function', 'has isEmpty method'); var returned = collection.isEmpty(); assert.equal(returned, true, 'collection is empty'); - collection.add(obj2, obj); + collection.add([obj2, obj]); returned = collection.isEmpty(); assert.equal(returned, false, 'collection is not empty'); }); @@ -162,7 +173,7 @@ var returned = collection.size(); assert.ok(typeof returned === 'number', 'returns a number'); assert.equal(returned, 0, 'collection is empty'); - collection.add(obj2, obj); + collection.add([obj2, obj]); returned = collection.size(); assert.equal(returned, 2, 'collection has 2 objects'); }); @@ -173,12 +184,12 @@ var returned = collection.contains(obj); assert.ok(typeof returned === 'boolean', 'returns a boolean'); assert.equal(returned, false, 'collection is empty so does not contains obj'); - collection.add(obj); + collection.add([obj]); returned = collection.contains(obj); assert.equal(returned, true, 'collection contains obj'); var obj2 = { type: 'b' }; - collection2.add(obj2); - collection.add(collection2); + collection2.add([obj2]); + collection.add([collection2]); returned = collection.contains(obj2); assert.equal(returned, false, 'collection deeply contains obj, this check is shallow'); returned = collection.contains(obj2, false); @@ -193,12 +204,12 @@ var returned = collection.complexity(); assert.ok(typeof returned === 'number', 'returns a number'); assert.equal(returned, 0, 'collection has complexity 0'); - collection.add(obj2, obj); + collection.add([obj2, obj]); returned = collection.complexity(); assert.equal(returned, 0, 'collection has complexity 0 if objects have no complexity themselves'); var complexObject = { complexity: function() { return 9; }}; var complexObject2 = { complexity: function() { return 10; }}; - collection.add(complexObject, complexObject2); + collection.add([complexObject, complexObject2]); returned = collection.complexity(); assert.equal(returned, 19, 'collection has complexity 9 + 10'); }); diff --git a/test/unit/controls_handlers.js b/test/unit/controls_handlers.js index 9a30bf60b02..f986eca4d58 100644 --- a/test/unit/controls_handlers.js +++ b/test/unit/controls_handlers.js @@ -21,11 +21,22 @@ }); QUnit.test('changeWidth changes the width', function(assert) { assert.equal(transform.target.width, 100); - fabric.controlsUtils.changeWidth(eventData, transform, 200, 300); + var changed = fabric.controlsUtils.changeWidth(eventData, transform, 200, 300); + assert.ok(changed, 'control changed target'); assert.equal(transform.target.width, 199); assert.equal(transform.target.left, 0); assert.equal(transform.target.top, 0); }); + QUnit.test('changeWidth does not change the width', function (assert) { + var target = new fabric.Rect({ width: 100, height: 100, canvas }); + target._set = () => { }; + assert.equal(target.width, 100); + var changed = fabric.controlsUtils.changeWidth(eventData, Object.assign({}, transform, { target }), 200, 300); + assert.ok(!changed, 'control change was rejected'); + assert.equal(target.width, 100); + assert.equal(target.left, 0); + assert.equal(target.top, 0); + }); QUnit.test('changeWidth changes the width with centered transform', function(assert) { transform.originX = 'center'; transform.originY = 'center'; @@ -51,13 +62,13 @@ transform.target.strokeUniform = true; transform.target.scaleX = 3; fabric.controlsUtils.changeWidth(eventData, transform, 200, 300); - assert.equal(Math.floor(transform.target.width), 61); + assert.equal(Math.ceil(transform.target.width), 62); }); QUnit.test('changeWidth changes the width with big strokeWidth + scaling', function(assert) { transform.target.strokeWidth = 15; transform.target.scaleX = 3; fabric.controlsUtils.changeWidth(eventData, transform, 200, 300); - assert.equal(Math.floor(transform.target.width), 51); + assert.equal(Math.ceil(transform.target.width), 52); }); QUnit.test('changeWidth will fire events on canvas and target resizing', function(assert) { var done = assert.async(); diff --git a/test/unit/group.js b/test/unit/group.js index fbb44674aa9..88aa19625f9 100644 --- a/test/unit/group.js +++ b/test/unit/group.js @@ -5,7 +5,7 @@ var rect1 = new fabric.Rect({ top: 100, left: 100, width: 30, height: 10, strokeWidth: 0 }), rect2 = new fabric.Rect({ top: 120, left: 50, width: 10, height: 40, strokeWidth: 0 }); - return new fabric.Group([rect1, rect2], {strokeWidth: 0}); + return new fabric.Group([rect1, rect2], { strokeWidth: 0 }); } function makeGroupWith2ObjectsWithOpacity() { @@ -59,7 +59,7 @@ var group = new fabric.Group([rect1, rect2]); assert.ok(typeof group.getObjects === 'function'); - assert.ok(Object.prototype.toString.call(group.getObjects()) == '[object Array]', 'should be an array'); + assert.ok(Array.isArray(group.getObjects()), 'should be an array'); assert.equal(group.getObjects().length, 2, 'should have 2 items'); assert.deepEqual(group.getObjects(), [rect1, rect2], 'should return deepEqual objects as those passed to constructor'); }); @@ -74,6 +74,7 @@ assert.deepEqual(group.getObjects('rect'), [rect], 'should return rect only'); assert.deepEqual(group.getObjects('circle'), [circle], 'should return circle only'); + assert.deepEqual(group.getObjects('circle', 'rect'), [rect, circle], 'should return circle and rect, in the same order they are'); }); QUnit.test('add', function(assert) { @@ -83,7 +84,7 @@ rect3 = new fabric.Rect(); assert.ok(typeof group.add === 'function'); - assert.equal(group.add(rect1), group, 'should be chainable'); + group.add(rect1); assert.strictEqual(group.item(group.size() - 1), rect1, 'last object should be newly added one'); assert.equal(group.getObjects().length, 3, 'there should be 3 objects'); @@ -99,7 +100,7 @@ group = new fabric.Group([rect1, rect2, rect3]); assert.ok(typeof group.remove === 'function'); - assert.equal(group.remove(rect2), group, 'should be chainable'); + group.remove(rect2); assert.deepEqual(group.getObjects(), [rect1, rect3], 'should remove object properly'); group.remove(rect1, rect3); @@ -124,7 +125,7 @@ assert.ok(typeof group.set === 'function'); - assert.equal(group.set('opacity', 0.12345), group, 'should be chainable'); + group.set('opacity', 0.12345); assert.equal(group.get('opacity'), 0.12345, 'group\'s "own" property should be set properly'); assert.equal(firstObject.get('opacity'), 1, 'objects\' value of non delegated property should stay same'); @@ -168,6 +169,7 @@ width: 80, height: 60, fill: 'rgb(0,0,0)', + layout: 'fit-content', stroke: null, strokeWidth: 0, strokeDashArray: null, @@ -190,7 +192,9 @@ skewX: 0, skewY: 0, objects: clone.objects, - strokeUniform: false + strokeUniform: false, + subTargetCheck: false, + interactive: false, }; assert.deepEqual(clone, expectedObject); @@ -211,7 +215,7 @@ top: -30, width: 30, height: 10, - strokeWidth: 0 + strokeWidth: 0, }, { version: fabric.version, type: 'rect', @@ -219,7 +223,7 @@ top: -10, width: 10, height: 40, - strokeWidth: 0 + strokeWidth: 0, }]; var expectedObject = { version: fabric.version, @@ -228,7 +232,7 @@ top: 100, width: 80, height: 60, - objects: objects + objects: objects, }; assert.deepEqual(clone, expectedObject); }); @@ -301,38 +305,22 @@ assert.equal(group.complexity(), 2); }); - QUnit.test('destroy', function(assert) { + QUnit.test('removeAll', function(assert) { var group = makeGroupWith2Objects(), firstObject = group.item(0), initialLeftValue = 100, initialTopValue = 100; - assert.ok(typeof group.destroy === 'function'); + assert.ok(typeof group.removeAll === 'function'); assert.ok(initialLeftValue !== firstObject.get('left')); assert.ok(initialTopValue !== firstObject.get('top')); - group.destroy(); + group.removeAll(); assert.equal(firstObject.get('left'), initialLeftValue, 'should restore initial left value'); assert.equal(firstObject.get('top'), initialTopValue, 'should restore initial top value'); }); - QUnit.test('setObjectCoords', function(assert) { - var group = makeGroupWith2Objects(); - - assert.ok(typeof group.setObjectsCoords === 'function'); - - var invokedObjects = []; - group.forEachObject(function(groupObject){ - groupObject.setCoords = function() { - invokedObjects.push(this); - }; - }, this); - - assert.equal(group.setObjectsCoords(), group, 'should be chainable'); - // this.assertEnumEqualUnordered(invokedObjects, group.getObjects(), 'setObjectsCoords should call setCoords on all objects'); - }); - QUnit.test('containsPoint', function(assert) { var group = makeGroupWith2Objects(); @@ -362,7 +350,6 @@ var group = makeGroupWith2Objects(); assert.ok(typeof group.forEachObject === 'function'); - assert.equal(group.forEachObject(function(){}), group, 'should be chainable'); var iteratedObjects = []; group.forEachObject(function(groupObject) { @@ -558,20 +545,60 @@ assert.ok(typeof firstObjInGroup.group === 'undefined'); }); - QUnit.test('insertAt', function(assert) { - var rect1 = new fabric.Rect(), - rect2 = new fabric.Rect(), - group = new fabric.Group(); + QUnit.test('insertAt', function (assert) { + var rect1 = new fabric.Rect({ id: 1 }), + rect2 = new fabric.Rect({ id: 2 }), + rect3 = new fabric.Rect({ id: 3 }), + rect4 = new fabric.Rect({ id: 4 }), + rect5 = new fabric.Rect({ id: 5 }), + rect6 = new fabric.Rect({ id: 6 }), + rect7 = new fabric.Rect({ id: 7 }), + rect8 = new fabric.Rect({ id: 8 }), + group = new fabric.Group(), + control = [], + fired = [], + firingControl = []; group.add(rect1, rect2); + control.push(rect1, rect2); assert.ok(typeof group.insertAt === 'function', 'should respond to `insertAt` method'); - group.insertAt(rect1, 1); - assert.equal(group.item(1), rect1); - group.insertAt(rect2, 2); - assert.equal(group.item(2), rect2); - assert.equal(group.insertAt(rect1, 2), group, 'should be chainable'); + const equalsControl = (description) => { + assert.deepEqual(group.getObjects().map(o => o.id), control.map(o => o.id), 'should equal control array ' + description); + assert.deepEqual(group.getObjects(), control, 'should equal control array ' + description); + assert.deepEqual(fired.map(o => o.id), firingControl.map(o => o.id), 'fired events should equal control array ' + description); + assert.deepEqual(fired, firingControl, 'fired events should equal control array ' + description); + } + + assert.ok(typeof group._onObjectAdded === 'function', 'has a standard _onObjectAdded method'); + [rect1, rect2, rect3, rect4, rect5, rect6, rect7, rect8].forEach(obj => { + obj.on('added', e => { + assert.equal(e.target, group); + fired.push(obj); + }); + }); + + group.insertAt(rect3, 1); + control.splice(1, 0, rect3); + firingControl.push(rect3); + equalsControl('rect3'); + group.insertAt(rect4, 0); + control.splice(0, 0, rect4); + firingControl.push(rect4); + equalsControl('rect4'); + group.insertAt(rect5, 2); + control.splice(2, 0, rect5); + firingControl.push(rect5); + equalsControl('rect5'); + group.insertAt([rect6], 2); + control.splice(2, 0, rect6); + firingControl.push(rect6); + equalsControl('rect6'); + group.insertAt([rect7, rect8], 3); + control.splice(3, 0, rect7, rect8); + firingControl.push(rect7, rect8); + equalsControl('rect7'); }); QUnit.test('dirty flag propagation from children up', function(assert) { @@ -612,7 +639,7 @@ assert.equal(g1.dirty, false, 'Group has no dirty flag set'); }); - QUnit.test('dirty flag propagation from children up with', function(assert) { + QUnit.test('dirty flag propagation from children up with', function (assert) { var g1 = makeGroupWith4Objects(); var obj = g1.item(0); g1.dirty = false; @@ -638,7 +665,7 @@ QUnit.test('test group - pixels.', function(assert) { var rect1 = new fabric.Rect({ top: 1, left: 1, width: 2, height: 2, strokeWidth: 0, fill: 'red', opacity: 1, objectCaching: false}), rect2 = new fabric.Rect({ top: 5, left: 5, width: 2, height: 2, strokeWidth: 0, fill: 'red', opacity: 1, objectCaching: false}), - group = new fabric.Group([rect1, rect2], {opacity: 1, fill: 'blue', strokeWidth: 0, objectCaching: false}), + group = new fabric.Group([rect1, rect2], {opacity: 1, fill: '', strokeWidth: 0, objectCaching: false}), isTransparent = fabric.util.isTransparent, ctx = canvas.contextContainer; canvas.add(group); @@ -654,34 +681,24 @@ assert.equal(isTransparent(ctx, 7, 7, 0), true, '7,7 is transparent'); }); - QUnit.test('group toDatalessObject', function(assert) { - var rect1 = new fabric.Rect({ top: 1, left: 1, width: 2, height: 2, strokeWidth: 0, fill: 'red', opacity: 1, objectCaching: false}), - rect2 = new fabric.Rect({ top: 5, left: 5, width: 2, height: 2, strokeWidth: 0, fill: 'red', opacity: 1, objectCaching: false}), - pathGroup = new fabric.Group([rect1, rect2], { sourcePath: 'sourcePath'}), - group = new fabric.Group([pathGroup]), - dataless = group.toDatalessObject(); - - assert.equal(dataless.objects[0].objects, 'sourcePath', 'the paths have been changed with the sourcePath'); - }); - - QUnit.test('group addWithUpdate', function(assert) { + QUnit.test('group add', function(assert) { var rect1 = new fabric.Rect({ top: 1, left: 1, width: 2, height: 2, strokeWidth: 0, fill: 'red', opacity: 1, objectCaching: false}), rect2 = new fabric.Rect({ top: 5, left: 5, width: 2, height: 2, strokeWidth: 0, fill: 'red', opacity: 1, objectCaching: false}), group = new fabric.Group([rect1]); var coords = group.oCoords; - group.addWithUpdate(rect2); + group.add(rect2); var newCoords = group.oCoords; assert.notEqual(coords, newCoords, 'object coords have been recalculated - add'); }); - QUnit.test('group removeWithUpdate', function(assert) { + QUnit.test('group remove', function(assert) { var rect1 = new fabric.Rect({ top: 1, left: 1, width: 2, height: 2, strokeWidth: 0, fill: 'red', opacity: 1, objectCaching: false}), rect2 = new fabric.Rect({ top: 5, left: 5, width: 2, height: 2, strokeWidth: 0, fill: 'red', opacity: 1, objectCaching: false}), group = new fabric.Group([rect1, rect2]); var coords = group.oCoords; - group.removeWithUpdate(rect2); + group.remove(rect2); var newCoords = group.oCoords; assert.notEqual(coords, newCoords, 'object coords have been recalculated - remove'); }); @@ -759,31 +776,6 @@ }); - QUnit.test('useSetOnGroup', function(assert) { - var rect1 = new fabric.Rect({ top: 1, left: 1, width: 2, height: 2, strokeWidth: 0, fill: 'red', opacity: 1, objectCaching: true}), - rect2 = new fabric.Rect({ top: 5, left: 5, width: 2, height: 2, strokeWidth: 0, fill: 'red', opacity: 1, objectCaching: true}), - group = new fabric.Group([rect1, rect2]); - - var count = 0; - var inspectKey = ''; - var inspectValue = ''; - rect1.setOnGroup = function(key, value) { - count++; - inspectKey = key; - inspectValue = value; - }; - - group.set('fill', 'red'); - assert.equal(count, 0, 'setOnGroup has not been called'); - assert.equal(inspectKey, '', 'setOnGroup has not been called'); - assert.equal(inspectValue, '', 'setOnGroup has not been called'); - group.useSetOnGroup = true; - group.set('fill', 'red'); - assert.equal(count, 1, 'setOnGroup has been called'); - assert.equal(inspectKey, 'fill', 'setOnGroup has been called'); - assert.equal(inspectValue, 'red', 'setOnGroup has been called'); - }); - QUnit.test('canvas prop propagation with set', function(assert) { var rect1 = new fabric.Rect({ top: 1, left: 1, width: 2, height: 2, strokeWidth: 0, fill: 'red', opacity: 1, objectCaching: true}), rect2 = new fabric.Rect({ top: 5, left: 5, width: 2, height: 2, strokeWidth: 0, fill: 'red', opacity: 1, objectCaching: true}), @@ -815,21 +807,21 @@ assert.equal(group.canvas, canvas, 'canvas has been set'); group.add(rect1); assert.equal(group._objects[0].canvas, canvas, 'canvas has been set on object 0'); - group.addWithUpdate(rect2); + group.add(rect2); assert.equal(group._objects[1].canvas, canvas, 'canvas has been set on object 0'); }); - QUnit.test('addWithUpdate and coordinates', function(assert) { + QUnit.test('add and coordinates', function(assert) { var rect1 = new fabric.Rect({ top: 1, left: 1, width: 3, height: 2, strokeWidth: 0, fill: 'red' }), rect2 = new fabric.Rect({ top: 5, left: 5, width: 2, height: 6, angle: 90, strokeWidth: 0, fill: 'red' }), group = new fabric.Group([]); - group.addWithUpdate(rect1); - group.addWithUpdate(rect2); + group.add(rect1); + group.add(rect2); group.left = 5; group.top = 5; group.scaleX = 3; group.scaleY = 2; - group.destroy(); + group.removeAll(); assert.equal(rect1.top, 5, 'top has been moved'); assert.equal(rect1.left, 11, 'left has been moved'); assert.equal(rect1.scaleX, 3, 'scaleX has been scaled'); @@ -840,7 +832,7 @@ assert.equal(rect2.scaleY, 3, 'scaleY has been scaled inverted because of angle 90'); }); - QUnit.test('addWithUpdate and coordinates with nested groups', function(assert) { + QUnit.skip('addRelativeToGroup and coordinates with nested groups', function(assert) { var rect1 = new fabric.Rect({ top: 1, left: 1, width: 3, height: 2, strokeWidth: 0, fill: 'red' }), rect2 = new fabric.Rect({ top: 5, left: 5, width: 2, height: 6, angle: 90, strokeWidth: 0, fill: 'red' }), group0 = new fabric.Group([rect1, rect2]), @@ -850,13 +842,15 @@ group = new fabric.Group([group0, group1], { angle: 90, scaleX: 2, scaleY: 0.5 }), rect5 = new fabric.Rect({ top: 1, left: 1, width: 3, height: 2, strokeWidth: 0, fill: 'red' }); - group1.addWithUpdate(rect5); + group1.addRelativeToGroup(rect5); + var t = group1.calcTransformMatrix(); + var pos = fabric.util.transformPoint(new fabric.Point(rect5.left, rect5.top), t); assert.equal(rect5.top, -5.5, 'top has been moved'); assert.equal(rect5.left, -19.5, 'left has been moved'); assert.equal(rect5.scaleX, 2, 'scaleX has been scaled'); assert.equal(rect5.scaleY, 0.5, 'scaleY has been scaled'); - group.destroy(); - group1.destroy(); + group.removeAll(); + group1.removeAll(); assert.equal(rect5.top, 1, 'top is back to original minus rounding errors'); assert.equal(rect5.left, 1, 'left is back to original'); assert.equal(rect5.scaleX, 1, 'scaleX is back to original'); diff --git a/test/unit/itext.js b/test/unit/itext.js index 3bb0704e8de..d4c27921a89 100644 --- a/test/unit/itext.js +++ b/test/unit/itext.js @@ -299,10 +299,17 @@ assert.deepEqual(_savedProps, iText._savedProps, 'iText saves a copy of important props'); assert.equal(iText.selectable, false, 'selectable is set to false'); assert.equal(iText.hasControls, false, 'hasControls is set to false'); + assert.equal(iText.lockMovementX, true, 'lockMovementX is set to true'); + assert.equal(iText._savedProps.lockMovementX, false, 'lockMovementX is set to false originally'); + iText.set({ hasControls: true, lockMovementX: true }); + assert.equal(iText.hasControls, false, 'hasControls is still set to false'); + assert.equal(iText._savedProps.lockMovementX, true, 'lockMovementX should have been set to true'); iText.exitEditing(); + assert.ok(!iText._savedProps, 'removed ref'); iText.abortCursorAnimation(); assert.equal(iText.selectable, true, 'selectable is set back to true'); assert.equal(iText.hasControls, true, 'hasControls is set back to true'); + assert.equal(iText.lockMovementX, true, 'lockMovementX is set back to true, after changing saved props'); iText.selectable = false; iText.enterEditing(); iText.exitEditing(); diff --git a/test/unit/object.js b/test/unit/object.js index 437178a965a..54807171622 100644 --- a/test/unit/object.js +++ b/test/unit/object.js @@ -425,7 +425,7 @@ assert.equal(cObj.canvas, canvas, 'canvas is the main one step 2'); - activeSel.destroy(); + activeSel.removeAll(); assert.equal(cObj.canvas, canvas, 'canvas is the main one step 3'); @@ -494,6 +494,8 @@ cObj = new fabric.Rect(); assert.ok(cObj.isType('rect')); assert.ok(!cObj.isType('object')); + assert.ok(cObj.isType('object', 'rect')); + assert.ok(!cObj.isType('object', 'circle')); }); QUnit.test('toggle', function(assert) { @@ -806,7 +808,8 @@ canvas.setZoom(3); canvas.add(object); var objectScale = object.getTotalObjectScaling(); - assert.deepEqual(objectScale, { scaleX: object.scaleX * 3, scaleY: object.scaleY * 3 }); + assert.ok(objectScale instanceof fabric.Point); + assert.deepEqual(objectScale, new fabric.Point(object.scaleX * 3, object.scaleY * 3)); }); QUnit.test('getTotalObjectScaling with retina', function(assert) { @@ -815,13 +818,15 @@ fabric.devicePixelRatio = 4; canvas.add(object); var objectScale = object.getTotalObjectScaling(); - assert.deepEqual(objectScale, { scaleX: object.scaleX * 4, scaleY: object.scaleY * 4 }); + assert.ok(objectScale instanceof fabric.Point); + assert.deepEqual(objectScale, new fabric.Point(object.scaleX * 4, object.scaleY * 4)); }); QUnit.test('getObjectScaling', function(assert) { var object = new fabric.Object({ scaleX: 3, scaleY: 2}); var objectScale = object.getObjectScaling(); - assert.deepEqual(objectScale, {scaleX: object.scaleX, scaleY: object.scaleY}); + assert.ok(objectScale instanceof fabric.Point); + assert.deepEqual(objectScale, new fabric.Point(object.scaleX, object.scaleY)); }); QUnit.test('getObjectScaling in group', function(assert) { @@ -831,10 +836,11 @@ group.scaleY = 2; object.group = group; var objectScale = object.getObjectScaling(); - assert.deepEqual(objectScale, { - scaleX: object.scaleX * group.scaleX, - scaleY: object.scaleY * group.scaleY - }); + assert.ok(objectScale instanceof fabric.Point); + assert.deepEqual(objectScale, new fabric.Point( + object.scaleX * group.scaleX, + object.scaleY * group.scaleY + )); }); QUnit.test('getObjectScaling in group with object rotated', function(assert) { @@ -844,12 +850,10 @@ group.scaleY = 3; object.group = group; var objectScale = object.getObjectScaling(); - objectScale.scaleX = objectScale.scaleX.toFixed(3); - objectScale.scaleY = objectScale.scaleY.toFixed(3); - assert.deepEqual(objectScale, { - scaleX: '7.649', - scaleY: '4.707', - }); + assert.deepEqual( + new fabric.Point(Math.round(objectScale.x * 1000) / 1000, Math.round(objectScale.y * 1000) / 1000), + new fabric.Point(7.649, 4.707) + ); }); QUnit.test('dirty flag on set property', function(assert) { diff --git a/test/unit/object_origin.js b/test/unit/object_origin.js index 97d32bbf65d..8c5eac317d8 100644 --- a/test/unit/object_origin.js +++ b/test/unit/object_origin.js @@ -161,39 +161,39 @@ }); - QUnit.test('toLocalPoint', function(assert) { + QUnit.test('normalizePoint', function(assert) { var rect = new fabric.Rect(rectOptions), p, point = new fabric.Point(15, 20); - p = rect.toLocalPoint(point, 'center', 'center'); + p = rect.normalizePoint(point, 'center', 'center'); assert.deepEqual(p, new fabric.Point(-42, -67)); - p = rect.toLocalPoint(point, 'center', 'top'); + p = rect.normalizePoint(point, 'center', 'top'); assert.deepEqual(p, new fabric.Point(-42, -25)); - p = rect.toLocalPoint(point, 'center', 'bottom'); + p = rect.normalizePoint(point, 'center', 'bottom'); assert.deepEqual(p, new fabric.Point(-42, -109)); - p = rect.toLocalPoint(point, 'left', 'center'); + p = rect.normalizePoint(point, 'left', 'center'); assert.deepEqual(p, new fabric.Point(-20, -67)); - p = rect.toLocalPoint(point, 'left', 'top'); + p = rect.normalizePoint(point, 'left', 'top'); assert.deepEqual(p, new fabric.Point(-20, -25)); - p = rect.toLocalPoint(point, 'left', 'bottom'); + p = rect.normalizePoint(point, 'left', 'bottom'); assert.deepEqual(p, new fabric.Point(-20, -109)); - p = rect.toLocalPoint(point, 'right', 'center'); + p = rect.normalizePoint(point, 'right', 'center'); assert.deepEqual(p, new fabric.Point(-64, -67)); - p = rect.toLocalPoint(point, 'right', 'top'); + p = rect.normalizePoint(point, 'right', 'top'); assert.deepEqual(p, new fabric.Point(-64, -25)); - p = rect.toLocalPoint(point, 'right', 'bottom'); + p = rect.normalizePoint(point, 'right', 'bottom'); assert.deepEqual(p, new fabric.Point(-64, -109)); - p = rect.toLocalPoint(point); + p = rect.normalizePoint(point); assert.deepEqual(p, new fabric.Point(-20, -25)); }); @@ -203,34 +203,34 @@ point = new fabric.Point(15, 20); rect.angle = 35; - p = rect.toLocalPoint(point, 'center', 'center'); + p = rect.normalizePoint(point, 'center', 'center'); assert.deepEqual(p, new fabric.Point(-52.72245179455599, -51.00727238020387)); - p = rect.toLocalPoint(point, 'center', 'top'); + p = rect.normalizePoint(point, 'center', 'top'); assert.deepEqual(p, new fabric.Point(-52.72245179455599, -9.007272380203872)); - p = rect.toLocalPoint(point, 'center', 'bottom'); + p = rect.normalizePoint(point, 'center', 'bottom'); assert.deepEqual(p, new fabric.Point(-52.72245179455599, -93.00727238020387)); - p = rect.toLocalPoint(point, 'left', 'center'); + p = rect.normalizePoint(point, 'left', 'center'); assert.deepEqual(p, new fabric.Point(-30.722451794555987, -51.00727238020387)); - p = rect.toLocalPoint(point, 'left', 'top'); + p = rect.normalizePoint(point, 'left', 'top'); assert.deepEqual(p, new fabric.Point(-30.722451794555987, -9.007272380203872)); - p = rect.toLocalPoint(point, 'left', 'bottom'); + p = rect.normalizePoint(point, 'left', 'bottom'); assert.deepEqual(p, new fabric.Point(-30.722451794555987, -93.00727238020387)); - p = rect.toLocalPoint(point, 'right', 'center'); + p = rect.normalizePoint(point, 'right', 'center'); assert.deepEqual(p, new fabric.Point(-74.722451794556, -51.00727238020387)); - p = rect.toLocalPoint(point, 'right', 'top'); + p = rect.normalizePoint(point, 'right', 'top'); assert.deepEqual(p, new fabric.Point(-74.722451794556, -9.007272380203872)); - p = rect.toLocalPoint(point, 'right', 'bottom'); + p = rect.normalizePoint(point, 'right', 'bottom'); assert.deepEqual(p, new fabric.Point(-74.722451794556, -93.00727238020387)); - p = rect.toLocalPoint(point); + p = rect.normalizePoint(point); assert.deepEqual(p, new fabric.Point(-58.791317146942106, -3.9842049203432026)); }); @@ -487,39 +487,39 @@ }); - QUnit.test('toLocalPoint with numeric origins', function(assert) { + QUnit.test('normalizePoint with numeric origins', function(assert) { var rect = new fabric.Rect(rectOptions), p, point = new fabric.Point(15, 20); - p = rect.toLocalPoint(point, 0.5, 0.5); + p = rect.normalizePoint(point, 0.5, 0.5); assert.deepEqual(p, new fabric.Point(-42, -67)); - p = rect.toLocalPoint(point, 0.5, 0); + p = rect.normalizePoint(point, 0.5, 0); assert.deepEqual(p, new fabric.Point(-42, -25)); - p = rect.toLocalPoint(point, 0.5, 1); + p = rect.normalizePoint(point, 0.5, 1); assert.deepEqual(p, new fabric.Point(-42, -109)); - p = rect.toLocalPoint(point, 0, 0.5); + p = rect.normalizePoint(point, 0, 0.5); assert.deepEqual(p, new fabric.Point(-20, -67)); - p = rect.toLocalPoint(point, 0, 0); + p = rect.normalizePoint(point, 0, 0); assert.deepEqual(p, new fabric.Point(-20, -25)); - p = rect.toLocalPoint(point, 0, 1); + p = rect.normalizePoint(point, 0, 1); assert.deepEqual(p, new fabric.Point(-20, -109)); - p = rect.toLocalPoint(point, 1, 0.5); + p = rect.normalizePoint(point, 1, 0.5); assert.deepEqual(p, new fabric.Point(-64, -67)); - p = rect.toLocalPoint(point, 1, 0); + p = rect.normalizePoint(point, 1, 0); assert.deepEqual(p, new fabric.Point(-64, -25)); - p = rect.toLocalPoint(point, 1, 1); + p = rect.normalizePoint(point, 1, 1); assert.deepEqual(p, new fabric.Point(-64, -109)); - p = rect.toLocalPoint(point); + p = rect.normalizePoint(point); assert.deepEqual(p, new fabric.Point(-20, -25)); }); @@ -529,34 +529,34 @@ point = new fabric.Point(15, 20); rect.angle = 35; - p = rect.toLocalPoint(point, 0.5, 0.5); + p = rect.normalizePoint(point, 0.5, 0.5); assert.deepEqual(p, new fabric.Point(-52.72245179455599, -51.00727238020387)); - p = rect.toLocalPoint(point, 0.5, 0); + p = rect.normalizePoint(point, 0.5, 0); assert.deepEqual(p, new fabric.Point(-52.72245179455599, -9.007272380203872)); - p = rect.toLocalPoint(point, 0.5, 1); + p = rect.normalizePoint(point, 0.5, 1); assert.deepEqual(p, new fabric.Point(-52.72245179455599, -93.00727238020387)); - p = rect.toLocalPoint(point, 0, 0.5); + p = rect.normalizePoint(point, 0, 0.5); assert.deepEqual(p, new fabric.Point(-30.722451794555987, -51.00727238020387)); - p = rect.toLocalPoint(point, 0, 0); + p = rect.normalizePoint(point, 0, 0); assert.deepEqual(p, new fabric.Point(-30.722451794555987, -9.007272380203872)); - p = rect.toLocalPoint(point, 0, 1); + p = rect.normalizePoint(point, 0, 1); assert.deepEqual(p, new fabric.Point(-30.722451794555987, -93.00727238020387)); - p = rect.toLocalPoint(point, 1, 0.5); + p = rect.normalizePoint(point, 1, 0.5); assert.deepEqual(p, new fabric.Point(-74.722451794556, -51.00727238020387)); - p = rect.toLocalPoint(point, 1, 0); + p = rect.normalizePoint(point, 1, 0); assert.deepEqual(p, new fabric.Point(-74.722451794556, -9.007272380203872)); - p = rect.toLocalPoint(point, 1, 1); + p = rect.normalizePoint(point, 1, 1); assert.deepEqual(p, new fabric.Point(-74.722451794556, -93.00727238020387)); - p = rect.toLocalPoint(point); + p = rect.normalizePoint(point); assert.deepEqual(p, new fabric.Point(-58.791317146942106, -3.9842049203432026)); }); diff --git a/test/unit/point.js b/test/unit/point.js index fe3effa9de0..c562a59f9d5 100644 --- a/test/unit/point.js +++ b/test/unit/point.js @@ -149,56 +149,76 @@ assert.equal(point.y, y1 - scalar, 'y coords should be added'); }); - QUnit.test('multiply', function(assert) { + QUnit.test('multiply', function (assert) { + var a = new fabric.Point(2, 3), b = new fabric.Point(4, 5); + + assert.ok(typeof a.multiply === 'function'); + var returned = a.multiply(b); + assert.ok(returned instanceof fabric.Point, 'returns a point class'); + assert.equal(returned.x, a.x * b.x, 'should be the product of the x coords'); + assert.equal(returned.y, a.y * b.y, 'should be the product of the y coords'); + }); + + QUnit.test('scalarMultiply', function(assert) { var x1 = 2, y1 = 3, scalar = 3, point = new fabric.Point(x1, y1); - assert.ok(typeof point.multiply === 'function'); + assert.ok(typeof point.scalarMultiply === 'function'); assert.equal(point.x, x1, 'constructor pass x value'); assert.equal(point.y, y1, 'constructor pass y value'); - var returned = point.multiply(scalar); + var returned = point.scalarMultiply(scalar); assert.ok(returned instanceof fabric.Point, 'returns a point class'); assert.notEqual(returned, point, 'is not chainable'); assert.equal(returned.x, x1 * scalar, 'x coords should be added'); assert.equal(returned.y, y1 * scalar, 'y coords should be added'); }); - QUnit.test('multiplyEquals', function(assert) { + QUnit.test('scalarMultiplyEquals', function(assert) { var x1 = 2, y1 = 3, scalar = 3, point = new fabric.Point(x1, y1); - assert.ok(typeof point.multiplyEquals === 'function'); + assert.ok(typeof point.scalarMultiplyEquals === 'function'); assert.equal(point.x, x1, 'constructor pass x value'); assert.equal(point.y, y1, 'constructor pass y value'); - var returned = point.multiplyEquals(scalar); + var returned = point.scalarMultiplyEquals(scalar); assert.ok(returned instanceof fabric.Point, 'returns a point class'); assert.equal(returned, point, 'is chainable'); assert.equal(point.x, x1 * scalar, 'x coords should be added'); assert.equal(point.y, y1 * scalar, 'y coords should be added'); }); - QUnit.test('divide', function(assert) { + QUnit.test('divide', function (assert) { + var a = new fabric.Point(2, 3), b = new fabric.Point(4, 5); + + assert.ok(typeof a.divide === 'function'); + var returned = a.divide(b); + assert.ok(returned instanceof fabric.Point, 'returns a point class'); + assert.equal(returned.x, a.x / b.x, 'should be the quotient of the x coords'); + assert.equal(returned.y, a.y / b.y, 'should be the quotient of the y coords'); + }); + + QUnit.test('scalarDivide', function(assert) { var x1 = 2, y1 = 3, scalar = 3, point = new fabric.Point(x1, y1); - assert.ok(typeof point.divide === 'function'); + assert.ok(typeof point.scalarDivide === 'function'); assert.equal(point.x, x1, 'constructor pass x value'); assert.equal(point.y, y1, 'constructor pass y value'); - var returned = point.divide(scalar); + var returned = point.scalarDivide(scalar); assert.ok(returned instanceof fabric.Point, 'returns a point class'); assert.notEqual(returned, point, 'is not chainable'); assert.equal(returned.x, x1 / scalar, 'x coords should be added'); assert.equal(returned.y, y1 / scalar, 'y coords should be added'); }); - QUnit.test('divideEquals', function(assert) { + QUnit.test('scalarDivideEquals', function(assert) { var x1 = 2, y1 = 3, scalar = 3, point = new fabric.Point(x1, y1); - assert.ok(typeof point.divideEquals === 'function'); + assert.ok(typeof point.scalarDivideEquals === 'function'); assert.equal(point.x, x1, 'constructor pass x value'); assert.equal(point.y, y1, 'constructor pass y value'); - var returned = point.divideEquals(scalar); + var returned = point.scalarDivideEquals(scalar); assert.ok(returned instanceof fabric.Point, 'returns a point class'); assert.equal(returned, point, 'is chainable'); assert.equal(point.x, x1 / scalar, 'x coords should be added'); diff --git a/test/unit/textbox.js b/test/unit/textbox.js index 90f8ff75461..636b48dd5fa 100644 --- a/test/unit/textbox.js +++ b/test/unit/textbox.js @@ -440,58 +440,31 @@ assert.deepEqual(textbox.styles[0], {}, 'style is an empty object'); }); - QUnit.test('_deleteStyleDeclaration', function(assert) { - var textbox = new fabric.Textbox('aaa aaq ggg gg oee eee', { - styles: { - 0: { - 0: { fontSize: 4 }, - 1: { fontSize: 4 }, - 2: { fontSize: 4 }, - 3: { fontSize: 4 }, - 4: { fontSize: 4 }, - 5: { fontSize: 4 }, - 6: { fontSize: 4 }, - 7: { fontSize: 4 }, - 8: { fontSize: 4 }, - 9: { fontSize: 4 }, - 10: { fontSize: 4 }, - 11: { fontSize: 4 }, - 12: { fontSize: 4 }, - 13: { fontSize: 4 }, - 14: { fontSize: 4 }, - 15: { fontSize: 4 }, - 16: { fontSize: 4 }, - }, - }, + QUnit.test('_deleteStyleDeclaration', function (assert) { + var text = 'aaa aaq ggg gg oee eee'; + var styles = {}; + for (var index = 0; index < text.length; index++) { + styles[index] = { fontSize: 4 }; + + } + var textbox = new fabric.Textbox(text, { + styles: { 0: styles }, width: 5, }); + assert.equal(typeof textbox._deleteStyleDeclaration, 'function', 'function exists'); textbox._deleteStyleDeclaration(2, 2); assert.equal(textbox.styles[0][10], undefined, 'style has been removed'); }); QUnit.test('_setStyleDeclaration', function(assert) { - var textbox = new fabric.Textbox('aaa aaq ggg gg oee eee', { - styles: { - 0: { - 0: { fontSize: 4 }, - 1: { fontSize: 4 }, - 2: { fontSize: 4 }, - 3: { fontSize: 4 }, - 4: { fontSize: 4 }, - 5: { fontSize: 4 }, - 6: { fontSize: 4 }, - 7: { fontSize: 4 }, - 8: { fontSize: 4 }, - 9: { fontSize: 4 }, - 10: { fontSize: 4 }, - 11: { fontSize: 4 }, - 12: { fontSize: 4 }, - 13: { fontSize: 4 }, - 14: { fontSize: 4 }, - 15: { fontSize: 4 }, - 16: { fontSize: 4 }, - }, - }, + var text = 'aaa aaq ggg gg oee eee'; + var styles = {}; + for (var index = 0; index < text.length; index++) { + styles[index] = { fontSize: 4 }; + + } + var textbox = new fabric.Textbox(text, { + styles: { 0: styles }, width: 5, }); assert.equal(typeof textbox._setStyleDeclaration, 'function', 'function exists'); diff --git a/test/unit/util.js b/test/unit/util.js index a1d0a52889b..8a224c5f7d4 100644 --- a/test/unit/util.js +++ b/test/unit/util.js @@ -835,6 +835,143 @@ assert.equal(Math.round(tp.y), 8); }); + /** + * + * @param {*} actual + * @param {*} expected + * @param {*} [message] + * @param {number} [error] floating point percision, defaults to 10 + */ + QUnit.assert.matrixIsEqualEnough = function (actual, expected, message, error) { + var error = Math.pow(10, error ? -error : -10); + this.pushResult({ + result: actual.every((x, i) => Math.abs(x - expected[i]) < error), + actual: actual, + expected: expected, + message: message + }) + } + + QUnit.test('sendPointToPlane', function (assert) { + assert.ok(typeof fabric.util.sendPointToPlane === 'function'); + var m1 = [3, 0, 0, 2, 10, 4], + m2 = [1, 2, 3, 4, 5, 6], + p, t, + obj1 = new fabric.Object(), + obj2 = new fabric.Object(), + point = new fabric.Point(2, 2), + applyTransformToObject = fabric.util.applyTransformToObject, + invert = fabric.util.invertTransform, + multiply = fabric.util.multiplyTransformMatrices, + transformPoint = fabric.util.transformPoint; + + function sendPointToPlane(point, from, to, relationFrom, relationTo) { + return fabric.util.sendPointToPlane( + point, + from ? + relationFrom === 'child' ? from.calcTransformMatrix() : from.group?.calcTransformMatrix() : + null, + to ? + relationTo === 'child' ? to.calcTransformMatrix() : to.group?.calcTransformMatrix() : + null + ); + } + + applyTransformToObject(obj1, m1); + applyTransformToObject(obj2, m2); + obj1.group = new fabric.Object(); + obj2.group = new fabric.Object(); + applyTransformToObject(obj1.group, m1); + applyTransformToObject(obj2.group, m2); + p = sendPointToPlane(point, obj1, obj2, 'child', 'child'); + t = multiply(invert(obj2.calcTransformMatrix()), obj1.calcTransformMatrix()); + assert.deepEqual(p, transformPoint(point, t)); + p = sendPointToPlane(point, obj1, obj2, 'sibling', 'child'); + t = multiply(invert(obj2.calcTransformMatrix()), obj1.group.calcTransformMatrix()); + assert.deepEqual(p, transformPoint(point, t)); + p = sendPointToPlane(point, obj1, obj2, 'child', 'sibling'); + t = multiply(invert(obj2.group.calcTransformMatrix()), obj1.calcTransformMatrix()); + assert.deepEqual(p, transformPoint(point, t)); + p = sendPointToPlane(point, obj1, obj2, 'sibling', 'sibling'); + t = multiply(invert(obj2.group.calcTransformMatrix()), obj1.group.calcTransformMatrix()); + assert.deepEqual(p, transformPoint(point, t)); + p = sendPointToPlane(point, null, obj2, null, 'sibling'); + t = invert(obj2.group.calcTransformMatrix()); + assert.deepEqual(p, transformPoint(point, t)); + + var obj = new fabric.Rect({ left: 20, top: 20, width: 60, height: 60, strokeWidth: 0 }); + var group = new fabric.Group([obj], { strokeWidth: 0 }); + var sentPoint = sendPointToPlane(new fabric.Point(50, 50), null, obj, null, 'sibling'); + assert.deepEqual(sentPoint, new fabric.Point(0, 0)); + sentPoint = sendPointToPlane(new fabric.Point(50, 50), null, group, null, 'child'); + assert.deepEqual(sentPoint, new fabric.Point(0, 0)); + group.scaleX = 2; + sentPoint = sendPointToPlane(new fabric.Point(80, 50), null, obj, null, 'sibling'); + assert.deepEqual(sentPoint, new fabric.Point(0, 0)); + sentPoint = sendPointToPlane(new fabric.Point(80, 50), null, group, null, 'child'); + assert.deepEqual(sentPoint, new fabric.Point(0, 0)); + assert.deepEqual(sendPointToPlane(point), point, 'sending to nowhere, point remains unchanged'); + }); + + QUnit.test('transformPointRelativeToCanvas', function(assert) { + assert.ok(typeof fabric.util.transformPointRelativeToCanvas === 'function'); + var point = new fabric.Point(2, 2); + var matrix = [3, 0, 0, 2, 10, 4]; + var canvas = { + viewportTransform: matrix + } + var transformPoint = fabric.util.transformPoint; + var invertTransform = fabric.util.invertTransform; + var transformPointRelativeToCanvas = fabric.util.transformPointRelativeToCanvas; + var p = transformPointRelativeToCanvas(point, canvas, 'sibling', 'child'); + assert.deepEqual(p, transformPoint(point, invertTransform(matrix))); + p = transformPointRelativeToCanvas(point, canvas, 'child', 'sibling'); + assert.deepEqual(p, transformPoint(point, matrix)); + p = transformPointRelativeToCanvas(point, canvas, 'child', 'child'); + assert.deepEqual(p, point); + p = transformPointRelativeToCanvas(point, canvas, 'sibling', 'sibling'); + assert.deepEqual(p, point); + assert.throws(function () { + transformPointRelativeToCanvas(point, canvas, 'sibling'); + }); + assert.throws(function () { + transformPointRelativeToCanvas(point, canvas, 'sibling', true); + }); + assert.throws(function () { + transformPointRelativeToCanvas(point, canvas, 'sibling', 'chil'); + }); + }); + + QUnit.test('sendObjectToPlane', function (assert) { + assert.ok(typeof fabric.util.sendObjectToPlane === 'function'); + var m = [6, Math.SQRT1_2, 0, 3, 2, 1], + m1 = [3, 0, 0, 2, 10, 4], + m2 = [1, Math.SQRT1_2, Math.SQRT1_2, 4, 5, 6], + actual, expected, + obj1 = new fabric.Object(), + obj2 = new fabric.Object(), + obj = new fabric.Object(), + sendObjectToPlane = fabric.util.sendObjectToPlane, + applyTransformToObject = fabric.util.applyTransformToObject, + invert = fabric.util.invertTransform, + multiply = fabric.util.multiplyTransformMatrices; + // silence group check + obj1.isOnACache = () => false; + + applyTransformToObject(obj, m); + applyTransformToObject(obj1, m1); + applyTransformToObject(obj2, m2); + obj.group = obj1; + actual = sendObjectToPlane(obj, obj1.calcTransformMatrix(), obj2.calcTransformMatrix()); + expected = multiply(invert(obj2.calcTransformMatrix()), obj1.calcTransformMatrix()); + assert.matrixIsEqualEnough(actual, expected); + assert.matrixIsEqualEnough(obj.calcOwnMatrix(), multiply(actual, m)); + obj.group = obj2; + assert.matrixIsEqualEnough(obj.calcTransformMatrix(), multiply(multiply(obj2.calcTransformMatrix(), actual), m)); + assert.deepEqual(sendObjectToPlane(obj2), fabric.iMatrix, 'sending to nowhere, no transform was applied'); + assert.matrixIsEqualEnough(obj2.calcOwnMatrix(), m2, 'sending to nowhere, no transform was applied'); + }); + QUnit.test('makeBoundingBoxFromPoints', function(assert) { assert.ok(typeof fabric.util.makeBoundingBoxFromPoints === 'function'); }); diff --git a/test/visual/control_rendering.js b/test/visual/control_rendering.js index 6d4836f84f8..6733eee529d 100644 --- a/test/visual/control_rendering.js +++ b/test/visual/control_rendering.js @@ -318,5 +318,39 @@ fabricClass: 'Canvas', }); + function controlBoxes2(canvas, callback) { + canvas.loadFromJSON('{"version":"5.2.0","objects":[{"type":"rect","version":"5.2.0","left":38,"top":201,"width":150,"height":150,"fill":"red","skewX":0.15,"skewY":36},{"type":"rect","version":"5.2.0","left":20,"top":2,"width":150,"height":150,"fill":"#020aed","scaleX":1.24,"scaleY":0.81,"angle":35.95,"skewX":25.46},{"type":"group","version":"5.2.0","left":152.65,"top":21,"width":320.4,"height":335.5,"scaleX":0.75,"skewY":24.57,"objects":[{"type":"rect","version":"5.2.0","left":-29.85,"top":-167.75,"width":150,"height":150,"fill":"green","angle":30,"skewX":14.71,"skewY":36},{"type":"rect","version":"5.2.0","left":-29.85,"top":-167.75,"width":150,"height":150,"fill":"yellow","angle":45,"skewX":14.71}]},{"type":"group","version":"5.2.0","left":329.65,"top":65,"width":320.4,"height":335.5,"scaleX":1.29,"scaleY":1.29,"objects":[{"type":"rect","version":"5.2.0","left":-29.85,"top":-167.75,"width":150,"height":150,"fill":"purple","angle":30,"skewX":14.71,"skewY":36},{"type":"rect","version":"5.2.0","left":-29.85,"top":-167.75,"width":150,"height":150,"fill":"pink","angle":45,"skewX":14.71}]}]}') + .then(function() { + canvas.renderAll(); + canvas.getObjects().forEach(function(object) { + object.borderScaleFactor = 3; + object.transparentCorners = false; + object._renderControls(canvas.contextContainer, { + borderColor: object.fill, + cornerColor: object.fill, + }); + object._objects && object.getObjects().forEach(function(subTarget) { + subTarget.borderScaleFactor = 3; + subTarget.transparentCorners = false; + subTarget._renderControls(canvas.contextContainer, { + borderColor: subTarget.fill, + cornerColor: subTarget.fill, + }); + }); + }); + callback(canvas.lowerCanvasEl); + }); + } + + tests.push({ + test: 'controlboxes with skewY, green is wrong and needs fix', + code: controlBoxes2, + golden: 'controls13.png', + percentage: 0.002, + width: 700, + height: 600, + fabricClass: 'Canvas', + }); + tests.forEach(visualTestLoop(QUnit)); })(); diff --git a/test/visual/generic_rendering.js b/test/visual/generic_rendering.js index 7b20b3eb666..9d17b981fde 100644 --- a/test/visual/generic_rendering.js +++ b/test/visual/generic_rendering.js @@ -65,6 +65,50 @@ height: 60, }); + function renderStrokeWithNegativeScale(canvas, callback) { + var rect = new fabric.Rect({ + width: 10, + height: 10, + fill: 'transparent', + stroke: 'blue', + strokeWidth: 15, + strokeUniform: true, + strokeDashArray: [2, 2], + top: 65, + left: 30, + }); + // do not do this at init time or they will be positive + rect.scaleX = -2; + rect.scaleY = -4; + + var rect2 = new fabric.Rect({ + width: 10, + height: 10, + fill: 'transparent', + stroke: 'red', + strokeWidth: 15, + scaleX: -2, + scaleY: -4, + strokeDashArray: [2, 2], + strokeUniform: true, + top: 10, + left: 55, + }); + canvas.add(rect, rect2); + canvas.renderAll(); + callback(canvas.lowerCanvasEl); + } + + tests.push({ + test: 'Rect with strokeUniform: true and negative scaling', + code: renderStrokeWithNegativeScale, + golden: 'strokeNegativeScale.png', + percentage: 0.011, + disabled: fabric.isLikelyNode, + width: 100, + height: 100, + }); + function shadownonscaling(canvas, callback) { var obj = new fabric.Rect(); obj.set({ diff --git a/test/visual/golden/controls13.png b/test/visual/golden/controls13.png new file mode 100644 index 00000000000..6d5407e0121 Binary files /dev/null and b/test/visual/golden/controls13.png differ diff --git a/test/visual/golden/dataurl4_filter.png b/test/visual/golden/dataurl4_filter.png new file mode 100644 index 00000000000..87024adb11f Binary files /dev/null and b/test/visual/golden/dataurl4_filter.png differ diff --git a/test/visual/golden/group-layout/clip-path.png b/test/visual/golden/group-layout/clip-path.png new file mode 100644 index 00000000000..1dae5fcca2e Binary files /dev/null and b/test/visual/golden/group-layout/clip-path.png differ diff --git a/test/visual/golden/group-layout/clip-path1.png b/test/visual/golden/group-layout/clip-path1.png new file mode 100644 index 00000000000..1ee7e86807b Binary files /dev/null and b/test/visual/golden/group-layout/clip-path1.png differ diff --git a/test/visual/golden/group-layout/clip-path2.png b/test/visual/golden/group-layout/clip-path2.png new file mode 100644 index 00000000000..725b97138dc Binary files /dev/null and b/test/visual/golden/group-layout/clip-path2.png differ diff --git a/test/visual/golden/group-layout/clip-path3.png b/test/visual/golden/group-layout/clip-path3.png new file mode 100644 index 00000000000..46bd0fae243 Binary files /dev/null and b/test/visual/golden/group-layout/clip-path3.png differ diff --git a/test/visual/golden/group-layout/fit-content.png b/test/visual/golden/group-layout/fit-content.png new file mode 100644 index 00000000000..2f76f72e311 Binary files /dev/null and b/test/visual/golden/group-layout/fit-content.png differ diff --git a/test/visual/golden/group-layout/fit-content2.png b/test/visual/golden/group-layout/fit-content2.png new file mode 100644 index 00000000000..574a3e47a5e Binary files /dev/null and b/test/visual/golden/group-layout/fit-content2.png differ diff --git a/test/visual/golden/group-layout/fit-content3.png b/test/visual/golden/group-layout/fit-content3.png new file mode 100644 index 00000000000..17f052c7edb Binary files /dev/null and b/test/visual/golden/group-layout/fit-content3.png differ diff --git a/test/visual/golden/group-layout/unit-test-scene.png b/test/visual/golden/group-layout/unit-test-scene.png new file mode 100644 index 00000000000..c59ed8dd1fa Binary files /dev/null and b/test/visual/golden/group-layout/unit-test-scene.png differ diff --git a/test/visual/golden/strokeNegativeScale.png b/test/visual/golden/strokeNegativeScale.png new file mode 100644 index 00000000000..ab212569acd Binary files /dev/null and b/test/visual/golden/strokeNegativeScale.png differ diff --git a/test/visual/group_layout.js b/test/visual/group_layout.js new file mode 100644 index 00000000000..ebdae56cf34 --- /dev/null +++ b/test/visual/group_layout.js @@ -0,0 +1,338 @@ +(function () { + fabric.Object.prototype.objectCaching = true; + var visualTestLoop; + if (fabric.isLikelyNode) { + visualTestLoop = global.visualTestLoop; + } + else { + visualTestLoop = window.visualTestLoop; + } + + var tests = []; + + function createGroupForLayoutTests(text, options) { + var circle = new fabric.Circle({ + left: 100, + top: 50, + radius: 50 + }); + var itext = new fabric.IText(text, { + left: 100, + top: 150 + }); + var rect = new fabric.Rect({ + top: 200, + width: 50, + height: 50, + fill: 'red', + opacity: 0.3 + }) + return new fabric.Group([ + rect, + circle, + itext + ], options); + } + + function fixedLayout(canvas, callback) { + var g = createGroupForLayoutTests('fixed layout', { + backgroundColor: 'azure', + layout: 'fixed', + width: 50, + height: 50, + angle: 30 + }); + canvas.add(g); + canvas.renderAll(); + callback(canvas.lowerCanvasEl); + } +/* + tests.push({ + test: 'fixed layout with width, height, angle values', + code: fixedLayout, + golden: 'group-fixed-layout.png', + newModule: 'Group Layout', + percentage: 0.06, + width: 400, + height: 300 + }); +*/ + function fitContentLayout(canvas, callback) { + var g = createGroupForLayoutTests('fit-content layout', { + backgroundColor: 'blue' + }); + canvas.add(g); + canvas.renderAll(); + callback(canvas.lowerCanvasEl); + } + + tests.push({ + test: 'fit-content layout', + code: fitContentLayout, + golden: 'group-layout/fit-content.png', + newModule: 'Group Layout', + percentage: 0.06, + width: 400, + height: 300 + }); + + function fitContentReLayout(canvas, callback) { + var g = createGroupForLayoutTests('fit-content layout', { + backgroundColor: 'blue' + }); + var objects = g.removeAll(); + // layout + objects.forEach(o => g.add(o)); + canvas.add(g); + canvas.renderAll(); + callback(canvas.lowerCanvasEl); + } + + tests.push({ + test: 'fit-content relayout', + code: fitContentReLayout, + golden: 'group-layout/fit-content.png', + percentage: 0.06, + width: 400, + height: 300 + }); + + function nestedLayout(canvas, callback) { + var rect3 = new fabric.Rect({ + width: 100, + height: 100, + fill: 'yellow' + }); + var rect4 = new fabric.Rect({ + width: 100, + height: 100, + left: 100, + top: 100, + fill: 'purple' + }); + var group3 = new fabric.Group( + [rect3, rect4], + { scaleX: 0.5, scaleY: 0.5, top: 100, left: 0 }); + group3.subTargetCheck = true; + group3.setCoords(); + var rect1 = new fabric.Rect({ + width: 100, + height: 100, + fill: 'red' + }); + var rect2 = new fabric.Rect({ + width: 100, + height: 100, + left: 100, + top: 100, + fill: 'blue' + }); + var g = new fabric.Group([rect1, rect2, group3], { top: -150, left: -50 }); + g.subTargetCheck = true; + canvas.viewportTransform = [0.1, 0, 0, 0.1, 100, 200]; + canvas.add(g); + canvas.renderAll(); + callback(canvas.lowerCanvasEl); + } + + tests.push({ + test: 'unit test scene', + code: nestedLayout, + golden: 'group-layout/unit-test-scene.png', + percentage: 0.01, + width: 120, + height: 210 + }); + + function fitContentLayoutChange(canvas, callback) { + var g = createGroupForLayoutTests('fit-content layout', { + backgroundColor: 'blue' + }); + var point = fabric.util.transformPoint( + new fabric.Point(50, 0), + fabric.util.invertTransform(g.calcTransformMatrix()) + ); + g.item(0).set({ left: point.x }); + g.item(1).set({ skewX: -45 }); + g.item(2).rotate(45); + g.triggerLayout(); + canvas.add(g); + canvas.renderAll(); + callback(canvas.lowerCanvasEl); + } + + tests.push({ + test: 'fit-content layout after change', + code: fitContentLayoutChange, + golden: 'group-layout/fit-content2.png', + percentage: 0.06, + width: 400, + height: 300 + }); + + function fitContentLayoutAdd(canvas, callback) { + var g = createGroupForLayoutTests('fit-content layout', { + backgroundColor: 'blue' + }); + var rect = new fabric.Rect({ + top: 200, + left: 50, + width: 50, + height: 50, + fill: 'red', + angle: 15, + skewY: 30 + }) + g.add(rect); + canvas.add(g); + canvas.renderAll(); + callback(canvas.lowerCanvasEl); + } + + tests.push({ + test: 'fit-content layout add object', + code: fitContentLayoutAdd, + golden: 'group-layout/fit-content3.png', + newModule: 'Group Layout', + percentage: 0.06, + width: 400, + height: 300 + }); + + function clipPathLayout(canvas, callback) { + var g = createGroupForLayoutTests('clip path layout', { + backgroundColor: 'magenta', + clipPath: new fabric.Circle({ + radius: 110, + originX: 'center', + originY: 'center', + }), + layout: 'clip-path' + }); + canvas.add(g); + canvas.renderAll(); + callback(canvas.lowerCanvasEl); + } + + tests.push({ + test: 'clip-path layout', + code: clipPathLayout, + golden: 'group-layout/clip-path.png', + percentage: 0.06, + width: 300, + height: 300 + }); + + function clipPathLayoutWithScale(canvas, callback) { + var g = createGroupForLayoutTests('clip path layout', { + backgroundColor: 'magenta', + clipPath: new fabric.Circle({ + radius: 110, + originX: 'center', + originY: 'center', + scaleX: 0.6 + }), + layout: 'clip-path' + }); + canvas.add(g); + canvas.renderAll(); + callback(canvas.lowerCanvasEl); + } + + tests.push({ + test: 'clip-path layout scaleX value', + code: clipPathLayoutWithScale, + golden: 'group-layout/clip-path1.png', + percentage: 0.06, + width: 300, + height: 300 + }); + + function clipPathLayout2(canvas, callback) { + var g = createGroupForLayoutTests('clip path layout', { + backgroundColor: 'magenta', + clipPath: new fabric.Circle({ + radius: 110, + left: -150, + top: -100, + scaleX: 1.5, + }), + layout: 'clip-path' + }); + canvas.add(g); + canvas.renderAll(); + callback(canvas.lowerCanvasEl); + } + + tests.push({ + test: 'clip-path layout left, top, originX, originY, scaleX values - WRONG', + code: clipPathLayout2, + golden: 'group-layout/clip-path2.png', + percentage: 0.06, + width: 330, + height: 330 + }); + + function absClipPathLayout(canvas, callback) { + var g = createGroupForLayoutTests('clip path layout', { + backgroundColor: '#0dcaf0', + clipPath: new fabric.Circle({ + radius: 110, + originX: 'center', + originY: 'center', + absolutePositioned: true, + left: 50, + top: 150, + skewX: 20 + }), + layout: 'clip-path', + }); + canvas.add(g); + canvas.renderAll(); + callback(canvas.lowerCanvasEl); + } + + tests.push({ + test: 'absolute clip-path layout', + code: absClipPathLayout, + golden: 'group-layout/clip-path3.png', + percentage: 0.06, + width: 250, + height: 250 + }); + + function createObjectsForOriginTests(originX, originY, options) { + var rect1 = new fabric.Rect({ top: 100, left: 150, width: 30, height: 10, strokeWidth: 0 }), + rect2 = new fabric.Rect({ top: 120, left: 200, width: 10, height: 40, strokeWidth: 0 }), + controlPoint = new fabric.Circle({ radius: 5, fill: 'blue', left: 150, top: 100, originX: 'center', originY: 'center' }); + + var g = new fabric.Group([rect1, rect2], Object.assign({}, options, { + originX, originY, strokeWidth: 1, stroke: 'blue' + })); + return [controlPoint, g]; + } + + var originX = ['left', 'center', 'right']; + var originY = ['top', 'center', 'bottom']; +/* + for (let angle = 0; angle < 360; angle += 30) { + originX.forEach(ox => { + originY.forEach(oy => { + tests.push({ + test: `layout with originX=${ox}, originY=${oy} and angle=${angle} values - angle is WRONG`, + code: function (canvas, callback) { + canvas.add(...createObjectsForOriginTests(ox, oy, { angle })); + canvas.renderAll(); + callback(canvas.lowerCanvasEl); + }, + golden: `group-layout/origin-${ox}-${oy}-${angle}deg.png`, + percentage: 0.06, + width: 300, + height: 300 + }); + }); + }); + } +*/ + // tests.forEach(visualTestLoop(QUnit)); +})(); diff --git a/test/visual/svg_import.js b/test/visual/svg_import.js index d58b58db094..7230d11e593 100644 --- a/test/visual/svg_import.js +++ b/test/visual/svg_import.js @@ -22,10 +22,10 @@ fabric.loadSVGFromString(string, function(objects, options) { // something is disabling objectCaching and i cannot find where it is. var group = fabric.util.groupSVGElements(objects, options); + canvas.setDimensions({ width: group.width + group.left, height: group.height + group.top }); group.includeDefaultValues = false; canvas.includeDefaultValues = false; canvas.add(group); - canvas.setDimensions({ width: group.width + group.left, height: group.height + group.top }); canvas.renderAll(); callback(canvas.lowerCanvasEl); }); diff --git a/test/visual/toDataURL.js b/test/visual/toDataURL.js index 24fcd8668ad..c94b04402be 100644 --- a/test/visual/toDataURL.js +++ b/test/visual/toDataURL.js @@ -429,6 +429,22 @@ height: 100, }); + function toDataURLWithFilter(fabricCanvas, callback) { + fabricCanvas.loadFromJSON(canvasWithObjects).then(function () { + var dataurl = fabricCanvas.toDataURL({ filter: object => object.isType('polygon', 'rect') }); + callback(dataurl); + }); + } + + tests.push({ + test: 'Canvas toDataURL with filtered objects', + code: toDataURLWithFilter, + golden: 'dataurl4_filter.png', + percentage: 0.09, + width: 800, + height: 600, + }); + function testWrapper(test) { var actualTest = test.code; test.code = function(fabricCanvas, callback) { diff --git a/testem-visual.json b/testem-visual.json index 7f44cfae6a3..9b37ae1309b 100644 --- a/testem-visual.json +++ b/testem-visual.json @@ -27,7 +27,7 @@ ], "launchers": { "Node": { - "command": "npm run test:visual", + "command": "npm run test -- --suite visual", "protocol": "tap" } }, diff --git a/testem.json b/testem.json index 113e95b028e..ee21646a6a0 100644 --- a/testem.json +++ b/testem.json @@ -25,7 +25,7 @@ ], "launchers": { "Node": { - "command": "npm run test", + "command": "npm run test -- --suite unit", "protocol": "tap" } },