diff --git a/.browserslistrc b/.browserslistrc new file mode 100644 index 000000000..f13183ef8 --- /dev/null +++ b/.browserslistrc @@ -0,0 +1,2 @@ +defaults and fully supports es6-module +node >= 16 \ No newline at end of file diff --git a/.codeclimate.yml b/.codeclimate.yml new file mode 100644 index 000000000..657ec0641 --- /dev/null +++ b/.codeclimate.yml @@ -0,0 +1,25 @@ +plugins: + eslint: + enabled: false + channel: eslint-6 + extensions: + - .ts + - .js + duplication: + enabled: false + config: + languages: + - javascript + - typescript + +ratings: + paths: + - "packages/**/*.ts" + +exclude_paths: + - "**/examples/" + - "**/build/" + - "**/docs/" + - "**/_*" + - "**/*.spec.ts" + - "**/*.d.ts" diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000..ee5c3201a --- /dev/null +++ b/.eslintignore @@ -0,0 +1,4 @@ +dist +coverage +**/node_modules/** +_angular diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 000000000..3eda457dc --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,109 @@ +module.exports = { + extends: [ + 'plugin:import/errors', + 'plugin:import/warnings', + 'plugin:import/typescript', + 'plugin:react/all', + 'standard', + 'prettier', + ], + settings: { + 'import/resolver': { typescript: null }, + react: { version: '16' }, + }, + env: { + commonjs: true, + es6: true, + node: true, + }, + parser: '@typescript-eslint/parser', + parserOptions: { + sourceType: 'module', + ecmaVersion: 2020, + }, + plugins: ['@typescript-eslint', 'eslint-plugin-tsdoc', 'markdown'], + globals: { + globalThis: false, + }, + rules: { + 'linebreak-style': ['error', 'unix'], + 'lines-between-class-members': 'off', + 'no-caller': 'error', + 'no-console': 'off', + 'no-empty': 'off', + 'no-prototype-builtins': 'off', + 'no-shadow': 'error', + 'no-useless-constructor': 'off', + 'no-var': 'error', + 'import/no-extraneous-dependencies': ['error', { devDependencies: false }], + 'import/order': [ + 'error', + { + alphabetize: { order: 'asc', caseInsensitive: true }, + 'newlines-between': 'always', + groups: ['builtin', 'external', 'internal', 'parent', 'index', 'sibling'], + pathGroups: [{ pattern: '@interactjs/**', group: 'internal' }], + }, + ], + 'operator-linebreak': 'off', + 'prefer-arrow-callback': ['error', { allowNamedFunctions: true }], + 'prefer-const': 'error', + 'standard/array-bracket-even-spacing': 'off', + 'standard/computed-property-even-spacing': 'off', + 'standard/object-curly-even-spacing': 'off', + 'tsdoc/syntax': 'warn', + '@typescript-eslint/array-type': ['error', { default: 'array-simple' }], + '@typescript-eslint/consistent-type-imports': 'error', + '@typescript-eslint/explicit-member-accessibility': 'off', + '@typescript-eslint/member-accessibility': 'off', + '@typescript-eslint/no-empty-interface': 'error', + '@typescript-eslint/no-inferrable-types': 'error', + '@typescript-eslint/no-use-before-define': 'off', + }, + overrides: [ + { + files: '*.{ts{,x},vue}', + rules: { + 'import/named': 'off', + 'import/no-named-as-default': 'off', + 'import/no-unresolved': 'off', + 'no-redeclare': 'off', + 'no-shadow': 'off', + 'no-undef': 'off', + 'no-unused-vars': 'off', + 'no-use-before-define': 'off', + }, + }, + { + files: '{,.md/}*.vue', + extends: ['plugin:vue/vue3-essential'], + parserOptions: { parser: '@typescript-eslint/parser' }, + }, + { + files: '*.spec.ts', + extends: ['plugin:jest/recommended', 'plugin:jest/style'], + rules: { + 'array-bracket-spacing': 'off', + 'import/no-extraneous-dependencies': 'off', + 'jest/consistent-test-it': ['error', { fn: 'test' }], + }, + }, + { files: '**/*.md', processor: 'markdown/markdown' }, + { + files: '**/*.md/*.{{ts,js}{,x},vue}', + rules: { + 'arrow-parens': 'off', + 'import/no-named-as-default': 'off', + 'import/no-unresolved': 'off', + 'no-console': 'off', + 'no-redeclare': 'off', + 'no-shadow': 'off', + 'no-undef': 'off', + 'no-unused-vars': 'off', + 'no-use-before-define': 'off', + 'no-var': 'off', + 'prefer-arrow-callback': 'off', + }, + }, + ], +} diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 000000000..d70c4b5ed --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,19 @@ +# Number of days of inactivity before an issue becomes stale +daysUntilStale: 14 +# Number of days of inactivity before a stale issue is closed +daysUntilClose: 7 +# Issues with these labels will never be considered stale +exemptLabels: + - pinned + - security +only: issues +# Label to use when marking an issue as stale +staleLabel: stale +# Comment to post when marking an issue as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. +# Comment to post when closing a stale issue. Set to `false` to disable +closeComment: false +exemptProjects: true diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 000000000..3dc13be0a --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,28 @@ +on: + push: + branches: + - main + - next + +jobs: + test: + uses: ./.github/workflows/test.yml + + publish-npm: + name: '๐Ÿ“ฆ Build and Publish ๐Ÿš€' + needs: [test] + runs-on: ubuntu-latest + environment: production + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + registry-url: https://registry.npmjs.org/ + cache: yarn + - name: โš™ bootstrap + run: 'npm run bootstrap && git fetch --tags' + - name: ๐Ÿ“ฆ build and publish ๐Ÿš€ + run: npx _release + env: + NODE_AUTH_TOKEN: ${{secrets.npm_token}} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 000000000..bf05e68af --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,20 @@ +on: + pull_request: + workflow_dispatch: + workflow_call: + +jobs: + test: + name: '๐Ÿงช Test' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: ianwalter/playwright-container@43940dfa7d309fe3569b9df407ae9e84dcbf2e7f + - name: โš™ bootstrap + run: 'npm run bootstrap && npx _check_deps && npx _add_plugin_indexes' + - name: ๐Ÿ“ types + run: npx tsc -b -f + - name: ๐Ÿ” lint + run: npx _lint + - name: ๐Ÿงช tests + run: npm test diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..ff3700d55 --- /dev/null +++ b/.gitignore @@ -0,0 +1,30 @@ +*.d.ts +*.d.ts.map +!interactjs/index.d.ts +!shims.d.ts +packages/@interactjs/**/index.ts +packages/@interactjs/*/use/**/*.ts +!packages/@interactjs/types/index.ts +!packages/@interactjs/interact/index.ts +!packages/@interactjs/interactjs/index.ts +!packages/@interactjs/rebound/index.ts +packages/**/*.js +packages/**/*.js.map +!packages/@interactjs/dev-tools/babel-plugin-prod.js +node_modules +!test/fixtures/**/node_modules +dist +.projectroot +.env +.envrc +.nyc_output +yarn-error.log +.yarn-cache +.pnpm-store +npm-debug.log +pnpm-debug.log +coverage +cc-test-reporter +lerna-debug.log +.vim +.cjsescache \ No newline at end of file diff --git a/.husky/.gitignore b/.husky/.gitignore new file mode 100644 index 000000000..31354ec13 --- /dev/null +++ b/.husky/.gitignore @@ -0,0 +1 @@ +_ diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 000000000..d2ae35e84 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +yarn lint-staged diff --git a/.npmignore b/.npmignore index 0ac2dd313..d5cdeb8b3 100644 --- a/.npmignore +++ b/.npmignore @@ -1,5 +1,7 @@ -.git* -demo/ -docs/ -img/ -test/ +*.ts +!*.d.ts +*.map.* +*.spec.ts +*.spec.js +dist/docs +guide diff --git a/.npmrc b/.npmrc new file mode 100644 index 000000000..13e187a19 --- /dev/null +++ b/.npmrc @@ -0,0 +1,3 @@ +link-workspace-packages = true +shared-workspace-lockfile = true +prefer-frozen-lockfile = true diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 000000000..790e1105f --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +v20.10.0 diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..5bcb6fe81 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,3 @@ +dist +coverage +node_modules diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 000000000..ce263c63e --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,6 @@ +{ + "printWidth": 110, + "semi": false, + "singleQuote": true, + "trailingComma": "all" +} diff --git a/.stylelintrc.cjs b/.stylelintrc.cjs new file mode 100644 index 000000000..365cb852f --- /dev/null +++ b/.stylelintrc.cjs @@ -0,0 +1,4 @@ +module.exports = { + extends: ['stylelint-config-standard', 'stylelint-config-recess-order', 'stylelint-config-css-modules'], + ignoreFiles: ['dist/**/*', 'coverage/**/*'], +} diff --git a/.yarnrc b/.yarnrc new file mode 100644 index 000000000..142dad817 --- /dev/null +++ b/.yarnrc @@ -0,0 +1 @@ +registry "https://registry.npmjs.org" diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..b48a59f2b --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,847 @@ +## v1.10.27 + +- fix(types): fix issues with `skipLibCheck: false` + +## v1.10.26 + +- fix: improve build; check output for ES2018 compatibility + +## v1.10.25 + +- fix: bundle to ES5 syntax + +## v1.10.24 + +- chore: generate api docs + +## v1.10.23 + +- fix: transform nullish coalescing; fix symbol-tree build + +## v1.10.22 + +- fix(actions/gesture): fix error when inertia is enabled for gestures #995 + +## v1.10.21 + +- fix(actions/drop): fix regression with drop event targets #1016 + +## v1.10.20 + +- fix(types): import plugins for module augmentations #933 + +## v1.10.19 + +- fix(core/scope): remove duplicate Interactable super.unset +- fix(utils/pointerExtend): skip all vendor-prefixed props. Close #978 + +## v1.10.18 + +- fix(interact): remove types dependency +- fix: set "type": "module" for scoped packages +- fix(modifiers): allow toggling aspectRatio modifier during interaction +- fix(types): import plugins for module augmentations +- fix(interactjs): don't assign module.exports in esm package + +## v1.10.17 + +- fixed missing typings when only the `@interactjs/interactjs` package is + installed and imported +- added index to vue package for installing with side effect import + +## v1.10.16 + +- remove vue and react deps fron pro `@interactjs/interactjs` package + +## v1.10.15 + +- fixed an issue with broken `@interactjs/types` #972 + +## v1.10.14 + +- fixed an issue with iframes on Webkit #942. Thanks, @tulps, for PR #943 +- fixed top-right and bottom-left resizing with aspectRatio with sub-modifiers #944. Thanks again, @tulps, for PR #963 +- fixed typings for `@itneractjs/` scoped module packages #933 +- added `doubletap.double === true` event prop for consistency with `tap.double` +- fixed a bug with calling `interactable.unset()` in a `drop` listener #919 + +## v1.10.13 + +- Added `.d.ts` files to all `@interactjs/*` packages + +## v1.10.12 + +- fixed incorrect behaviour when `interactable.unset()` is called multiple + times + +## v1.10.11 + +- fixed incorrect "module" field in package.json https://github.com/taye/interact.js/issues/894#issuecomment-811046898 + +## v1.10.10 + +- fixed issue with unresolved stub files #894 +- fixed commonjs import of `interactjs` package + +## v1.10.9 + +- improved support for SSR environments + +## v1.10.8 + +- fixed imports of missing modules #891 + +## v1.10.7 + +- correctly replace `process.env.npm_package_version` in min bundle #890 + +## v1.10.6 + +- fix packaging error + +## v1.10.5 + +- fix packaging error + +## v1.10.4 + +- fix NPE in indexOfDeepestElement if first element has no parent #887 +- improve babel-plugin-prod on windows #885 + +## v1.10.3 + +- fixed issue with TS strict null checks #882 +- fixed issue with type imports being emitted in JS modules #881 + +## v1.10.2 + +- marked interact.{on,off} methods as deprecated + +## v1.10.1 + +- fixed mouseButtons option typings #865 +- removed plugin index module warnings + +## v1.10.0 + +- changed production files extension from '.min.js' to '.prod.js' #857 +- added experimental `@interactjs/dev-tools/babel-plugin-prod` babel plugin to + change `@interactjs/*` imports to production versions +- added `sideEffects` fields to package.json files + +## v1.9.22 + +- fixed inertia issue with arbitrary plugin order #834 +- fixed inertia regression #853 + +## v1.9.21 + +- used findIndex polyfill to support 1E11 #852 +- fixed issue where resize reflow increased element size #817 +- fixed drop event order: fire `dropmove` after `dragenter` #841 and final + drop events before `dragend` #842 +- updated docs #844 #829 + +## v1.9.20 + +- fixed ordering of plugins + +## v1.9.19 + +- exposed `DropEvent` type + +## v1.9.18 + +- fixed further issues with types + +## v1.9.17 + +- fixed missing types for interactjs package + +## v1.9.16 + +- fixed missing types for interactjs package + +## v1.9.15 + +- fixed missing types for interactjs package + +## v1.9.15 + +- fixed further regression breaking typescript builds #816 + +## v1.9.14 + +- fixed regression breaking typescript builds #816 + +## v1.9.13 + +- fixed regression breaking es5 compatibility of .min.js bundle #814 + +## v1.9.12 + +- fixed regression breaking commonjs imports withotu .default + +## v1.9.11 + +- fixed issue with missing width/height on rectChecker result +- fixed resize checker with negative sizes +- moved generated plugin use modules to @interactjs/_/{use/,}_/index.ts #800 +- changed snap function args to provide interaction proxy +- restored dev-tools helpers in development bundle + +## v1.9.10 + +- fixed issue with uninitialized scope in non browser env #803 + +## v1.9.9 + +- fixed typescript issue #807 + +## v1.9.8 + +- fixed minified bundle #802 +- fixed issue with removing delegated events #801 + +## v1.9.7 + +- fixed typing issues + +## v1.9.6 + +- improved package dependencies + +## v1.9.5 + +- made `core` and `utils` packages dependencies of `interact` + +## v1.9.4 + +- restored `@interactjs/*/use/*.js*` builds + +## v1.9.2 + +- fixed imports within generated modules + +## v1.9.1 + +- added `@interactjs/*/use/*.min.js` builds +- fixed issue with webpack minifier #800 +- fixed typescript issues + +## v1.9.0 + +- added various `@interactjs/*/use` packages for simpler selective imports + #800 +- fixed endOnly modifiers without inertia + +## v1.8.5 + +- fixed a but causing incorrect modifications after resuming inertia #790 + +## v1.8.4 + +- fixed bug when calling interaction.move() from start event #791 + +## v1.8.3 + +- fixed bug when calling interaction.move() from start event #791 +- fixed invalid non-array argument spread types #789 +- fixed missing typescript definition of some interactable methods #788 +- disabled `.d.ts.map` files output since the `.ts` source files are not + published +- fixed typings for modifiers + +## v1.8.2 + +- enabled `.d.ts.map` files output +- added license field to @interactjs/interact package.json + +## v1.8.1 + +- fixed an issue causing flickering a cursor on Firefox for Windows #781 + +## v1.8.0 + +Changes from prerelease versions listed below. See +https://github.com/taye/interact.js/projects/4#column-7093512 for a list of +issues and pull requests. + +## v1.8.0-rc.3 + +- fixed incorrect publish + +## v1.8.0-rc.2 + +- refactoring + +## v1.8.0-rc.1 + +- fixed `interact.snappers.grid` arg typings + (https://twitter.com/ksumarine/status/1204457347856424960) +- removed "?" from definitions for interact.{modifiers,snappers,createSnapGrid} + +## v1.8.0-rc.0 + +- fixed `modifiers.restrictSize` #779 +- fixed option types in typescript and fixed devTools options #776 + +## v1.8.0-alpha.7 + +- reverted to typescript@3.6 to avoid backwards compatibility issues #775 + +## v1.8.0-alpha.6 + +- fixed dev scripts + +## v1.8.0-alpha.5 + +- moved `interact.dynamicDrop` definition in order to avoid compilation errors + +## v1.8.0-alpha.4 + +- added `main` field to interactjs package.json #774 +- removed baseUrl from project tsconfig to avoid relative imports in generated + declarations + +## v1.8.0-alpha.3 + +- added missing typescript declaration files + +## v1.8.0-alpha.2 + +- used non relative imports in .ts files with correct config for + babel-plugin-bare-import-rewrite + +## v1.8.0-alpha.1 + +- added `event.modifiers` array #772 + +## v1.8.0-alpha.0 + +- added `aspectRatio` modifier #638 + +## v1.7.4 + +- fixed `interact.snappers.grid` arg typings + (https://twitter.com/ksumarine/status/1204457347856424960) +- removed "?" from definitions for interact.{modifiers,snappers,createSnapGrid} + +## v1.7.3 + +- fixed interactjs package main and browser fields #774 +- reverted to typescript@3.6 to avoid backwards compatibility issues #775 + +## v1.7.2 + +- fixed typescript definition files #771 + +## v1.7.1 + +- reorganized modules for esnext resolution + +## v1.7.0 + +- fixed hold repeat `event.count` +- added esnext js builds #769 + +## v1.6.3 + +- fixed issue with inertia resume with `endOnly: false` #765 + +## v1.6.2 + +- @mlucool added license field to package.json of sub modules #755 +- added `rect`, `deltaRect` and `edges` to resizestart and resizeend events #754 + +## v1.6.1 + +- fixed resize without invert + +## v1.6.0 + +- avoided accessing deprecated event.mozPressure #751 +- changed typings to use `HTMLElement | SVGElement` for `event.target` #747 +- added `interacting` arg to cursorChecker #739 +- added zIndex compare for sibling dropzones + +## v1.5.4 + +- fixed broken modifiers #746 + +## v1.5.3 + +- fixed issues with old modifiers API + +## v1.5.2 + +- fixed null restriction issue #737 +- improved typings for modifiers + +## v1.5.1 + +- fixed typing issues #738 + +## v1.5.0 + +- added `cursorChecker` option for drag and resize #736 +- allowed restrictions larger than the target element #735 +- added `interact.modifiers.restrictRect` with pre-set elementRect #735 + +## v1.4.14 + +- fixed issue with string restriction values that don't resolve to a rect + #731 +- changed plugin order so that `pointer-events` is installed before `inertia` + +## v1.4.13 + +- fixed restrictSize min and max function restrictions + +## v1.4.12 + +- fixed errors from calling `interaction.stop()` in start event #725 + +## v1.4.11 + +- fixed hold events #730 + +## v1.4.10 + +- fixed regression of preventing native drag behaviour #729 + +## v1.4.9 + +- fixed modifiers with inertia action-resume #728 +- fixed docs for snap grid limits #717 + +## v1.4.8 + +- fixed exports in generated typings #727 + +## v1.4.7 + +- fixed exports in generated typings #726 + +## v1.4.6 + +- fixed pointerEvents currentTarget + +## v1.4.5 + +- @0xflotus fixed typos in docs #724 +- fixed error on iOS #682 + +## v1.4.4 + +- fixed an issue with interactions lingering on removed elements #723 + +## v1.4.3 + +- destroy only relevant interactions on interactable.unset() + +## v1.4.2 + +- @jf-m fixed memory leaks and a bug on interactions stop [PR #715](https://github.com/taye/interact.js/pull/715) +- fixed dropzones in shadow DOM [PR #722](https://github.com/taye/interact.js/pull/722) + +## v1.4.1 + +- fixed scripts to run bundle optimizations and fix issues with browserify + +# v1.4.0 + +Most notablly: + +- `interactable.reflow(action)` to re-run modifiers, drop, etc [PR #610](https://github.com/taye/interact.js/pull/610) +- `dropEvent.reject()` [PR #613](https://github.com/taye/interact.js/pull/613) +- snapEdges modifier [PR #620](https://github.com/taye/interact.js/pull/620) +- per-action modifiers array [PR #625](https://github.com/taye/interact.js/pull/625) +- autoStart set cursor on both target and <html> [PR #639](https://github.com/taye/interact.js/pull/639) +- inertia: rename resume event to `${action}resume` +- `interactable.reflow(action)` to re-run modifiers, drop, etc [PR #610](https://github.com/taye/interact.js/pull/610) +- added `options.listeners` array/object for actions +- `snapEdges` modifier [PR #620](https://github.com/taye/interact.js/pull/620) +- fixed iOS preventDefault passive event issue ([issue #631](https://github.com/taye/interact.js/issues/631)) +- added `console.warn` messages for common, easily detected issues +- improved docs +- various fixes + +Full list of [changes on Github](https://github.com/taye/interact.js/compare/1.3.4...v1.4.0). + +## v1.3.3 + +- fixed issues with action options ([PR #567](https://github.com/taye/interact.js/pull/567), [issue #570](https://github.com/taye/interact.js/issues/570)) + +## v1.3.2 + +- fixed iOS preventDefault passive event issue ([issue #561](https://github.com/taye/interact.js/issues/561)) + +## v1.3.1 + +- allowed calling `draggable.unset()` during `dragend` and `drop` event + listeners ([issue #560](https://github.com/taye/interact.js/issues/560)) +- allowed snap to be enabled with falsey targets value [issue #562](https://github.com/taye/interact.js/issues/562) + +## v1.3.0 + +Most notably: + +- changed the npm and bower package names to "interactjs" ([issue + #399](https://github.com/taye/interact.js/issues/399) +- major refactor with [PR #231](https://github.com/taye/interact.js/pull/231). +- removed deprecated methods: + - `Interactable`: `squareResize`, `snap`, `restrict`, `inertia`, + `autoScroll`, `accept` + - `interact`: `enabbleDragging`, `enableResizing`, `enableGesturing`, + `margin` +- new `hold` option for starting actions +- new `interaction.end()` method + ([df963b0](https://github.com/taye/interact.js/commit/df963b0)) +- `snap.offset` `self` option ([issue + #204](https://github.com/taye/interact.js/issues/204/#issuecomment-154879052)) +- `interaction.doMove()` + ([3489ee1](https://github.com/taye/interact.js/commit/3489ee1)) + ([c5c658a](https://github.com/taye/interact.js/commit/c5c658a)) +- snap grid limits + ([d549672](https://github.com/taye/interact.js/commit/d549672)) +- improved iframe support ([PR + #313](https://github.com/taye/interact.js/pull/313)) +- `actionend` event dx/dy are now `0`, not the difference between start and + end coords ([cbfaf00](https://github.com/taye/interact.js/commit/cbfaf00)) +- replaced drag `axis` option with `startAxis` and `lockAxis` +- added pointerEvents options: + - `holdDuration` + ([1c58f92](https://github.com/taye/interact.js/commit/1c58f927)), + - `ignoreFrom` and `allowFrom` + ([6cbaad6](https://github.com/taye/interact.js/commit/6cbaad6d)) + - `origin` ([7823bb9](https://github.com/taye/interact.js/commit/7823bb95)) +- action events set with action method options (eg. + `target.draggable({onmove})` are removed when that action is disabled with a + method call ([cca4e26](https://github.com/taye/interact.js/commit/cca4e260)) +- `context` option now works for Element targets + ([8f64a7a](https://github.com/taye/interact.js/commit/8f64a7a4)) +- added an action `mouseButtons` option and allowed actions only with the left + mouse button by default + ([54ebdc3](https://github.com/taye/interact.js/commit/54ebdc3e)) +- added repeating `hold` events + ([fe11a8e](https://github.com/taye/interact.js/commit/fe11a8e5)) +- fixed `Interactable.off` ([PR + #477](https://github.com/taye/interact.js/pull/477)) +- added `restrictEdges`, `restrictSize` and `snapSize` resize modifiers ([PR + #455](https://github.com/taye/interact.js/pull/455)) + +Full list of [changes on Github](https://github.com/taye/interact.js/compare/v1.2.6...v1.3.0). + +## 1.2.6 + +### resize.preserveAspectRatio + +```javascript +interact(target).resizable({ preserveAspectRatio: true }) +``` + +See [PR #260](https://github.com/taye/interact.js/pull/260). + +### Deprecated + +- `interact.margin(number)` - Use `interact(target).resizable({ margin: number });` instead + +### Fixed + +- incorrect coordinates of the first movement of every action ([5e5a040](https://github.com/taye/interact.js/commit/5e5a040)) +- warning about deprecated "webkitForce" event property ([0943290](https://github.com/taye/interact.js/commit/0943290)) +- bugs with multiple concurrent interactions ([ed53aee](http://github.com/taye/interact.js/commit/ed53aee)) +- iPad 1, iOS 5.1.1 error "undefined is not a function" when autoScroll is set + to true ([PR #194](https://github.com/taye/interact.js/pull/194)) + +Full list of [changes on Github](https://github.com/taye/interact.js/compare/v1.2.5...v1.2.6) + +## 1.2.5 + +### Changed parameters to actionChecker and drop.checker + +- Added `event` as the first argument to actionCheckers. See commit [88dc583](https://github.com/taye/interact.js/commit/88dc583) +- Added `dragEvent` as the first parameter to drop.checker functions. See + commits [16d74d4](https://github.com/taye/interact.js/commit/16d74d4) and [d0c4b69](https://github.com/taye/interact.js/commit/d0c4b69) + +### Deprecated methods + +interactable.accept - instead, use: + +```javascript +interact(target).dropzone({ accept: stringOrElement }) +``` + +interactable.dropChecker - instead, use: + +```javascript +interact(target).dropzone({ checker: function () {} }) +``` + +### Added resize.margin + +See https://github.com/taye/interact.js/issues/166#issuecomment-91234390 + +### Fixes + +- touch coords on Presto Opera Mobile - see commits [886e54c](https://github.com/taye/interact.js/commit/886e54c) and [5a3a850](https://github.com/taye/interact.js/commit/5a3a850) +- bug with multiple pointers - see commit [64882d3](https://github.com/taye/interact.js/commit/64882d3) +- accessing certain recently deprecated event properties in Blink - see + commits [e91fbc6](https://github.com/taye/interact.js/commit/e91fbc6) and [195cfe9](https://github.com/taye/interact.js/commit/195cfe9) +- dropzones with `accept: 'pointer'` in scrolled pages on iOS6 and lower - see + commit [0b94aac](https://github.com/taye/interact.js/commit/0b94aac) +- setting styleCursor through Interactable options object - see [PR + #270](https://github.com/taye/interact.js/pull/270) +- one missed interaction element on stop triggered - see [PR + #258](https://github.com/taye/interact.js/pull/258) +- pointer dt on touchscreen devices - see [PR + #215](https://github.com/taye/interact.js/pull/215) +- autoScroll with containers with fixed position - see commit [3635840](https://github.com/taye/interact.js/commit/3635840) +- autoScroll for mobile - see #180 +- preventDefault - see commits [1984c80](https://github.com/taye/interact.js/commit/1984c80) and [6913959](https://github.com/taye/interact.js/commit/6913959) +- occasional error - see [issue + #183](https://github.com/taye/interact.js/issue/183) +- Interactable#unset - see [PR + #178](https://github.com/taye/interact.js/pull/178) +- coords of start event after manual start - see commit [fec73b2](https://github.com/taye/interact.js/commit/fec73b2) +- bug with touch and selector interactables - see commit [d8df3de](https://github.com/taye/interact.js/commit/d8df3de) +- touch doubletap bug - see [273f461](https://github.com/taye/interact.js/commit/273f461) +- event x0/y0 with origin - see [PR + #167](https://github.com/taye/interact.js/pull/167) + +## 1.2.4 + +### Resizing from all edges + +With the new [resize edges API](https://github.com/taye/interact.js/pull/145), +you can resize from the top and left edges of an element in addition to the +bottom and right. It also allows you to specify CSS selectors, regions or +elements as the resize handles. + +### Better `dropChecker` arguments + +The arguments to `dropChecker` functions have been expanded to include the +value of the default drop check and some other useful objects. See [PR +161](https://github.com/taye/interact.js/pull/161) + +### Improved `preventDefault('auto')` + +If manuanStart is `true`, default prevention will happen only while +interacting. Related to [Issue +138](https://github.com/taye/interact.js/issues/138). + +### Fixed inaccurate snapping + +This removes a small inaccuracy when snapping with one or more +`relativeOffsets`. + +### Fixed bugs with multiple pointers + +## 1.2.3 + +### ShadowDOM + +Basic support for ShadowDOM was implemented in [PR +143](https://github.com/taye/interact.js/pull/143) + +### Fixed some issues with events + +Fixed Interactable#on({ type: listener }). b8a5e89 + +Added a `double` property to tap events. `tap.double === true` if the tap will +be followed by a `doubletap` event. See [issue +155](https://github.com/taye/interact.js/issues/155#issuecomment-71202352). + +Fixed [issue 150](https://github.com/taye/interact.js/issues/150). + +## 1.2.2 + +### Fixed DOM event removal + +See [issue 149](https://github.com/taye/interact.js/issues/149). + +## 1.2.1 + +### Fixed Gestures + +Gestures were completely [broken in +v1.2.0](https://github.com/taye/interact.js/issues/146). They're fixed now. + +### Restriction + +Fixed restriction to an element when the element doesn't have a rect (`display: none`, not in DOM, etc.). [Issue +144](https://github.com/taye/interact.js/issues/144). + +## 1.2.0 + +### Multiple interactions + +Multiple interactions have been enabled by default. For example: + +```javascript +interact('.drag-element').draggable({ + enabled: true, + // max : Infinity, // default + // maxPerElement: 1, // default +}) +``` + +will allow multiple `.drag-element` to be dragged simultaneously without having +to explicitly set max: integerGreaterThan1. The default +`maxPerElement` value is still 1 so only one drag would be able to happen on +each `.drag-element` unless the `maxPerElement` is changed. + +If you don't want multiple interactions, call `interact.maxInteractions(1)`. + +### Snapping + +#### Unified snap modes + +Snap modes have been +[unified](https://github.com/taye/interact.js/pull/127). A `targets` array +now holds all the snap objects and functions for snapping. +`interact.createSnapGrid(gridObject)` returns a function that snaps to the +dimensions of the given grid. + +#### `relativePoints` and `origin` + +```javascript +interact(target).draggable({ + snap: { + targets: [{ x: 300, y: 300 }], + relativePoints: [ + { x: 0, y: 0 }, // snap relative to the top left of the element + { x: 1, y: 1 }, // and also to the bottom right + ], + + // offset the snap target coordinates + // can be an object with x/y or 'startCoords' + offset: { x: 50, y: 50 }, + }, +}) +``` + +#### snap function interaction arg + +The current `Interaction` is now passed as the third parameter to snap functions. + +```js +interact(target).draggable({ + snap: { + targets: [ + function (x, y, interaction) { + if (!interaction.dropTarget) { + return { x: 0, y: 0 } + } + }, + ], + }, +}) +``` + +#### snap.relativePoints and offset + +The `snap.relativePoints` array succeeds the snap.elementOriign object. But +backwards compatibility with `elementOrigin` and the old snapping interface is +maintained. + +`snap.offset` lets you offset all snap target coords. + +See [this PR](https://github.com/taye/interact.js/pull/133) for more info. + +#### slight change to snap range calculation + +Snapping now occurs if the distance to the snap target is [less than or +equal](https://github.com/taye/interact.js/commit/430c28c) to the target's +range. + +### Inertia + +`inertia.zeroResumeDelta` is now `true` by default. + +### Per-action settings + +Snap, restrict, inertia, autoScroll can be different for drag, restrict and +gesture. See [PR 115](https://github.com/taye/interact.js/pull/115). + +Methods for these settings on the `interact` object (`interact.snap()`, +`interact.autoScroll()`, etc.) have been removed. + +### Space-separated string and array event list and eventType:listener object + +```javascript +function logEventType(event) { + console.log(event.type, event.target) +} + +interact(target).on('down tap dragstart gestureend', logEventType) + +interact(target).on(['move', 'resizestart'], logEventType) + +interact(target).on({ + dragmove: logEvent, + keydown: logEvent, +}) +``` + +### Interactable actionChecker + +The expected return value from an action checker has changed from a string to +an object. The object should have a `name` and can also have an `axis` +property. For example, to resize horizontally: + +```javascript +interact(target) + .resizeable(true) + .actionChecker(function (pointer, defaultAction, interactable, element) { + return { + name: 'resize', + axis: 'x', + } + }) +``` + +### Plain drop event objects + +All drop-related events are [now plain +objects](https://github.com/taye/interact.js/issues/122). The related drag +events are referenced in their `dragEvent` property. + +### Interactable.preventDefault('always' || 'never' || 'auto') + +The method takes one of the above string values. It will still accept +`true`/`false` parameters which are changed to `'always'`/`'never'`. + +## 1.1.3 + +### Better Events + +Adding a function as a listener for an InteractEvent or pointerEvent type +multiple times will cause that function to be fired multiple times for the +event. Previously, adding the event type + function combination again had no +effect. + +Added new event types [down, move, up, cancel, +hold](https://github.com/taye/interact.js/pull/101). + +Tap and doubletap with multiple pointers was improved. + +Added a workaround for IE8's unusual [dblclick event +sequence](http://www.quirksmode.org/dom/events/click.html) so that doubletap +events are fired. + +Fixed a [tapping issue](https://github.com/taye/interact.js/issues/104) on +Windows Phone/RT. + +Fixed a bug that caused the origins of all elements with tap listeners to be +subtracted successively as a tap event propagated. + +[Fixed delegated events](https://github.com/taye/interact.js/commit/e972154) +when different contexts have been used. + +### iFrames + +[Added basic support](https://github.com/taye/interact.js/pull/98) for sharing +one instance of interact.js between multiplie windows/frames. There are still +some issues. diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md new file mode 100644 index 000000000..1eb380c50 --- /dev/null +++ b/ISSUE_TEMPLATE.md @@ -0,0 +1,14 @@ +If you have questions about the [API](http://interactjs.io/api) that aren't answered in the [docs](http://interactjs.io/docs) or [FAQ](http://interactjs.io/docs/faq), try asking in the [Gitter chatroom](https://gitter.im/taye/interact.js) or on [Stackoverflow](https://stackoverflow.com/questions/tagged/interact.js). + +If you've found something that looks like a bug, include a link to a minimal demo on [JSFilddle](https://jsfiddle.net), [Codepen](https://codepen.io) with instructions to reproduce the bug with and roughly follow the following issue description format: + +### Expected behavior +Tell us what should happen + +### Actual behavior +Tell us what happens instead + +### System configuration +**interact.js version**: +**Browser name and version**: +**Operating System**: diff --git a/LICENSE b/LICENSE index 3d9c609db..e4854f77d 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2012, 2013, 2014 Taye Adeyemi +Copyright (c) 2012-present Taye Adeyemi Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..12bf4d1bf --- /dev/null +++ b/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1 @@ +Make sure to include tests in your pull request. diff --git a/README.md b/README.md index 53e6f69ee..1824b547d 100644 --- a/README.md +++ b/README.md @@ -1,263 +1,108 @@ -interact.js +interact.js + +

+ JavaScript drag and drop, resizing and multi-touch gestures with inertia and snapping for modern browsers (and also IE9+). +

+ +
+Gitter +jsDelivr +Build Status + +
+
+ +Features include: + +- **inertia** and **snapping** +- **multi-touch**, simultaneous interactions +- cross browser and device, supporting the **desktop and mobile** versions of + Chrome, Firefox and Opera as well as **Internet Explorer 9+** +- interaction with [**SVG**](http://interactjs.io/#use_in_svg_files) elements +- being **standalone and customizable** +- **not modifying the DOM** except to change the cursor (but you can disable + that) + +## Installation + +- [npm](https://www.npmjs.org/): `npm install interactjs` +- [jsDelivr CDN](https://cdn.jsdelivr.net/npm/interactjs/): `` +- [unpkg CDN](https://unpkg.com/interactjs/): `` +- [Rails 5.1+](https://rubyonrails.org/): + 1. `yarn add interactjs` + 2. `//= require interactjs/interact` +- [Webjars SBT/Play 2](https://www.webjars.org/): `libraryDependencies ++= Seq("org.webjars.npm" % "interactjs" % version)` + +### Typescript definitions + +The project is written in Typescript and the npm package includes the type +definitions, but if you need the typings alone, you can install them with: -Javascript drag and drop, resizing and gestures for modern desktop and mobile browsers. - -Awesomeness includes: - - - **inertia** - - [**snapping**](http://interactjs.io/repo/demo/snap.html) to a grid, custom - anchors or paths. - - cross browser and device, supporting the **desktop and mobile** versions of - Chrome, Firefox and Opera as well as **Internet Explorer 8+** - - interaction with [**SVG**](http://interactjs.io/repo/demo/star.svg) elements - - being **lightweight and standalone** (not _yet another_ jQuery plugin) - - **not modifying anything** it doesn't own (except to support IE8 and to - change the cursor (but you can disable that)) - -Demos ------ - - - http://interactjs.io has some demos showing drag and drop, gestures, - inertia, snap and some other things. - - The [html & svg demo](http://interactjs.io/repo/demo/html_svg.html "drag, drop, - resize and gesture demo") shows div elements which can be dragged and - dropped onto each other and resized by dragging the bottom and right edges. The - yellow elements are SVG elements (these won't show up on IE8). - - [star.svg](http://interactjs.io/repo/demo/star.svg "editing an SVG document") - demonstrates interact.js being used within an SVG document. - - The [snapping demo](http://interactjs.io/repo/demo/snap.html "Oh snap!") shows - how flexible the grid and anchor snapping system is and it's pretty fun. - - This [blog post on path - snapping](http://taye.me/blog/interact-js/snap/2013/09/29/interactjs-path-snapping.html) - demonstrates and graphs some interesting path snapping functions. - -interact.js began as a [Google Summer of Code 2012 project]("http://www.google-melange.com/gsoc/project/details/google/gsoc2012/taye/5668600916475904") for -[Biographer](https://code.google.com/p/biographer "Biographer on Google Code"). - -Example -------- - -```javascript -var // x and y to keep the position that's been dragged to - x = 0, - y = 0, - // vendor prefixes (prefices?) - transformProp = 'transform' in document.body.style? - 'transform': 'webkitTransform' in document.body.style? - 'webkitTransform': 'mozTransform' in document.body.style? - 'mozTransform': 'oTransform' in document.body.style? - 'oTransform': 'msTransform'; - -// make an Interactable of the document body element -interact(document.body) - // make a draggable of the Interactable - .draggable({ - // on(drag)move - // could also have done interact(document.body).draggable(true).ondragmove = function... - onmove: function (event) { - x += event.dx; - y += event.dy; - - // translate the document body by the change in pointer position - document.body.style[transformProp] = 'translate(' + x + 'px, ' + y + 'px)'; - } - }) - // you should really add listeners like this if you want to add multiple listeners - .on('dragend', function (event) { - console.log('dragged a distance of ' + - Math.sqrt(event.dx*event.dx + event.dy*event.dy) + - ' pixels to ' + event.pageX + ', ' + event.pageY); - }) - // allow inertia throwing - .inertia({ - resistance: 15, - zeroResumeDelta: true - }); - // snap to the corners of the specified grid - .snap({ - mode: 'grid', - grid: { - x: 100, - y: 5 - }, - gridOffset: { - x: 20, - y: 10 - }, - range: Infinity // can also use -1 which gets changed to Infinity - }); - - -// you can also listen to InteractEvents for every Interactable -interact.on('dragstart', function (event) { - console.log('starting drag from ' + event.x0 + ', ' + event.y0); -}); ``` - -Documentation -------------- -http://interactjs.io/docs - -Usage ------ -Pass the element you want to interact with or a CSS selector string to -`interact`. That returns an object with methods, notably `draggable`, -`resizable`, `gesturable`, `dropzone` which let you allow or disallow the -related actions and `on` which let's you add event listeners for InteractEvents -and any DOM event. - -Details -------- - -### Interactables -The `interact` function takes an Element or CSS selector and an optional -`options` object. This returns an `Interactable` object which has several -methods and properties to configure what events it can fire and to modify the -reported coordinates. These methods have a fluent interface so method calls can -be chained nicely. - -For example, to make a DOM element fire drag and resize events you can do: - -```javascript -interact(document.getElementById('anElement')) - .draggable (true) - .resizable(true); +npm install --save-dev @interactjs/types ``` -### Acting -Now that the element has been made interactable, when it is clicked on or -touched and then dragged, an action is determined depending on the input type -and position of the event over the element. InteractEvents are then fired as -the pointer moves around the page until it is finally released or the -window loses focus. - -When a sequence of user actions results in an InteractEvent, that event type is -fired and all listeners of that type which were bound to that Interactable or -bound globally are called. - -Even though InteractEvents are being fired, the element is not actually -modified by interact.js at all. To do that, you need to add listeners for -InteractEvents either to each Interactable or globally for all Interacables and -style the element according to event data. - -### Listening - The `InteractEvent` types are: +## Documentation - - Draggable: `dragstart`, `dragmove`, `draginertiastart`, `dragend` - - Dropzone: `dropactivate`, `dropdeactivate`, `dragenter`, `dragleave`, - `dropmove`, `drop` - - Resizable: `resizestart`, `resizemove`, `resizeinertiastart`, `resizeend` - - Gesturable: `gesturestart`, `gesturemove`, `gestureinertiastart`, - `gestureend` - -There are also the `tap` and `doubletap` events which are equivalent to `click` -and `doubleclik`. - -To respond to an InteractEvent, you must add a listener for its event type -either directly to an interactable - -``` javascript -Interactable#on(eventType, listenerFunction) -``` +http://interactjs.io/docs -or globally for all events of that type +## Example ```javascript -interact.on('resizemove', resizeElement)`. -``` - -The `InteractEvent` object that was created is passed to these functions as the -first parameter. - -InteractEvent properties include the usual properties of mouse/touch events -such as pageX/Y, clientX/Y, modifier keys etc. but also some properties -providing information about the change in coordinates and event specific data. -The table below displays all of these events. - -#### InteractEvent properties -| Common | | -| ----------------------- | --------------------------------------------------| -| target | The element that is being interacted with | -| `x0`, `y0` | Page x and y coordinates of the starting event | -| `clientX0`, `clientY0` | Client x and y coordinates of the starting event | -| `dx`, `dy` | Change in coordinates of the mouse/touch | -| `velocityX`, `velocityY`| The Velocity of the pointer | -| `speed` | The speed of the pointer | -| `timeStamp` | The time of creation of the event object | - -| Draggables | | -| ----------------------- | --------------------------------------------------| -| **dragmove** | | -| `dragEnter` | The dropzone this Interactable was dragged over | -| `dragLeave` | The dropzone this Interactable was dragged out of | -| **dragenter, dragLeave**| | -| `draggable` | The draggable that's over this dropzone | - -| Dropzones | | -| ----------------------- | --------------------------------------------------| -| **drop(de)activate**, **dropmove**, **drag(enter\|leave)**, **drop** | | -| `draggable` | The draggable element that was dropped into this dropzone | -| | | - -| Resize | | -| ----------------------- | --------------------------------------------------| -| `axes` | The axes the resizing is constrained to (x/y/xy) | - -| Gesture | | -| ----------------------- | --------------------------------------------------| -| `touches` | The array of touches that triggered the event | -| `distance` | The distance between the event's first two touches| -| `angle` | The angle of the line made by the two touches | -| `da` | The change in angle since previous event | -| `scale` | The ratio of the distance of the start event to the distance of the current event | -| `ds` | The change in scale since the previous event | -| `box` | A box enclosing all touch points | - -In gesture events, page and client coordinates are the averages of touch -coordinates. Velocity is calculated from these averages. - -Tap and doubletap event coordinates are copied directly from the source -mouseup/touchend/pointerup event and are not modified โ€“ no snapping, -restriction or origin for tap and doubletap. - -The [dropmove](https://github.com/taye/interact.js/issues/67) event is a plain -object created like this: +var pixelSize = 16; + +interact('.rainbow-pixel-canvas') + .origin('self') + .draggable({ + modifiers: [ + interact.modifiers.snap({ + // snap to the corners of a grid + targets: [ + interact.snappers.grid({ x: pixelSize, y: pixelSize }), + ], + }) + ], + listeners: { + // draw colored squares on move + move: function (event) { + var context = event.target.getContext('2d'), + // calculate the angle of the drag direction + dragAngle = 180 * Math.atan2(event.dx, event.dy) / Math.PI; + + // set color based on drag angle and speed + context.fillStyle = 'hsl(' + dragAngle + ', 86%, ' + + (30 + Math.min(event.speed / 1000, 1) * 50) + '%)'; + + // draw squares + context.fillRect(event.pageX - pixelSize / 2, event.pageY - pixelSize / 2, + pixelSize, pixelSize); + } + } + }) + // clear the canvas on doubletap + .on('doubletap', function (event) { + var context = event.target.getContext('2d'); + + context.clearRect(0, 0, context.canvas.width, context.canvas.height); + }); + + function resizeCanvases () { + [].forEach.call(document.querySelectorAll('.rainbow-pixel-canvas'), function (canvas) { + canvas.width = document.body.clientWidth; + canvas.height = window.innerHeight * 0.7; + }); + } -``` javascript -dropMoveEvent = { - target : dropElement, - relatedTarget: dragEvent.target, - dragmove : dragEvent, - type : 'dropmove', - timeStamp : dragEvent.timeStamp -}; + // interact.js can also add DOM event listeners + interact(document).on('DOMContentLoaded', resizeCanvases); + interact(window).on('resize', resizeCanvases); ``` -### Interacting -One way to move an element in response to a dragmove is to add a listener that -transforms the element according to `dy` and `dx` of the InteractEvent. +See the above code in action at https://codepen.io/taye/pen/tCKAm -```javascript -// Set element and listen for dragmove events -interact('.drag-element') - .draggable({ - onmove: function(event) { - var target = event.target, - // use data-x, data-y to record the drag position - x = (parseFloat(target.dataset.x) || 0) + event.dx, - y = (parseFloat(target.dataset.y) || 0) + event.dy; - - // update the CSS transform - target.style.transform = - 'translate(' + x + 'px, ' + y + 'px)'; - - // save the newly dragged position - target.dataset.x = x; - target.dataset.y = y; - }); -``` - -License -------- +## License interact.js is released under the [MIT License](http://taye.mit-license.org). + +[ijs-twitter]: https://twitter.com/interactjs +[upcoming-changes]: https://github.com/taye/interact.js/blob/main/CHANGELOG.md#upcoming-changes diff --git a/babel.config.cjs b/babel.config.cjs new file mode 100644 index 000000000..eee873213 --- /dev/null +++ b/babel.config.cjs @@ -0,0 +1,3 @@ +module.exports = { + presets: [['@babel/preset-env', { targets: { node: 'current' } }], '@babel/preset-typescript'], +} diff --git a/bin/_add_plugin_indexes b/bin/_add_plugin_indexes new file mode 100755 index 000000000..1d05c113a --- /dev/null +++ b/bin/_add_plugin_indexes @@ -0,0 +1,2 @@ +#!/usr/bin/env node +require('../scripts/bin/add_plugin_indexes') diff --git a/bin/_bundle b/bin/_bundle new file mode 100755 index 000000000..5e62ec03d --- /dev/null +++ b/bin/_bundle @@ -0,0 +1,2 @@ +#!/usr/bin/env node +require('../scripts/bin/bundle') diff --git a/bin/_check_deps b/bin/_check_deps new file mode 100755 index 000000000..c84232f7c --- /dev/null +++ b/bin/_check_deps @@ -0,0 +1,2 @@ +#!/usr/bin/env node +require('../scripts/bin/_check_deps') diff --git a/bin/_clean b/bin/_clean new file mode 100755 index 000000000..40988c0e1 --- /dev/null +++ b/bin/_clean @@ -0,0 +1,2 @@ +#!/usr/bin/env node +require('../scripts/bin/clean') diff --git a/bin/_link b/bin/_link new file mode 100755 index 000000000..353cbb4fa --- /dev/null +++ b/bin/_link @@ -0,0 +1,29 @@ +#!/bin/bash +ROOT=$(dirname $(dirname $(readlink -f $0))) +if [ -z "$ROOT" ] +then + ROOT=$(dirname $(dirname $0)) +fi + +modules_scope_dir=node_modules/@interactjs +modules_bin_dir=node_modules/.bin + +mkdir -p $modules_scope_dir $modules_bin_dir + +rm $modules_scope_dir/* 2> /dev/null + +# link _dev package +ln -sf $ROOT $modules_scope_dir/_dev + +# link all scoped packages from CWD +for package in $(cd packages/@interactjs && ls -d *); do + ln -sf ../../packages/@interactjs/$package $modules_scope_dir +done + +# link all packages from this repo +for package in $(cd $ROOT/packages/@interactjs && ls -d *); do + ln -sf $ROOT/packages/@interactjs/$package $modules_scope_dir +done + +# link all bins from this repo and from CWD +cd node_modules/.bin && ln -sf $ROOT/bin/* ../../bin/* . diff --git a/bin/_lint b/bin/_lint new file mode 100755 index 000000000..b13364f38 --- /dev/null +++ b/bin/_lint @@ -0,0 +1,2 @@ +#!/usr/bin/env node +require('../scripts/bin/lint') diff --git a/bin/_release b/bin/_release new file mode 100755 index 000000000..38e62e904 --- /dev/null +++ b/bin/_release @@ -0,0 +1,2 @@ +#!/usr/bin/env node +require('../scripts/bin/release') diff --git a/bin/_types b/bin/_types new file mode 100755 index 000000000..0da7f76a8 --- /dev/null +++ b/bin/_types @@ -0,0 +1,2 @@ +#!/usr/bin/env node +require('../scripts/bin/types') diff --git a/bin/_version b/bin/_version new file mode 100755 index 000000000..7c04d127b --- /dev/null +++ b/bin/_version @@ -0,0 +1,2 @@ +#!/usr/bin/env node +require('../scripts/bin/version') diff --git a/bower.json b/bower.json deleted file mode 100644 index adc935c1b..000000000 --- a/bower.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": "interact", - "version": "1.1.2", - "main": "interact.js", - "description": "Drag and drop, resizing and multi-touch gestures with inertia and snapping for modern browsers (and also IE8+)", - "homepage": "http://interactjs.io", - "authors": [{ - "name" : "Taye Adeyemi", - "email": "dev@taye.me", - "url" : "http://taye.me" - }], - "keywords": [ - "interact.js", - "draggable", - "droppable", - "drag", - "drop", - "drag and drop", - "resize", - "touch", - "multi-touch", - "gesture", - "snap", - "inertia", - "grid", - "autoscroll", - "SVG" - ], - "devDependencies": { - "mocha": "*", - "chai": "*" - }, - "moduleType": [ - "amd", - "globals", - "node" - ], - "license": "MIT", - "ignore": [ - ".*", - "test", - "demo", - "img", - "docs" - ] -} diff --git a/bundle.rollup.config.cjs b/bundle.rollup.config.cjs new file mode 100644 index 000000000..b5342c743 --- /dev/null +++ b/bundle.rollup.config.cjs @@ -0,0 +1,90 @@ +/* eslint-disable import/no-extraneous-dependencies */ +const { resolve } = require('path') + +const babel = require('@rollup/plugin-babel') +const commonjs = require('@rollup/plugin-commonjs') +const nodeResolve = require('@rollup/plugin-node-resolve') +const replace = require('@rollup/plugin-replace') +const terser = require('@rollup/plugin-terser') +const { defineConfig } = require('rollup') + +const headers = require('./scripts/headers') +const { extendBabelOptions, getModuleDirectories, isPro } = require('./scripts/utils') + +const globals = { + react: 'React', + vue: 'Vue', +} +const external = Object.keys(globals) +const INPUT_EXTENSIONS = ['.ts', '.tsx', '.vue'] + +module.exports = defineConfig(async () => { + const variations = [ + { env: { NODE_ENV: 'development' }, ext: '.js', minify: isPro }, + { env: { NODE_ENV: 'production' }, ext: '.min.js', minify: true }, + ] + + return variations.map(({ minify, ext, env }) => { + const babelConfig = extendBabelOptions({ + babelrc: false, + configFile: false, + browserslistConfigFile: false, + targets: { ie: 9 }, + babelHelpers: 'bundled', + skipPreflightCheck: true, + extensions: INPUT_EXTENSIONS, + plugins: [[require.resolve('@babel/plugin-transform-runtime'), { helpers: false, regenerator: true }]], + }) + + return defineConfig({ + input: resolve(__dirname, 'packages', 'interactjs', 'index.ts'), + external, + plugins: [ + nodeResolve({ + modulePaths: getModuleDirectories(), + extensions: INPUT_EXTENSIONS, + }), + commonjs({ include: '**/node_modules/{rebound,symbol-tree}/**' }), + babel(babelConfig), + replace({ + preventAssignment: true, + values: Object.entries({ + npm_package_version: process.env.npm_package_version, + IJS_BUNDLE: '1', + ...env, + }).reduce((acc, [key, value]) => { + acc[`process.env.${key}`] = JSON.stringify(value) + return acc + }, {}), + }), + minify && + terser({ + module: false, + mangle: true, + compress: { + ecma: 5, + unsafe: true, + unsafe_Function: true, + unsafe_arrows: false, + unsafe_methods: true, + }, + format: { + preamble: headers?.min, + }, + }), + ], + context: 'window', + moduleContext: 'window', + output: { + file: resolve(__dirname, 'packages', 'interactjs', 'dist', `interact${ext}`), + format: 'umd', + name: 'interact', + banner: minify ? headers.min : headers.raw, + minifyInternalExports: true, + inlineDynamicImports: true, + sourcemap: true, + globals, + }, + }) + }) +}) diff --git a/demo/css/html_svg.css b/demo/css/html_svg.css deleted file mode 100644 index e8994ffcd..000000000 --- a/demo/css/html_svg.css +++ /dev/null @@ -1,39 +0,0 @@ -div.demo-node:before { - content : attr(id); - border-bottom : 2px solid red; -} - -div.demo-node { - position : absolute; - border-radius : 10px; - border : 5px solid #333333; - border : 5px solid #333333; - width : 200px; - height : 200px; - padding : 15px; - margin : 15px; - overflow : auto; - background-color: #2288FF; - - touch-action: none; -} - -ellipse { - fill : #EEEE00; - stroke : #000000; - stroke-width : 3px; - - touch-action: none; -} - -svg text.title { - transform : translate(0px, 20px); - - touch-action: none; -} - -svg text.eventData { - transform : translate(0px, -10px); - - touch-action: none; -} diff --git a/demo/css/snap.css b/demo/css/snap.css deleted file mode 100644 index 6bf792b3b..000000000 --- a/demo/css/snap.css +++ /dev/null @@ -1,73 +0,0 @@ -body { - margin: 0; - padding: 0; - border: 0; - font-family: "Arial", sans-serif; -} - -canvas { - position: absolute; - top: 0; - left: 0; - - margin: 20px; - padding: 0; - - touch-action: none; -} - -#status { - width: 20%; - height: 100%; - position: fixed; - right: 0; - top: 0; - - padding: 5px 5px; - border: none; - border-left: solid 8px #3a6bff; - - background-color: rgba(0, 143, 179, 0.298); - text-align: center; - font-size: 1.4em; -} - -#status h3 { - font-size: 1.1em; - margin: 3px 0px 0px 0px; - padding: 0; - line-height: 22px; - text-transform: capitalize; - font-weight: normal -} - -#status input[type=radio], #status input[type=checkbox] { - float: right; -} - -#status [type=range] { - margin: auto; - width: 90%; -} - -#status [disabled] { - cursor: default; -} - -#status label { - float: left; - cursor: pointer; - width: 95%; - text-align: left; -} - -#modes,#sliders { - overflow: hidden; - width: 100%; - margin: auto; -} - -#modes.disabled label.snap-mode { - cursor: default; - color: gray; -} diff --git a/demo/gallery.html b/demo/gallery.html deleted file mode 100644 index 1a176762d..000000000 --- a/demo/gallery.html +++ /dev/null @@ -1,113 +0,0 @@ - - - - -Image gallery with interact.js inertia and snapping - - - - - - - - - - - - - - - - diff --git a/demo/html_svg.html b/demo/html_svg.html deleted file mode 100644 index 48661b60a..000000000 --- a/demo/html_svg.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - interact.js demo - - - - - - - - - - diff --git a/demo/js/dropzones.js b/demo/js/dropzones.js deleted file mode 100644 index d982a6d42..000000000 --- a/demo/js/dropzones.js +++ /dev/null @@ -1,123 +0,0 @@ -(function (interact) { - - 'use strict'; - - var transformProp; - - interact.maxInteractions(Infinity); - - // setup draggable elements. - interact('.js-drag') - .draggable({ max: Infinity }) - .on('dragstart', function (event) { - event.interaction.x = parseInt(event.target.getAttribute('data-x'), 10) || 0; - event.interaction.y = parseInt(event.target.getAttribute('data-y'), 10) || 0; - }) - .on('dragmove', function (event) { - event.interaction.x += event.dx; - event.interaction.y += event.dy; - - if (transformProp) { - event.target.style[transformProp] = - 'translate(' + event.interaction.x + 'px, ' + event.interaction.y + 'px)'; - } - else { - event.target.style.left = event.interaction.x + 'px'; - event.target.style.top = event.interaction.y + 'px'; - } - }) - .on('dragend', function (event) { - event.target.setAttribute('data-x', event.interaction.x); - event.target.setAttribute('data-y', event.interaction.y); - }); - - // setup drop areas. - // dropzone #1 accepts draggable #1 - setupDropzone('#drop1', '#drag1'); - // dropzone #2 accepts draggable #1 and #2 - setupDropzone('#drop2', '#drag1, #drag2'); - // every dropzone accepts draggable #3 - setupDropzone('.js-drop', '#drag3'); - - /** - * Setup a given element as a dropzone. - * - * @param {HTMLElement|String} el - * @param {String} accept - */ - function setupDropzone(el, accept) { - interact(el) - .dropzone({ - accept: accept, - ondropactivate: function (event) { - addClass(event.relatedTarget, '-drop-possible'); - }, - ondropdeactivate: function (event) { - removeClass(event.relatedTarget, '-drop-possible'); - } - }) - .on('dropactivate', function (event) { - var active = event.target.getAttribute('active')|0; - - // change style if it was previously not active - if (active === 0) { - addClass(event.target, '-drop-possible'); - event.target.textContent = 'Drop me here!'; - } - - event.target.setAttribute('active', active + 1); - }) - .on('dropdeactivate', function (event) { - var active = event.target.getAttribute('active')|0; - - // change style if it was previously active - // but will no longer be active - if (active === 1) { - removeClass(event.target, '-drop-possible'); - event.target.textContent = 'Dropzone'; - } - - event.target.setAttribute('active', active - 1); - }) - .on('dragenter', function (event) { - addClass(event.target, '-drop-over'); - event.relatedTarget.textContent = 'I\'m in'; - }) - .on('dragleave', function (event) { - removeClass(event.target, '-drop-over'); - event.relatedTarget.textContent = 'Drag meโ€ฆ'; - }) - .on('drop', function (event) { - removeClass(event.target, '-drop-over'); - event.relatedTarget.textContent = 'Dropped'; - }); - } - - function addClass (element, className) { - if (element.classList) { - return element.classList.add(className); - } - else { - element.className += ' ' + className; - } - } - - function removeClass (element, className) { - if (element.classList) { - return element.classList.remove(className); - } - else { - element.className = element.className.replace(new RegExp(className + ' *', 'g'), ''); - } - } - - interact(document).on('ready', function () { - transformProp = 'transform' in document.body.style - ? 'transform': 'webkitTransform' in document.body.style - ? 'webkitTransform': 'mozTransform' in document.body.style - ? 'mozTransform': 'oTransform' in document.body.style - ? 'oTransform': 'msTransform' in document.body.style - ? 'msTransform': null; - }); - -}(window.interact)); diff --git a/demo/js/gallery.js b/demo/js/gallery.js deleted file mode 100644 index 9f173820d..000000000 --- a/demo/js/gallery.js +++ /dev/null @@ -1,67 +0,0 @@ -interact(document).on('DOMContentLoaded', function () { -"use strict"; -/* global interact, Modernizr */ - -var preTransform = Modernizr.prefixed('transform'), - snapTarget = {}; - -interact('#gallery .thumbnail') - .draggable({ - onstart: function (event) { - snapTarget = { - x: $('#gallery .stage').width() / 2, - y: $('#gallery .stage').height() / 2, - range: Infinity - }; - - var thumb = event.target; - - thumb.classList.add('dragging'); - thumb.dataset.dragX = 0; - thumb.dataset.dragY = 0; - }, - onmove: function (event) { - var thumb = event.target, - x = (thumb.dataset.dragX|0) + event.dx, - y = (thumb.dataset.dragY|0) + event.dy; - - thumb.style[preTransform] = 'translate(' + x + 'px,' + y + 'px)'; - - thumb.dataset.dragX = x; - thumb.dataset.dragY = y; - }, - onend: function (event) { - var $thumb = $(event.target); - - // if the drag was snapped to the stage - if (event.pageX === snapTarget.x && event.pageY === snapTarget.y) { - $('#gallery .stage img').removeClass('active'); - $('#gallery .thumbnail').removeClass('expanded') - .not($thumb).css(preTransform, ''); - - $thumb.addClass('expanded'); - $('#gallery [data-image=' + $thumb.data('for') + ']').addClass('active'); - } - else { - $thumb.css(preTransform, ''); - } - - $thumb.removeClass('dragging'); - } - }) - .origin($('#gallery')[0]) - .snap({ - mode: 'path', - // If the pointer is far enough above the bottom of the stage - // then snap to the center of the stage - paths: [function (x, y) { - if (y < $('#gallery .stage').height() * 0.7) { - return snapTarget; - } - return {}; - }], - endOnly: true - }) - //.snap(false) - .inertia(true); -}()); diff --git a/demo/js/html_svg.js b/demo/js/html_svg.js deleted file mode 100644 index 41b232cd2..000000000 --- a/demo/js/html_svg.js +++ /dev/null @@ -1,259 +0,0 @@ -/* - * Copyright (c) 2012, 2013 Taye Adeyemi - * Open source under the MIT License. - * https://raw.github.com/taye/interact.js/master/LICENSE - */ - -(function(interact) { - 'use strict'; - - var svg, - svgNS = 'http://www.w3.org/2000/svg', - SVGElement = window.SVGElement; - - function DemoGraphic(id) { - var width = window.innerWidth, - height = window.innerHeight, - group = svg.appendChild(document.createElementNS(svgNS, 'g')), - ellipse = group.appendChild(document.createElementNS(svgNS, 'ellipse')), - text = group.appendChild(document.createElementNS( svgNS, 'text')), - title = group.appendChild(document.createElementNS(svgNS, 'text')); - - - ellipse.demo = true; - ellipse.text = text; - window[id] = ellipse; - - ellipse.dragX = Math.random()*(width - 200); - ellipse.dragY = Math.random()*(height - 200); - ellipse.resizeWidth = 80; - ellipse.resizeHeight = 80; - - group.setAttribute('transform', ['translate(', ellipse.dragX, ellipse.dragY, ')'].join(' ')); - group.setAttribute('class', 'demo-node'); - - ellipse.setAttribute('cx', 0); - ellipse.setAttribute('cy', 0); - ellipse.setAttribute('rx', ellipse.resizeWidth); - ellipse.setAttribute('ry', ellipse.resizeHeight); - - title.textContent = id; - title.setAttribute('class', 'title'); - text.setAttribute('class', 'eventData'); - - - this.group = group; - this.ellipse = ellipse; - this.text = text; - this.title = title; - - /* - interact(this.ellipse).set({ - draggable: true, - dropzone: true, - resizable: true - }); - */ - } - - function DemoNode (id) { - this.element = document.body.appendChild(document.createElement('div')); - this.element.className = 'demo-node'; - this.element.id = id; - this.element.demo = true; - this.element.style.width = '200px'; - this.element.style.height = '200px'; - - this.element.text = this.element.appendChild(document.createElement('pre')); - this.element.appendChild(document.createElement('br')); - this.element.style.left = Math.random()*((window.innerWidth || 800) - 200) + 'px'; - this.element.style.top = Math.random()*((window.innerHeight || 800) - 200) + 'px'; - - /*interact(this.element).set({ - draggable: true, - dropzone: true, - resizable: true, - gesturable: true - });*/ - window[id] = this.element; - } - - function nodeEventDebug(e) { - var textProp, - target = e.target, - nl; - - textProp = 'textContent'; - nl = '\n'; - - if ( target.demo && indexOf(interact.eventTypes, e.type) !== -1 ) { - target.text[textProp] = nl + e.type; - target.text[textProp] += nl + ' x0, y0 : (' + e.x0 + ', ' + e.y0 + ')'; - target.text[textProp] += nl + ' dx, dy : (' + e.dx + ', ' + e.dy + ')'; - target.text[textProp] += nl + ' pageX, pageY: (' + e.pageX + ', ' + e.pageY + ')'; - target.text[textProp] += nl + ' speed : ' + e.speed; - - if (indexOf(e.type, 'gesture') !== -1) { - target.text[textProp] += nl + ' distance: ' + e.distance; - target.text[textProp] += nl + ' scale : ' + e.scale; - target.text[textProp] += nl + ' angle : ' + e.angle; - target.text[textProp] += nl + ' rotation: ' + e.rotation; - } - } - } - - function eventProps(e) { - var debug = [], - prop; - if (typeof e === 'object') { - debug.push('event: '); - for (prop in e) { - if (e.hasOwnProperty(prop)) { - debug.push('\n ' + prop + ' : ' + e[prop]); - } - } - } - if (event.touches && event.touches.length) { - debug += 'touches[0]: '; - for (prop in e.touches[0]) { - if (e.touches[0].hasOwnProperty(prop)) { - debug.push('\n ' + prop + ' : ' + e.touches[0][prop]); - } - } - } - for (prop in e) { - if (e.hasOwnProperty(prop)) { - debug.push('\n' + prop + ' : ' + e[prop]); - } - } - - return debug.join(''); - } - - function dragMove(e) { - var target = e.target; - - if ('SVGElement' in window && e.target instanceof SVGElement) { - target.dragX += e.dx; - target.dragY += e.dy; - - target.parentNode.setAttribute('transform', ['translate(', target.dragX, target.dragY, ')'].join(' ')); - } else { - target.style.left = parseInt(target.style.left || 0, 10) + e.dx + 'px'; - target.style.top = parseInt(target.style.top || 0, 10) + e.dy + 'px'; - } - } - - function resizeMove(e) { - var target = e.target, - rect = target.getClientRects()[0]; - - if ('SVGElement' in window && target instanceof SVGElement) { - target.resizeWidth += e.dx; - target.resizeHeight += e.dy; - - target.setAttribute('rx', target.resizeWidth); - target.setAttribute('ry', target.resizeHeight); - } - else { - target.style.width = Math.max(parseInt(target.style.width || 0, 10) + e.dx, 50) + 'px'; - target.style.height = Math.max(parseInt(target.style.height || 0, 10) + e.dy, 50) + 'px'; - } - } - - interact.on('resizemove', resizeMove); - interact.on('dragmove', dragMove); - - function dropNode (e) { - if ('SVGElement' in window && e.draggable instanceof SVGElement) { - return; - } - - var dropzone = e.target, - draggable = e.relatedTarget, - dropzoneRect = dropzone.getClientRects()[0], - parent = draggable.parentNode, - dropRect = { - x: dropzoneRect.left + 20, - y: dropzoneRect.top + 20 - }; - - dropRect.x += (window.scrollX || document.documentElement.scrollLeft); - dropRect.y += (window.scrollY || document.documentElement.scrollTop); - dropRect.width = (dropzoneRect.right - dropzoneRect.left) / 2; - dropRect.height = (dropzoneRect.bottom - dropzoneRect.top) / 2; - - draggable.style.left = dropRect.x + 'px'; - draggable.style.top = dropRect.y + 'px'; - draggable.style.width = dropRect.width + 'px'; - draggable.style.height = dropRect.height + 'px'; - - parent.appendChild(parent.removeChild(draggable)); - } - - function onReady () { - var i; - - if ('SVGElement' in window) { - var width = window.innerWidth, - height = window.innerHeight; - - svg = document.createElementNS(svgNS, 'svg'); - svg.id = 'svg'; - svg.setAttribute('viewBox', '0 0 ' + width + ' ' + height); - svg.setAttribute('width', width); - svg.setAttribute('height', height); - document.body.appendChild(svg); - - window.svg = svg; - - for (i = 0; i < 2; i++) { - new DemoGraphic('graphic' + i); - } - } - - for (i = 0; i < 2; i++) { - new DemoNode('node' + i); - } - - interact('div.demo-node, .demo-node ellipse') - .draggable({ max: 2 }) - .gesturable({ max: 1 }) - .resizable({ max: 2 }) - .dropzone(true); - } - - function indexOf (array, target) { - for (var i = 0, len = array.length; i < len; i++) { - if (array[i] === target) { - return i; - } - } - - return -1; - } - - interact.maxInteractions(Infinity); - - interact(window).on('addEventListener' in document? 'DOMContentLoaded': 'load', onReady); - - // Display event properties for debugging - interact.on('resizestart', nodeEventDebug); - interact.on('resizemove', nodeEventDebug); - interact.on('resizeend', nodeEventDebug); - interact.on('gesturestart', nodeEventDebug); - interact.on('gesturemove', nodeEventDebug); - interact.on('gestureend', nodeEventDebug); - interact.on('dragstart', nodeEventDebug); - interact.on('dragmove', nodeEventDebug); - interact.on('dragend', nodeEventDebug); - - interact.on('drop', dropNode); - - window.demo = { - DemoNode : DemoNode, - DemoGraphic : DemoGraphic, - nodeEventDebug: nodeEventDebug, - eventProps : eventProps - }; -}(window.interact)); diff --git a/demo/js/jquery-2.1.0.min.js b/demo/js/jquery-2.1.0.min.js deleted file mode 100644 index cbe6abe59..000000000 --- a/demo/js/jquery-2.1.0.min.js +++ /dev/null @@ -1,4 +0,0 @@ -/*! jQuery v2.1.0 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */ -!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k="".trim,l={},m=a.document,n="2.1.0",o=function(a,b){return new o.fn.init(a,b)},p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};o.fn=o.prototype={jquery:n,constructor:o,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=o.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return o.each(this,a,b)},map:function(a){return this.pushStack(o.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},o.extend=o.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||o.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(o.isPlainObject(d)||(e=o.isArray(d)))?(e?(e=!1,f=c&&o.isArray(c)?c:[]):f=c&&o.isPlainObject(c)?c:{},g[b]=o.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},o.extend({expando:"jQuery"+(n+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===o.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){return a-parseFloat(a)>=0},isPlainObject:function(a){if("object"!==o.type(a)||a.nodeType||o.isWindow(a))return!1;try{if(a.constructor&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(b){return!1}return!0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=o.trim(a),a&&(1===a.indexOf("use strict")?(b=m.createElement("script"),b.text=a,m.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=s(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":k.call(a)},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?o.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:g.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=s(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(c=a[b],b=a,a=c),o.isFunction(a)?(e=d.call(arguments,2),f=function(){return a.apply(b||this,e.concat(d.call(arguments)))},f.guid=a.guid=a.guid||o.guid++,f):void 0},now:Date.now,support:l}),o.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=a.length,c=o.type(a);return"function"===c||o.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s="sizzle"+-new Date,t=a.document,u=0,v=0,w=eb(),x=eb(),y=eb(),z=function(a,b){return a===b&&(j=!0),0},A="undefined",B=1<<31,C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=D.indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]===a)return b;return-1},J="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",K="[\\x20\\t\\r\\n\\f]",L="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",M=L.replace("w","w#"),N="\\["+K+"*("+L+")"+K+"*(?:([*^$|!~]?=)"+K+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+M+")|)|)"+K+"*\\]",O=":("+L+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+N.replace(3,8)+")*)|.*)\\)|)",P=new RegExp("^"+K+"+|((?:^|[^\\\\])(?:\\\\.)*)"+K+"+$","g"),Q=new RegExp("^"+K+"*,"+K+"*"),R=new RegExp("^"+K+"*([>+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(O),U=new RegExp("^"+M+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L.replace("w","w*")+")"),ATTR:new RegExp("^"+N),PSEUDO:new RegExp("^"+O),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=/'|\\/g,ab=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),bb=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)};try{G.apply(D=H.call(t.childNodes),t.childNodes),D[t.childNodes.length].nodeType}catch(cb){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function db(a,b,d,e){var f,g,h,i,j,m,p,q,u,v;if((b?b.ownerDocument||b:t)!==l&&k(b),b=b||l,d=d||[],!a||"string"!=typeof a)return d;if(1!==(i=b.nodeType)&&9!==i)return[];if(n&&!e){if(f=Z.exec(a))if(h=f[1]){if(9===i){if(g=b.getElementById(h),!g||!g.parentNode)return d;if(g.id===h)return d.push(g),d}else if(b.ownerDocument&&(g=b.ownerDocument.getElementById(h))&&r(b,g)&&g.id===h)return d.push(g),d}else{if(f[2])return G.apply(d,b.getElementsByTagName(a)),d;if((h=f[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(h)),d}if(c.qsa&&(!o||!o.test(a))){if(q=p=s,u=b,v=9===i&&a,1===i&&"object"!==b.nodeName.toLowerCase()){m=ob(a),(p=b.getAttribute("id"))?q=p.replace(_,"\\$&"):b.setAttribute("id",q),q="[id='"+q+"'] ",j=m.length;while(j--)m[j]=q+pb(m[j]);u=$.test(a)&&mb(b.parentNode)||b,v=m.join(",")}if(v)try{return G.apply(d,u.querySelectorAll(v)),d}catch(w){}finally{p||b.removeAttribute("id")}}}return xb(a.replace(P,"$1"),b,d,e)}function eb(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function fb(a){return a[s]=!0,a}function gb(a){var b=l.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function hb(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function ib(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||B)-(~a.sourceIndex||B);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function jb(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function kb(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function lb(a){return fb(function(b){return b=+b,fb(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function mb(a){return a&&typeof a.getElementsByTagName!==A&&a}c=db.support={},f=db.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},k=db.setDocument=function(a){var b,e=a?a.ownerDocument||a:t,g=e.defaultView;return e!==l&&9===e.nodeType&&e.documentElement?(l=e,m=e.documentElement,n=!f(e),g&&g!==g.top&&(g.addEventListener?g.addEventListener("unload",function(){k()},!1):g.attachEvent&&g.attachEvent("onunload",function(){k()})),c.attributes=gb(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=gb(function(a){return a.appendChild(e.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(e.getElementsByClassName)&&gb(function(a){return a.innerHTML="
",a.firstChild.className="i",2===a.getElementsByClassName("i").length}),c.getById=gb(function(a){return m.appendChild(a).id=s,!e.getElementsByName||!e.getElementsByName(s).length}),c.getById?(d.find.ID=function(a,b){if(typeof b.getElementById!==A&&n){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ab,bb);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ab,bb);return function(a){var c=typeof a.getAttributeNode!==A&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return typeof b.getElementsByTagName!==A?b.getElementsByTagName(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return typeof b.getElementsByClassName!==A&&n?b.getElementsByClassName(a):void 0},p=[],o=[],(c.qsa=Y.test(e.querySelectorAll))&&(gb(function(a){a.innerHTML="",a.querySelectorAll("[t^='']").length&&o.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||o.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll(":checked").length||o.push(":checked")}),gb(function(a){var b=e.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&o.push("name"+K+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||o.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),o.push(",.*:")})),(c.matchesSelector=Y.test(q=m.webkitMatchesSelector||m.mozMatchesSelector||m.oMatchesSelector||m.msMatchesSelector))&&gb(function(a){c.disconnectedMatch=q.call(a,"div"),q.call(a,"[s!='']:x"),p.push("!=",O)}),o=o.length&&new RegExp(o.join("|")),p=p.length&&new RegExp(p.join("|")),b=Y.test(m.compareDocumentPosition),r=b||Y.test(m.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},z=b?function(a,b){if(a===b)return j=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===e||a.ownerDocument===t&&r(t,a)?-1:b===e||b.ownerDocument===t&&r(t,b)?1:i?I.call(i,a)-I.call(i,b):0:4&d?-1:1)}:function(a,b){if(a===b)return j=!0,0;var c,d=0,f=a.parentNode,g=b.parentNode,h=[a],k=[b];if(!f||!g)return a===e?-1:b===e?1:f?-1:g?1:i?I.call(i,a)-I.call(i,b):0;if(f===g)return ib(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)k.unshift(c);while(h[d]===k[d])d++;return d?ib(h[d],k[d]):h[d]===t?-1:k[d]===t?1:0},e):l},db.matches=function(a,b){return db(a,null,null,b)},db.matchesSelector=function(a,b){if((a.ownerDocument||a)!==l&&k(a),b=b.replace(S,"='$1']"),!(!c.matchesSelector||!n||p&&p.test(b)||o&&o.test(b)))try{var d=q.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return db(b,l,null,[a]).length>0},db.contains=function(a,b){return(a.ownerDocument||a)!==l&&k(a),r(a,b)},db.attr=function(a,b){(a.ownerDocument||a)!==l&&k(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!n):void 0;return void 0!==f?f:c.attributes||!n?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},db.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},db.uniqueSort=function(a){var b,d=[],e=0,f=0;if(j=!c.detectDuplicates,i=!c.sortStable&&a.slice(0),a.sort(z),j){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return i=null,a},e=db.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=db.selectors={cacheLength:50,createPseudo:fb,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ab,bb),a[3]=(a[4]||a[5]||"").replace(ab,bb),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||db.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&db.error(a[0]),a},PSEUDO:function(a){var b,c=!a[5]&&a[2];return V.CHILD.test(a[0])?null:(a[3]&&void 0!==a[4]?a[2]=a[4]:c&&T.test(c)&&(b=ob(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ab,bb).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=w[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&w(a,function(a){return b.test("string"==typeof a.className&&a.className||typeof a.getAttribute!==A&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=db.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),t=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&t){k=q[s]||(q[s]={}),j=k[a]||[],n=j[0]===u&&j[1],m=j[0]===u&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[u,n,m];break}}else if(t&&(j=(b[s]||(b[s]={}))[a])&&j[0]===u)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(t&&((l[s]||(l[s]={}))[a]=[u,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||db.error("unsupported pseudo: "+a);return e[s]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?fb(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I.call(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:fb(function(a){var b=[],c=[],d=g(a.replace(P,"$1"));return d[s]?fb(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:fb(function(a){return function(b){return db(a,b).length>0}}),contains:fb(function(a){return function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:fb(function(a){return U.test(a||"")||db.error("unsupported lang: "+a),a=a.replace(ab,bb).toLowerCase(),function(b){var c;do if(c=n?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===m},focus:function(a){return a===l.activeElement&&(!l.hasFocus||l.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:lb(function(){return[0]}),last:lb(function(a,b){return[b-1]}),eq:lb(function(a,b,c){return[0>c?c+b:c]}),even:lb(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:lb(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:lb(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:lb(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function qb(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=v++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[u,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[s]||(b[s]={}),(h=i[d])&&h[0]===u&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function rb(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function sb(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function tb(a,b,c,d,e,f){return d&&!d[s]&&(d=tb(d)),e&&!e[s]&&(e=tb(e,f)),fb(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||wb(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:sb(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=sb(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?I.call(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=sb(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ub(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],i=g||d.relative[" "],j=g?1:0,k=qb(function(a){return a===b},i,!0),l=qb(function(a){return I.call(b,a)>-1},i,!0),m=[function(a,c,d){return!g&&(d||c!==h)||((b=c).nodeType?k(a,c,d):l(a,c,d))}];f>j;j++)if(c=d.relative[a[j].type])m=[qb(rb(m),c)];else{if(c=d.filter[a[j].type].apply(null,a[j].matches),c[s]){for(e=++j;f>e;e++)if(d.relative[a[e].type])break;return tb(j>1&&rb(m),j>1&&pb(a.slice(0,j-1).concat({value:" "===a[j-2].type?"*":""})).replace(P,"$1"),c,e>j&&ub(a.slice(j,e)),f>e&&ub(a=a.slice(e)),f>e&&pb(a))}m.push(c)}return rb(m)}function vb(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,i,j,k){var m,n,o,p=0,q="0",r=f&&[],s=[],t=h,v=f||e&&d.find.TAG("*",k),w=u+=null==t?1:Math.random()||.1,x=v.length;for(k&&(h=g!==l&&g);q!==x&&null!=(m=v[q]);q++){if(e&&m){n=0;while(o=a[n++])if(o(m,g,i)){j.push(m);break}k&&(u=w)}c&&((m=!o&&m)&&p--,f&&r.push(m))}if(p+=q,c&&q!==p){n=0;while(o=b[n++])o(r,s,g,i);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=E.call(j));s=sb(s)}G.apply(j,s),k&&!f&&s.length>0&&p+b.length>1&&db.uniqueSort(j)}return k&&(u=w,h=t),r};return c?fb(f):f}g=db.compile=function(a,b){var c,d=[],e=[],f=y[a+" "];if(!f){b||(b=ob(a)),c=b.length;while(c--)f=ub(b[c]),f[s]?d.push(f):e.push(f);f=y(a,vb(e,d))}return f};function wb(a,b,c){for(var d=0,e=b.length;e>d;d++)db(a,b[d],c);return c}function xb(a,b,e,f){var h,i,j,k,l,m=ob(a);if(!f&&1===m.length){if(i=m[0]=m[0].slice(0),i.length>2&&"ID"===(j=i[0]).type&&c.getById&&9===b.nodeType&&n&&d.relative[i[1].type]){if(b=(d.find.ID(j.matches[0].replace(ab,bb),b)||[])[0],!b)return e;a=a.slice(i.shift().value.length)}h=V.needsContext.test(a)?0:i.length;while(h--){if(j=i[h],d.relative[k=j.type])break;if((l=d.find[k])&&(f=l(j.matches[0].replace(ab,bb),$.test(i[0].type)&&mb(b.parentNode)||b))){if(i.splice(h,1),a=f.length&&pb(i),!a)return G.apply(e,f),e;break}}}return g(a,m)(f,b,!n,e,$.test(a)&&mb(b.parentNode)||b),e}return c.sortStable=s.split("").sort(z).join("")===s,c.detectDuplicates=!!j,k(),c.sortDetached=gb(function(a){return 1&a.compareDocumentPosition(l.createElement("div"))}),gb(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||hb("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&gb(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||hb("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),gb(function(a){return null==a.getAttribute("disabled")})||hb(J,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),db}(a);o.find=t,o.expr=t.selectors,o.expr[":"]=o.expr.pseudos,o.unique=t.uniqueSort,o.text=t.getText,o.isXMLDoc=t.isXML,o.contains=t.contains;var u=o.expr.match.needsContext,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^.[^:#\[\.,]*$/;function x(a,b,c){if(o.isFunction(b))return o.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return o.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(w.test(b))return o.filter(b,a,c);b=o.filter(b,a)}return o.grep(a,function(a){return g.call(b,a)>=0!==c})}o.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?o.find.matchesSelector(d,a)?[d]:[]:o.find.matches(a,o.grep(b,function(a){return 1===a.nodeType}))},o.fn.extend({find:function(a){var b,c=this.length,d=[],e=this;if("string"!=typeof a)return this.pushStack(o(a).filter(function(){for(b=0;c>b;b++)if(o.contains(e[b],this))return!0}));for(b=0;c>b;b++)o.find(a,e[b],d);return d=this.pushStack(c>1?o.unique(d):d),d.selector=this.selector?this.selector+" "+a:a,d},filter:function(a){return this.pushStack(x(this,a||[],!1))},not:function(a){return this.pushStack(x(this,a||[],!0))},is:function(a){return!!x(this,"string"==typeof a&&u.test(a)?o(a):a||[],!1).length}});var y,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=o.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||y).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof o?b[0]:b,o.merge(this,o.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:m,!0)),v.test(c[1])&&o.isPlainObject(b))for(c in b)o.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}return d=m.getElementById(c[2]),d&&d.parentNode&&(this.length=1,this[0]=d),this.context=m,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):o.isFunction(a)?"undefined"!=typeof y.ready?y.ready(a):a(o):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),o.makeArray(a,this))};A.prototype=o.fn,y=o(m);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};o.extend({dir:function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&o(a).is(c))break;d.push(a)}return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),o.fn.extend({has:function(a){var b=o(a,this),c=b.length;return this.filter(function(){for(var a=0;c>a;a++)if(o.contains(this,b[a]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=u.test(a)||"string"!=typeof a?o(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&o.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?o.unique(f):f)},index:function(a){return a?"string"==typeof a?g.call(o(a),this[0]):g.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(o.unique(o.merge(this.get(),o(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){while((a=a[b])&&1!==a.nodeType);return a}o.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return o.dir(a,"parentNode")},parentsUntil:function(a,b,c){return o.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return o.dir(a,"nextSibling")},prevAll:function(a){return o.dir(a,"previousSibling")},nextUntil:function(a,b,c){return o.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return o.dir(a,"previousSibling",c)},siblings:function(a){return o.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return o.sibling(a.firstChild)},contents:function(a){return a.contentDocument||o.merge([],a.childNodes)}},function(a,b){o.fn[a]=function(c,d){var e=o.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=o.filter(d,e)),this.length>1&&(C[a]||o.unique(e),B.test(a)&&e.reverse()),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return o.each(a.match(E)||[],function(a,c){b[c]=!0}),b}o.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):o.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(b=a.memory&&l,c=!0,g=e||0,e=0,f=h.length,d=!0;h&&f>g;g++)if(h[g].apply(l[0],l[1])===!1&&a.stopOnFalse){b=!1;break}d=!1,h&&(i?i.length&&j(i.shift()):b?h=[]:k.disable())},k={add:function(){if(h){var c=h.length;!function g(b){o.each(b,function(b,c){var d=o.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&g(c)})}(arguments),d?f=h.length:b&&(e=c,j(b))}return this},remove:function(){return h&&o.each(arguments,function(a,b){var c;while((c=o.inArray(b,h,c))>-1)h.splice(c,1),d&&(f>=c&&f--,g>=c&&g--)}),this},has:function(a){return a?o.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],f=0,this},disable:function(){return h=i=b=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,b||k.disable(),this},locked:function(){return!i},fireWith:function(a,b){return!h||c&&!i||(b=b||[],b=[a,b.slice?b.slice():b],d?i.push(b):j(b)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!c}};return k},o.extend({Deferred:function(a){var b=[["resolve","done",o.Callbacks("once memory"),"resolved"],["reject","fail",o.Callbacks("once memory"),"rejected"],["notify","progress",o.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return o.Deferred(function(c){o.each(b,function(b,f){var g=o.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&o.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?o.extend(a,d):d}},e={};return d.pipe=d.then,o.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&o.isFunction(a.promise)?e:0,g=1===f?a:o.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&o.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;o.fn.ready=function(a){return o.ready.promise().done(a),this},o.extend({isReady:!1,readyWait:1,holdReady:function(a){a?o.readyWait++:o.ready(!0)},ready:function(a){(a===!0?--o.readyWait:o.isReady)||(o.isReady=!0,a!==!0&&--o.readyWait>0||(H.resolveWith(m,[o]),o.fn.trigger&&o(m).trigger("ready").off("ready")))}});function I(){m.removeEventListener("DOMContentLoaded",I,!1),a.removeEventListener("load",I,!1),o.ready()}o.ready.promise=function(b){return H||(H=o.Deferred(),"complete"===m.readyState?setTimeout(o.ready):(m.addEventListener("DOMContentLoaded",I,!1),a.addEventListener("load",I,!1))),H.promise(b)},o.ready.promise();var J=o.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===o.type(c)){e=!0;for(h in c)o.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,o.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(o(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f};o.acceptData=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function K(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=o.expando+Math.random()}K.uid=1,K.accepts=o.acceptData,K.prototype={key:function(a){if(!K.accepts(a))return 0;var b={},c=a[this.expando];if(!c){c=K.uid++;try{b[this.expando]={value:c},Object.defineProperties(a,b)}catch(d){b[this.expando]=c,o.extend(a,b)}}return this.cache[c]||(this.cache[c]={}),c},set:function(a,b,c){var d,e=this.key(a),f=this.cache[e];if("string"==typeof b)f[b]=c;else if(o.isEmptyObject(f))o.extend(this.cache[e],b);else for(d in b)f[d]=b[d];return f},get:function(a,b){var c=this.cache[this.key(a)];return void 0===b?c:c[b]},access:function(a,b,c){var d;return void 0===b||b&&"string"==typeof b&&void 0===c?(d=this.get(a,b),void 0!==d?d:this.get(a,o.camelCase(b))):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d,e,f=this.key(a),g=this.cache[f];if(void 0===b)this.cache[f]={};else{o.isArray(b)?d=b.concat(b.map(o.camelCase)):(e=o.camelCase(b),b in g?d=[b,e]:(d=e,d=d in g?[d]:d.match(E)||[])),c=d.length;while(c--)delete g[d[c]]}},hasData:function(a){return!o.isEmptyObject(this.cache[a[this.expando]]||{})},discard:function(a){a[this.expando]&&delete this.cache[a[this.expando]]}};var L=new K,M=new K,N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(O,"-$1").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?o.parseJSON(c):c}catch(e){}M.set(a,b,c)}else c=void 0;return c}o.extend({hasData:function(a){return M.hasData(a)||L.hasData(a)},data:function(a,b,c){return M.access(a,b,c)},removeData:function(a,b){M.remove(a,b)},_data:function(a,b,c){return L.access(a,b,c)},_removeData:function(a,b){L.remove(a,b)}}),o.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=M.get(f),1===f.nodeType&&!L.get(f,"hasDataAttrs"))){c=g.length; -while(c--)d=g[c].name,0===d.indexOf("data-")&&(d=o.camelCase(d.slice(5)),P(f,d,e[d]));L.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){M.set(this,a)}):J(this,function(b){var c,d=o.camelCase(a);if(f&&void 0===b){if(c=M.get(f,a),void 0!==c)return c;if(c=M.get(f,d),void 0!==c)return c;if(c=P(f,d,void 0),void 0!==c)return c}else this.each(function(){var c=M.get(this,d);M.set(this,d,b),-1!==a.indexOf("-")&&void 0!==c&&M.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){M.remove(this,a)})}}),o.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=L.get(a,b),c&&(!d||o.isArray(c)?d=L.access(a,b,o.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=o.queue(a,b),d=c.length,e=c.shift(),f=o._queueHooks(a,b),g=function(){o.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return L.get(a,c)||L.access(a,c,{empty:o.Callbacks("once memory").add(function(){L.remove(a,[b+"queue",c])})})}}),o.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length",l.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="",l.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var U="undefined";l.focusinBubbles="onfocusin"in a;var V=/^key/,W=/^(?:mouse|contextmenu)|click/,X=/^(?:focusinfocus|focusoutblur)$/,Y=/^([^.]*)(?:\.(.+)|)$/;function Z(){return!0}function $(){return!1}function _(){try{return m.activeElement}catch(a){}}o.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,p,q,r=L.get(a);if(r){c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=o.guid++),(i=r.events)||(i=r.events={}),(g=r.handle)||(g=r.handle=function(b){return typeof o!==U&&o.event.triggered!==b.type?o.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(E)||[""],j=b.length;while(j--)h=Y.exec(b[j])||[],n=q=h[1],p=(h[2]||"").split(".").sort(),n&&(l=o.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=o.event.special[n]||{},k=o.extend({type:n,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&o.expr.match.needsContext.test(e),namespace:p.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,p,g)!==!1||a.addEventListener&&a.addEventListener(n,g,!1)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),o.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,p,q,r=L.hasData(a)&&L.get(a);if(r&&(i=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=Y.exec(b[j])||[],n=q=h[1],p=(h[2]||"").split(".").sort(),n){l=o.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&q!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||o.removeEvent(a,n,r.handle),delete i[n])}else for(n in i)o.event.remove(a,n+b[j],c,d,!0);o.isEmptyObject(i)&&(delete r.handle,L.remove(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,l,n,p=[d||m],q=j.call(b,"type")?b.type:b,r=j.call(b,"namespace")?b.namespace.split("."):[];if(g=h=d=d||m,3!==d.nodeType&&8!==d.nodeType&&!X.test(q+o.event.triggered)&&(q.indexOf(".")>=0&&(r=q.split("."),q=r.shift(),r.sort()),k=q.indexOf(":")<0&&"on"+q,b=b[o.expando]?b:new o.Event(q,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=r.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:o.makeArray(c,[b]),n=o.event.special[q]||{},e||!n.trigger||n.trigger.apply(d,c)!==!1)){if(!e&&!n.noBubble&&!o.isWindow(d)){for(i=n.delegateType||q,X.test(i+q)||(g=g.parentNode);g;g=g.parentNode)p.push(g),h=g;h===(d.ownerDocument||m)&&p.push(h.defaultView||h.parentWindow||a)}f=0;while((g=p[f++])&&!b.isPropagationStopped())b.type=f>1?i:n.bindType||q,l=(L.get(g,"events")||{})[b.type]&&L.get(g,"handle"),l&&l.apply(g,c),l=k&&g[k],l&&l.apply&&o.acceptData(g)&&(b.result=l.apply(g,c),b.result===!1&&b.preventDefault());return b.type=q,e||b.isDefaultPrevented()||n._default&&n._default.apply(p.pop(),c)!==!1||!o.acceptData(d)||k&&o.isFunction(d[q])&&!o.isWindow(d)&&(h=d[k],h&&(d[k]=null),o.event.triggered=q,d[q](),o.event.triggered=void 0,h&&(d[k]=h)),b.result}},dispatch:function(a){a=o.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(L.get(this,"events")||{})[a.type]||[],k=o.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=o.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(g.namespace))&&(a.handleObj=g,a.data=g.data,e=((o.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(a.result=e)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!==this;i=i.parentNode||this)if(i.disabled!==!0||"click"!==a.type){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?o(e,this).index(i)>=0:o.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h]*)\/>/gi,bb=/<([\w:]+)/,cb=/<|&#?\w+;/,db=/<(?:script|style|link)/i,eb=/checked\s*(?:[^=]|=\s*.checked.)/i,fb=/^$|\/(?:java|ecma)script/i,gb=/^true\/(.*)/,hb=/^\s*\s*$/g,ib={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ib.optgroup=ib.option,ib.tbody=ib.tfoot=ib.colgroup=ib.caption=ib.thead,ib.th=ib.td;function jb(a,b){return o.nodeName(a,"table")&&o.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function kb(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function lb(a){var b=gb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function mb(a,b){for(var c=0,d=a.length;d>c;c++)L.set(a[c],"globalEval",!b||L.get(b[c],"globalEval"))}function nb(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(L.hasData(a)&&(f=L.access(a),g=L.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)o.event.add(b,e,j[e][c])}M.hasData(a)&&(h=M.access(a),i=o.extend({},h),M.set(b,i))}}function ob(a,b){var c=a.getElementsByTagName?a.getElementsByTagName(b||"*"):a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&o.nodeName(a,b)?o.merge([a],c):c}function pb(a,b){var c=b.nodeName.toLowerCase();"input"===c&&T.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}o.extend({clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=o.contains(a.ownerDocument,a);if(!(l.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||o.isXMLDoc(a)))for(g=ob(h),f=ob(a),d=0,e=f.length;e>d;d++)pb(f[d],g[d]);if(b)if(c)for(f=f||ob(a),g=g||ob(h),d=0,e=f.length;e>d;d++)nb(f[d],g[d]);else nb(a,h);return g=ob(h,"script"),g.length>0&&mb(g,!i&&ob(a,"script")),h},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,k=b.createDocumentFragment(),l=[],m=0,n=a.length;n>m;m++)if(e=a[m],e||0===e)if("object"===o.type(e))o.merge(l,e.nodeType?[e]:e);else if(cb.test(e)){f=f||k.appendChild(b.createElement("div")),g=(bb.exec(e)||["",""])[1].toLowerCase(),h=ib[g]||ib._default,f.innerHTML=h[1]+e.replace(ab,"<$1>")+h[2],j=h[0];while(j--)f=f.lastChild;o.merge(l,f.childNodes),f=k.firstChild,f.textContent=""}else l.push(b.createTextNode(e));k.textContent="",m=0;while(e=l[m++])if((!d||-1===o.inArray(e,d))&&(i=o.contains(e.ownerDocument,e),f=ob(k.appendChild(e),"script"),i&&mb(f),c)){j=0;while(e=f[j++])fb.test(e.type||"")&&c.push(e)}return k},cleanData:function(a){for(var b,c,d,e,f,g,h=o.event.special,i=0;void 0!==(c=a[i]);i++){if(o.acceptData(c)&&(f=c[L.expando],f&&(b=L.cache[f]))){if(d=Object.keys(b.events||{}),d.length)for(g=0;void 0!==(e=d[g]);g++)h[e]?o.event.remove(c,e):o.removeEvent(c,e,b.handle);L.cache[f]&&delete L.cache[f]}delete M.cache[c[M.expando]]}}}),o.fn.extend({text:function(a){return J(this,function(a){return void 0===a?o.text(this):this.empty().each(function(){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&(this.textContent=a)})},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=jb(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=jb(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?o.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||o.cleanData(ob(c)),c.parentNode&&(b&&o.contains(c.ownerDocument,c)&&mb(ob(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(o.cleanData(ob(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return o.clone(this,a,b)})},html:function(a){return J(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!db.test(a)&&!ib[(bb.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(ab,"<$1>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(o.cleanData(ob(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,o.cleanData(ob(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,k=this.length,m=this,n=k-1,p=a[0],q=o.isFunction(p);if(q||k>1&&"string"==typeof p&&!l.checkClone&&eb.test(p))return this.each(function(c){var d=m.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(k&&(c=o.buildFragment(a,this[0].ownerDocument,!1,this),d=c.firstChild,1===c.childNodes.length&&(c=d),d)){for(f=o.map(ob(c,"script"),kb),g=f.length;k>j;j++)h=c,j!==n&&(h=o.clone(h,!0,!0),g&&o.merge(f,ob(h,"script"))),b.call(this[j],h,j);if(g)for(i=f[f.length-1].ownerDocument,o.map(f,lb),j=0;g>j;j++)h=f[j],fb.test(h.type||"")&&!L.access(h,"globalEval")&&o.contains(i,h)&&(h.src?o._evalUrl&&o._evalUrl(h.src):o.globalEval(h.textContent.replace(hb,"")))}return this}}),o.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){o.fn[a]=function(a){for(var c,d=[],e=o(a),g=e.length-1,h=0;g>=h;h++)c=h===g?this:this.clone(!0),o(e[h])[b](c),f.apply(d,c.get());return this.pushStack(d)}});var qb,rb={};function sb(b,c){var d=o(c.createElement(b)).appendTo(c.body),e=a.getDefaultComputedStyle?a.getDefaultComputedStyle(d[0]).display:o.css(d[0],"display");return d.detach(),e}function tb(a){var b=m,c=rb[a];return c||(c=sb(a,b),"none"!==c&&c||(qb=(qb||o(" + + + + +
+

An element in the TOP frame

+
+ diff --git a/examples/iframes/index.js b/examples/iframes/index.js new file mode 100644 index 000000000..766eaff45 --- /dev/null +++ b/examples/iframes/index.js @@ -0,0 +1,28 @@ +export default function setInteractables() { + interact('.draggable', { context: document }).draggable({ + onmove: onMove, + inertia: { enabled: true }, + restrict: { + drag: 'parent', + endOnly: true, + elementRect: { top: 0, left: 0, bottom: 1, right: 1 }, + }, + autoScroll: true, + }) + + function onMove(event) { + const target = event.target + const x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx + const y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy + + if ('webkitTransform' in target.style || 'transform' in target.style) { + target.style.webkitTransform = target.style.transform = 'translate(' + x + 'px, ' + y + 'px)' + } else { + target.style.left = x + 'px' + target.style.top = y + 'px' + } + + target.setAttribute('data-x', x) + target.setAttribute('data-y', y) + } +} diff --git a/examples/iframes/middle.html b/examples/iframes/middle.html new file mode 100644 index 000000000..4f3701c2f --- /dev/null +++ b/examples/iframes/middle.html @@ -0,0 +1,18 @@ + + + + + + + +
+

An element in the MIDDLE frame

+
diff --git a/examples/snap/index.css b/examples/snap/index.css new file mode 100644 index 000000000..b6942d2a3 --- /dev/null +++ b/examples/snap/index.css @@ -0,0 +1,73 @@ +body { + margin: 0; + padding: 0; + border: 0; + font-family: "Arial", sans-serif; +} + +canvas { + position: absolute; + top: 0; + left: 0; + + margin: 20px; + padding: 0; + + touch-action: none; +} + +#status { + width: 20%; + height: 100%; + position: fixed; + right: 0; + top: 0; + + padding: 5px 5px; + border: none; + border-left: solid 8px #3a6bff; + + background-color: rgba(0, 143, 179, 0.298); + text-align: center; + font-size: 1.4em; +} + +#status h3 { + font-size: 1.1em; + margin: 3px 0px 0px 0px; + padding: 0; + line-height: 22px; + text-transform: capitalize; + font-weight: normal +} + +#status input[type=radio], #status input[type=checkbox] { + float: right; +} + +#status [type=range] { + margin: auto; + width: 90%; +} + +#status [disabled] { + cursor: default; +} + +#status label { + float: left; + cursor: pointer; + width: 95%; + text-align: left; +} + +#modes,#sliders { + overflow: hidden; + width: 100%; + margin: auto; +} + +#modes.disabled label.snap-mode { + cursor: default; + color: gray; +} diff --git a/demo/snap.html b/examples/snap/index.html similarity index 70% rename from demo/snap.html rename to examples/snap/index.html index 23377b4c7..c92682e83 100644 --- a/demo/snap.html +++ b/examples/snap/index.html @@ -5,9 +5,8 @@ interact.js drag snapping - - - + + Your browser does not support the HTML5 canvas @@ -21,25 +20,25 @@

grid spacing


grid offset

- - + +

snap range

- +

snap mode




@@ -47,12 +46,17 @@

snap mode

+ +

diff --git a/examples/snap/index.js b/examples/snap/index.js new file mode 100644 index 000000000..6be151b63 --- /dev/null +++ b/examples/snap/index.js @@ -0,0 +1,329 @@ +/* eslint-disable import/no-absolute-path */ +import interact from '@interactjs/interactjs' + +window.interact = interact + +let canvas +let context +let guidesCanvas +let guidesContext +const width = 800 +const height = 800 +let status +const blue = '#2299ee' +const lightBlue = '#88ccff' +const tango = '#ff4400' +let draggingAnchor = null + +const snapOffset = { x: 0, y: 0 } + +const snapGrid = { + x: 10, + y: 10, + range: 10, + offset: { x: 0, y: 0 }, +} +const gridFunc = interact.snappers.grid(snapGrid) +const anchors = [ + { x: 100, y: 100, range: 200 }, + { x: 600, y: 400, range: Infinity }, + { x: 500, y: 150, range: Infinity }, + { x: 250, y: 250, range: Infinity }, +] + +const prevCoords = { x: 0, y: 0 } +let prevClosest = { target: { x: 0, y: 0 }, range: 0 } +const cursorRadius = 10 + +function drawGrid(grid, gridOffset, range) { + if (!grid.x || !grid.y) return + + const barLength = 16 + const offset = { + x: gridOffset.x + snapOffset.x, + y: gridOffset.y + snapOffset.y, + } + + guidesContext.clearRect(0, 0, width, height) + + guidesContext.fillStyle = lightBlue + + if (range < 0 || range === Infinity) { + guidesContext.fillRect(0, 0, width, height) + } + + for (let i = -((1 + offset.x / grid.x) | 0), lenX = width / grid.x + 1; i < lenX; i++) { + for (let j = -((1 + offset.y / grid.y) | 0), lenY = height / grid.y + 1; j < lenY; j++) { + if (range > 0 && range !== Infinity) { + guidesContext.circle(i * grid.x + offset.x, j * grid.y + offset.y, range, blue).fill() + } + + guidesContext.beginPath() + guidesContext.moveTo(i * grid.x + offset.x, j * grid.y + offset.y - barLength / 2) + guidesContext.lineTo(i * grid.x + offset.x, j * grid.y + offset.y + barLength / 2) + guidesContext.stroke() + + guidesContext.beginPath() + guidesContext.moveTo(i * grid.x + offset.x - barLength / 2, j * grid.y + offset.y) + guidesContext.lineTo(i * grid.x + offset.x + barLength / 2, j * grid.y + offset.y) + guidesContext.stroke() + } + } +} + +function drawAnchors(defaultRange) { + const barLength = 16 + + guidesContext.clearRect(0, 0, width, height) + + if (status.range.value < 0 && status.range.value !== Infinity) { + guidesContext.fillStyle = lightBlue + guidesContext.fillRect(0, 0, width, height) + } + + for (let i = 0, len = anchors.length; i < len; i++) { + const anchor = { + x: anchors[i].x + snapOffset.x, + y: anchors[i].y + snapOffset.y, + range: anchors[i].range, + } + const range = typeof anchor.range === 'number' ? anchor.range : defaultRange + + if (range > 0 && range !== Infinity) { + guidesContext.circle(anchor.x, anchor.y, range, blue).fill() + } + + guidesContext.beginPath() + guidesContext.moveTo(anchor.x, anchor.y - barLength / 2) + guidesContext.lineTo(anchor.x, anchor.y + barLength / 2) + guidesContext.stroke() + + guidesContext.beginPath() + guidesContext.moveTo(anchor.x - barLength / 2, anchor.y) + guidesContext.lineTo(anchor.x + barLength / 2, anchor.y) + guidesContext.stroke() + } +} + +function drawSnap(snap) { + context.clearRect(0, 0, width, height) + guidesContext.clearRect(0, 0, width, height) + + if (status.gridMode.checked) { + drawGrid(snapGrid, snapGrid.offset, snapGrid.range) + } else if (status.anchorMode.checked) { + drawAnchors(snap.range) + } +} + +function circle(x, y, radius, color) { + this.fillStyle = color || this.fillStyle + this.beginPath() + this.arc(x, y, radius, 0, 2 * Math.PI) + + return this +} +window.CanvasRenderingContext2D.prototype.circle = circle + +function dragMove(event) { + const snap = event._interaction.modification.states.find((m) => m.name === 'snap') + const closest = snap && snap.closest + const rect = interact.getElementRect(canvas) + + context.clearRect( + prevCoords.x - cursorRadius - 2, + prevCoords.y - cursorRadius - 2, + cursorRadius * 2 + 4, + cursorRadius * 2 + 4, + ) + + context.clearRect( + prevClosest.target.x - prevClosest.range - rect.left - 2, + prevClosest.target.y - prevClosest.range - rect.top - 2, + prevClosest.range * 2 + 4 + rect.left, + prevClosest.range * 2 + 4 + rect.top, + ) + + if (closest && closest.range !== Infinity) { + const closestTarget = { + x: closest.target.x - rect.left, + y: closest.target.y - rect.top, + } + + context.circle(closestTarget.x, closestTarget.y, closest.range + 1, 'rgba(102, 225, 117, 0.8)').fill() + } + + context.circle(event.pageX, event.pageY, cursorRadius, tango).fill() + + prevCoords.x = event.pageX + prevCoords.y = event.pageY + prevClosest = closest || prevClosest +} + +function dragEnd(event) { + context.clearRect(0, 0, width, height) + context.circle(event.pageX, event.pageY, cursorRadius, tango).fill() + + prevCoords.x = event.pageX + prevCoords.y = event.pageY +} + +function anchorDragStart(event) { + if (event.snap.locked) { + interact(canvas).snap(false) + draggingAnchor = event.snap.anchors.closest + } +} + +function anchorDragMove(event) { + if (draggingAnchor) { + const snap = interact(canvas).snap().drag + + draggingAnchor.x += event.dx + draggingAnchor.y += event.dy + + drawAnchors(snap.range) + } +} + +function anchorDragEnd(event) { + interact(canvas).draggable(true) + draggingAnchor = null +} + +function sliderChange() { + snapGrid.x = Number(status.gridX.value) + snapGrid.y = Number(status.gridY.value) + snapGrid.range = Number(status.range.value) + + snapGrid.offset.x = Number(status.offsetX.value) + snapGrid.offset.y = Number(status.offsetY.value) + + if (snapGrid.range < 0) { + snapGrid.range = Infinity + } + + drawSnap(interact(canvas).draggable().snap) +} + +function modeChange(event) { + if (status.anchorDrag.checked && !status.anchorMode.checked) { + status.anchorMode.checked = true + } + + if (status.anchorDrag.checked) { + status.anchorMode.disabled = status.offMode.disabled = status.gridMode.disabled = true + status.modes.className += ' disabled' + + interact(canvas) + .off('dragstart', dragMove) + .off('dragmove', dragMove) + .off('dragend', dragEnd) + .on('dragstart', anchorDragStart) + .on('dragmove', anchorDragMove) + .on('dragend', anchorDragEnd) + } else { + status.anchorMode.disabled = status.offMode.disabled = status.gridMode.disabled = false + status.modes.className = status.modes.className.replace(/ *\bdisabled\b/g, '') + + interact(canvas) + .on('dragstart', dragMove) + .on('dragmove', dragMove) + .on('dragend', dragEnd) + .off('dragstart', anchorDragStart) + .off('dragmove', anchorDragMove) + .off('dragend', anchorDragEnd) + } + + interact(canvas).draggable({ + inertia: { + enabled: status.inertia.checked, + }, + modifiers: [ + interact.modifiers.restrict({ restriction: 'self' }), + interact.modifiers.snap({ + targets: status.gridMode.checked ? [gridFunc] : status.anchorMode.checked ? anchors : null, + enabled: !status.offMode.checked, + endOnly: status.endOnly.checked, + offset: status.relative.checked ? 'startCoords' : null, + }), + interact.modifiers.spring(), + ], + }) + + if (!status.relative.checked) { + snapOffset.x = snapOffset.y = 0 + } + + drawSnap(interact(canvas).draggable().snap) +} + +function sliderInput(event) { + // eslint-disable-next-line no-mixed-operators + if ( + (event.target.type === 'range' && + // eslint-disable-next-line no-mixed-operators + Number(event.target.value) > Number(event.target.max)) || + Number(event.target.value) < Number(event.target.min) + ) { + return + } + + sliderChange() +} + +interact(document).on('DOMContentLoaded', () => { + canvas = document.getElementById('drag') + canvas.width = width + canvas.height = height + context = canvas.getContext('2d') + + interact(canvas) + .on('move down', (event) => { + if ((event.type === 'down' || !event.interaction.pointerIsDown) && status.relative.checked) { + const rect = interact.getElementRect(canvas) + + snapOffset.x = event.pageX - rect.left + snapOffset.y = event.pageY - rect.top + + drawSnap(interact(canvas).draggable().snap) + } + }) + .draggable({ origin: 'self' }) + + guidesCanvas = document.getElementById('grid') + guidesCanvas.width = width + guidesCanvas.height = height + guidesContext = guidesCanvas.getContext('2d') + + status = { + container: document.getElementById('status'), + + sliders: document.getElementById('sliders'), + gridX: document.getElementById('grid-x'), + gridY: document.getElementById('grid-y'), + offsetX: document.getElementById('offset-x'), + offsetY: document.getElementById('offset-y'), + range: document.getElementById('snap-range'), + + modes: document.getElementById('modes'), + offMode: document.getElementById('off-mode'), + gridMode: document.getElementById('grid-mode'), + anchorMode: document.getElementById('anchor-mode'), + anchorDrag: document.getElementById('drag-anchors'), + endOnly: document.getElementById('end-only'), + inertia: document.getElementById('inertia'), + relative: document.getElementById('relative'), + } + + interact('#sliders').on('change', sliderInput).on('input', sliderInput) + + interact('#modes').on('change', modeChange) + + sliderChange() + modeChange() +}) + +window.grid = { + drawGrid, +} diff --git a/examples/sortable/index.html b/examples/sortable/index.html new file mode 100644 index 000000000..ed70ff3b2 --- /dev/null +++ b/examples/sortable/index.html @@ -0,0 +1,35 @@ +interact.js Sortable and Swappable demo + + + + + +
+ +
+
+

{{ list.title }}

+ +
{{ list.items }}
+ + +
+
{{ index }}. {{ item }}
+
+
+
+ +
[ {{ tags.join(', ') }} ]
+ + +
{{ tag }}
+
+ +
+ +
+ + + + + diff --git a/examples/sortable/react.js b/examples/sortable/react.js new file mode 100644 index 000000000..193f58c34 --- /dev/null +++ b/examples/sortable/react.js @@ -0,0 +1,41 @@ +import '@interactjs/react' +import { createElement as h, useState } from 'react' +import { createRoot } from 'react-dom/client' + +import interact from '@interactjs/interactjs' + +import { getData } from './shared.js' + +// eslint-disable-next-line no-undef +const data = getData() + +const { Interactable, Sortable } = interact.react.components +const root = createRoot(document.getElementById('react-app')) + +root.render( + h(() => { + return h( + 'div', + {}, + data.lists.map((list, index) => { + const [items, setItems] = useState(list.items) + + return h('div', { key: list.title, className: 'box' }, [ + list.title, + h('pre', {}, JSON.stringify(items, null, 2)), + h( + Sortable, + { className: 'container', key: `list-${list.title}`, items, onUpdate: setItems }, + items.map((item) => + h( + Interactable, + { key: item, onTap: (event) => console.log(event), className: 'item card' }, + h('div', { className: 'card-content' }, item), + ), + ), + ), + ]) + }), + ) + }), +) diff --git a/examples/sortable/shared.js b/examples/sortable/shared.js new file mode 100644 index 000000000..c981c2642 --- /dev/null +++ b/examples/sortable/shared.js @@ -0,0 +1,56 @@ +import interact from '@interactjs/interactjs' + +function sortListener(event) { + console.log(event.type, event.position) +} + +export function getData() { + return { + lists: [ + { + title: 'Animals', + items: ['elephant', 'turtle', 'frog'], + }, + { + title: 'Numbers', + items: ['first', 'second', 'third'], + }, + ], + tags: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + } +} + +export const sortableOptions = { + draggable: { + // lockAxis: 'y', + inertia: true, + autoScroll: { container: '.container' }, + listeners: [interact.feedback.pointers()], + modifiers: [ + interact.modifiers.restrict({ + restriction: 'html', + elementRect: { left: 0, top: 0, right: 1, bottom: 1 }, + }), + interact.modifiers.transform(), + interact.modifiers.spring(), + ], + }, + _spillTo: document.getElementById('no-parent'), + mirror: false, + listeners: [ + { + start: sortListener, + change: sortListener, + end: sortListener, + }, + ], +} + +export const swappableOptions = { + draggable: { + // lockAxis: 'y', + inertia: true, + modifiers: [interact.modifiers.spring()], + listeners: interact.feedback.pointers(), + }, +} diff --git a/examples/sortable/style.css b/examples/sortable/style.css new file mode 100644 index 000000000..2ceacf86e --- /dev/null +++ b/examples/sortable/style.css @@ -0,0 +1,85 @@ +@import "bulma/css/bulma.css"; + +body { + margin: 1em; + _font-size: 2em; +} +.container { + max-width: 500px; +} + +.item.i-sorting { + z-index: 1; +} + +*, *::before, *::after { + box-sizing: border-box; +} + +body { + _margin: 0; +} + +.box { + display: inline-flex; + flex-direction: column; + margin: 1em; +} + +.container { + margin: 0; + transform-origin: 0 0; + _transform: rotate(0deg) scale(2); + _transform: rotate(10deg); + width: 50vw; + padding: 1em 0; + flex-direction: row-reverse; +} + +.item { + margin: 1em 0; + background-color: #29e; + max-width: none; +} + +.item:first-child { + width: 75%; +} + +.item.disabled { + background-color: gray; +} + +.container, .tags { + touch-action: none; + user-select: none; +} + +.tags { + background-color: lightgray; + padding: 1em; +} + +.tag { + font-size: 2em !important; +} + +#no-parent { + position: absolute; + bottom: 2em; + right: 2em; + width: 300px; +} + +.i-ghost { + opacity: 0.5; +} + +.i-mirror { + z-index: 100; +} + +.container { + height: 300px; + overflow: auto; +} diff --git a/examples/sortable/vue.js b/examples/sortable/vue.js new file mode 100644 index 000000000..3d1880ee7 --- /dev/null +++ b/examples/sortable/vue.js @@ -0,0 +1,19 @@ +import '@interactjs/vue' +import { createApp } from 'vue/dist/vue.esm-bundler' + +import interact from '@interactjs/interactjs' + +import { getData, sortableOptions, swappableOptions } from './shared.js' + +const app = createApp({ + data() { + return { + ...getData(), + sortableOptions, + swappableOptions, + } + }, +}) + +app.use(interact.vue) +app.mount('#vue-app') diff --git a/demo/css/star.css b/examples/star/index.css similarity index 100% rename from demo/css/star.css rename to examples/star/index.css diff --git a/examples/star/index.js b/examples/star/index.js new file mode 100644 index 000000000..e1b85c140 --- /dev/null +++ b/examples/star/index.js @@ -0,0 +1,75 @@ +document.addEventListener('DOMContentLoaded', () => { + const sns = 'http://www.w3.org/2000/svg' + const xns = 'http://www.w3.org/1999/xlink' + const root = document.getElementById('svg-edit-demo') + const star = document.getElementById('edit-star') + let rootMatrix + const originalPoints = [] + let transformedPoints = [] + + for (let i = 0, len = star.points.numberOfItems; i < len; i++) { + const handle = document.createElementNS(sns, 'use') + const point = star.points.getItem(i) + const newPoint = root.createSVGPoint() + + handle.setAttributeNS(xns, 'href', '#point-handle') + handle.setAttribute('class', 'point-handle') + + handle.x.baseVal.value = newPoint.x = point.x + handle.y.baseVal.value = newPoint.y = point.y + + handle.setAttribute('data-index', i) + + originalPoints.push(newPoint) + + root.appendChild(handle) + } + + function applyTransforms(event) { + rootMatrix = root.getScreenCTM() + + transformedPoints = originalPoints.map((point) => { + return point.matrixTransform(rootMatrix) + }) + + interact('.point-handle').draggable({ + snap: { + targets: transformedPoints, + range: 20 * Math.max(rootMatrix.a, rootMatrix.d), + }, + }) + } + + interact(root).on('mousedown', applyTransforms).on('touchstart', applyTransforms) + + interact('.point-handle') + .draggable({ + onstart: function (event) { + root.setAttribute('class', 'dragging') + }, + onmove: function (event) { + const i = event.target.getAttribute('data-index') | 0 + const point = star.points.getItem(i) + + point.x += event.dx / rootMatrix.a + point.y += event.dy / rootMatrix.d + + event.target.x.baseVal.value = point.x + event.target.y.baseVal.value = point.y + }, + onend: function (event) { + root.setAttribute('class', '') + }, + snap: { + targets: originalPoints, + range: 10, + relativePoints: [{ x: 0.5, y: 0.5 }], + }, + restrict: { restriction: document.rootElement }, + }) + .styleCursor(false) + + document.addEventListener('dragstart', (event) => { + event.preventDefault() + }) +}) diff --git a/demo/star.svg b/examples/star/star.svg similarity index 81% rename from demo/star.svg rename to examples/star/star.svg index 739d794f7..e043003ef 100644 --- a/demo/star.svg +++ b/examples/star/star.svg @@ -1,8 +1,8 @@ - + - - + + + + + + + + + diff --git a/examples/svg-editor/index.js b/examples/svg-editor/index.js new file mode 100644 index 000000000..12af4ac13 --- /dev/null +++ b/examples/svg-editor/index.js @@ -0,0 +1,127 @@ +/* eslint-disable import/no-absolute-path */ +import '@interactjs/actions' +import '@interactjs/modifiers' +import '@interactjs/inertia' +import '@interactjs/auto-start' +import '@interactjs/dev-tools' + +import interact from '@interactjs/interact' + +const svgCanvas = document.querySelector('svg') +const svgNS = 'http://www.w3.org/2000/svg' +const rectangles = [] + +class Rectangle { + constructor(x, y, w, h) { + this.x = x + this.y = y + this.w = w + this.h = h + this.scale = 1.0 + this.stroke = 5 + this.el = document.createElementNS(svgNS, 'rect') + + this.el.setAttribute('data-index', rectangles.length) + this.el.setAttribute('class', 'edit-rectangle') + rectangles.push(this) + + this.draw() + } + + draw() { + let x = this.x + let y = this.y + let w = this.w + let h = this.h + let cssClass = 'edit-rectangle' + + if (w < 0) { + x += w + w = Math.abs(w) + cssClass += ' neg-w' + } + if (h < 0) { + y += h + h = Math.abs(h) + cssClass += ' neg-h' + } + + this.el.setAttribute('x', x + this.stroke / 2) + this.el.setAttribute('y', y + this.stroke / 2) + this.el.setAttribute('width', Math.max(w, 10) - this.stroke) + this.el.setAttribute('height', Math.max(h, 10) - this.stroke) + this.el.setAttribute('stroke-width', this.stroke) + this.el.style.transform = `scale(${this.scale})` + + this.el.setAttribute('class', cssClass) + } +} + +interact('.edit-rectangle') + // change how interact gets the + // dimensions of '.edit-rectangle' elements + .rectChecker((element) => { + // find the Rectangle object that the element belongs to + const { x, y, w, h, scale } = rectangles[element.getAttribute('data-index')] + + // return a suitable object for interact.js + const left = x * scale + const top = y * scale + + return { + left, + top, + right: left + w * scale, + bottom: top + h * scale, + } + }) + .draggable({ + // inertia: true, + modifiers: [ + interact.modifiers.restrictRect({ + // restrict to a parent element that matches this CSS selector + restriction: 'svg', + // only restrict before ending the drag + endOnly: true, + }), + interact.modifiers.transform(), + ], + onmove: function (event) { + const rectangle = rectangles[event.target.getAttribute('data-index')] + + rectangle.x += event.dx + rectangle.y += event.dy + rectangle.draw() + }, + }) + .resizable({ + edges: { left: true, right: true, top: true, bottom: true }, + invert: 'reposition', + modifiers: [ + interact.modifiers.transform(), + interact.modifiers.restrictEdges({ + restriction: 'svg', + }), + ], + listeners: { + move(event) { + const rectangle = rectangles[event.target.getAttribute('data-index')] + + rectangle.x = event.rect.left + rectangle.y = event.rect.top + rectangle.w = event.rect.width + rectangle.h = event.rect.height + rectangle.draw() + }, + }, + }) + +for (let i = 0; i < 5; i++) { + const r = new Rectangle(50 + 100 * i, 80, 80, 80) + svgCanvas.appendChild(r.el) +} + +interact('#invert').on('input change', (event) => { + interact('.edit-rectangle').resizable({ invert: event.target.value }) + console.log(event.target.value) +}) diff --git a/examples/transform/index.html b/examples/transform/index.html new file mode 100644 index 000000000..7cadceed8 --- /dev/null +++ b/examples/transform/index.html @@ -0,0 +1,53 @@ + +interact.js transforms modifier demo + + + + + +
+
+
item 1
+
+
+
item 2
+
+
+
item 3
+
+
+
item 4
+
+
+ + + diff --git a/examples/transform/index.js b/examples/transform/index.js new file mode 100644 index 000000000..52ddadfef --- /dev/null +++ b/examples/transform/index.js @@ -0,0 +1,40 @@ +import interact from '@interactjs/interactjs' +// import Vue from '../../node_modules/vue/dist/vue.esm.browser.js' + +interact('.item') + .draggable({ + origin: 'parent', + // inertia: true, + modifiers: [ + interact.modifiers.snap({ + // enabled: false, + // targets: [interact.snappers.grid({ x: 20, y: 20 })], + targets: [interact.snappers.elements({ targets: '.item', range: 20 })], + relativePoints: [{ x: 0, y: 0 }], + }), + interact.modifiers.avoid({ targets: ['.item'] }), + interact.modifiers.spring({ allowResume: true }), + interact.modifiers.transform(), + ], + }) + .resizable({ + enabled: true, + origin: 'self', + edges: { left: true, right: true, top: true, bottom: true }, + modifiers: [ + // interact.modifiers.snapSize({ + // targets: [interact.snappers.grid({ x: 100, y: 100 })], + // }), + // interact.modifiers.aspectRatio({ ratio: 'preserve' }), + // interact.modifiers.restrictSize({ + // max: { width: 350, y: 200 }, + // }), + interact.modifiers.spring({ allowResume: true }), + // interact.modifiers.avoid({ targets: ['.item'] }), + interact.modifiers.transform(), + ], + }) + .on('resizemove', (event) => { + console.log(event.rect.width / event.rect.height) + }) + .on('resize drag', interact.feedback.dragResize()) diff --git a/img/a.png b/img/a.png deleted file mode 100644 index f1c4a3ce0..000000000 Binary files a/img/a.png and /dev/null differ diff --git a/img/b.png b/img/b.png deleted file mode 100644 index de22b217f..000000000 Binary files a/img/b.png and /dev/null differ diff --git a/img/c.png b/img/c.png deleted file mode 100644 index 11e0feebc..000000000 Binary files a/img/c.png and /dev/null differ diff --git a/img/ijs-anim-short.svg b/img/ijs-anim-short.svg new file mode 100644 index 000000000..56a7ab666 --- /dev/null +++ b/img/ijs-anim-short.svg @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/img/ijs-anim.svg b/img/ijs-anim.svg index e6b87caec..4d2703569 100644 --- a/img/ijs-anim.svg +++ b/img/ijs-anim.svg @@ -1,8 +1,7 @@ - + - diff --git a/interact.js b/interact.js deleted file mode 100644 index 67926b6fd..000000000 --- a/interact.js +++ /dev/null @@ -1,5588 +0,0 @@ -/** - * interact.js v1.1.2 - * - * Copyright (c) 2012, 2013, 2014 Taye Adeyemi - * Open source under the MIT License. - * https://raw.github.com/taye/interact.js/master/LICENSE - */ -(function () { - 'use strict'; - - var document = window.document, - SVGElement = window.SVGElement || blank, - SVGSVGElement = window.SVGSVGElement || blank, - SVGElementInstance = window.SVGElementInstance || blank, - HTMLElement = window.HTMLElement || window.Element, - - PointerEvent = (window.PointerEvent || window.MSPointerEvent), - pEventTypes, - - hypot = Math.hypot || function (x, y) { return Math.sqrt(x * x + y * y); }, - - tmpXY = {}, // reduce object creation in getXY() - - interactables = [], // all set interactables - interactions = [], // all interactions - - dynamicDrop = false, - - // { - // type: { - // selectors: ['selector', ...], - // contexts : [document, ...], - // listeners: [[listener, useCapture], ...] - // } - // } - delegatedEvents = {}, - - defaultOptions = { - draggable : false, - dragAxis : 'xy', - dropzone : false, - accept : null, - dropOverlap : 'pointer', - resizable : false, - squareResize: false, - resizeAxis : 'xy', - gesturable : false, - - // no more than this number of actions can target the Interactable - dragMax : 1, - resizeMax : 1, - gestureMax: 1, - - // no more than this number of actions can target the same - // element of this Interactable simultaneously - dragMaxPerElement : 1, - resizeMaxPerElement : 1, - gestureMaxPerElement: 1, - - pointerMoveTolerance: 1, - - actionChecker: null, - - styleCursor: true, - preventDefault: 'auto', - - // aww snap - snap: { - mode : 'grid', - endOnly : false, - actions : ['drag'], - range : Infinity, - grid : { x: 100, y: 100 }, - gridOffset : { x: 0, y: 0 }, - anchors : [], - paths : [], - - elementOrigin: null, - - arrayTypes : /^anchors$|^paths$|^actions$/, - objectTypes : /^grid$|^gridOffset$|^elementOrigin$/, - stringTypes : /^mode$/, - numberTypes : /^range$/, - boolTypes : /^endOnly$/ - }, - snapEnabled: false, - - restrict: { - drag: null, - resize: null, - gesture: null, - endOnly: false - }, - restrictEnabled: false, - - autoScroll: { - container : window, // the item that is scrolled (Window or HTMLElement) - margin : 60, - speed : 300, // the scroll speed in pixels per second - - numberTypes : /^margin$|^speed$/ - }, - autoScrollEnabled: false, - - inertia: { - resistance : 10, // the lambda in exponential decay - minSpeed : 100, // target speed must be above this for inertia to start - endSpeed : 10, // the speed at which inertia is slow enough to stop - allowResume : true, // allow resuming an action in inertia phase - zeroResumeDelta : false, // if an action is resumed after launch, set dx/dy to 0 - smoothEndDuration: 300, // animate to snap/restrict endOnly if there's no inertia - actions : ['drag', 'resize'], // allow inertia on these actions. gesture might not work - - numberTypes: /^resistance$|^minSpeed$|^endSpeed$|^smoothEndDuration$/, - arrayTypes : /^actions$/, - boolTypes : /^(allowResume|zeroResumeDelta)$/ - }, - inertiaEnabled: false, - - origin : { x: 0, y: 0 }, - deltaSource : 'page', - - context : document // the Node on which querySelector will be called - }, - - // Things related to autoScroll - autoScroll = { - target: null, - i: null, // the handle returned by window.setInterval - x: 0, y: 0, // Direction each pulse is to scroll in - - // scroll the window by the values in scroll.x/y - scroll: function () { - var options = autoScroll.target.options.autoScroll, - container = options.container, - now = new Date().getTime(), - // change in time in seconds - dt = (now - autoScroll.prevTime) / 1000, - // displacement - s = options.speed * dt; - - if (s >= 1) { - if (container instanceof window.Window) { - container.scrollBy(autoScroll.x * s, autoScroll.y * s); - } - else if (container) { - container.scrollLeft += autoScroll.x * s; - container.scrollTop += autoScroll.y * s; - } - - autoScroll.prevTime = now; - } - - if (autoScroll.isScrolling) { - cancelFrame(autoScroll.i); - autoScroll.i = reqFrame(autoScroll.scroll); - } - }, - - edgeMove: function (event) { - var target, - doAutoscroll = false; - - for (var i = 0; i < interactions.length; i++) { - var interaction = interactions[i]; - target = interaction.target; - - if (target && target.options.autoScrollEnabled && (interaction.dragging || interaction.resizing)) { - doAutoscroll = true; - break; - } - } - - if (!doAutoscroll) { return; } - - var top, - right, - bottom, - left, - options = target.options.autoScroll; - - if (options.container instanceof window.Window) { - left = event.clientX < autoScroll.margin; - top = event.clientY < autoScroll.margin; - right = event.clientX > options.container.innerWidth - autoScroll.margin; - bottom = event.clientY > options.container.innerHeight - autoScroll.margin; - } - else { - var rect = getElementRect(options.container); - - left = event.clientX < rect.left + autoScroll.margin; - top = event.clientY < rect.top + autoScroll.margin; - right = event.clientX > rect.right - autoScroll.margin; - bottom = event.clientY > rect.bottom - autoScroll.margin; - } - - autoScroll.x = (right ? 1: left? -1: 0); - autoScroll.y = (bottom? 1: top? -1: 0); - - if (!autoScroll.isScrolling) { - // set the autoScroll properties to those of the target - autoScroll.margin = options.margin; - autoScroll.speed = options.speed; - - autoScroll.start(target); - } - }, - - isScrolling: false, - prevTime: 0, - - start: function (target) { - autoScroll.isScrolling = true; - cancelFrame(autoScroll.i); - - autoScroll.target = target; - autoScroll.prevTime = new Date().getTime(); - autoScroll.i = reqFrame(autoScroll.scroll); - }, - - stop: function () { - autoScroll.isScrolling = false; - cancelFrame(autoScroll.i); - } - }, - - // Does the browser support touch input? - supportsTouch = (('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch), - - // Does the browser support PointerEvents - supportsPointerEvent = !!PointerEvent, - - // Less Precision with touch input - margin = supportsTouch || supportsPointerEvent? 20: 10, - - // for ignoring browser's simulated mouse events - prevTouchTime = 0, - - // Allow this many interactions to happen simultaneously - maxInteractions = 1, - - actionCursors = { - drag : 'move', - resizex : 'e-resize', - resizey : 's-resize', - resizexy: 'se-resize', - gesture : '' - }, - - actionIsEnabled = { - drag : true, - resize : true, - gesture: true - }, - - // because Webkit and Opera still use 'mousewheel' event type - wheelEvent = 'onmousewheel' in document? 'mousewheel': 'wheel', - - eventTypes = [ - 'dragstart', - 'dragmove', - 'draginertiastart', - 'dragend', - 'dragenter', - 'dragleave', - 'dropactivate', - 'dropdeactivate', - 'dropmove', - 'drop', - 'resizestart', - 'resizemove', - 'resizeinertiastart', - 'resizeend', - 'gesturestart', - 'gesturemove', - 'gestureinertiastart', - 'gestureend', - - 'tap', - 'doubletap' - ], - - globalEvents = {}, - - // Opera Mobile must be handled differently - isOperaMobile = navigator.appName == 'Opera' && - supportsTouch && - navigator.userAgent.match('Presto'), - - // scrolling doesn't change the result of - // getBoundingClientRect/getClientRects on iOS <=7 but it does on iOS 8 - isIOS7orLower = (/iP(hone|od|ad)/.test(navigator.platform) - && /OS [1-7][^\d]/.test(navigator.appVersion)), - - // prefix matchesSelector - prefixedMatchesSelector = 'matchesSelector' in Element.prototype? - 'matchesSelector': 'webkitMatchesSelector' in Element.prototype? - 'webkitMatchesSelector': 'mozMatchesSelector' in Element.prototype? - 'mozMatchesSelector': 'oMatchesSelector' in Element.prototype? - 'oMatchesSelector': 'msMatchesSelector', - - // will be polyfill function if browser is IE8 - ie8MatchesSelector, - - // native requestAnimationFrame or polyfill - reqFrame = window.requestAnimationFrame, - cancelFrame = window.cancelAnimationFrame, - - // used for adding event listeners to window and document - windowTarget = { _element: window , events : {} }, - docTarget = { _element: document , events : {} }, - parentWindowTarget = { _element: window.parent, events : {} }, - parentDocTarget = { _element: null , events : {} }, - - // Events wrapper - events = (function () { - var useAttachEvent = ('attachEvent' in window) && !('addEventListener' in window), - addEvent = useAttachEvent? 'attachEvent': 'addEventListener', - removeEvent = useAttachEvent? 'detachEvent': 'removeEventListener', - on = useAttachEvent? 'on': '', - - elements = [], - targets = [], - attachedListeners = []; - - function add (element, type, listener, useCapture) { - var elementIndex = indexOf(elements, element), - target = targets[elementIndex]; - - if (!target) { - target = { - events: {}, - typeCount: 0 - }; - - elementIndex = elements.push(element) - 1; - targets.push(target); - - attachedListeners.push((useAttachEvent ? { - supplied: [], - wrapped : [], - useCount: [] - } : null)); - } - - if (!target.events[type]) { - target.events[type] = []; - target.typeCount++; - } - - if (!contains(target.events[type], listener)) { - var ret; - - if (useAttachEvent) { - var listeners = attachedListeners[elementIndex], - listenerIndex = indexOf(listeners.supplied, listener); - - var wrapped = listeners.wrapped[listenerIndex] || function (event) { - if (!event.immediatePropagationStopped) { - event.target = event.srcElement; - event.currentTarget = element; - - event.preventDefault = event.preventDefault || preventDef; - event.stopPropagation = event.stopPropagation || stopProp; - event.stopImmediatePropagation = event.stopImmediatePropagation || stopImmProp; - - if (/mouse|click/.test(event.type)) { - event.pageX = event.clientX + document.documentElement.scrollLeft; - event.pageY = event.clientY + document.documentElement.scrollTop; - } - - listener(event); - } - }; - - ret = element[addEvent](on + type, wrapped, Boolean(useCapture)); - - if (listenerIndex === -1) { - listeners.supplied.push(listener); - listeners.wrapped.push(wrapped); - listeners.useCount.push(1); - } - else { - listeners.useCount[listenerIndex]++; - } - } - else { - ret = element[addEvent](type, listener, useCapture || false); - } - target.events[type].push(listener); - - return ret; - } - } - - function remove (element, type, listener, useCapture) { - var i, - elementIndex = indexOf(elements, element), - target = targets[elementIndex], - listeners, - listenerIndex, - wrapped = listener; - - if (!target || !target.events) { - return; - } - - if (useAttachEvent) { - listeners = attachedListeners[elementIndex]; - listenerIndex = indexOf(listeners.supplied, listener); - wrapped = listeners.wrapped[listenerIndex]; - } - - if (type === 'all') { - for (type in target.events) { - if (target.events.hasOwnProperty(type)) { - remove(element, type, 'all'); - } - } - return; - } - - if (target.events[type]) { - var len = target.events[type].length; - - if (listener === 'all') { - for (i = 0; i < len; i++) { - remove(element, type, target.events[type][i], Boolean(useCapture)); - } - } else { - for (i = 0; i < len; i++) { - if (target.events[type][i] === listener) { - element[removeEvent](on + type, wrapped, useCapture || false); - target.events[type].splice(i, 1); - - if (useAttachEvent && listeners) { - listeners.useCount[listenerIndex]--; - if (listeners.useCount[listenerIndex] === 0) { - listeners.supplied.splice(listenerIndex, 1); - listeners.wrapped.splice(listenerIndex, 1); - listeners.useCount.splice(listenerIndex, 1); - } - } - - break; - } - } - } - - if (target.events[type] && target.events[type].length === 0) { - target.events[type] = null; - target.typeCount--; - } - } - - if (!target.typeCount) { - targets.splice(elementIndex); - elements.splice(elementIndex); - attachedListeners.splice(elementIndex); - } - } - - function preventDef () { - this.returnValue = false; - } - - function stopProp () { - this.cancelBubble = true; - } - - function stopImmProp () { - this.cancelBubble = true; - this.immediatePropagationStopped = true; - } - - return { - add: function (target, type, listener, useCapture) { - add(target._element, type, listener, useCapture); - }, - remove: function (target, type, listener, useCapture) { - remove(target._element, type, listener, useCapture); - }, - addToElement: add, - removeFromElement: remove, - useAttachEvent: useAttachEvent, - - _elements: elements, - _targets: targets, - _attachedListeners: attachedListeners - }; - }()); - - function blank () {} - - function isElement (o) { - return !!o && (typeof o === 'object') && ( - /object|function/.test(typeof Element) - ? o instanceof Element //DOM2 - : o.nodeType === 1 && typeof o.nodeName === "string"); - } - function isObject (thing) { return thing instanceof Object; } - function isArray (thing) { return thing instanceof Array ; } - function isFunction (thing) { return typeof thing === 'function'; } - function isNumber (thing) { return typeof thing === 'number' ; } - function isBool (thing) { return typeof thing === 'boolean' ; } - function isString (thing) { return typeof thing === 'string' ; } - - function trySelector (value) { - if (!isString(value)) { return false; } - - // an exception will be raised if it is invalid - document.querySelector(value); - return true; - } - - function extend (dest, source) { - for (var prop in source) { - dest[prop] = source[prop]; - } - return dest; - } - - function copyCoords (dest, src) { - dest.page = dest.page || {}; - dest.page.x = src.page.x; - dest.page.y = src.page.y; - - dest.client = dest.client || {}; - dest.client.x = src.client.x; - dest.client.y = src.client.y; - - dest.timeStamp = src.timeStamp; - } - - function setEventXY (targetObj, pointer, interaction) { - if (!pointer) { - if (interaction.pointerIds.length > 1) { - pointer = touchAverage(interaction.pointers); - } - else { - pointer = interaction.pointers[0]; - } - } - - getPageXY(pointer, tmpXY, interaction); - targetObj.page.x = tmpXY.x; - targetObj.page.y = tmpXY.y; - - getClientXY(pointer, tmpXY, interaction); - targetObj.client.x = tmpXY.x; - targetObj.client.y = tmpXY.y; - - targetObj.timeStamp = new Date().getTime(); - } - - function setEventDeltas (targetObj, prev, cur) { - targetObj.page.x = cur.page.x - prev.page.x; - targetObj.page.y = cur.page.y - prev.page.y; - targetObj.client.x = cur.client.x - prev.client.x; - targetObj.client.y = cur.client.y - prev.client.y; - targetObj.timeStamp = new Date().getTime() - prev.timeStamp; - - // set pointer velocity - var dt = Math.max(targetObj.timeStamp / 1000, 0.001); - targetObj.page.speed = hypot(targetObj.page.x, targetObj.page.y) / dt; - targetObj.page.vx = targetObj.page.x / dt; - targetObj.page.vy = targetObj.page.y / dt; - - targetObj.client.speed = hypot(targetObj.client.x, targetObj.page.y) / dt; - targetObj.client.vx = targetObj.client.x / dt; - targetObj.client.vy = targetObj.client.y / dt; - } - - // Get specified X/Y coords for mouse or event.touches[0] - function getXY (type, pointer, xy) { - xy = xy || {}; - type = type || 'page'; - - xy.x = pointer[type + 'X']; - xy.y = pointer[type + 'Y']; - - return xy; - } - - function getPageXY (pointer, page, interaction) { - page = page || {}; - - if (pointer instanceof InteractEvent) { - if (/inertiastart/.test(pointer.type)) { - interaction = interaction || pointer.interaction; - - extend(page, interaction.inertiaStatus.upCoords.page); - - page.x += interaction.inertiaStatus.sx; - page.y += interaction.inertiaStatus.sy; - } - else { - page.x = pointer.pageX; - page.y = pointer.pageY; - } - } - // Opera Mobile handles the viewport and scrolling oddly - else if (isOperaMobile) { - getXY('screen', pointer, page); - - page.x += window.scrollX; - page.y += window.scrollY; - } - else { - getXY('page', pointer, page); - } - - return page; - } - - function getClientXY (pointer, client, interaction) { - client = client || {}; - - if (pointer instanceof InteractEvent) { - if (/inertiastart/.test(pointer.type)) { - extend(client, interaction.inertiaStatus.upCoords.client); - - client.x += interaction.inertiaStatus.sx; - client.y += interaction.inertiaStatus.sy; - } - else { - client.x = pointer.clientX; - client.y = pointer.clientY; - } - } - else { - // Opera Mobile handles the viewport and scrolling oddly - getXY(isOperaMobile? 'screen': 'client', pointer, client); - } - - return client; - } - - function getScrollXY () { - return { - x: window.scrollX || document.documentElement.scrollLeft, - y: window.scrollY || document.documentElement.scrollTop - }; - } - - function getPointerId (pointer) { - return isNumber(pointer.pointerId)? pointer.pointerId : pointer.identifier; - } - - function getActualElement (element) { - return (element instanceof SVGElementInstance - ? element.correspondingUseElement - : element); - } - - function getElementRect (element) { - var scroll = isIOS7orLower - ? { x: 0, y: 0 } - : getScrollXY(), - clientRect = (element instanceof SVGElement)? - element.getBoundingClientRect(): - element.getClientRects()[0]; - - return clientRect && { - left : clientRect.left + scroll.x, - right : clientRect.right + scroll.x, - top : clientRect.top + scroll.y, - bottom: clientRect.bottom + scroll.y, - width : clientRect.width || clientRect.right - clientRect.left, - height: clientRect.heigh || clientRect.bottom - clientRect.top - }; - } - - function getTouchPair (event) { - var touches = []; - - // array of touches is supplied - if (isArray(event)) { - touches[0] = event[0]; - touches[1] = event[1]; - } - // an event - else { - if (event.type === 'touchend') { - if (event.touches.length === 1) { - touches[0] = event.touches[0]; - touches[1] = event.changedTouches[0]; - } - else if (event.touches.length === 0) { - touches[0] = event.changedTouches[0]; - touches[1] = event.changedTouches[1]; - } - } - else { - touches[0] = event.touches[0]; - touches[1] = event.touches[1]; - } - } - - return touches; - } - - function touchAverage (event) { - var touches = getTouchPair(event); - - return { - pageX: (touches[0].pageX + touches[1].pageX) / 2, - pageY: (touches[0].pageY + touches[1].pageY) / 2, - clientX: (touches[0].clientX + touches[1].clientX) / 2, - clientY: (touches[0].clientY + touches[1].clientY) / 2 - }; - } - - function touchBBox (event) { - if (!event.length && !(event.touches && event.touches.length > 1)) { - return; - } - - var touches = getTouchPair(event), - minX = Math.min(touches[0].pageX, touches[1].pageX), - minY = Math.min(touches[0].pageY, touches[1].pageY), - maxX = Math.max(touches[0].pageX, touches[1].pageX), - maxY = Math.max(touches[0].pageY, touches[1].pageY); - - return { - x: minX, - y: minY, - left: minX, - top: minY, - width: maxX - minX, - height: maxY - minY - }; - } - - function touchDistance (event, deltaSource) { - deltaSource = deltaSource || defaultOptions.deltaSource; - - var sourceX = deltaSource + 'X', - sourceY = deltaSource + 'Y', - touches = getTouchPair(event); - - - var dx = touches[0][sourceX] - touches[1][sourceX], - dy = touches[0][sourceY] - touches[1][sourceY]; - - return hypot(dx, dy); - } - - function touchAngle (event, prevAngle, deltaSource) { - deltaSource = deltaSource || defaultOptions.deltaSource; - - var sourceX = deltaSource + 'X', - sourceY = deltaSource + 'Y', - touches = getTouchPair(event), - dx = touches[0][sourceX] - touches[1][sourceX], - dy = touches[0][sourceY] - touches[1][sourceY], - angle = 180 * Math.atan(dy / dx) / Math.PI; - - if (isNumber(prevAngle)) { - var dr = angle - prevAngle, - drClamped = dr % 360; - - if (drClamped > 315) { - angle -= 360 + (angle / 360)|0 * 360; - } - else if (drClamped > 135) { - angle -= 180 + (angle / 360)|0 * 360; - } - else if (drClamped < -315) { - angle += 360 + (angle / 360)|0 * 360; - } - else if (drClamped < -135) { - angle += 180 + (angle / 360)|0 * 360; - } - } - - return angle; - } - - function getOriginXY (interactable, element) { - var origin = interactable - ? interactable.options.origin - : defaultOptions.origin; - - if (origin === 'parent') { - origin = element.parentNode; - } - else if (origin === 'self') { - origin = interactable.getRect(element); - } - else if (trySelector(origin)) { - origin = matchingParent(element, origin) || { x: 0, y: 0 }; - } - - if (isFunction(origin)) { - origin = origin(interactable && element); - } - - if (isElement(origin)) { - origin = getElementRect(origin); - } - - origin.x = ('x' in origin)? origin.x : origin.left; - origin.y = ('y' in origin)? origin.y : origin.top; - - return origin; - } - - // http://stackoverflow.com/a/5634528/2280888 - function _getQBezierValue(t, p1, p2, p3) { - var iT = 1 - t; - return iT * iT * p1 + 2 * iT * t * p2 + t * t * p3; - } - - function getQuadraticCurvePoint(startX, startY, cpX, cpY, endX, endY, position) { - return { - x: _getQBezierValue(position, startX, cpX, endX), - y: _getQBezierValue(position, startY, cpY, endY) - }; - } - - // http://gizma.com/easing/ - function easeOutQuad (t, b, c, d) { - t /= d; - return -c * t*(t-2) + b; - } - - function nodeContains (parent, child) { - while ((child = child.parentNode)) { - - if (child === parent) { - return true; - } - } - - return false; - } - - function matchingParent (child, selector) { - var parent = child.parentNode; - - while (isElement(parent)) { - if (matchesSelector(parent, selector)) { return parent; } - - parent = parent.parentNode; - } - - return null; - } - - function inContext (interactable, element) { - return interactable._context === document - || nodeContains(interactable._context, element); - } - - function testIgnore (interactable, interactableElement, element) { - var ignoreFrom = interactable.options.ignoreFrom; - - if (!ignoreFrom - // limit test to the interactable's element and its children - || !isElement(element) || element === interactableElement.parentNode) { - return false; - } - - if (isString(ignoreFrom)) { - return matchesSelector(element, ignoreFrom) || testIgnore(interactable, element.parentNode); - } - else if (isElement(ignoreFrom)) { - return element === ignoreFrom || nodeContains(ignoreFrom, element); - } - - return false; - } - - function testAllow (interactable, interactableElement, element) { - var allowFrom = interactable.options.allowFrom; - - if (!allowFrom) { return true; } - - // limit test to the interactable's element and its children - if (!isElement(element) || element === interactableElement.parentNode) { - return false; - } - - if (isString(allowFrom)) { - return matchesSelector(element, allowFrom) || testAllow(interactable, element.parentNode); - } - else if (isElement(allowFrom)) { - return element === allowFrom || nodeContains(allowFrom, element); - } - - return false; - } - - function checkAxis (axis, interactable) { - if (!interactable) { return false; } - - var thisAxis = interactable.options.dragAxis; - - return (axis === 'xy' || thisAxis === 'xy' || thisAxis === axis); - } - - function checkSnap (interactable, action) { - var options = interactable.options; - - if (/^resize/.test(action)) { - action = 'resize'; - } - - return action !== 'gesture' && options.snapEnabled && contains(options.snap.actions, action); - } - - function checkRestrict (interactable, action) { - var options = interactable.options; - - if (/^resize/.test(action)) { - action = 'resize'; - } - - return options.restrictEnabled && options.restrict[action]; - } - - function withinInteractionLimit (interactable, element, action) { - action = /resize/.test(action)? 'resize': action; - - var options = interactable.options, - maxActions = options[action + 'Max'], - maxPerElement = options[action + 'MaxPerElement'], - activeInteractions = 0, - targetCount = 0, - targetElementCount = 0; - - for (var i = 0, len = interactions.length; i < len; i++) { - var interaction = interactions[i], - otherAction = /resize/.test(interaction.prepared)? 'resize': interaction.prepared, - active = interaction.interacting(); - - if (!active) { continue; } - - activeInteractions++; - - if (activeInteractions >= maxInteractions) { - return false; - } - - if (interaction.target !== interactable) { continue; } - - targetCount += (otherAction === action)|0; - - if (targetCount >= maxActions) { - return false; - } - - if (interaction.element === element) { - targetElementCount++; - - if (otherAction !== action || targetElementCount >= maxPerElement) { - return false; - } - } - } - - return maxInteractions > 0; - } - - // Test for the element that's "above" all other qualifiers - function indexOfDeepestElement (elements) { - var dropzone, - deepestZone = elements[0], - index = deepestZone? 0: -1, - parent, - deepestZoneParents = [], - dropzoneParents = [], - child, - i, - n; - - for (i = 1; i < elements.length; i++) { - dropzone = elements[i]; - - // an element might belong to multiple selector dropzones - if (!dropzone || dropzone === deepestZone) { - continue; - } - - if (!deepestZone) { - deepestZone = dropzone; - index = i; - continue; - } - - // check if the deepest or current are document.documentElement or document.rootElement - // - if the current dropzone is, do nothing and continue - if (dropzone.parentNode === document) { - continue; - } - // - if deepest is, update with the current dropzone and continue to next - else if (deepestZone.parentNode === document) { - deepestZone = dropzone; - index = i; - continue; - } - - if (!deepestZoneParents.length) { - parent = deepestZone; - while (parent.parentNode && parent.parentNode !== document) { - deepestZoneParents.unshift(parent); - parent = parent.parentNode; - } - } - - // if this element is an svg element and the current deepest is - // an HTMLElement - if (deepestZone instanceof HTMLElement - && dropzone instanceof SVGElement - && !(dropzone instanceof SVGSVGElement)) { - - if (dropzone === deepestZone.parentNode) { - continue; - } - - parent = dropzone.ownerSVGElement; - } - else { - parent = dropzone; - } - - dropzoneParents = []; - - while (parent.parentNode !== document) { - dropzoneParents.unshift(parent); - parent = parent.parentNode; - } - - n = 0; - - // get (position of last common ancestor) + 1 - while (dropzoneParents[n] && dropzoneParents[n] === deepestZoneParents[n]) { - n++; - } - - var parents = [ - dropzoneParents[n - 1], - dropzoneParents[n], - deepestZoneParents[n] - ]; - - child = parents[0].lastChild; - - while (child) { - if (child === parents[1]) { - deepestZone = dropzone; - index = i; - deepestZoneParents = []; - - break; - } - else if (child === parents[2]) { - break; - } - - child = child.previousSibling; - } - } - - return index; - } - - function Interaction () { - this.target = null; // current interactable being interacted with - this.element = null; // the target element of the interactable - this.dropTarget = null; // the dropzone a drag target might be dropped into - this.dropElement = null; // the element at the time of checking - this.prevDropTarget = null; // the dropzone that was recently dragged away from - this.prevDropElement = null; // the element at the time of checking - - this.prepared = null; // Action that's ready to be fired on next move event - - this.matches = []; // all selectors that are matched by target element - this.matchElements = []; // corresponding elements - - this.inertiaStatus = { - active : false, - smoothEnd : false, - - startEvent: null, - upCoords: {}, - - xe: 0, ye: 0, - sx: 0, sy: 0, - - t0: 0, - vx0: 0, vys: 0, - duration: 0, - - resumeDx: 0, - resumeDy: 0, - - lambda_v0: 0, - one_ve_v0: 0, - i : null - }; - - if (isFunction(Function.prototype.bind)) { - this.boundInertiaFrame = this.inertiaFrame.bind(this); - this.boundSmoothEndFrame = this.smoothEndFrame.bind(this); - } - else { - var that = this; - - this.boundInertiaFrame = function () { return that.inertiaFrame(); }; - this.boundSmoothEndFrame = function () { return that.smoothEndFrame(); }; - } - - this.activeDrops = { - dropzones: [], // the dropzones that are mentioned below - elements : [], // elements of dropzones that accept the target draggable - rects : [] // the rects of the elements mentioned above - }; - - // keep track of added pointers - this.pointers = []; - this.pointerIds = []; - - // Previous native pointer move event coordinates - this.prevCoords = { - page : { x: 0, y: 0 }, - client : { x: 0, y: 0 }, - timeStamp: 0 - }; - // current native pointer move event coordinates - this.curCoords = { - page : { x: 0, y: 0 }, - client : { x: 0, y: 0 }, - timeStamp: 0 - }; - - // Starting InteractEvent pointer coordinates - this.startCoords = { - page : { x: 0, y: 0 }, - client : { x: 0, y: 0 }, - timeStamp: 0 - }; - - // Change in coordinates and time of the pointer - this.pointerDelta = { - page : { x: 0, y: 0, vx: 0, vy: 0, speed: 0 }, - client : { x: 0, y: 0, vx: 0, vy: 0, speed: 0 }, - timeStamp: 0 - }; - - this.downTime = 0; // the timeStamp of the starting event - this.downEvent = null; // pointerdown/mousedown/touchstart event - this.downPointer = {}; - this.downTarget = null; - - this.prevEvent = null; // previous action event - this.tapTime = 0; // time of the most recent tap event - this.prevTap = null; - - this.startOffset = { left: 0, right: 0, top: 0, bottom: 0 }; - this.restrictOffset = { left: 0, right: 0, top: 0, bottom: 0 }; - this.snapOffset = { x: 0, y: 0}; - - this.gesture = { - start: { x: 0, y: 0 }, - - startDistance: 0, // distance between two touches of touchStart - prevDistance : 0, - distance : 0, - - scale: 1, // gesture.distance / gesture.startDistance - - startAngle: 0, // angle of line joining two touches - prevAngle : 0 // angle of the previous gesture event - }; - - this.snapStatus = { - x : 0, y : 0, - dx : 0, dy : 0, - realX : 0, realY : 0, - snappedX: 0, snappedY: 0, - anchors : [], - paths : [], - locked : false, - changed : false - }; - - this.restrictStatus = { - dx : 0, dy : 0, - restrictedX: 0, restrictedY: 0, - snap : null, - restricted : false, - changed : false - }; - - this.restrictStatus.snap = this.snapStatus; - - this.pointerIsDown = false; - this.pointerWasMoved = false; - this.gesturing = false; - this.dragging = false; - this.resizing = false; - this.resizeAxes = 'xy'; - - this.mouse = false; - - interactions.push(this); - } - - Interaction.prototype = { - getPageXY : function (pointer, xy) { return getPageXY(pointer, xy, this); }, - getClientXY: function (pointer, xy) { return getClientXY(pointer, xy, this); }, - setEventXY : function (target, ptr) { return setEventXY(target, ptr, this); }, - - pointerOver: function (pointer, event, eventTarget) { - if (this.prepared || !this.mouse) { return; } - - var curMatches = [], - curMatchElements = [], - prevTargetElement = this.element; - - this.addPointer(pointer); - - if (this.target - && (testIgnore(this.target, this.element, eventTarget) - || !testAllow(this.target, this.element, eventTarget) - || !withinInteractionLimit(this.target, this.element, this.prepared))) { - // if the eventTarget should be ignored or shouldn't be allowed - // clear the previous target - this.target = null; - this.element = null; - this.matches = []; - this.matchElements = []; - } - - var elementInteractable = interactables.get(eventTarget), - elementAction = (elementInteractable - && !testIgnore(elementInteractable, eventTarget, eventTarget) - && testAllow(elementInteractable, eventTarget, eventTarget) - && validateAction( - elementInteractable.getAction(pointer, this, eventTarget), - elementInteractable)); - - elementAction = elementInteractable && withinInteractionLimit(elementInteractable, eventTarget, elementAction) - ? elementAction - : null; - - function pushCurMatches (interactable, selector) { - if (interactable - && inContext(interactable, eventTarget) - && !testIgnore(interactable, eventTarget, eventTarget) - && testAllow(interactable, eventTarget, eventTarget) - && matchesSelector(eventTarget, selector)) { - - curMatches.push(interactable); - curMatchElements.push(eventTarget); - } - } - - if (elementAction) { - this.target = elementInteractable; - this.element = eventTarget; - this.matches = []; - this.matchElements = []; - } - else { - interactables.forEachSelector(pushCurMatches); - - if (this.validateSelector(pointer, curMatches, curMatchElements)) { - this.matches = curMatches; - this.matchElements = curMatchElements; - - this.pointerHover(pointer, event, this.matches, this.matchElements); - events.addToElement(eventTarget, - PointerEvent? pEventTypes.move : 'mousemove', - listeners.pointerHover); - } - else if (this.target) { - if (nodeContains(prevTargetElement, eventTarget)) { - this.pointerHover(pointer, event, this.matches, this.matchElements); - events.addToElement(this.element, - PointerEvent? pEventTypes.move : 'mousemove', - listeners.pointerHover); - } - else { - this.target = null; - this.element = null; - this.matches = []; - this.matchElements = []; - } - } - } - }, - - // Check what action would be performed on pointerMove target if a mouse - // button were pressed and change the cursor accordingly - pointerHover: function (pointer, event, eventTarget, curEventTarget, matches, matchElements) { - var target = this.target; - - if (!this.prepared && this.mouse) { - - var action; - - // update pointer coords for defaultActionChecker to use - this.setEventXY(this.curCoords, pointer); - - if (matches) { - action = this.validateSelector(pointer, matches, matchElements); - } - else if (target) { - action = validateAction(target.getAction(this.pointers[0], this, this.element), this.target); - } - - if (target && target.options.styleCursor) { - if (action) { - document.documentElement.style.cursor = actionCursors[action]; - } - else { - document.documentElement.style.cursor = ''; - } - } - } - else if (this.prepared) { - this.checkAndPreventDefault(event, target, this.element); - } - }, - - pointerOut: function (pointer, event, eventTarget) { - if (this.prepared) { return; } - - // Remove temporary event listeners for selector Interactables - if (!interactables.get(eventTarget)) { - events.removeFromElement(eventTarget, - PointerEvent? pEventTypes.move : 'mousemove', - listeners.pointerHover); - } - - if (this.target && this.target.options.styleCursor && !this.interacting()) { - document.documentElement.style.cursor = ''; - } - }, - - selectorDown: function (pointer, event, eventTarget, curEventTarget) { - this.pointerIsDown = true; - - var element = eventTarget, - action; - - this.addPointer(pointer); - - // Check if the down event hits the current inertia target - if (this.inertiaStatus.active && this.target.selector) { - // climb up the DOM tree from the event target - while (element && element !== document) { - - // if this element is the current inertia target element - if (element === this.element - // and the prospective action is the same as the ongoing one - && validateAction(this.target.getAction(pointer, this, this.element), this.target) === this.prepared) { - - // stop inertia so that the next move will be a normal one - cancelFrame(this.inertiaStatus.i); - this.inertiaStatus.active = false; - - return; - } - element = element.parentNode; - } - } - - // do nothing if interacting - if (this.interacting()) { - return; - } - - var that = this; - - function pushMatches (interactable, selector, context) { - var elements = ie8MatchesSelector - ? context.querySelectorAll(selector) - : undefined; - - if (inContext(interactable, element) - && !testIgnore(interactable, element, eventTarget) - && testAllow(interactable, element, eventTarget) - && matchesSelector(element, selector, elements)) { - - that.matches.push(interactable); - that.matchElements.push(element); - } - } - - // update pointer coords for defaultActionChecker to use - this.setEventXY(this.curCoords, pointer); - - if (this.matches.length && this.mouse) { - action = this.validateSelector(pointer, this.matches, this.matchElements); - } - else { - while (element && element !== document && !action) { - this.matches = []; - this.matchElements = []; - - interactables.forEachSelector(pushMatches); - - action = this.validateSelector(pointer, this.matches, this.matchElements); - element = element.parentNode; - } - } - - if (action) { - this.prepared = action; - - return this.pointerDown(pointer, event, eventTarget, curEventTarget, action); - } - else { - // do these now since pointerDown isn't being called from here - this.downTime = new Date().getTime(); - this.downTarget = eventTarget; - this.downEvent = event; - extend(this.downPointer, pointer); - - copyCoords(this.prevCoords, this.curCoords); - this.pointerWasMoved = false; - } - }, - - // Determine action to be performed on next pointerMove and add appropriate - // style and event Listeners - pointerDown: function (pointer, event, eventTarget, curEventTarget, forceAction) { - if (!forceAction && !this.inertiaStatus.active && this.pointerWasMoved && this.prepared) { - this.checkAndPreventDefault(event, this.target, this.element); - - return; - } - - this.pointerIsDown = true; - - this.addPointer(pointer); - - var action; - - // If it is the second touch of a multi-touch gesture, keep the target - // the same if a target was set by the first touch - // Otherwise, set the target if there is no action prepared - if ((this.pointerIds.length < 2 && !this.target) || !this.prepared) { - - var interactable = interactables.get(curEventTarget); - - if (interactable - && !testIgnore(interactable, curEventTarget, eventTarget) - && testAllow(interactable, curEventTarget, eventTarget) - && (action = validateAction(forceAction || interactable.getAction(pointer, this), interactable, eventTarget)) - && withinInteractionLimit(interactable, curEventTarget, action)) { - this.target = interactable; - this.element = curEventTarget; - } - } - - var target = this.target, - options = target && target.options; - - if (target && !this.interacting()) { - action = action || validateAction(forceAction || target.getAction(pointer, this), target, this.element); - - this.setEventXY(this.startCoords); - - if (!action) { return; } - - if (options.styleCursor) { - document.documentElement.style.cursor = actionCursors[action]; - } - - this.resizeAxes = action === 'resizexy'? - 'xy': - action === 'resizex'? - 'x': - action === 'resizey'? - 'y': - ''; - - if (action === 'gesture' && this.pointerIds.length < 2) { - action = null; - } - - this.prepared = action; - - this.snapStatus.snappedX = this.snapStatus.snappedY = - this.restrictStatus.restrictedX = this.restrictStatus.restrictedY = NaN; - - this.downTime = new Date().getTime(); - this.downTarget = eventTarget; - this.downEvent = event; - extend(this.downPointer, pointer); - - this.setEventXY(this.prevCoords); - this.pointerWasMoved = false; - - this.checkAndPreventDefault(event, target, this.element); - } - // if inertia is active try to resume action - else if (this.inertiaStatus.active - && this.target.options.inertia.allowResume - && curEventTarget === this.element - && validateAction(target.getAction(pointer, this, this.element), target) === this.prepared) { - - cancelFrame(this.inertiaStatus.i); - this.inertiaStatus.active = false; - - this.checkAndPreventDefault(event, target, this.element); - } - }, - - pointerMove: function (pointer, event, eventTarget, curEventTarget, preEnd) { - if (!this.pointerIsDown) { return; } - - this.setEventXY(this.curCoords, (pointer instanceof InteractEvent) - ? this.inertiaStatus.startEvent - : undefined); - - if (this.pointerWasMoved && !preEnd - && this.curCoords.page.x === this.prevCoords.page.x - && this.curCoords.page.y === this.prevCoords.page.y - && this.curCoords.client.x === this.prevCoords.client.x - && this.curCoords.client.y === this.prevCoords.client.y) { - - this.checkAndPreventDefault(event, this.target, this.element); - return; - } - - // set pointer coordinate, time changes and speeds - setEventDeltas(this.pointerDelta, this.prevCoords, this.curCoords); - - var dx, dy; - - // register movement of more than 1 pixel - if (!this.pointerWasMoved) { - dx = this.curCoords.client.x - this.startCoords.client.x; - dy = this.curCoords.client.y - this.startCoords.client.y; - - this.pointerWasMoved = hypot(dx, dy) > defaultOptions.pointerMoveTolerance; - } - - if (!this.prepared) { return; } - - if (this.pointerWasMoved - // ignore movement while inertia is active - && (!this.inertiaStatus.active || (pointer instanceof InteractEvent && /inertiastart/.test(pointer.type)))) { - - // if just starting an action, calculate the pointer speed now - if (!this.interacting()) { - setEventDeltas(this.pointerDelta, this.prevCoords, this.curCoords); - - // check if a drag is in the correct axis - if (this.prepared === 'drag') { - var absX = Math.abs(dx), - absY = Math.abs(dy), - targetAxis = this.target.options.dragAxis, - axis = (absX > absY ? 'x' : absX < absY ? 'y' : 'xy'); - - // if the movement isn't in the axis of the interactable - if (axis !== 'xy' && targetAxis !== 'xy' && targetAxis !== axis) { - // cancel the prepared action - this.prepared = null; - - // then try to get a drag from another ineractable - - var element = eventTarget; - - // check element interactables - while (element && element !== document) { - var elementInteractable = interactables.get(element); - - if (elementInteractable - && elementInteractable !== this.target - && elementInteractable.getAction(this.downPointer, this, element) === 'drag' - && checkAxis(axis, elementInteractable)) { - - this.prepared = 'drag'; - this.target = elementInteractable; - this.element = element; - break; - } - - element = element.parentNode; - } - - // if there's no drag from element interactables, - // check the selector interactables - if (!this.prepared) { - var getDraggable = function (interactable, selector, context) { - var elements = ie8MatchesSelector - ? context.querySelectorAll(selector) - : undefined; - - if (interactable === this.target) { return; } - - if (inContext(interactable, eventTarget) - && !testIgnore(interactable, element, eventTarget) - && testAllow(interactable, element, eventTarget) - && matchesSelector(element, selector, elements) - && interactable.getAction(this.downPointer, this, element) === 'drag' - && checkAxis(axis, interactable) - && withinInteractionLimit(interactable, element, 'drag')) { - - return interactable; - } - }; - - element = eventTarget; - - while (element && element !== document) { - var selectorInteractable = interactables.forEachSelector(getDraggable); - - if (selectorInteractable) { - this.prepared = 'drag'; - this.target = selectorInteractable; - this.element = element; - break; - } - - element = element.parentNode; - } - } - } - } - } - - var starting = !!this.prepared && !this.interacting(); - - if (starting && !withinInteractionLimit(this.target, this.element, this.prepared)) { - this.stop(); - return; - } - - if (this.prepared && this.target) { - var target = this.target, - shouldSnap = checkSnap(target, this.prepared) && (!target.options.snap.endOnly || preEnd), - shouldRestrict = checkRestrict(target, this.prepared) && (!target.options.restrict.endOnly || preEnd); - - if (starting) { - var rect = target.getRect(this.element), - snap = target.options.snap, - restrict = target.options.restrict, - width, height; - - if (rect) { - this.startOffset.left = this.startCoords.page.x - rect.left; - this.startOffset.top = this.startCoords.page.y - rect.top; - - this.startOffset.right = rect.right - this.startCoords.page.x; - this.startOffset.bottom = rect.bottom - this.startCoords.page.y; - - if ('width' in rect) { width = rect.width; } - else { width = rect.right - rect.left; } - if ('height' in rect) { height = rect.height; } - else { height = rect.bottom - rect.top; } - } - else { - this.startOffset.left = this.startOffset.top = this.startOffset.right = this.startOffset.bottom = 0; - } - - if (rect && snap.elementOrigin) { - this.snapOffset.x = this.startOffset.left - (width * snap.elementOrigin.x); - this.snapOffset.y = this.startOffset.top - (height * snap.elementOrigin.y); - } - else { - this.snapOffset.x = this.snapOffset.y = 0; - } - - if (rect && restrict.elementRect) { - this.restrictOffset.left = this.startOffset.left - (width * restrict.elementRect.left); - this.restrictOffset.top = this.startOffset.top - (height * restrict.elementRect.top); - - this.restrictOffset.right = this.startOffset.right - (width * (1 - restrict.elementRect.right)); - this.restrictOffset.bottom = this.startOffset.bottom - (height * (1 - restrict.elementRect.bottom)); - } - else { - this.restrictOffset.left = this.restrictOffset.top = this.restrictOffset.right = this.restrictOffset.bottom = 0; - } - } - - var snapCoords = starting? this.startCoords.page : this.curCoords.page; - - if (shouldSnap ) { this.setSnapping (snapCoords); } else { this.snapStatus .locked = false; } - if (shouldRestrict) { this.setRestriction(snapCoords); } else { this.restrictStatus.restricted = false; } - - var shouldMove = (shouldSnap? (this.snapStatus.changed || !this.snapStatus.locked): true) - && (shouldRestrict? (!this.restrictStatus.restricted || (this.restrictStatus.restricted && this.restrictStatus.changed)): true); - - // move if snapping or restriction doesn't prevent it - if (shouldMove) { - var action = /resize/.test(this.prepared)? 'resize': this.prepared; - if (starting) { - var dragStartEvent = this[action + 'Start'](this.downEvent); - - this.prevEvent = dragStartEvent; - - // reset active dropzones - this.activeDrops.dropzones = []; - this.activeDrops.elements = []; - this.activeDrops.rects = []; - - if (!this.dynamicDrop) { - this.setActiveDrops(this.element); - } - - var dropEvents = this.getDropEvents(event, dragStartEvent); - - if (dropEvents.activate) { - this.fireActiveDrops(dropEvents.activate); - } - - snapCoords = this.curCoords.page; - - // set snapping and restriction for the move event - if (shouldSnap ) { this.setSnapping (snapCoords); } - if (shouldRestrict) { this.setRestriction(snapCoords); } - } - - this.prevEvent = this[action + 'Move'](event); - } - - this.checkAndPreventDefault(event, this.target, this.element); - } - } - - copyCoords(this.prevCoords, this.curCoords); - - if (this.dragging || this.resizing) { - autoScroll.edgeMove(event); - } - }, - - dragStart: function (event) { - var dragEvent = new InteractEvent(this, event, 'drag', 'start', this.element); - - this.dragging = true; - this.target.fire(dragEvent); - - return dragEvent; - }, - - dragMove: function (event) { - var target = this.target, - dragEvent = new InteractEvent(this, event, 'drag', 'move', this.element), - draggableElement = this.element, - drop = this.getDrop(dragEvent, draggableElement); - - this.dropTarget = drop.dropzone; - this.dropElement = drop.element; - - var dropEvents = this.getDropEvents(event, dragEvent); - - target.fire(dragEvent); - - if (dropEvents.leave) { this.prevDropTarget.fire(dropEvents.leave); } - if (dropEvents.enter) { this.dropTarget.fire(dropEvents.enter); } - if (dropEvents.move ) { this.dropTarget.fire(dropEvents.move ); } - - this.prevDropTarget = this.dropTarget; - this.prevDropElement = this.dropElement; - - return dragEvent; - }, - - resizeStart: function (event) { - var resizeEvent = new InteractEvent(this, event, 'resize', 'start', this.element); - - this.target.fire(resizeEvent); - - this.resizing = true; - - return resizeEvent; - }, - - resizeMove: function (event) { - var resizeEvent = new InteractEvent(this, event, 'resize', 'move', this.element); - - this.target.fire(resizeEvent); - - return resizeEvent; - }, - - gestureStart: function (event) { - var gestureEvent = new InteractEvent(this, event, 'gesture', 'start', this.element); - - gestureEvent.ds = 0; - - this.gesture.startDistance = this.gesture.prevDistance = gestureEvent.distance; - this.gesture.startAngle = this.gesture.prevAngle = gestureEvent.angle; - this.gesture.scale = 1; - - this.gesturing = true; - - this.target.fire(gestureEvent); - - return gestureEvent; - }, - - gestureMove: function (event) { - if (!this.pointerIds.length) { - return this.prevEvent; - } - - var gestureEvent; - - gestureEvent = new InteractEvent(this, event, 'gesture', 'move', this.element); - gestureEvent.ds = gestureEvent.scale - this.gesture.scale; - - this.target.fire(gestureEvent); - - this.gesture.prevAngle = gestureEvent.angle; - this.gesture.prevDistance = gestureEvent.distance; - - if (gestureEvent.scale !== Infinity && - gestureEvent.scale !== null && - gestureEvent.scale !== undefined && - !isNaN(gestureEvent.scale)) { - - this.gesture.scale = gestureEvent.scale; - } - - return gestureEvent; - }, - - // End interact move events and stop auto-scroll unless inertia is enabled - pointerUp: function (pointer, event, eventTarget, curEventTarget) { - var endEvent, - target = this.target, - options = target && target.options, - inertiaOptions = options && options.inertia, - inertiaStatus = this.inertiaStatus; - - if (this.interacting()) { - - if (inertiaStatus.active) { return; } - - var pointerSpeed, - now = new Date().getTime(), - inertiaPossible = false, - inertia = false, - smoothEnd = false, - endSnap = checkSnap(target, this.prepared) && options.snap.endOnly, - endRestrict = checkRestrict(target, this.prepared) && options.restrict.endOnly, - dx = 0, - dy = 0, - startEvent; - - if (this.dragging) { - if (options.dragAxis === 'x' ) { pointerSpeed = Math.abs(this.pointerDelta.client.vx); } - else if (options.dragAxis === 'y' ) { pointerSpeed = Math.abs(this.pointerDelta.client.vy); } - else /*options.dragAxis === 'xy'*/{ pointerSpeed = this.pointerDelta.client.speed; } - } - - // check if inertia should be started - inertiaPossible = (options.inertiaEnabled - && this.prepared !== 'gesture' - && contains(inertiaOptions.actions, this.prepared) - && event !== inertiaStatus.startEvent); - - inertia = (inertiaPossible - && (now - this.curCoords.timeStamp) < 50 - && pointerSpeed > inertiaOptions.minSpeed - && pointerSpeed > inertiaOptions.endSpeed); - - if (inertiaPossible && !inertia && (endSnap || endRestrict)) { - - var snapRestrict = {}; - - snapRestrict.snap = snapRestrict.restrict = snapRestrict; - - if (endSnap) { - this.setSnapping(this.curCoords.page, snapRestrict); - if (snapRestrict.locked) { - dx += snapRestrict.dx; - dy += snapRestrict.dy; - } - } - - if (endRestrict) { - this.setRestriction(this.curCoords.page, snapRestrict); - if (snapRestrict.restricted) { - dx += snapRestrict.dx; - dy += snapRestrict.dy; - } - } - - if (dx || dy) { - smoothEnd = true; - } - } - - if (inertia || smoothEnd) { - copyCoords(inertiaStatus.upCoords, this.curCoords); - - this.pointers[0] = inertiaStatus.startEvent = startEvent = - new InteractEvent(this, event, this.prepared, 'inertiastart', this.element); - - inertiaStatus.t0 = now; - - target.fire(inertiaStatus.startEvent); - - if (inertia) { - inertiaStatus.vx0 = this.pointerDelta.client.vx; - inertiaStatus.vy0 = this.pointerDelta.client.vy; - inertiaStatus.v0 = pointerSpeed; - - this.calcInertia(inertiaStatus); - - var page = extend({}, this.curCoords.page), - origin = getOriginXY(target, this.element), - statusObject; - - page.x = page.x + inertiaStatus.xe - origin.x; - page.y = page.y + inertiaStatus.ye - origin.y; - - statusObject = { - useStatusXY: true, - x: page.x, - y: page.y, - dx: 0, - dy: 0, - snap: null - }; - - statusObject.snap = statusObject; - - dx = dy = 0; - - if (endSnap) { - var snap = this.setSnapping(this.curCoords.page, statusObject); - - if (snap.locked) { - dx += snap.dx; - dy += snap.dy; - } - } - - if (endRestrict) { - var restrict = this.setRestriction(this.curCoords.page, statusObject); - - if (restrict.restricted) { - dx += restrict.dx; - dy += restrict.dy; - } - } - - inertiaStatus.modifiedXe += dx; - inertiaStatus.modifiedYe += dy; - - inertiaStatus.i = reqFrame(this.boundInertiaFrame); - } - else { - inertiaStatus.smoothEnd = true; - inertiaStatus.xe = dx; - inertiaStatus.ye = dy; - - inertiaStatus.sx = inertiaStatus.sy = 0; - - inertiaStatus.i = reqFrame(this.boundSmoothEndFrame); - } - - inertiaStatus.active = true; - return; - } - - if (endSnap || endRestrict) { - // fire a move event at the snapped coordinates - this.pointerMove(pointer, event, eventTarget, curEventTarget, true); - } - } - - if (this.dragging) { - endEvent = new InteractEvent(this, event, 'drag', 'end', this.element); - - var draggableElement = this.element, - drop = this.getDrop(endEvent, draggableElement); - - this.dropTarget = drop.dropzone; - this.dropElement = drop.element; - - var dropEvents = this.getDropEvents(event, endEvent); - - if (dropEvents.leave) { this.prevDropTarget.fire(dropEvents.leave); } - if (dropEvents.enter) { this.dropTarget.fire(dropEvents.enter); } - if (dropEvents.drop ) { this.dropTarget.fire(dropEvents.drop ); } - if (dropEvents.deactivate) { - this.fireActiveDrops(dropEvents.deactivate); - } - - target.fire(endEvent); - } - else if (this.resizing) { - endEvent = new InteractEvent(this, event, 'resize', 'end', this.element); - target.fire(endEvent); - } - else if (this.gesturing) { - endEvent = new InteractEvent(this, event, 'gesture', 'end', this.element); - target.fire(endEvent); - } - - this.stop(event); - }, - - collectDrops: function (element) { - var drops = [], - elements = [], - i; - - element = element || this.element; - - // collect all dropzones and their elements which qualify for a drop - for (i = 0; i < interactables.length; i++) { - if (!interactables[i].options.dropzone) { continue; } - - var current = interactables[i]; - - // test the draggable element against the dropzone's accept setting - if ((isElement(current.options.accept) && current.options.accept !== element) - || (isString(current.options.accept) - && !matchesSelector(element, current.options.accept))) { - - continue; - } - - // query for new elements if necessary - var dropElements = current.selector? current._context.querySelectorAll(current.selector) : [current._element]; - - for (var j = 0, len = dropElements.length; j < len; j++) { - var currentElement = dropElements[j]; - - if (currentElement === element) { - continue; - } - - drops.push(current); - elements.push(currentElement); - } - } - - return { - dropzones: drops, - elements: elements - }; - }, - - fireActiveDrops: function (event) { - var i, - current, - currentElement, - prevElement; - - // loop through all active dropzones and trigger event - for (i = 0; i < this.activeDrops.dropzones.length; i++) { - current = this.activeDrops.dropzones[i]; - currentElement = this.activeDrops.elements [i]; - - // prevent trigger of duplicate events on same element - if (currentElement !== prevElement) { - // set current element as event target - event.target = currentElement; - current.fire(event); - } - prevElement = currentElement; - } - }, - - // Collect a new set of possible drops and save them in activeDrops. - // setActiveDrops should always be called when a drag has just started or a - // drag event happens while dynamicDrop is true - setActiveDrops: function (dragElement) { - // get dropzones and their elements that could receive the draggable - var possibleDrops = this.collectDrops(dragElement, true); - - this.activeDrops.dropzones = possibleDrops.dropzones; - this.activeDrops.elements = possibleDrops.elements; - this.activeDrops.rects = []; - - for (var i = 0; i < this.activeDrops.dropzones.length; i++) { - this.activeDrops.rects[i] = this.activeDrops.dropzones[i].getRect(this.activeDrops.elements[i]); - } - }, - - getDrop: function (event, dragElement) { - var validDrops = []; - - if (dynamicDrop) { - this.setActiveDrops(dragElement); - } - - // collect all dropzones and their elements which qualify for a drop - for (var j = 0; j < this.activeDrops.dropzones.length; j++) { - var current = this.activeDrops.dropzones[j], - currentElement = this.activeDrops.elements [j], - rect = this.activeDrops.rects [j]; - - validDrops.push(current.dropCheck(this.pointers[0], this.target, dragElement, currentElement, rect) - ? currentElement - : null); - } - - // get the most appropriate dropzone based on DOM depth and order - var dropIndex = indexOfDeepestElement(validDrops), - dropzone = this.activeDrops.dropzones[dropIndex] || null, - element = this.activeDrops.elements [dropIndex] || null; - - return { - dropzone: dropzone, - element: element - }; - }, - - getDropEvents: function (pointerEvent, dragEvent) { - var dragLeaveEvent = null, - dragEnterEvent = null, - dropActivateEvent = null, - dropDeactivateEvent = null, - dropMoveEvent = null, - dropEvent = null; - - if (this.dropElement !== this.prevDropElement) { - // if there was a prevDropTarget, create a dragleave event - if (this.prevDropTarget) { - dragLeaveEvent = new InteractEvent(this, pointerEvent, 'drag', 'leave', this.prevDropElement, dragEvent.target); - dragLeaveEvent.draggable = dragEvent.interactable; - dragEvent.dragLeave = this.prevDropElement; - dragEvent.prevDropzone = this.prevDropTarget; - } - // if the dropTarget is not null, create a dragenter event - if (this.dropTarget) { - dragEnterEvent = new InteractEvent(this, pointerEvent, 'drag', 'enter', this.dropElement, dragEvent.target); - dragEnterEvent.draggable = dragEvent.interactable; - dragEvent.dragEnter = this.dropElement; - dragEvent.dropzone = this.dropTarget; - } - } - - if (dragEvent.type === 'dragend' && this.dropTarget) { - dropEvent = new InteractEvent(this, pointerEvent, 'drop', null, this.dropElement, dragEvent.target); - dropEvent.draggable = dragEvent.interactable; - dragEvent.dropzone = this.dropTarget; - } - if (dragEvent.type === 'dragstart') { - dropActivateEvent = new InteractEvent(this, pointerEvent, 'drop', 'activate', this.element, dragEvent.target); - dropActivateEvent.draggable = dragEvent.interactable; - } - if (dragEvent.type === 'dragend') { - dropDeactivateEvent = new InteractEvent(this, pointerEvent, 'drop', 'deactivate', this.element, dragEvent.target); - dropDeactivateEvent.draggable = dragEvent.interactable; - } - if (dragEvent.type === 'dragmove' && this.dropTarget) { - dropMoveEvent = { - target : this.dropElement, - relatedTarget: dragEvent.target, - draggable : dragEvent.interactable, - dragmove : dragEvent, - type : 'dropmove', - timeStamp : dragEvent.timeStamp - }; - dragEvent.dropzone = this.dropTarget; - } - - return { - enter : dragEnterEvent, - leave : dragLeaveEvent, - activate : dropActivateEvent, - deactivate : dropDeactivateEvent, - move : dropMoveEvent, - drop : dropEvent - }; - }, - - currentAction: function () { - return (this.dragging && 'drag') || (this.resizing && 'resize') || (this.gesturing && 'gesture') || null; - }, - - interacting: function () { - return this.dragging || this.resizing || this.gesturing; - }, - - clearTargets: function () { - if (this.target && !this.target.selector) { - this.target = this.element = null; - } - - this.dropTarget = this.dropElement = this.prevDropTarget = this.prevDropElement = null; - }, - - stop: function (event) { - if (this.interacting()) { - autoScroll.stop(); - this.matches = []; - this.matchElements = []; - - var target = this.target; - - if (target.options.styleCursor) { - document.documentElement.style.cursor = ''; - } - - // prevent Default only if were previously interacting - if (event && isFunction(event.preventDefault)) { - this.checkAndPreventDefault(event, target, this.element); - } - - if (this.dragging) { - this.activeDrops.dropzones = this.activeDrops.elements = this.activeDrops.rects = null; - } - - this.clearTargets(); - } - - this.pointerIsDown = this.snapStatus.locked = this.dragging = this.resizing = this.gesturing = false; - this.prepared = this.prevEvent = null; - this.inertiaStatus.resumeDx = this.inertiaStatus.resumeDy = 0; - - this.pointerIds.splice(0); - // pointers should be retained - //this.pointers.splice(0); - - // delete interaction if it's not the only one - if (interactions.length > 1) { - interactions.splice(indexOf(interactions, this), 1); - } - }, - - inertiaFrame: function () { - var inertiaStatus = this.inertiaStatus, - options = this.target.options.inertia, - lambda = options.resistance, - t = new Date().getTime() / 1000 - inertiaStatus.t0; - - if (t < inertiaStatus.te) { - - var progress = 1 - (Math.exp(-lambda * t) - inertiaStatus.lambda_v0) / inertiaStatus.one_ve_v0; - - if (inertiaStatus.modifiedXe === inertiaStatus.xe && inertiaStatus.modifiedYe === inertiaStatus.ye) { - inertiaStatus.sx = inertiaStatus.xe * progress; - inertiaStatus.sy = inertiaStatus.ye * progress; - } - else { - var quadPoint = getQuadraticCurvePoint( - 0, 0, - inertiaStatus.xe, inertiaStatus.ye, - inertiaStatus.modifiedXe, inertiaStatus.modifiedYe, - progress); - - inertiaStatus.sx = quadPoint.x; - inertiaStatus.sy = quadPoint.y; - } - - this.pointerMove(inertiaStatus.startEvent, inertiaStatus.startEvent); - - inertiaStatus.i = reqFrame(this.boundInertiaFrame); - } - else { - inertiaStatus.sx = inertiaStatus.modifiedXe; - inertiaStatus.sy = inertiaStatus.modifiedYe; - - this.pointerMove(inertiaStatus.startEvent, inertiaStatus.startEvent); - - inertiaStatus.active = false; - this.pointerUp(inertiaStatus.startEvent, inertiaStatus.startEvent); - } - }, - - smoothEndFrame: function () { - var inertiaStatus = this.inertiaStatus, - t = new Date().getTime() - inertiaStatus.t0, - duration = this.target.options.inertia.smoothEndDuration; - - if (t < duration) { - inertiaStatus.sx = easeOutQuad(t, 0, inertiaStatus.xe, duration); - inertiaStatus.sy = easeOutQuad(t, 0, inertiaStatus.ye, duration); - - this.pointerMove(inertiaStatus.startEvent, inertiaStatus.startEvent); - - inertiaStatus.i = reqFrame(this.boundSmoothEndFrame); - } - else { - inertiaStatus.sx = inertiaStatus.xe; - inertiaStatus.sy = inertiaStatus.ye; - - this.pointerMove(inertiaStatus.startEvent, inertiaStatus.startEvent); - - inertiaStatus.active = false; - inertiaStatus.smoothEnd = false; - - this.pointerUp(inertiaStatus.startEvent, inertiaStatus.startEvent); - } - }, - - addPointer: function (pointer) { - var id = getPointerId(pointer), - index = this.mouse? 0 : indexOf(this.pointerIds, id); - - if (index === -1) { - index = this.pointerIds.length; - this.pointerIds.push(id); - - // move events are kept so that multi-touch properties can still be - // calculated at the end of a gesture; use pointerIds index - this.pointers[index] = pointer; - } - else { - this.pointers[index] = pointer; - } - }, - - removePointer: function (pointer) { - var id = getPointerId(pointer), - index = this.mouse? 0 : indexOf(this.pointerIds, id); - - if (index === -1) { return; } - - this.pointerIds.splice(index, 1); - - // move events are kept so that multi-touch properties can still be - // calculated at the end of a GestureEvent sequence - //this.pointers.splice(index, 1); - }, - - recordPointer: function (pointer) { - // Do not update pointers while inertia is active. - // The inertia start event should be this.pointers[0] - if (this.inertiaStatus.active) { return; } - - var index = this.mouse? 0: indexOf(this.pointerIds, getPointerId(pointer)); - - if (index === -1) { return; } - - this.pointers[index] = pointer; - }, - - fireTaps: function (pointer, event, targets, elements) { - var tap = {}, - i; - - extend(tap, event); - extend(tap, pointer); - - tap.preventDefault = preventOriginalDefault; - tap.stopPropagation = InteractEvent.prototype.stopPropagation; - tap.stopImmediatePropagation = InteractEvent.prototype.stopImmediatePropagation; - - tap.timeStamp = new Date().getTime(); - tap.originalEvent = event; - tap.dt = tap.timeStamp - this.downTime; - tap.type = 'tap'; - - var interval = tap.timeStamp - this.tapTime, - dbl = (this.prevTap && this.prevTap.type !== 'doubletap' - && this.prevTap.target === tap.target - && interval < 500); - - this.tapTime = tap.timeStamp; - - for (i = 0; i < targets.length; i++) { - tap.currentTarget = elements[i]; - targets[i].fire(tap); - - if (tap.immediatePropagationStopped - ||(tap.propagationStopped && targets[i + 1] !== tap.currentTarget)) { - break; - } - } - - if (dbl) { - var doubleTap = {}; - - extend(doubleTap, tap); - - doubleTap.dt = interval; - doubleTap.type = 'doubletap'; - - for (i = 0; i < targets.length; i++) { - doubleTap.currentTarget = elements[i]; - targets[i].fire(doubleTap); - - if (doubleTap.immediatePropagationStopped - ||(doubleTap.propagationStopped && targets[i + 1] !== doubleTap.currentTarget)) { - break; - } - } - - this.prevTap = doubleTap; - } - else { - this.prevTap = tap; - } - }, - - collectTaps: function (pointer, event, eventTarget) { - if(this.pointerWasMoved - || !(this.downTarget && this.downTarget === eventTarget)) { - return; - } - - var tapTargets = [], - tapElements = []; - - var element = eventTarget; - - function collectSelectorTaps (interactable, selector, context) { - var elements = ie8MatchesSelector - ? context.querySelectorAll(selector) - : undefined; - - if (element !== document - && inContext(interactable, element) - && !testIgnore(interactable, element, eventTarget) - && testAllow(interactable, element, eventTarget) - && matchesSelector(element, selector, elements)) { - - tapTargets.push(interactable); - tapElements.push(element); - } - } - - while (element) { - if (interact.isSet(element)) { - tapTargets.push(interact(element)); - tapElements.push(element); - } - - interactables.forEachSelector(collectSelectorTaps); - - element = element.parentNode; - } - - if (tapTargets.length) { - this.fireTaps(pointer, event, tapTargets, tapElements); - } - }, - - validateSelector: function (pointer, matches, matchElements) { - for (var i = 0, len = matches.length; i < len; i++) { - var match = matches[i], - matchElement = matchElements[i], - action = validateAction(match.getAction(pointer, this, matchElement), match); - - if (action && withinInteractionLimit(match, matchElement, action)) { - this.target = match; - this.element = matchElement; - - return action; - } - } - }, - - setSnapping: function (pageCoords, status) { - var snap = this.target.options.snap, - anchors = snap.anchors, - page, - closest, - range, - inRange, - snapChanged, - dx, - dy, - distance, - i, len; - - status = status || this.snapStatus; - - if (status.useStatusXY) { - page = { x: status.x, y: status.y }; - } - else { - var origin = getOriginXY(this.target, this.element); - - page = extend({}, pageCoords); - - page.x -= origin.x; - page.y -= origin.y; - } - - page.x -= this.inertiaStatus.resumeDx; - page.y -= this.inertiaStatus.resumeDy; - - status.realX = page.x; - status.realY = page.y; - - // change to infinite range when range is negative - if (snap.range < 0) { snap.range = Infinity; } - - // create an anchor representative for each path's returned point - if (snap.mode === 'path') { - anchors = []; - - for (i = 0, len = snap.paths.length; i < len; i++) { - var path = snap.paths[i]; - - if (isFunction(path)) { - path = path(page.x, page.y); - } - - anchors.push({ - x: isNumber(path.x) ? path.x : page.x, - y: isNumber(path.y) ? path.y : page.y, - - range: isNumber(path.range)? path.range: snap.range - }); - } - } - - if ((snap.mode === 'anchor' || snap.mode === 'path') && anchors.length) { - closest = { - anchor: null, - distance: 0, - range: 0, - dx: 0, - dy: 0 - }; - - for (i = 0, len = anchors.length; i < len; i++) { - var anchor = anchors[i]; - - range = isNumber(anchor.range)? anchor.range: snap.range; - - dx = anchor.x - page.x + this.snapOffset.x; - dy = anchor.y - page.y + this.snapOffset.y; - distance = hypot(dx, dy); - - inRange = distance < range; - - // Infinite anchors count as being out of range - // compared to non infinite ones that are in range - if (range === Infinity && closest.inRange && closest.range !== Infinity) { - inRange = false; - } - - if (!closest.anchor || (inRange? - // is the closest anchor in range? - (closest.inRange && range !== Infinity)? - // the pointer is relatively deeper in this anchor - distance / range < closest.distance / closest.range: - //the pointer is closer to this anchor - distance < closest.distance: - // The other is not in range and the pointer is closer to this anchor - (!closest.inRange && distance < closest.distance))) { - - if (range === Infinity) { - inRange = true; - } - - closest.anchor = anchor; - closest.distance = distance; - closest.range = range; - closest.inRange = inRange; - closest.dx = dx; - closest.dy = dy; - - status.range = range; - } - } - - inRange = closest.inRange; - snapChanged = (closest.anchor.x !== status.x || closest.anchor.y !== status.y); - - status.snappedX = closest.anchor.x; - status.snappedY = closest.anchor.y; - status.dx = closest.dx; - status.dy = closest.dy; - } - else if (snap.mode === 'grid') { - var gridx = Math.round((page.x - snap.gridOffset.x - this.snapOffset.x) / snap.grid.x), - gridy = Math.round((page.y - snap.gridOffset.y - this.snapOffset.y) / snap.grid.y), - - newX = gridx * snap.grid.x + snap.gridOffset.x + this.snapOffset.x, - newY = gridy * snap.grid.y + snap.gridOffset.y + this.snapOffset.y; - - dx = newX - page.x; - dy = newY - page.y; - - distance = hypot(dx, dy); - - inRange = distance < snap.range; - snapChanged = (newX !== status.snappedX || newY !== status.snappedY); - - status.snappedX = newX; - status.snappedY = newY; - status.dx = dx; - status.dy = dy; - - status.range = snap.range; - } - - status.changed = (snapChanged || (inRange && !status.locked)); - status.locked = inRange; - - return status; - }, - - setRestriction: function (pageCoords, status) { - var target = this.target, - action = /resize/.test(this.prepared)? 'resize' : this.prepared, - restrict = target && target.options.restrict, - restriction = restrict && restrict[action], - page; - - if (!restriction) { - return status; - } - - status = status || this.restrictStatus; - - page = status.useStatusXY - ? page = { x: status.x, y: status.y } - : page = extend({}, pageCoords); - - if (status.snap && status.snap.locked) { - page.x += status.snap.dx || 0; - page.y += status.snap.dy || 0; - } - - page.x -= this.inertiaStatus.resumeDx; - page.y -= this.inertiaStatus.resumeDy; - - status.dx = 0; - status.dy = 0; - status.restricted = false; - - var rect, restrictedX, restrictedY; - - if (isString(restriction)) { - if (restriction === 'parent') { - restriction = this.element.parentNode; - } - else if (restriction === 'self') { - restriction = target.getRect(this.element); - } - else { - restriction = matchingParent(this.element, restriction); - } - - if (!restriction) { return status; } - } - - if (isFunction(restriction)) { - restriction = restriction(page.x, page.y, this.element); - } - - if (isElement(restriction)) { - restriction = getElementRect(restriction); - } - - rect = restriction; - - // object is assumed to have - // x, y, width, height or - // left, top, right, bottom - if ('x' in restriction && 'y' in restriction) { - restrictedX = Math.max(Math.min(rect.x + rect.width - this.restrictOffset.right , page.x), rect.x + this.restrictOffset.left); - restrictedY = Math.max(Math.min(rect.y + rect.height - this.restrictOffset.bottom, page.y), rect.y + this.restrictOffset.top ); - } - else { - restrictedX = Math.max(Math.min(rect.right - this.restrictOffset.right , page.x), rect.left + this.restrictOffset.left); - restrictedY = Math.max(Math.min(rect.bottom - this.restrictOffset.bottom, page.y), rect.top + this.restrictOffset.top ); - } - - status.dx = restrictedX - page.x; - status.dy = restrictedY - page.y; - - status.changed = status.restrictedX !== restrictedX || status.restrictedY !== restrictedY; - status.restricted = !!(status.dx || status.dy); - - status.restrictedX = restrictedX; - status.restrictedY = restrictedY; - - return status; - }, - - checkAndPreventDefault: function (event, interactable, element) { - if (!(interactable = interactable || this.target)) { return; } - - var options = interactable.options, - prevent = options.preventDefault; - - if (prevent === 'auto' && element && !/^input$|^textarea$/i.test(element.nodeName)) { - // do not preventDefault on pointerdown if the prepared action is a drag - // and dragging can only start from a certain direction - this allows - // a touch to pan the viewport if a drag isn't in the right direction - if (/down|start/i.test(event.type) - && this.prepared === 'drag' && options.dragAxis !== 'xy') { - - return; - } - - event.preventDefault(); - return; - } - - if (prevent === true) { - event.preventDefault(); - return; - } - }, - - calcInertia: function (status) { - var inertiaOptions = this.target.options.inertia, - lambda = inertiaOptions.resistance, - inertiaDur = -Math.log(inertiaOptions.endSpeed / status.v0) / lambda; - - status.x0 = this.prevEvent.pageX; - status.y0 = this.prevEvent.pageY; - status.t0 = status.startEvent.timeStamp / 1000; - status.sx = status.sy = 0; - - status.modifiedXe = status.xe = (status.vx0 - inertiaDur) / lambda; - status.modifiedYe = status.ye = (status.vy0 - inertiaDur) / lambda; - status.te = inertiaDur; - - status.lambda_v0 = lambda / status.v0; - status.one_ve_v0 = 1 - inertiaOptions.endSpeed / status.v0; - } - - }; - - function getInteractionFromPointer (pointer, eventType, eventTarget) { - var i = 0, len = interactions.length, - mouseEvent = (/mouse/i.test(pointer.pointerType || eventType) - // MSPointerEvent.MSPOINTER_TYPE_MOUSE - || pointer.pointerType === 4), - interaction; - - var id = getPointerId(pointer); - - // try to resume inertia with a new pointer - if (/down|start/i.test(eventType)) { - for (i = 0; i < len; i++) { - interaction = interactions[i]; - - var element = eventTarget; - - if (interaction.inertiaStatus.active && (interaction.mouse === mouseEvent)) { - while (element) { - // if the element is the interaction element - if (element === interaction.element) { - // update the interaction's pointer - interaction.removePointer(interaction.pointers[0]); - interaction.addPointer(pointer); - - return interaction; - } - element = element.parentNode; - } - } - } - } - - // if it's a mouse interaction - if (mouseEvent || !(supportsTouch || supportsPointerEvent)) { - - // find a mouse interaction that's not in inertia phase - for (i = 0; i < len; i++) { - if (interactions[i].mouse && !interactions[i].inertiaStatus.active) { - return interactions[i]; - } - } - - // find any interaction specifically for mouse. - // if the eventType is a mousedown, and inertia is active - // ignore the interaction - for (i = 0; i < len; i++) { - if (interactions[i].mouse && !(/down/.test(eventType) && interactions[i].inertiaStatus.active)) { - return interaction; - } - } - - // create a new interaction for mouse - interaction = new Interaction(); - interaction.mouse = true; - - return interaction; - } - - // get interaction that has this pointer - for (i = 0; i < len; i++) { - if (contains(interactions[i].pointerIds, id)) { - return interactions[i]; - } - } - - // at this stage, a pointerUp should not return an interaction - if (/up|end|out/i.test(eventType)) { - return null; - } - - // get first idle interaction - for (i = 0; i < len; i++) { - interaction = interactions[i]; - - if ((!interaction.prepared || (interaction.target.gesturable())) - && !interaction.interacting() - && !(!mouseEvent && interaction.mouse)) { - - interaction.addPointer(pointer); - - return interaction; - } - } - - return new Interaction(); - } - - function doOnInteractions (method) { - return (function (event) { - var interaction, - eventTarget = getActualElement(event.target), - curEventTarget = getActualElement(event.currentTarget), - i; - - if (supportsTouch && /touch/.test(event.type)) { - prevTouchTime = new Date().getTime(); - - for (i = 0; i < event.changedTouches.length; i++) { - var pointer = event.changedTouches[i]; - - interaction = getInteractionFromPointer(pointer, event.type, eventTarget); - - if (!interaction) { continue; } - - interaction[method](pointer, event, eventTarget, curEventTarget); - } - } - else { - if (!supportsPointerEvent && /mouse/.test(event.type)) { - // ignore mouse events while touch interactions are active - for (i = 0; i < interactions.length; i++) { - if (!interactions[i].mouse && interactions[i].pointerIsDown) { - return; - } - } - - // try to ignore mouse events that are simulated by the browser - // after a touch event - if (new Date().getTime() - prevTouchTime < 500) { - return; - } - } - - interaction = getInteractionFromPointer(event, event.type, eventTarget); - - if (!interaction) { return; } - - interaction[method](event, event, eventTarget, curEventTarget); - } - }); - } - - function InteractEvent (interaction, event, action, phase, element, related) { - var client, - page, - target = interaction.target, - snapStatus = interaction.snapStatus, - restrictStatus = interaction.restrictStatus, - pointers = interaction.pointers, - deltaSource = (target && target.options || defaultOptions).deltaSource, - sourceX = deltaSource + 'X', - sourceY = deltaSource + 'Y', - options = target? target.options: defaultOptions, - origin = getOriginXY(target, element), - starting = phase === 'start', - ending = phase === 'end', - coords = starting? interaction.startCoords : interaction.curCoords; - - element = element || interaction.element; - - page = extend({}, coords.page); - client = extend({}, coords.client); - - page.x -= origin.x; - page.y -= origin.y; - - client.x -= origin.x; - client.y -= origin.y; - - if (checkSnap(target, action) && !(starting && options.snap.elementOrigin)) { - this.snap = { - range : snapStatus.range, - locked : snapStatus.locked, - x : snapStatus.snappedX, - y : snapStatus.snappedY, - realX : snapStatus.realX, - realY : snapStatus.realY, - dx : snapStatus.dx, - dy : snapStatus.dy - }; - - if (snapStatus.locked) { - page.x += snapStatus.dx; - page.y += snapStatus.dy; - client.x += snapStatus.dx; - client.y += snapStatus.dy; - } - } - - if (checkRestrict(target, action) && !(starting && options.restrict.elementRect) && restrictStatus.restricted) { - page.x += restrictStatus.dx; - page.y += restrictStatus.dy; - client.x += restrictStatus.dx; - client.y += restrictStatus.dy; - - this.restrict = { - dx: restrictStatus.dx, - dy: restrictStatus.dy - }; - } - - this.pageX = page.x; - this.pageY = page.y; - this.clientX = client.x; - this.clientY = client.y; - - this.x0 = interaction.startCoords.page.x; - this.y0 = interaction.startCoords.page.y; - this.clientX0 = interaction.startCoords.client.x; - this.clientY0 = interaction.startCoords.client.y; - this.ctrlKey = event.ctrlKey; - this.altKey = event.altKey; - this.shiftKey = event.shiftKey; - this.metaKey = event.metaKey; - this.button = event.button; - this.target = element; - this.t0 = interaction.downTime; - this.type = action + (phase || ''); - - this.interaction = interaction; - this.interactable = target; - - var inertiaStatus = interaction.inertiaStatus; - - if (inertiaStatus.active) { - this.detail = 'inertia'; - } - - if (related) { - this.relatedTarget = related; - } - - // end event dx, dy is difference between start and end points - if (ending || action === 'drop') { - if (deltaSource === 'client') { - this.dx = client.x - interaction.startCoords.client.x; - this.dy = client.y - interaction.startCoords.client.y; - } - else { - this.dx = page.x - interaction.startCoords.page.x; - this.dy = page.y - interaction.startCoords.page.y; - } - } - else if (starting) { - this.dx = 0; - this.dy = 0; - } - // copy properties from previousmove if starting inertia - else if (phase === 'inertiastart') { - this.dx = interaction.prevEvent.dx; - this.dy = interaction.prevEvent.dy; - } - else { - if (deltaSource === 'client') { - this.dx = client.x - interaction.prevEvent.clientX; - this.dy = client.y - interaction.prevEvent.clientY; - } - else { - this.dx = page.x - interaction.prevEvent.pageX; - this.dy = page.y - interaction.prevEvent.pageY; - } - } - if (interaction.prevEvent && interaction.prevEvent.detail === 'inertia' - && !inertiaStatus.active && options.inertia.zeroResumeDelta) { - - inertiaStatus.resumeDx += this.dx; - inertiaStatus.resumeDy += this.dy; - - this.dx = this.dy = 0; - } - - if (action === 'resize') { - if (options.squareResize || event.shiftKey) { - if (interaction.resizeAxes === 'y') { - this.dx = this.dy; - } - else { - this.dy = this.dx; - } - this.axes = 'xy'; - } - else { - this.axes = interaction.resizeAxes; - - if (interaction.resizeAxes === 'x') { - this.dy = 0; - } - else if (interaction.resizeAxes === 'y') { - this.dx = 0; - } - } - } - else if (action === 'gesture') { - this.touches = [pointers[0], pointers[1]]; - - if (starting) { - this.distance = touchDistance(pointers, deltaSource); - this.box = touchBBox(pointers); - this.scale = 1; - this.ds = 0; - this.angle = touchAngle(pointers, undefined, deltaSource); - this.da = 0; - } - else if (ending || event instanceof InteractEvent) { - this.distance = interaction.prevEvent.distance; - this.box = interaction.prevEvent.box; - this.scale = interaction.prevEvent.scale; - this.ds = this.scale - 1; - this.angle = interaction.prevEvent.angle; - this.da = this.angle - interaction.gesture.startAngle; - } - else { - this.distance = touchDistance(pointers, deltaSource); - this.box = touchBBox(pointers); - this.scale = this.distance / interaction.gesture.startDistance; - this.angle = touchAngle(pointers, interaction.gesture.prevAngle, deltaSource); - - this.ds = this.scale - interaction.gesture.prevScale; - this.da = this.angle - interaction.gesture.prevAngle; - } - } - - if (starting) { - this.timeStamp = interaction.downTime; - this.dt = 0; - this.duration = 0; - this.speed = 0; - this.velocityX = 0; - this.velocityY = 0; - } - else if (phase === 'inertiastart') { - this.timeStamp = interaction.prevEvent.timeStamp; - this.dt = interaction.prevEvent.dt; - this.duration = interaction.prevEvent.duration; - this.speed = interaction.prevEvent.speed; - this.velocityX = interaction.prevEvent.velocityX; - this.velocityY = interaction.prevEvent.velocityY; - } - else { - this.timeStamp = new Date().getTime(); - this.dt = this.timeStamp - interaction.prevEvent.timeStamp; - this.duration = this.timeStamp - interaction.downTime; - - if (event instanceof InteractEvent) { - var dx = this[sourceX] - interaction.prevEvent[sourceX], - dy = this[sourceY] - interaction.prevEvent[sourceY], - dt = this.dt / 1000; - - this.speed = hypot(dx, dy) / dt; - this.velocityX = dx / dt; - this.velocityY = dy / dt; - } - // if normal move or end event, use previous user event coords - else { - // speed and velocity in pixels per second - this.speed = interaction.pointerDelta[deltaSource].speed; - this.velocityX = interaction.pointerDelta[deltaSource].vx; - this.velocityY = interaction.pointerDelta[deltaSource].vy; - } - } - - if ((ending || phase === 'inertiastart') - && interaction.prevEvent.speed > 600 && this.timeStamp - interaction.prevEvent.timeStamp < 150) { - - var angle = 180 * Math.atan2(interaction.prevEvent.velocityY, interaction.prevEvent.velocityX) / Math.PI, - overlap = 22.5; - - if (angle < 0) { - angle += 360; - } - - var left = 135 - overlap <= angle && angle < 225 + overlap, - up = 225 - overlap <= angle && angle < 315 + overlap, - - right = !left && (315 - overlap <= angle || angle < 45 + overlap), - down = !up && 45 - overlap <= angle && angle < 135 + overlap; - - this.swipe = { - up : up, - down : down, - left : left, - right: right, - angle: angle, - speed: interaction.prevEvent.speed, - velocity: { - x: interaction.prevEvent.velocityX, - y: interaction.prevEvent.velocityY - } - }; - } - } - - InteractEvent.prototype = { - preventDefault: blank, - stopImmediatePropagation: function () { - this.immediatePropagationStopped = this.propagationStopped = true; - }, - stopPropagation: function () { - this.propagationStopped = true; - } - }; - - function preventOriginalDefault () { - this.originalEvent.preventDefault(); - } - - function defaultActionChecker (pointer, interaction, element) { - var rect = this.getRect(element), - right, - bottom, - action = null, - page = extend({}, interaction.curCoords.page), - options = this.options; - - if (!rect) { return null; } - - if (actionIsEnabled.resize && options.resizable) { - right = options.resizeAxis !== 'y' && page.x > (rect.right - margin); - bottom = options.resizeAxis !== 'x' && page.y > (rect.bottom - margin); - } - - interaction.resizeAxes = (right?'x': '') + (bottom?'y': ''); - - action = (interaction.resizeAxes)? - 'resize' + interaction.resizeAxes: - actionIsEnabled.drag && options.draggable? - 'drag': - null; - - if (actionIsEnabled.gesture - && interaction.pointerIds.length >=2 - && !(interaction.dragging || interaction.resizing)) { - action = 'gesture'; - } - - return action; - } - - // Check if action is enabled globally and the current target supports it - // If so, return the validated action. Otherwise, return null - function validateAction (action, interactable) { - if (!isString(action)) { return null; } - - var actionType = action.search('resize') !== -1? 'resize': action, - options = interactable; - - if (( (actionType === 'resize' && options.resizable ) - || (action === 'drag' && options.draggable ) - || (action === 'gesture' && options.gesturable)) - && actionIsEnabled[actionType]) { - - if (action === 'resize' || action === 'resizeyx') { - action = 'resizexy'; - } - - return action; - } - return null; - } - - var listeners = {}, - interactionListeners = [ - 'dragStart', 'dragMove', 'resizeStart', 'resizeMove', 'gestureStart', 'gestureMove', - 'pointerOver', 'pointerOut', 'pointerHover', 'selectorDown', 'pointerDown', 'pointerMove', 'pointerUp', - 'addPointer', 'removePointer', 'recordPointer', 'collectTaps' - ]; - - for (var i = 0, len = interactionListeners.length; i < len; i++) { - var name = interactionListeners[i]; - - listeners[name] = doOnInteractions(name); - } - - // bound to the interactable context when a DOM event - // listener is added to a selector interactable - function delegateListener (event, useCapture) { - var fakeEvent = {}, - delegated = delegatedEvents[event.type], - element = event.target; - - useCapture = useCapture? true: false; - - // duplicate the event so that currentTarget can be changed - for (var prop in event) { - fakeEvent[prop] = event[prop]; - } - - fakeEvent.originalEvent = event; - fakeEvent.preventDefault = preventOriginalDefault; - - // climb up document tree looking for selector matches - while (element && element !== document) { - for (var i = 0; i < delegated.selectors.length; i++) { - var selector = delegated.selectors[i], - context = delegated.contexts[i]; - - if (matchesSelector(element, selector) - && context === event.currentTarget - && nodeContains(context, element)) { - - var listeners = delegated.listeners[i]; - - fakeEvent.currentTarget = element; - - for (var j = 0; j < listeners.length; j++) { - if (listeners[j][1] === useCapture) { - listeners[j][0](fakeEvent); - } - } - } - } - - element = element.parentNode; - } - } - - function delegateUseCapture (event) { - return delegateListener.call(this, event, true); - } - - interactables.indexOfElement = function indexOfElement (element, context) { - for (var i = 0; i < this.length; i++) { - var interactable = this[i]; - - if ((interactable.selector === element - && (interactable._context === (context || document))) - - || (!interactable.selector && interactable._element === element)) { - - return i; - } - } - return -1; - }; - - interactables.get = function interactableGet (element, options) { - return this[this.indexOfElement(element, options && options.context)]; - }; - - interactables.forEachSelector = function (callback) { - for (var i = 0; i < this.length; i++) { - var interactable = this[i]; - - if (!interactable.selector) { - continue; - } - - var ret = callback(interactable, interactable.selector, interactable._context, i, this); - - if (ret !== undefined) { - return ret; - } - } - }; - - /*\ - * interact - [ method ] - * - * The methods of this variable can be used to set elements as - * interactables and also to change various default settings. - * - * Calling it as a function and passing an element or a valid CSS selector - * string returns an Interactable object which has various methods to - * configure it. - * - - element (Element | string) The HTML or SVG Element to interact with or CSS selector - = (object) An @Interactable - * - > Usage - | interact(document.getElementById('draggable')).draggable(true); - | - | var rectables = interact('rect'); - | rectables - | .gesturable(true) - | .on('gesturemove', function (event) { - | // something cool... - | }) - | .autoScroll(true); - \*/ - function interact (element, options) { - return interactables.get(element, options) || new Interactable(element, options); - } - - // A class for easy inheritance and setting of an Interactable's options - function IOptions (options) { - for (var option in defaultOptions) { - if (options.hasOwnProperty(option) - && typeof options[option] === typeof defaultOptions[option]) { - this[option] = options[option]; - } - } - } - - IOptions.prototype = defaultOptions; - - /*\ - * Interactable - [ property ] - ** - * Object type returned by @interact - \*/ - function Interactable (element, options) { - this._element = element; - this._iEvents = this._iEvents || {}; - - if (trySelector(element)) { - this.selector = element; - - if (options && options.context - && (window.Node - ? options.context instanceof window.Node - : (isElement(options.context) || options.context === document))) { - this._context = options.context; - } - } - else if (isElement(element)) { - if (PointerEvent) { - events.add(this, pEventTypes.down, listeners.pointerDown ); - events.add(this, pEventTypes.move, listeners.pointerHover); - } - else { - events.add(this, 'mousedown' , listeners.pointerDown ); - events.add(this, 'mousemove' , listeners.pointerHover); - events.add(this, 'touchstart', listeners.pointerDown ); - events.add(this, 'touchmove' , listeners.pointerHover); - } - } - - interactables.push(this); - - this.set(options); - } - - Interactable.prototype = { - setOnEvents: function (action, phases) { - if (action === 'drop') { - var drop = phases.ondrop || phases.onDrop || phases.drop, - dropactivate = phases.ondropactivate || phases.onDropActivate || phases.dropactivate - || phases.onactivate || phases.onActivate || phases.activate, - dropdeactivate = phases.ondropdeactivate || phases.onDropDeactivate || phases.dropdeactivate - || phases.ondeactivate || phases.onDeactivate || phases.deactivate, - dragenter = phases.ondragenter || phases.onDropEnter || phases.dragenter - || phases.onenter || phases.onEnter || phases.enter, - dragleave = phases.ondragleave || phases.onDropLeave || phases.dragleave - || phases.onleave || phases.onLeave || phases.leave, - dropmove = phases.ondropmove || phases.onDropMove || phases.dropmove - || phases.onmove || phases.onMove || phases.move; - - if (isFunction(drop) ) { this.ondrop = drop ; } - if (isFunction(dropactivate) ) { this.ondropactivate = dropactivate ; } - if (isFunction(dropdeactivate)) { this.ondropdeactivate = dropdeactivate; } - if (isFunction(dragenter) ) { this.ondragenter = dragenter ; } - if (isFunction(dragleave) ) { this.ondragleave = dragleave ; } - if (isFunction(dropmove) ) { this.ondropmove = dropmove ; } - } - else { - var start = phases.onstart || phases.onStart || phases.start, - move = phases.onmove || phases.onMove || phases.move, - end = phases.onend || phases.onEnd || phases.end, - inertiastart = phases.oninertiastart || phases.onInertiaStart || phases.inertiastart; - - action = 'on' + action; - - if (isFunction(start) ) { this[action + 'start' ] = start ; } - if (isFunction(move) ) { this[action + 'move' ] = move ; } - if (isFunction(end) ) { this[action + 'end' ] = end ; } - if (isFunction(inertiastart)) { this[action + 'inertiastart' ] = inertiastart ; } - } - - return this; - }, - - /*\ - * Interactable.draggable - [ method ] - * - * Gets or sets whether drag actions can be performed on the - * Interactable - * - = (boolean) Indicates if this can be the target of drag events - | var isDraggable = interact('ul li').draggable(); - * or - - options (boolean | object) #optional true/false or An object with event listeners to be fired on drag events (object makes the Interactable draggable) - = (object) This Interactable - | interact(element).draggable({ - | onstart: function (event) {}, - | onmove : function (event) {}, - | onend : function (event) {}, - | - | // the axis in which the first movement must be - | // for the drag sequence to start - | // 'xy' by default - any direction - | axis: 'x' || 'y' || 'xy', - | - | // max number of drags that can happen concurrently - | // with elements of this Interactable. 1 by default - | max: Infinity, - | - | // max number of drags that can target the same element - | // 1 by default - | maxPerElement: 2 - | }); - \*/ - draggable: function (options) { - if (isObject(options)) { - this.options.draggable = true; - this.setOnEvents('drag', options); - - if (isNumber(options.max)) { - this.options.dragMax = options.max; - } - if (isNumber(options.maxPerElement)) { - this.options.dragMaxPerElement = options.maxPerElement; - } - - if (/^x$|^y$|^xy$/.test(options.axis)) { - this.options.dragAxis = options.axis; - } - else if (options.axis === null) { - delete this.options.dragAxis; - } - - return this; - } - - if (isBool(options)) { - this.options.draggable = options; - - return this; - } - - if (options === null) { - delete this.options.draggable; - - return this; - } - - return this.options.draggable; - }, - - /*\ - * Interactable.dropzone - [ method ] - * - * Returns or sets whether elements can be dropped onto this - * Interactable to trigger drop events - * - * Dropzones can receive the following events: - * - `dragactivate` and `dragdeactivate` when an acceptable drag starts and ends - * - `dragenter` and `dragleave` when a draggable enters and leaves the dropzone - * - `drop` when a draggable is dropped into this dropzone - * - * Use the `accept` option to allow only elements that match the given CSS selector or element. - * - * Use the `overlap` option to set how drops are checked for. The allowed values are: - * - `'pointer'`, the pointer must be over the dropzone (default) - * - `'center'`, the draggable element's center must be over the dropzone - * - a number from 0-1 which is the `(intersection area) / (draggable area)`. - * e.g. `0.5` for drop to happen when half of the area of the - * draggable is over the dropzone - * - - options (boolean | object | null) #optional The new value to be set. - | interact('.drop').dropzone({ - | accept: '.can-drop' || document.getElementById('single-drop'), - | overlap: 'pointer' || 'center' || zeroToOne - | } - = (boolean | object) The current setting or this Interactable - \*/ - dropzone: function (options) { - if (isObject(options)) { - this.options.dropzone = true; - this.setOnEvents('drop', options); - this.accept(options.accept); - - if (/^(pointer|center)$/.test(options.overlap)) { - this.options.dropOverlap = options.overlap; - } - else if (isNumber(options.overlap)) { - this.options.dropOverlap = Math.max(Math.min(1, options.overlap), 0); - } - - return this; - } - - if (isBool(options)) { - this.options.dropzone = options; - - return this; - } - - if (options === null) { - delete this.options.dropzone; - - return this; - } - - return this.options.dropzone; - }, - - /*\ - * Interactable.dropCheck - [ method ] - * - * The default function to determine if a dragend event occured over - * this Interactable's element. Can be overridden using - * @Interactable.dropChecker. - * - - pointer (MouseEvent | PointerEvent | Touch) The event that ends a drag - - draggable (Interactable) The Interactable being dragged - - draggableElement (Element) The actual element that's being dragged - - dropElement (Element) The dropzone element - - rect (object) #optional The rect of dropElement - = (boolean) whether the pointer was over this Interactable - \*/ - dropCheck: function (pointer, draggable, draggableElement, dropElement, rect) { - if (!(rect = rect || this.getRect(dropElement))) { - return false; - } - - var dropOverlap = this.options.dropOverlap; - - if (dropOverlap === 'pointer') { - var page = getPageXY(pointer), - origin = getOriginXY(draggable, draggableElement), - horizontal, - vertical; - - page.x += origin.x; - page.y += origin.y; - - horizontal = (page.x > rect.left) && (page.x < rect.right); - vertical = (page.y > rect.top ) && (page.y < rect.bottom); - - return horizontal && vertical; - } - - var dragRect = draggable.getRect(draggableElement); - - if (dropOverlap === 'center') { - var cx = dragRect.left + dragRect.width / 2, - cy = dragRect.top + dragRect.height / 2; - - return cx >= rect.left && cx <= rect.right && cy >= rect.top && cy <= rect.bottom; - } - - if (isNumber(dropOverlap)) { - var overlapArea = (Math.max(0, Math.min(rect.right , dragRect.right ) - Math.max(rect.left, dragRect.left)) - * Math.max(0, Math.min(rect.bottom, dragRect.bottom) - Math.max(rect.top , dragRect.top ))), - overlapRatio = overlapArea / (dragRect.width * dragRect.height); - - return overlapRatio >= dropOverlap; - } - }, - - /*\ - * Interactable.dropChecker - [ method ] - * - * Gets or sets the function used to check if a dragged element is - * over this Interactable. See @Interactable.dropCheck. - * - - checker (function) #optional - * The checker is a function which takes a mouseUp/touchEnd event as a - * parameter and returns true or false to indicate if the the current - * draggable can be dropped into this Interactable - * - = (Function | Interactable) The checker function or this Interactable - \*/ - dropChecker: function (checker) { - if (isFunction(checker)) { - this.dropCheck = checker; - - return this; - } - return this.dropCheck; - }, - - /*\ - * Interactable.accept - [ method ] - * - * Gets or sets the Element or CSS selector match that this - * Interactable accepts if it is a dropzone. - * - - newValue (Element | string | null) #optional - * If it is an Element, then only that element can be dropped into this dropzone. - * If it is a string, the element being dragged must match it as a selector. - * If it is null, the accept options is cleared - it accepts any element. - * - = (string | Element | null | Interactable) The current accept option if given `undefined` or this Interactable - \*/ - accept: function (newValue) { - if (isElement(newValue)) { - this.options.accept = newValue; - - return this; - } - - // test if it is a valid CSS selector - if (trySelector(newValue)) { - this.options.accept = newValue; - - return this; - } - - if (newValue === null) { - delete this.options.accept; - - return this; - } - - return this.options.accept; - }, - - /*\ - * Interactable.resizable - [ method ] - * - * Gets or sets whether resize actions can be performed on the - * Interactable - * - = (boolean) Indicates if this can be the target of resize elements - | var isResizeable = interact('input[type=text]').resizable(); - * or - - options (boolean | object) #optional true/false or An object with event listeners to be fired on resize events (object makes the Interactable resizable) - = (object) This Interactable - | interact(element).resizable({ - | onstart: function (event) {}, - | onmove : function (event) {}, - | onend : function (event) {}, - | - | axis : 'x' || 'y' || 'xy' // default is 'xy', - | - | // limit multiple resizes. - | // See the explanation in @Interactable.draggable example - | max: 1, - | maxPerElement: 1, - | }); - \*/ - resizable: function (options) { - if (isObject(options)) { - this.options.resizable = true; - this.setOnEvents('resize', options); - - if (isNumber(options.max)) { - this.options.resizeMax = options.max; - } - if (isNumber(options.maxPerElement)) { - this.options.resizeMaxPerElement = options.maxPerElement; - } - - if (/^x$|^y$|^xy$/.test(options.axis)) { - this.options.resizeAxis = options.axis; - } - else if (options.axis === null) { - this.options.resizeAxis = defaultOptions.resizeAxis; - } - - return this; - } - if (isBool(options)) { - this.options.resizable = options; - - return this; - } - return this.options.resizable; - }, - - // misspelled alias - resizeable: blank, - - /*\ - * Interactable.squareResize - [ method ] - * - * Gets or sets whether resizing is forced 1:1 aspect - * - = (boolean) Current setting - * - * or - * - - newValue (boolean) #optional - = (object) this Interactable - \*/ - squareResize: function (newValue) { - if (isBool(newValue)) { - this.options.squareResize = newValue; - - return this; - } - - if (newValue === null) { - delete this.options.squareResize; - - return this; - } - - return this.options.squareResize; - }, - - /*\ - * Interactable.gesturable - [ method ] - * - * Gets or sets whether multitouch gestures can be performed on the - * Interactable's element - * - = (boolean) Indicates if this can be the target of gesture events - | var isGestureable = interact(element).gesturable(); - * or - - options (boolean | object) #optional true/false or An object with event listeners to be fired on gesture events (makes the Interactable gesturable) - = (object) this Interactable - | interact(element).gesturable({ - | onstart: function (event) {}, - | onmove : function (event) {}, - | onend : function (event) {}, - | - | // limit multiple gestures. - | // See the explanation in @Interactable.draggable example - | max: 1, - | maxPerElement: 1, - | }); - \*/ - gesturable: function (options) { - if (isObject(options)) { - this.options.gesturable = true; - this.setOnEvents('gesture', options); - - if (isNumber(options.max)) { - this.options.gestureMax = options.max; - } - if (isNumber(options.maxPerElement)) { - this.options.gestureMaxPerElement = options.maxPerElement; - } - - return this; - } - - if (isBool(options)) { - this.options.gesturable = options; - - return this; - } - - if (options === null) { - delete this.options.gesturable; - - return this; - } - - return this.options.gesturable; - }, - - // misspelled alias - gestureable: blank, - - /*\ - * Interactable.autoScroll - [ method ] - * - * Returns or sets whether or not any actions near the edges of the - * window/container trigger autoScroll for this Interactable - * - = (boolean | object) - * `false` if autoScroll is disabled; object with autoScroll properties - * if autoScroll is enabled - * - * or - * - - options (object | boolean | null) #optional - * options can be: - * - an object with margin, distance and interval properties, - * - true or false to enable or disable autoScroll or - * - null to use default settings - = (Interactable) this Interactable - \*/ - autoScroll: function (options) { - var defaults = defaultOptions.autoScroll; - - if (isObject(options)) { - var autoScroll = this.options.autoScroll; - - if (autoScroll === defaults) { - autoScroll = this.options.autoScroll = { - margin : defaults.margin, - distance : defaults.distance, - interval : defaults.interval, - container: defaults.container - }; - } - - autoScroll.margin = this.validateSetting('autoScroll', 'margin', options.margin); - autoScroll.speed = this.validateSetting('autoScroll', 'speed' , options.speed); - - autoScroll.container = - (isElement(options.container) || options.container instanceof window.Window - ? options.container - : defaults.container); - - - this.options.autoScrollEnabled = true; - this.options.autoScroll = autoScroll; - - return this; - } - - if (isBool(options)) { - this.options.autoScrollEnabled = options; - - return this; - } - - if (options === null) { - delete this.options.autoScrollEnabled; - delete this.options.autoScroll; - - return this; - } - - return (this.options.autoScrollEnabled - ? this.options.autoScroll - : false); - }, - - /*\ - * Interactable.snap - [ method ] - ** - * Returns or sets if and how action coordinates are snapped. By - * default, snapping is relative to the pointer coordinates. You can - * change this by setting the - * [`elementOrigin`](https://github.com/taye/interact.js/pull/72). - ** - = (boolean | object) `false` if snap is disabled; object with snap properties if snap is enabled - ** - * or - ** - - options (object | boolean | null) #optional - = (Interactable) this Interactable - > Usage - | interact('.handle').snap({ - | mode : 'grid', // event coords should snap to the corners of a grid - | range : Infinity, // the effective distance of snap points - | grid : { x: 100, y: 100 }, // the x and y spacing of the grid points - | gridOffset : { x: 0, y: 0 }, // the offset of the grid points - | }); - | - | interact('.handle').snap({ - | mode : 'anchor', // snap to specified points - | anchors : [ - | { x: 100, y: 100, range: 25 }, // a point with x, y and a specific range - | { x: 200, y: 200 } // a point with x and y. it uses the default range - | ] - | }); - | - | interact(document.querySelector('#thing')).snap({ - | mode : 'path', - | paths: [ - | { // snap to points on these x and y axes - | x: 100, - | y: 100, - | range: 25 - | }, - | // give this function the x and y page coords and snap to the object returned - | function (x, y) { - | return { - | x: x, - | y: (75 + 50 * Math.sin(x * 0.04)), - | range: 40 - | }; - | }] - | }) - | - | interact(element).snap({ - | // do not snap during normal movement. - | // Instead, trigger only one snapped move event - | // immediately before the end event. - | endOnly: true, - | - | // https://github.com/taye/interact.js/pull/72#issue-41813493 - | elementOrigin: { x: 0, y: 0 } - | }); - \*/ - snap: function (options) { - var defaults = defaultOptions.snap; - - if (isObject(options)) { - var snap = this.options.snap; - - if (snap === defaults) { - snap = {}; - } - - snap.mode = this.validateSetting('snap', 'mode' , options.mode); - snap.endOnly = this.validateSetting('snap', 'endOnly' , options.endOnly); - snap.actions = this.validateSetting('snap', 'actions' , options.actions); - snap.range = this.validateSetting('snap', 'range' , options.range); - snap.paths = this.validateSetting('snap', 'paths' , options.paths); - snap.grid = this.validateSetting('snap', 'grid' , options.grid); - snap.gridOffset = this.validateSetting('snap', 'gridOffset' , options.gridOffset); - snap.anchors = this.validateSetting('snap', 'anchors' , options.anchors); - snap.elementOrigin = this.validateSetting('snap', 'elementOrigin', options.elementOrigin); - - this.options.snapEnabled = true; - this.options.snap = snap; - - return this; - } - - if (isBool(options)) { - this.options.snapEnabled = options; - - return this; - } - - if (options === null) { - delete this.options.snapEnabled; - delete this.options.snap; - - return this; - } - - return (this.options.snapEnabled - ? this.options.snap - : false); - }, - - /*\ - * Interactable.inertia - [ method ] - ** - * Returns or sets if and how events continue to run after the pointer is released - ** - = (boolean | object) `false` if inertia is disabled; `object` with inertia properties if inertia is enabled - ** - * or - ** - - options (object | boolean | null) #optional - = (Interactable) this Interactable - > Usage - | // enable and use default settings - | interact(element).inertia(true); - | - | // enable and use custom settings - | interact(element).inertia({ - | // value greater than 0 - | // high values slow the object down more quickly - | resistance : 16, - | - | // the minimum launch speed (pixels per second) that results in inertia start - | minSpeed : 200, - | - | // inertia will stop when the object slows down to this speed - | endSpeed : 20, - | - | // boolean; should actions be resumed when the pointer goes down during inertia - | allowResume : true, - | - | // boolean; should the jump when resuming from inertia be ignored in event.dx/dy - | zeroResumeDelta: false, - | - | // if snap/restrict are set to be endOnly and inertia is enabled, releasing - | // the pointer without triggering inertia will animate from the release - | // point to the snaped/restricted point in the given amount of time (ms) - | smoothEndDuration: 300, - | - | // an array of action types that can have inertia (no gesture) - | actions : ['drag', 'resize'] - | }); - | - | // reset custom settings and use all defaults - | interact(element).inertia(null); - \*/ - inertia: function (options) { - var defaults = defaultOptions.inertia; - - if (isObject(options)) { - var inertia = this.options.inertia; - - if (inertia === defaults) { - inertia = this.options.inertia = { - resistance : defaults.resistance, - minSpeed : defaults.minSpeed, - endSpeed : defaults.endSpeed, - actions : defaults.actions, - allowResume : defaults.allowResume, - zeroResumeDelta : defaults.zeroResumeDelta, - smoothEndDuration: defaults.smoothEndDuration - }; - } - - inertia.resistance = this.validateSetting('inertia', 'resistance' , options.resistance); - inertia.minSpeed = this.validateSetting('inertia', 'minSpeed' , options.minSpeed); - inertia.endSpeed = this.validateSetting('inertia', 'endSpeed' , options.endSpeed); - inertia.actions = this.validateSetting('inertia', 'actions' , options.actions); - inertia.allowResume = this.validateSetting('inertia', 'allowResume' , options.allowResume); - inertia.zeroResumeDelta = this.validateSetting('inertia', 'zeroResumeDelta' , options.zeroResumeDelta); - inertia.smoothEndDuration = this.validateSetting('inertia', 'smoothEndDuration', options.smoothEndDuration); - - this.options.inertiaEnabled = true; - this.options.inertia = inertia; - - return this; - } - - if (isBool(options)) { - this.options.inertiaEnabled = options; - - return this; - } - - if (options === null) { - delete this.options.inertiaEnabled; - delete this.options.inertia; - - return this; - } - - return (this.options.inertiaEnabled - ? this.options.inertia - : false); - }, - - getAction: function (pointer, interaction, element) { - var action = this.defaultActionChecker(pointer, interaction, element); - - if (this.options.actionChecker) { - action = this.options.actionChecker(pointer, action, this, element, interaction); - } - - return action; - }, - - defaultActionChecker: defaultActionChecker, - - /*\ - * Interactable.actionChecker - [ method ] - * - * Gets or sets the function used to check action to be performed on - * pointerDown - * - - checker (function | null) #optional A function which takes a pointer event, defaultAction string and an interactable as parameters and returns 'drag' 'resize[axes]' or 'gesture' or null. - = (Function | Interactable) The checker function or this Interactable - \*/ - actionChecker: function (newValue) { - if (isFunction(newValue)) { - this.options.actionChecker = newValue; - - return this; - } - - if (newValue === null) { - delete this.options.actionChecker; - - return this; - } - - return this.options.actionChecker; - }, - - /*\ - * Interactable.getRect - [ method ] - * - * The default function to get an Interactables bounding rect. Can be - * overridden using @Interactable.rectChecker. - * - - element (Element) #optional The element to measure. Meant to be used for selector Interactables which don't have a specific element. - = (object) The object's bounding rectangle. - o { - o top : 0, - o left : 0, - o bottom: 0, - o right : 0, - o width : 0, - o height: 0 - o } - \*/ - getRect: function rectCheck (element) { - element = element || this._element; - - if (this.selector && !(isElement(element))) { - element = this._context.querySelector(this.selector); - } - - return getElementRect(element); - }, - - /*\ - * Interactable.rectChecker - [ method ] - * - * Returns or sets the function used to calculate the interactable's - * element's rectangle - * - - checker (function) #optional A function which returns this Interactable's bounding rectangle. See @Interactable.getRect - = (function | object) The checker function or this Interactable - \*/ - rectChecker: function (checker) { - if (isFunction(checker)) { - this.getRect = checker; - - return this; - } - - if (checker === null) { - delete this.options.getRect; - - return this; - } - - return this.getRect; - }, - - /*\ - * Interactable.styleCursor - [ method ] - * - * Returns or sets whether the action that would be performed when the - * mouse on the element are checked on `mousemove` so that the cursor - * may be styled appropriately - * - - newValue (boolean) #optional - = (boolean | Interactable) The current setting or this Interactable - \*/ - styleCursor: function (newValue) { - if (isBool(newValue)) { - this.options.styleCursor = newValue; - - return this; - } - - if (newValue === null) { - delete this.options.styleCursor; - - return this; - } - - return this.options.styleCursor; - }, - - /*\ - * Interactable.preventDefault - [ method ] - * - * Returns or sets whether to prevent the browser's default behaviour - * in response to pointer events. Can be set to - * - `true` to always prevent - * - `false` to never prevent - * - `'auto'` to allow interact.js to try to guess what would be best - * - `null` to set to the default ('auto') - * - - newValue (boolean | string | null) #optional `true`, `false` or `'auto'` - = (boolean | string | Interactable) The current setting or this Interactable - \*/ - preventDefault: function (newValue) { - if (isBool(newValue) || newValue === 'auto') { - this.options.preventDefault = newValue; - - return this; - } - - if (newValue === null) { - delete this.options.preventDefault; - - return this; - } - - return this.options.preventDefault; - }, - - /*\ - * Interactable.origin - [ method ] - * - * Gets or sets the origin of the Interactable's element. The x and y - * of the origin will be subtracted from action event coordinates. - * - - origin (object | string) #optional An object eg. { x: 0, y: 0 } or string 'parent', 'self' or any CSS selector - * OR - - origin (Element) #optional An HTML or SVG Element whose rect will be used - ** - = (object) The current origin or this Interactable - \*/ - origin: function (newValue) { - if (trySelector(newValue)) { - this.options.origin = newValue; - return this; - } - else if (isObject(newValue)) { - this.options.origin = newValue; - return this; - } - - if (newValue === null) { - delete this.options.origin; - - return this; - } - - return this.options.origin; - }, - - /*\ - * Interactable.deltaSource - [ method ] - * - * Returns or sets the mouse coordinate types used to calculate the - * movement of the pointer. - * - - newValue (string) #optional Use 'client' if you will be scrolling while interacting; Use 'page' if you want autoScroll to work - = (string | object) The current deltaSource or this Interactable - \*/ - deltaSource: function (newValue) { - if (newValue === 'page' || newValue === 'client') { - this.options.deltaSource = newValue; - - return this; - } - - if (newValue === null) { - delete this.options.deltaSource; - - return this; - } - - return this.options.deltaSource; - }, - - /*\ - * Interactable.restrict - [ method ] - ** - * Returns or sets the rectangles within which actions on this - * interactable (after snap calculations) are restricted. By default, - * restricting is relative to the pointer coordinates. You can change - * this by setting the - * [`elementRect`](https://github.com/taye/interact.js/pull/72). - ** - - newValue (object) #optional an object with keys drag, resize, and/or gesture whose values are rects, Elements, CSS selectors, or 'parent' or 'self' - = (object) The current restrictions object or this Interactable - ** - | interact(element).restrict({ - | // the rect will be `interact.getElementRect(element.parentNode)` - | drag: element.parentNode, - | - | // x and y are relative to the the interactable's origin - | resize: { x: 100, y: 100, width: 200, height: 200 } - | }) - | - | interact('.draggable').restrict({ - | // the rect will be the selected element's parent - | drag: 'parent', - | - | // do not restrict during normal movement. - | // Instead, trigger only one restricted move event - | // immediately before the end event. - | endOnly: true, - | - | // https://github.com/taye/interact.js/pull/72#issue-41813493 - | elementRect: { top: 0, left: 0, bottom: 1, right: 1 } - | }); - \*/ - restrict: function (newValue) { - if (newValue === undefined) { - return this.options.restrict; - } - - if (isBool(newValue)) { - defaultOptions.restrictEnabled = newValue; - } - else if (isObject(newValue)) { - var newRestrictions = {}; - - if (isObject(newValue.drag) || trySelector(newValue.drag)) { - newRestrictions.drag = newValue.drag; - } - if (isObject(newValue.resize) || trySelector(newValue.resize)) { - newRestrictions.resize = newValue.resize; - } - if (isObject(newValue.gesture) || trySelector(newValue.gesture)) { - newRestrictions.gesture = newValue.gesture; - } - - if (isBool(newValue.endOnly)) { - newRestrictions.endOnly = newValue.endOnly; - } - - if (isObject(newValue.elementRect)) { - newRestrictions.elementRect = newValue.elementRect; - } - - this.options.restrictEnabled = true; - this.options.restrict = newRestrictions; - } - else if (newValue === null) { - delete this.options.restrict; - delete this.options.restrictEnabled; - } - - return this; - }, - - /*\ - * Interactable.context - [ method ] - * - * Get's the selector context Node of the Interactable. The default is `window.document`. - * - = (Node) The context Node of this Interactable - ** - \*/ - context: function () { - return this._context; - }, - - _context: document, - - /*\ - * Interactable.ignoreFrom - [ method ] - * - * If the target of the `mousedown`, `pointerdown` or `touchstart` - * event or any of it's parents match the given CSS selector or - * Element, no drag/resize/gesture is started. - * - - newValue (string | Element | null) #optional a CSS selector string, an Element or `null` to not ignore any elements - = (string | Element | object) The current ignoreFrom value or this Interactable - ** - | interact(element, { ignoreFrom: document.getElementById('no-action') }); - | // or - | interact(element).ignoreFrom('input, textarea, a'); - \*/ - ignoreFrom: function (newValue) { - if (trySelector(newValue)) { // CSS selector to match event.target - this.options.ignoreFrom = newValue; - return this; - } - - if (isElement(newValue)) { // specific element - this.options.ignoreFrom = newValue; - return this; - } - - if (newValue === null) { - delete this.options.ignoreFrom; - return this; - } - - return this.options.ignoreFrom; - }, - - /*\ - * Interactable.allowFrom - [ method ] - * - * A drag/resize/gesture is started only If the target of the - * `mousedown`, `pointerdown` or `touchstart` event or any of it's - * parents match the given CSS selector or Element. - * - - newValue (string | Element | null) #optional a CSS selector string, an Element or `null` to allow from any element - = (string | Element | object) The current allowFrom value or this Interactable - ** - | interact(element, { allowFrom: document.getElementById('drag-handle') }); - | // or - | interact(element).allowFrom('.handle'); - \*/ - allowFrom: function (newValue) { - if (trySelector(newValue)) { // CSS selector to match event.target - this.options.allowFrom = newValue; - return this; - } - - if (isElement(newValue)) { // specific element - this.options.allowFrom = newValue; - return this; - } - - if (newValue === null) { - delete this.options.allowFrom; - return this; - } - - return this.options.allowFrom; - }, - - /*\ - * Interactable.validateSetting - [ method ] - * - - context (string) eg. 'snap', 'autoScroll' - - option (string) The name of the value being set - - value (any type) The value being validated - * - = (typeof value) A valid value for the give context-option pair - * - null if defaultOptions[context][value] is undefined - * - value if it is the same type as defaultOptions[context][value], - * - this.options[context][value] if it is the same type as defaultOptions[context][value], - * - or defaultOptions[context][value] - \*/ - validateSetting: function (context, option, value) { - var defaults = defaultOptions[context], - current = this.options[context]; - - if (defaults !== undefined && defaults[option] !== undefined) { - if ('objectTypes' in defaults && defaults.objectTypes.test(option)) { - if (isObject(value)) { return value; } - else { - return (option in current && isObject(current[option]) - ? current [option] - : defaults[option]); - } - } - - if ('arrayTypes' in defaults && defaults.arrayTypes.test(option)) { - if (isArray(value)) { return value; } - else { - return (option in current && isArray(current[option]) - ? current[option] - : defaults[option]); - } - } - - if ('stringTypes' in defaults && defaults.stringTypes.test(option)) { - if (isString(value)) { return value; } - else { - return (option in current && isString(current[option]) - ? current[option] - : defaults[option]); - } - } - - if ('numberTypes' in defaults && defaults.numberTypes.test(option)) { - if (isNumber(value)) { return value; } - else { - return (option in current && isNumber(current[option]) - ? current[option] - : defaults[option]); - } - } - - if ('boolTypes' in defaults && defaults.boolTypes.test(option)) { - if (isBool(value)) { return value; } - else { - return (option in current && isBool(current[option]) - ? current[option] - : defaults[option]); - } - } - - if ('elementTypes' in defaults && defaults.elementTypes.test(option)) { - if (isElement(value)) { return value; } - else { - return (option in current && isElement(current[option]) - ? current[option] - : defaults[option]); - } - } - } - - return null; - }, - - /*\ - * Interactable.element - [ method ] - * - * If this is not a selector Interactable, it returns the element this - * interactable represents - * - = (Element) HTML / SVG Element - \*/ - element: function () { - return this._element; - }, - - /*\ - * Interactable.fire - [ method ] - * - * Calls listeners for the given InteractEvent type bound globally - * and directly to this Interactable - * - - iEvent (InteractEvent) The InteractEvent object to be fired on this Interactable - = (Interactable) this Interactable - \*/ - fire: function (iEvent) { - if (!(iEvent && iEvent.type) || !contains(eventTypes, iEvent.type)) { - return this; - } - - var listeners, - i, - len, - onEvent = 'on' + iEvent.type, - funcName = ''; - - // Interactable#on() listeners - if (iEvent.type in this._iEvents) { - listeners = this._iEvents[iEvent.type]; - - for (i = 0, len = listeners.length; i < len && !iEvent.immediatePropagationStopped; i++) { - funcName = listeners[i].name; - listeners[i](iEvent); - } - } - - // interactable.onevent listener - if (isFunction(this[onEvent])) { - funcName = this[onEvent].name; - this[onEvent](iEvent); - } - - // interact.on() listeners - if (iEvent.type in globalEvents && (listeners = globalEvents[iEvent.type])) { - - for (i = 0, len = listeners.length; i < len && !iEvent.immediatePropagationStopped; i++) { - funcName = listeners[i].name; - listeners[i](iEvent); - } - } - - return this; - }, - - /*\ - * Interactable.on - [ method ] - * - * Binds a listener for an InteractEvent or DOM event. - * - - eventType (string) The type of event to listen for - - listener (function) The function to be called on that event - - useCapture (boolean) #optional useCapture flag for addEventListener - = (object) This Interactable - \*/ - on: function (eventType, listener, useCapture) { - if (eventType === 'wheel') { - eventType = wheelEvent; - } - - // convert to boolean - useCapture = useCapture? true: false; - - if (contains(eventTypes, eventType)) { - // if this type of event was never bound to this Interactable - if (!(eventType in this._iEvents)) { - this._iEvents[eventType] = [listener]; - } - // if the event listener is not already bound for this type - else if (!contains(this._iEvents[eventType], listener)) { - this._iEvents[eventType].push(listener); - } - } - // delegated event for selector - else if (this.selector) { - if (!delegatedEvents[eventType]) { - delegatedEvents[eventType] = { - selectors: [], - contexts : [], - listeners: [] - }; - - // add delegate listener functions - events.addToElement(this._context, eventType, delegateListener); - events.addToElement(this._context, eventType, delegateUseCapture, true); - } - - var delegated = delegatedEvents[eventType], - index; - - for (index = delegated.selectors.length - 1; index >= 0; index--) { - if (delegated.selectors[index] === this.selector - && delegated.contexts[index] === this._context) { - break; - } - } - - if (index === -1) { - index = delegated.selectors.length; - - delegated.selectors.push(this.selector); - delegated.contexts .push(this._context); - delegated.listeners.push([]); - } - - // keep listener and useCapture flag - delegated.listeners[index].push([listener, useCapture]); - } - else { - events.add(this, eventType, listener, useCapture); - } - - return this; - }, - - /*\ - * Interactable.off - [ method ] - * - * Removes an InteractEvent or DOM event listener - * - - eventType (string) The type of event that was listened for - - listener (function) The listener function to be removed - - useCapture (boolean) #optional useCapture flag for removeEventListener - = (object) This Interactable - \*/ - off: function (eventType, listener, useCapture) { - var eventList, - index = -1; - - // convert to boolean - useCapture = useCapture? true: false; - - if (eventType === 'wheel') { - eventType = wheelEvent; - } - - // if it is an action event type - if (contains(eventTypes, eventType)) { - eventList = this._iEvents[eventType]; - - if (eventList && (index = indexOf(eventList, listener)) !== -1) { - this._iEvents[eventType].splice(index, 1); - } - } - // delegated event - else if (this.selector) { - var delegated = delegatedEvents[eventType], - matchFound = false; - - if (!delegated) { return this; } - - // count from last index of delegated to 0 - for (index = delegated.selectors.length - 1; index >= 0; index--) { - // look for matching selector and context Node - if (delegated.selectors[index] === this.selector - && delegated.contexts[index] === this._context) { - - var listeners = delegated.listeners[index]; - - // each item of the listeners array is an array: [function, useCaptureFlag] - for (var i = listeners.length - 1; i >= 0; i--) { - var fn = listeners[i][0], - useCap = listeners[i][1]; - - // check if the listener functions and useCapture flags match - if (fn === listener && useCap === useCapture) { - // remove the listener from the array of listeners - listeners.splice(i, 1); - - // if all listeners for this interactable have been removed - // remove the interactable from the delegated arrays - if (!listeners.length) { - delegated.selectors.splice(index, 1); - delegated.contexts .splice(index, 1); - delegated.listeners.splice(index, 1); - - // remove delegate function from context - events.removeFromElement(this._context, eventType, delegateListener); - events.removeFromElement(this._context, eventType, delegateUseCapture, true); - - // remove the arrays if they are empty - if (!delegated.selectors.length) { - delegatedEvents[eventType] = null; - } - } - - // only remove one listener - matchFound = true; - break; - } - } - - if (matchFound) { break; } - } - } - } - // remove listener from this Interatable's element - else { - events.remove(this, listener, useCapture); - } - - return this; - }, - - /*\ - * Interactable.set - [ method ] - * - * Reset the options of this Interactable - - options (object) The new settings to apply - = (object) This Interactablw - \*/ - set: function (options) { - if (!options || !isObject(options)) { - options = {}; - } - this.options = new IOptions(options); - - this.draggable ('draggable' in options? options.draggable : this.options.draggable ); - this.dropzone ('dropzone' in options? options.dropzone : this.options.dropzone ); - this.resizable ('resizable' in options? options.resizable : this.options.resizable ); - this.gesturable('gesturable' in options? options.gesturable: this.options.gesturable); - - var settings = [ - 'accept', 'actionChecker', 'allowFrom', 'autoScroll', 'deltaSource', - 'dropChecker', 'ignoreFrom', 'inertia', 'origin', 'preventDefault', - 'rectChecker', 'restrict', 'snap', 'styleCursor' - ]; - - for (var i = 0, len = settings.length; i < len; i++) { - var setting = settings[i]; - - if (setting in options) { - this[setting](options[setting]); - } - } - - return this; - }, - - /*\ - * Interactable.unset - [ method ] - * - * Remove this interactable from the list of interactables and remove - * it's drag, drop, resize and gesture capabilities - * - = (object) @interact - \*/ - unset: function () { - events.remove(this, 'all'); - - if (!isString(this.selector)) { - events.remove(this, 'all'); - if (this.options.styleCursor) { - this._element.style.cursor = ''; - } - } - else { - // remove delegated events - for (var type in delegatedEvents) { - var delegated = delegatedEvents[type]; - - for (var i = 0; i < delegated.selectors.length; i++) { - if (delegated.selectors[i] === this.selector - && delegated.contexts[i] === this._context) { - - delegated.selectors.splice(i, 1); - delegated.contexts .splice(i, 1); - delegated.listeners.splice(i, 1); - - // remove the arrays if they are empty - if (!delegated.selectors.length) { - delegatedEvents[type] = null; - } - } - - events.removeFromElement(this._context, type, delegateListener); - events.removeFromElement(this._context, type, delegateUseCapture, true); - - break; - } - } - } - - this.dropzone(false); - - interactables.splice(indexOf(interactables, this), 1); - - return interact; - } - }; - - Interactable.prototype.gestureable = Interactable.prototype.gesturable; - Interactable.prototype.resizeable = Interactable.prototype.resizable; - - /*\ - * interact.isSet - [ method ] - * - * Check if an element has been set - - element (Element) The Element being searched for - = (boolean) Indicates if the element or CSS selector was previously passed to interact - \*/ - interact.isSet = function(element, options) { - return interactables.indexOfElement(element, options && options.context) !== -1; - }; - - /*\ - * interact.on - [ method ] - * - * Adds a global listener for an InteractEvent or adds a DOM event to - * `document` - * - - type (string) The type of event to listen for - - listener (function) The function to be called on that event - - useCapture (boolean) #optional useCapture flag for addEventListener - = (object) interact - \*/ - interact.on = function (type, listener, useCapture) { - // if it is an InteractEvent type, add listener to globalEvents - if (contains(eventTypes, type)) { - // if this type of event was never bound - if (!globalEvents[type]) { - globalEvents[type] = [listener]; - } - - // if the event listener is not already bound for this type - else if (!contains(globalEvents[type], listener)) { - - globalEvents[type].push(listener); - } - } - // If non InteractEvent type, addEventListener to document - else { - events.add(docTarget, type, listener, useCapture); - } - - return interact; - }; - - /*\ - * interact.off - [ method ] - * - * Removes a global InteractEvent listener or DOM event from `document` - * - - type (string) The type of event that was listened for - - listener (function) The listener function to be removed - - useCapture (boolean) #optional useCapture flag for removeEventListener - = (object) interact - \*/ - interact.off = function (type, listener, useCapture) { - if (!contains(eventTypes, type)) { - events.remove(docTarget, type, listener, useCapture); - } - else { - var index; - - if (type in globalEvents - && (index = indexOf(globalEvents[type], listener)) !== -1) { - globalEvents[type].splice(index, 1); - } - } - - return interact; - }; - - /*\ - * interact.simulate - [ method ] - * - * Simulate pointer down to begin to interact with an interactable element - - action (string) The action to be performed - drag, resize, etc. - - element (Element) The DOM Element to resize/drag - - pointerEvent (object) #optional Pointer event whose pageX/Y coordinates will be the starting point of the interact drag/resize - = (object) interact - \*/ - interact.simulate = function (action, element, pointerEvent) { - var event = {}, - clientRect; - - if (action === 'resize') { - action = 'resizexy'; - } - // return if the action is not recognised - if (!/^(drag|resizexy|resizex|resizey)$/.test(action)) { - return interact; - } - - if (pointerEvent) { - extend(event, pointerEvent); - } - else { - clientRect = (element instanceof SVGElement) - ? element.getBoundingClientRect() - : clientRect = element.getClientRects()[0]; - - if (action === 'drag') { - event.pageX = clientRect.left + clientRect.width / 2; - event.pageY = clientRect.top + clientRect.height / 2; - } - else { - event.pageX = clientRect.right; - event.pageY = clientRect.bottom; - } - } - - event.target = event.currentTarget = element; - event.preventDefault = event.stopPropagation = blank; - - listeners.pointerDown(event, action); - - return interact; - }; - - /*\ - * interact.enableDragging - [ method ] - * - * Returns or sets whether dragging is enabled for any Interactables - * - - newValue (boolean) #optional `true` to allow the action; `false` to disable action for all Interactables - = (boolean | object) The current setting or interact - \*/ - interact.enableDragging = function (newValue) { - if (newValue !== null && newValue !== undefined) { - actionIsEnabled.drag = newValue; - - return interact; - } - return actionIsEnabled.drag; - }; - - /*\ - * interact.enableResizing - [ method ] - * - * Returns or sets whether resizing is enabled for any Interactables - * - - newValue (boolean) #optional `true` to allow the action; `false` to disable action for all Interactables - = (boolean | object) The current setting or interact - \*/ - interact.enableResizing = function (newValue) { - if (newValue !== null && newValue !== undefined) { - actionIsEnabled.resize = newValue; - - return interact; - } - return actionIsEnabled.resize; - }; - - /*\ - * interact.enableGesturing - [ method ] - * - * Returns or sets whether gesturing is enabled for any Interactables - * - - newValue (boolean) #optional `true` to allow the action; `false` to disable action for all Interactables - = (boolean | object) The current setting or interact - \*/ - interact.enableGesturing = function (newValue) { - if (newValue !== null && newValue !== undefined) { - actionIsEnabled.gesture = newValue; - - return interact; - } - return actionIsEnabled.gesture; - }; - - interact.eventTypes = eventTypes; - - /*\ - * interact.debug - [ method ] - * - * Returns debugging data - = (object) An object with properties that outline the current state and expose internal functions and variables - \*/ - interact.debug = function () { - var interaction = interactions[0] || new Interaction(); - - return { - interactions : interactions, - target : interaction.target, - dragging : interaction.dragging, - resizing : interaction.resizing, - gesturing : interaction.gesturing, - prepared : interaction.prepared, - matches : interaction.matches, - matchElements : interaction.matchElements, - - prevCoords : interaction.prevCoords, - startCoords : interaction.startCoords, - - pointerIds : interaction.pointerIds, - pointers : interaction.pointers, - addPointer : listeners.addPointer, - removePointer : listeners.removePointer, - recordPointer : listeners.recordPointer, - - snap : interaction.snapStatus, - restrict : interaction.restrictStatus, - inertia : interaction.inertiaStatus, - - downTime : interaction.downTime, - downEvent : interaction.downEvent, - downPointer : interaction.downPointer, - prevEvent : interaction.prevEvent, - - Interactable : Interactable, - IOptions : IOptions, - interactables : interactables, - pointerIsDown : interaction.pointerIsDown, - defaultOptions : defaultOptions, - defaultActionChecker : defaultActionChecker, - - actionCursors : actionCursors, - dragMove : listeners.dragMove, - resizeMove : listeners.resizeMove, - gestureMove : listeners.gestureMove, - pointerUp : listeners.pointerUp, - pointerDown : listeners.pointerDown, - pointerMove : listeners.pointerMove, - pointerHover : listeners.pointerHover, - - events : events, - globalEvents : globalEvents, - delegatedEvents : delegatedEvents - }; - }; - - // expose the functions used to calculate multi-touch properties - interact.getTouchAverage = touchAverage; - interact.getTouchBBox = touchBBox; - interact.getTouchDistance = touchDistance; - interact.getTouchAngle = touchAngle; - - interact.getElementRect = getElementRect; - interact.matchesSelector = matchesSelector; - - /*\ - * interact.margin - [ method ] - * - * Returns or sets the margin for autocheck resizing used in - * @Interactable.getAction. That is the distance from the bottom and right - * edges of an element clicking in which will start resizing - * - - newValue (number) #optional - = (number | interact) The current margin value or interact - \*/ - interact.margin = function (newvalue) { - if (isNumber(newvalue)) { - margin = newvalue; - - return interact; - } - return margin; - }; - - /*\ - * interact.styleCursor - [ styleCursor ] - * - * Returns or sets whether the cursor style of the document is changed - * depending on what action is being performed - * - - newValue (boolean) #optional - = (boolean | interact) The current setting of interact - \*/ - interact.styleCursor = function (newValue) { - if (isBool(newValue)) { - defaultOptions.styleCursor = newValue; - - return interact; - } - return defaultOptions.styleCursor; - }; - - /*\ - * interact.autoScroll - [ method ] - * - * Returns or sets whether or not actions near the edges of the window or - * specified container element trigger autoScroll by default - * - - options (boolean | object) true or false to simply enable or disable or an object with margin, distance, container and interval properties - = (object) interact - * or - = (boolean | object) `false` if autoscroll is disabled and the default autoScroll settings if it is enabled - \*/ - interact.autoScroll = function (options) { - var defaults = defaultOptions.autoScroll; - - if (isObject(options)) { - defaultOptions.autoScrollEnabled = true; - - if (isNumber(options.margin)) { defaults.margin = options.margin;} - if (isNumber(options.speed) ) { defaults.speed = options.speed ;} - - defaults.container = - (isElement(options.container) || options.container instanceof window.Window - ? options.container - : defaults.container); - - return interact; - } - - if (isBool(options)) { - defaultOptions.autoScrollEnabled = options; - - return interact; - } - - // return the autoScroll settings if autoScroll is enabled - // otherwise, return false - return defaultOptions.autoScrollEnabled? defaults: false; - }; - - /*\ - * interact.snap - [ method ] - * - * Returns or sets whether actions are constrained to a grid or a - * collection of coordinates - * - - options (boolean | object) #optional New settings - * `true` or `false` to simply enable or disable - * or an object with some of the following properties - o { - o mode : 'grid', 'anchor' or 'path', - o range : the distance within which snapping to a point occurs, - o actions: ['drag', 'resizex', 'resizey', 'resizexy'], an array of action types that can snapped (['drag'] by default) (no gesture) - o grid : { - o x, y: the distances between the grid lines, - o }, - o gridOffset: { - o x, y: the x/y-axis values of the grid origin - o }, - o anchors: [ - o { - o x: x coordinate to snap to, - o y: y coordinate to snap to, - o range: optional range for this anchor - o } - o { - o another anchor - o } - o ] - o } - * - = (object | interact) The default snap settings object or interact - \*/ - interact.snap = function (options) { - var snap = defaultOptions.snap; - - if (isObject(options)) { - defaultOptions.snapEnabled = true; - - if (isString(options.mode) ) { snap.mode = options.mode; } - if (isBool (options.endOnly) ) { snap.endOnly = options.endOnly; } - if (isNumber(options.range) ) { snap.range = options.range; } - if (isArray (options.actions) ) { snap.actions = options.actions; } - if (isArray (options.anchors) ) { snap.anchors = options.anchors; } - if (isObject(options.grid) ) { snap.grid = options.grid; } - if (isObject(options.gridOffset) ) { snap.gridOffset = options.gridOffset; } - if (isObject(options.elementOrigin)) { snap.elementOrigin = options.elementOrigin; } - - return interact; - } - if (isBool(options)) { - defaultOptions.snapEnabled = options; - - return interact; - } - - return defaultOptions.snapEnabled; - }; - - /*\ - * interact.inertia - [ method ] - * - * Returns or sets inertia settings. - * - * See @Interactable.inertia - * - - options (boolean | object) #optional New settings - * `true` or `false` to simply enable or disable - * or an object of inertia options - = (object | interact) The default inertia settings object or interact - \*/ - interact.inertia = function (options) { - var inertia = defaultOptions.inertia; - - if (isObject(options)) { - defaultOptions.inertiaEnabled = true; - - if (isNumber(options.resistance) ) { inertia.resistance = options.resistance ; } - if (isNumber(options.minSpeed) ) { inertia.minSpeed = options.minSpeed ; } - if (isNumber(options.endSpeed) ) { inertia.endSpeed = options.endSpeed ; } - if (isNumber(options.smoothEndDuration)) { inertia.smoothEndDuration = options.smoothEndDuration; } - if (isBool (options.allowResume) ) { inertia.allowResume = options.allowResume ; } - if (isBool (options.zeroResumeDelta) ) { inertia.zeroResumeDelta = options.zeroResumeDelta ; } - if (isArray (options.actions) ) { inertia.actions = options.actions ; } - - return interact; - } - if (isBool(options)) { - defaultOptions.inertiaEnabled = options; - - return interact; - } - - return { - enabled: defaultOptions.inertiaEnabled, - resistance: inertia.resistance, - minSpeed: inertia.minSpeed, - endSpeed: inertia.endSpeed, - actions: inertia.actions, - allowResume: inertia.allowResume, - zeroResumeDelta: inertia.zeroResumeDelta - }; - }; - - /*\ - * interact.supportsTouch - [ method ] - * - = (boolean) Whether or not the browser supports touch input - \*/ - interact.supportsTouch = function () { - return supportsTouch; - }; - - /*\ - * interact.supportsPointerEvent - [ method ] - * - = (boolean) Whether or not the browser supports PointerEvents - \*/ - interact.supportsPointerEvent = function () { - return supportsPointerEvent; - }; - - /*\ - * interact.currentAction - [ method ] - * - = (string) What action is currently being performed - \*/ - interact.currentAction = function () { - for (var i = 0, len = interactions.length; i < len; i++) { - var action = interactions[i].currentAction(); - - if (action) { return action; } - } - - return null; - }; - - /*\ - * interact.stop - [ method ] - * - * Cancels the current interaction - * - - event (Event) An event on which to call preventDefault() - = (object) interact - \*/ - interact.stop = function (event) { - for (var i = interactions.length - 1; i > 0; i--) { - interactions[i].stop(event); - } - - return interact; - }; - - /*\ - * interact.dynamicDrop - [ method ] - * - * Returns or sets whether the dimensions of dropzone elements are - * calculated on every dragmove or only on dragstart for the default - * dropChecker - * - - newValue (boolean) #optional True to check on each move. False to check only before start - = (boolean | interact) The current setting or interact - \*/ - interact.dynamicDrop = function (newValue) { - if (isBool(newValue)) { - //if (dragging && dynamicDrop !== newValue && !newValue) { - //calcRects(dropzones); - //} - - dynamicDrop = newValue; - - return interact; - } - return dynamicDrop; - }; - - /*\ - * interact.deltaSource - [ method ] - * Returns or sets weather pageX/Y or clientX/Y is used to calculate dx/dy. - * - * See @Interactable.deltaSource - * - - newValue (string) #optional 'page' or 'client' - = (string | Interactable) The current setting or interact - \*/ - interact.deltaSource = function (newValue) { - if (newValue === 'page' || newValue === 'client') { - defaultOptions.deltaSource = newValue; - - return this; - } - return defaultOptions.deltaSource; - }; - - - /*\ - * interact.restrict - [ method ] - * - * Returns or sets the default rectangles within which actions (after snap - * calculations) are restricted. - * - * See @Interactable.restrict - * - - newValue (object) #optional an object with keys drag, resize, and/or gesture and rects or Elements as values - = (object) The current restrictions object or interact - \*/ - interact.restrict = function (newValue) { - var defaults = defaultOptions.restrict; - - if (newValue === undefined) { - return defaultOptions.restrict; - } - - if (isBool(newValue)) { - defaultOptions.restrictEnabled = newValue; - } - else if (isObject(newValue)) { - if (isObject(newValue.drag) || /^parent$|^self$/.test(newValue.drag)) { - defaults.drag = newValue.drag; - } - if (isObject(newValue.resize) || /^parent$|^self$/.test(newValue.resize)) { - defaults.resize = newValue.resize; - } - if (isObject(newValue.gesture) || /^parent$|^self$/.test(newValue.gesture)) { - defaults.gesture = newValue.gesture; - } - - if (isBool(newValue.endOnly)) { - defaults.endOnly = newValue.endOnly; - } - - if (isObject(newValue.elementRect)) { - defaults.elementRect = newValue.elementRect; - } - - defaultOptions.restrictEnabled = true; - } - else if (newValue === null) { - defaults.drag = defaults.resize = defaults.gesture = null; - defaults.endOnly = false; - } - - return this; - }; - - /*\ - * interact.pointerMoveTolerance - [ method ] - * Returns or sets the distance the pointer must be moved before an action - * sequence occurs. This also affects tolerance for tap events. - * - - newValue (number) #optional The movement from the start position must be greater than this value - = (number | Interactable) The current setting or interact - \*/ - interact.pointerMoveTolerance = function (newValue) { - if (isNumber(newValue)) { - defaultOptions.pointerMoveTolerance = newValue; - - return this; - } - - return defaultOptions.pointerMoveTolerance; - }; - - /*\ - * interact.maxInteractions - [ method ] - ** - * Returns or sets the maximum number of concurrent interactions allowed. - * By default only 1 interaction is allowed at a time (for backwards - * compatibility). To allow multiple interactions on the same Interactables - * and elements, you need to enable it in the draggable, resizable and - * gesturable `'max'` and `'maxPerElement'` options. - ** - - newValue (number) #optional Any number. newValue <= 0 means no interactions. - \*/ - interact.maxInteractions = function (newValue) { - if (isNumber(newValue)) { - maxInteractions = newValue; - - return this; - } - - return maxInteractions; - }; - - function endAllInteractions (event) { - for (var i = 0; i < interactions.length; i++) { - interactions[i].pointerUp(event, event); - } - } - - if (PointerEvent) { - if (PointerEvent === window.MSPointerEvent) { - pEventTypes = { - up: 'MSPointerUp', down: 'MSPointerDown', over: 'mouseover', - out: 'mouseout', move: 'MSPointerMove', cancel: 'MSPointerCancel' }; - } - else { - pEventTypes = { - up: 'pointerup', down: 'pointerdown', over: 'pointerover', - out: 'pointerout', move: 'pointermove', cancel: 'pointercancel' }; - } - - events.add(docTarget, pEventTypes.up , listeners.collectTaps); - - events.add(docTarget, pEventTypes.move , listeners.recordPointer); - - events.add(docTarget, pEventTypes.down , listeners.selectorDown); - events.add(docTarget, pEventTypes.move , listeners.pointerMove ); - events.add(docTarget, pEventTypes.up , listeners.pointerUp ); - events.add(docTarget, pEventTypes.over , listeners.pointerOver ); - events.add(docTarget, pEventTypes.out , listeners.pointerOut ); - - // remove pointers after ending actions in pointerUp - events.add(docTarget, pEventTypes.up , listeners.removePointer); - events.add(docTarget, pEventTypes.cancel, listeners.removePointer); - - // autoscroll - events.add(docTarget, pEventTypes.move, autoScroll.edgeMove); - } - else { - events.add(docTarget, 'mouseup' , listeners.collectTaps); - events.add(docTarget, 'touchend', listeners.collectTaps); - - events.add(docTarget, 'mousemove' , listeners.recordPointer); - events.add(docTarget, 'touchmove' , listeners.recordPointer); - - events.add(docTarget, 'mousedown', listeners.selectorDown); - events.add(docTarget, 'mousemove', listeners.pointerMove ); - events.add(docTarget, 'mouseup' , listeners.pointerUp ); - events.add(docTarget, 'mouseover', listeners.pointerOver ); - events.add(docTarget, 'mouseout' , listeners.pointerOut ); - - events.add(docTarget, 'touchstart' , listeners.selectorDown); - events.add(docTarget, 'touchmove' , listeners.pointerMove ); - events.add(docTarget, 'touchend' , listeners.pointerUp ); - events.add(docTarget, 'touchcancel', listeners.pointerUp ); - - // remove touches after ending actions in pointerUp - events.add(docTarget, 'touchend' , listeners.removePointer); - events.add(docTarget, 'touchcancel', listeners.removePointer); - - // autoscroll - events.add(docTarget, 'mousemove', autoScroll.edgeMove); - events.add(docTarget, 'touchmove', autoScroll.edgeMove); - } - - events.add(windowTarget, 'blur', endAllInteractions); - - try { - if (window.frameElement) { - parentDocTarget._element = window.frameElement.ownerDocument; - - events.add(parentDocTarget , 'mouseup' , listeners.pointerUp); - events.add(parentDocTarget , 'touchend' , listeners.pointerUp); - events.add(parentDocTarget , 'touchcancel' , listeners.pointerUp); - events.add(parentDocTarget , 'pointerup' , listeners.pointerUp); - events.add(parentDocTarget , 'MSPointerUp' , listeners.pointerUp); - events.add(parentWindowTarget, 'blur' , endAllInteractions ); - } - } - catch (error) { - interact.windowParentError = error; - } - - function indexOf (array, target) { - for (var i = 0, len = array.length; i < len; i++) { - if (array[i] === target) { - return i; - } - } - - return -1; - } - - function contains (array, target) { - return indexOf(array, target) !== -1; - } - - // For IE's lack of Event#preventDefault - if (events.useAttachEvent) { - events.add(docTarget, 'selectstart', function (event) { - var interaction = interactions[0]; - - if (interaction.currentAction()) { - interaction.checkAndPreventDefault(event); - } - }); - } - - function matchesSelector (element, selector, nodeList) { - if (ie8MatchesSelector) { - return ie8MatchesSelector(element, selector, nodeList); - } - - return element[prefixedMatchesSelector](selector); - } - - // For IE8's lack of an Element#matchesSelector - // taken from http://tanalin.com/en/blog/2012/12/matches-selector-ie8/ and modified - if (!(prefixedMatchesSelector in Element.prototype) || !isFunction(Element.prototype[prefixedMatchesSelector])) { - ie8MatchesSelector = function (element, selector, elems) { - elems = elems || element.parentNode.querySelectorAll(selector); - - for (var i = 0, len = elems.length; i < len; i++) { - if (elems[i] === element) { - return true; - } - } - - return false; - }; - } - - // requestAnimationFrame polyfill - (function() { - var lastTime = 0, - vendors = ['ms', 'moz', 'webkit', 'o']; - - for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { - reqFrame = window[vendors[x]+'RequestAnimationFrame']; - cancelFrame = window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame']; - } - - if (!reqFrame) { - reqFrame = function(callback) { - var currTime = new Date().getTime(), - timeToCall = Math.max(0, 16 - (currTime - lastTime)), - id = window.setTimeout(function() { callback(currTime + timeToCall); }, - timeToCall); - lastTime = currTime + timeToCall; - return id; - }; - } - - if (!cancelFrame) { - cancelFrame = function(id) { - clearTimeout(id); - }; - } - }()); - - /* global exports: true, module, define */ - - // http://documentcloud.github.io/underscore/docs/underscore.html#section-11 - if (typeof exports !== 'undefined') { - if (typeof module !== 'undefined' && module.exports) { - exports = module.exports = interact; - } - exports.interact = interact; - } - // AMD - else if (typeof define === 'function' && define.amd) { - define('interact', function() { - return interact; - }); - } - else { - window.interact = interact; - } - -} ()); diff --git a/interact.min.js b/interact.min.js deleted file mode 100644 index 3a342de8a..000000000 --- a/interact.min.js +++ /dev/null @@ -1,3 +0,0 @@ -/* interact.js v1.1.2 | https://raw.github.com/taye/interact.js/master/LICENSE */ -!function(){"use strict";function e(){}function t(e){return!!e&&"object"==typeof e&&(/object|function/.test(typeof Element)?e instanceof Element:1===e.nodeType&&"string"==typeof e.nodeName)}function i(e){return e instanceof Object}function r(e){return e instanceof Array}function n(e){return"function"==typeof e}function s(e){return"number"==typeof e}function o(e){return"boolean"==typeof e}function a(e){return"string"==typeof e}function h(e){return a(e)?(rt.querySelector(e),!0):!1}function p(e,t){for(var i in t)e[i]=t[i];return e}function l(e,t){e.page=e.page||{},e.page.x=t.page.x,e.page.y=t.page.y,e.client=e.client||{},e.client.x=t.client.x,e.client.y=t.client.y,e.timeStamp=t.timeStamp}function d(e,t,i){t||(t=i.pointerIds.length>1?S(i.pointers):i.pointers[0]),g(t,lt,i),e.page.x=lt.x,e.page.y=lt.y,v(t,lt,i),e.client.x=lt.x,e.client.y=lt.y,e.timeStamp=(new Date).getTime()}function c(e,t,i){e.page.x=i.page.x-t.page.x,e.page.y=i.page.y-t.page.y,e.client.x=i.client.x-t.client.x,e.client.y=i.client.y-t.client.y,e.timeStamp=(new Date).getTime()-t.timeStamp;var r=Math.max(e.timeStamp/1e3,.001);e.page.speed=pt(e.page.x,e.page.y)/r,e.page.vx=e.page.x/r,e.page.vy=e.page.y/r,e.client.speed=pt(e.client.x,e.page.y)/r,e.client.vx=e.client.x/r,e.client.vy=e.client.y/r}function u(e,t,i){return i=i||{},e=e||"page",i.x=t[e+"X"],i.y=t[e+"Y"],i}function g(e,t,i){return t=t||{},e instanceof q?/inertiastart/.test(e.type)?(i=i||e.interaction,p(t,i.inertiaStatus.upCoords.page),t.x+=i.inertiaStatus.sx,t.y+=i.inertiaStatus.sy):(t.x=e.pageX,t.y=e.pageY):Ct?(u("screen",e,t),t.x+=window.scrollX,t.y+=window.scrollY):u("page",e,t),t}function v(e,t,i){return t=t||{},e instanceof q?/inertiastart/.test(e.type)?(p(t,i.inertiaStatus.upCoords.client),t.x+=i.inertiaStatus.sx,t.y+=i.inertiaStatus.sy):(t.x=e.clientX,t.y=e.clientY):u(Ct?"screen":"client",e,t),t}function m(){return{x:window.scrollX||rt.documentElement.scrollLeft,y:window.scrollY||rt.documentElement.scrollTop}}function f(e){return s(e.pointerId)?e.pointerId:e.identifier}function y(e){return e instanceof ot?e.correspondingUseElement:e}function x(e){var t=Ot?{x:0,y:0}:m(),i=e instanceof nt?e.getBoundingClientRect():e.getClientRects()[0];return i&&{left:i.left+t.x,right:i.right+t.x,top:i.top+t.y,bottom:i.bottom+t.y,width:i.width||i.right-i.left,height:i.heigh||i.bottom-i.top}}function E(e){var t=[];return r(e)?(t[0]=e[0],t[1]=e[1]):"touchend"===e.type?1===e.touches.length?(t[0]=e.touches[0],t[1]=e.changedTouches[0]):0===e.touches.length&&(t[0]=e.changedTouches[0],t[1]=e.changedTouches[1]):(t[0]=e.touches[0],t[1]=e.touches[1]),t}function S(e){var t=E(e);return{pageX:(t[0].pageX+t[1].pageX)/2,pageY:(t[0].pageY+t[1].pageY)/2,clientX:(t[0].clientX+t[1].clientX)/2,clientY:(t[0].clientY+t[1].clientY)/2}}function w(e){if(e.length||e.touches&&e.touches.length>1){var t=E(e),i=Math.min(t[0].pageX,t[1].pageX),r=Math.min(t[0].pageY,t[1].pageY),n=Math.max(t[0].pageX,t[1].pageX),s=Math.max(t[0].pageY,t[1].pageY);return{x:i,y:r,left:i,top:r,width:n-i,height:s-r}}}function b(e,t){t=t||vt.deltaSource;var i=t+"X",r=t+"Y",n=E(e),s=n[0][i]-n[1][i],o=n[0][r]-n[1][r];return pt(s,o)}function D(e,t,i){i=i||vt.deltaSource;var r=i+"X",n=i+"Y",o=E(e),a=o[0][r]-o[1][r],h=o[0][n]-o[1][n],p=180*Math.atan(h/a)/Math.PI;if(s(t)){var l=p-t,d=l%360;d>315?p-=360+p/360|0:d>135?p-=180+p/360|0:-315>d?p+=360+p/360|0:-135>d&&(p+=180+p/360|0)}return p}function z(e,i){var r=e?e.options.origin:vt.origin;return"parent"===r?r=i.parentNode:"self"===r?r=e.getRect(i):h(r)&&(r=P(i,r)||{x:0,y:0}),n(r)&&(r=r(e&&i)),t(r)&&(r=x(r)),r.x="x"in r?r.x:r.left,r.y="y"in r?r.y:r.top,r}function T(e,t,i,r){var n=1-e;return n*n*t+2*n*e*i+e*e*r}function C(e,t,i,r,n,s,o){return{x:T(o,e,i,n),y:T(o,t,r,s)}}function O(e,t,i,r){return e/=r,-i*e*(e-2)+t}function M(e,t){for(;t=t.parentNode;)if(t===e)return!0;return!1}function P(e,i){for(var r=e.parentNode;t(r);){if(et(r,i))return r;r=r.parentNode}return null}function A(e,t){return e._context===rt||M(e._context,t)}function R(e,i,r){var n=e.options.ignoreFrom;return n&&t(r)&&r!==i.parentNode?a(n)?et(r,n)||R(e,r.parentNode):t(n)?r===n||M(n,r):!1:!1}function X(e,i,r){var n=e.options.allowFrom;return n?t(r)&&r!==i.parentNode?a(n)?et(r,n)||X(e,r.parentNode):t(n)?r===n||M(n,r):!1:!1:!0}function Y(e,t){if(!t)return!1;var i=t.options.dragAxis;return"xy"===e||"xy"===i||i===e}function k(e,t){var i=e.options;return/^resize/.test(t)&&(t="resize"),"gesture"!==t&&i.snapEnabled&&Z(i.snap.actions,t)}function _(e,t){var i=e.options;return/^resize/.test(t)&&(t="resize"),i.restrictEnabled&&i.restrict[t]}function I(e,t,i){i=/resize/.test(i)?"resize":i;for(var r=e.options,n=r[i+"Max"],s=r[i+"MaxPerElement"],o=0,a=0,h=0,p=0,l=ct.length;l>p;p++){var d=ct[p],c=/resize/.test(d.prepared)?"resize":d.prepared,u=d.interacting();if(u){if(o++,o>=St)return!1;if(d.target===e){if(a+=c===i|0,a>=n)return!1;if(d.element===t&&(h++,c!==i||h>=s))return!1}}}return St>0}function F(e){var t,i,r,n,s,o=e[0],a=o?0:-1,h=[],p=[];for(n=1;nn;n++){r=ct[n];var h=i;if(r.inertiaStatus.active&&r.mouse===o)for(;h;){if(h===r.element)return r.removePointer(r.pointers[0]),r.addPointer(e),r;h=h.parentNode}}if(o||!ft&&!yt){for(n=0;s>n;n++)if(ct[n].mouse&&!ct[n].inertiaStatus.active)return ct[n];for(n=0;s>n;n++)if(ct[n].mouse&&(!/down/.test(t)||!ct[n].inertiaStatus.active))return r;return r=new $,r.mouse=!0,r}for(n=0;s>n;n++)if(Z(ct[n].pointerIds,a))return ct[n];if(/up|end|out/i.test(t))return null;for(n=0;s>n;n++)if(r=ct[n],!(r.prepared&&!r.target.gesturable()||r.interacting()||!o&&r.mouse))return r.addPointer(e),r;return new $}function U(e){return function(t){var i,r,n=y(t.target),s=y(t.currentTarget);if(ft&&/touch/.test(t.type))for(r=0;r600&&this.timeStamp-e.prevEvent.timeStamp<150){var M=180*Math.atan2(e.prevEvent.velocityY,e.prevEvent.velocityX)/Math.PI,P=22.5;0>M&&(M+=360);var A=M>=135-P&&225+P>M,R=M>=225-P&&315+P>M,X=!A&&(M>=315-P||45+P>M),Y=!R&&M>=45-P&&135+P>M;this.swipe={up:R,down:Y,left:A,right:X,angle:M,speed:e.prevEvent.speed,velocity:{x:e.prevEvent.velocityX,y:e.prevEvent.velocityY}}}}function H(){this.originalEvent.preventDefault()}function W(e,t,i){var r,n,s=this.getRect(i),o=null,a=p({},t.curCoords.page),h=this.options;return s?(bt.resize&&h.resizable&&(r="y"!==h.resizeAxis&&a.x>s.right-xt,n="x"!==h.resizeAxis&&a.y>s.bottom-xt),t.resizeAxes=(r?"x":"")+(n?"y":""),o=t.resizeAxes?"resize"+t.resizeAxes:bt.drag&&h.draggable?"drag":null,bt.gesture&&t.pointerIds.length>=2&&!t.dragging&&!t.resizing&&(o="gesture"),o):null}function L(e,t){if(!a(e))return null;var i=-1!==e.search("resize")?"resize":e,r=t;return("resize"===i&&r.resizable||"drag"===e&&r.draggable||"gesture"===e&&r.gesturable)&&bt[i]?(("resize"===e||"resizeyx"===e)&&(e="resizexy"),e):null}function B(e,t){var i={},r=gt[e.type],n=e.target;t=t?!0:!1;for(var s in e)i[s]=e[s];for(i.originalEvent=e,i.preventDefault=H;n&&n!==rt;){for(var o=0;oi;i++)if(e[i]===t)return i;return-1}function Z(e,t){return-1!==Q(e,t)}function et(e,t,i){return it?it(e,t,i):e[Mt](t)}var tt,it,rt=window.document,nt=window.SVGElement||e,st=window.SVGSVGElement||e,ot=window.SVGElementInstance||e,at=window.HTMLElement||window.Element,ht=window.PointerEvent||window.MSPointerEvent,pt=Math.hypot||function(e,t){return Math.sqrt(e*e+t*t)},lt={},dt=[],ct=[],ut=!1,gt={},vt={draggable:!1,dragAxis:"xy",dropzone:!1,accept:null,dropOverlap:"pointer",resizable:!1,squareResize:!1,resizeAxis:"xy",gesturable:!1,dragMax:1,resizeMax:1,gestureMax:1,dragMaxPerElement:1,resizeMaxPerElement:1,gestureMaxPerElement:1,pointerMoveTolerance:1,actionChecker:null,styleCursor:!0,preventDefault:"auto",snap:{mode:"grid",endOnly:!1,actions:["drag"],range:1/0,grid:{x:100,y:100},gridOffset:{x:0,y:0},anchors:[],paths:[],elementOrigin:null,arrayTypes:/^anchors$|^paths$|^actions$/,objectTypes:/^grid$|^gridOffset$|^elementOrigin$/,stringTypes:/^mode$/,numberTypes:/^range$/,boolTypes:/^endOnly$/},snapEnabled:!1,restrict:{drag:null,resize:null,gesture:null,endOnly:!1},restrictEnabled:!1,autoScroll:{container:window,margin:60,speed:300,numberTypes:/^margin$|^speed$/},autoScrollEnabled:!1,inertia:{resistance:10,minSpeed:100,endSpeed:10,allowResume:!0,zeroResumeDelta:!1,smoothEndDuration:300,actions:["drag","resize"],numberTypes:/^resistance$|^minSpeed$|^endSpeed$|^smoothEndDuration$/,arrayTypes:/^actions$/,boolTypes:/^(allowResume|zeroResumeDelta)$/},inertiaEnabled:!1,origin:{x:0,y:0},deltaSource:"page",context:rt},mt={target:null,i:null,x:0,y:0,scroll:function(){var e=mt.target.options.autoScroll,t=e.container,i=(new Date).getTime(),r=(i-mt.prevTime)/1e3,n=e.speed*r;n>=1&&(t instanceof window.Window?t.scrollBy(mt.x*n,mt.y*n):t&&(t.scrollLeft+=mt.x*n,t.scrollTop+=mt.y*n),mt.prevTime=i),mt.isScrolling&&(At(mt.i),mt.i=Pt(mt.scroll))},edgeMove:function(e){for(var t,i=!1,r=0;rp.container.innerWidth-mt.margin,a=e.clientY>p.container.innerHeight-mt.margin;else{var l=x(p.container);h=e.clientXl.right-mt.margin,a=e.clientY>l.bottom-mt.margin}mt.x=o?1:h?-1:0,mt.y=a?1:s?-1:0,mt.isScrolling||(mt.margin=p.margin,mt.speed=p.speed,mt.start(t))}},isScrolling:!1,prevTime:0,start:function(e){mt.isScrolling=!0,At(mt.i),mt.target=e,mt.prevTime=(new Date).getTime(),mt.i=Pt(mt.scroll)},stop:function(){mt.isScrolling=!1,At(mt.i)}},ft="ontouchstart"in window||window.DocumentTouch&&rt instanceof window.DocumentTouch,yt=!!ht,xt=ft||yt?20:10,Et=0,St=1,wt={drag:"move",resizex:"e-resize",resizey:"s-resize",resizexy:"se-resize",gesture:""},bt={drag:!0,resize:!0,gesture:!0},Dt="onmousewheel"in rt?"mousewheel":"wheel",zt=["dragstart","dragmove","draginertiastart","dragend","dragenter","dragleave","dropactivate","dropdeactivate","dropmove","drop","resizestart","resizemove","resizeinertiastart","resizeend","gesturestart","gesturemove","gestureinertiastart","gestureend","tap","doubletap"],Tt={},Ct="Opera"==navigator.appName&&ft&&navigator.userAgent.match("Presto"),Ot=/iP(hone|od|ad)/.test(navigator.platform)&&/OS [1-7][^\d]/.test(navigator.appVersion),Mt="matchesSelector"in Element.prototype?"matchesSelector":"webkitMatchesSelector"in Element.prototype?"webkitMatchesSelector":"mozMatchesSelector"in Element.prototype?"mozMatchesSelector":"oMatchesSelector"in Element.prototype?"oMatchesSelector":"msMatchesSelector",Pt=window.requestAnimationFrame,At=window.cancelAnimationFrame,Rt={_element:window,events:{}},Xt={_element:rt,events:{}},Yt={_element:window.parent,events:{}},kt={_element:null,events:{}},_t=function(){function e(e,t,a,c){var u=Q(p,e),g=l[u];if(g||(g={events:{},typeCount:0},u=p.push(e)-1,l.push(g),d.push(s?{supplied:[],wrapped:[],useCount:[]}:null)),g.events[t]||(g.events[t]=[],g.typeCount++),!Z(g.events[t],a)){var v;if(s){var m=d[u],f=Q(m.supplied,a),y=m.wrapped[f]||function(t){t.immediatePropagationStopped||(t.target=t.srcElement,t.currentTarget=e,t.preventDefault=t.preventDefault||i,t.stopPropagation=t.stopPropagation||r,t.stopImmediatePropagation=t.stopImmediatePropagation||n,/mouse|click/.test(t.type)&&(t.pageX=t.clientX+rt.documentElement.scrollLeft,t.pageY=t.clientY+rt.documentElement.scrollTop),a(t))};v=e[o](h+t,y,Boolean(c)),-1===f?(m.supplied.push(a),m.wrapped.push(y),m.useCount.push(1)):m.useCount[f]++}else v=e[o](t,a,c||!1);return g.events[t].push(a),v}}function t(e,i,r,n){var o,c,u,g=Q(p,e),v=l[g],m=r;if(v&&v.events)if(s&&(c=d[g],u=Q(c.supplied,r),m=c.wrapped[u]),"all"!==i){if(v.events[i]){var f=v.events[i].length;if("all"===r)for(o=0;f>o;o++)t(e,i,v.events[i][o],Boolean(n));else for(o=0;f>o;o++)if(v.events[i][o]===r){e[a](h+i,m,n||!1),v.events[i].splice(o,1),s&&c&&(c.useCount[u]--,0===c.useCount[u]&&(c.supplied.splice(u,1),c.wrapped.splice(u,1),c.useCount.splice(u,1)));break}v.events[i]&&0===v.events[i].length&&(v.events[i]=null,v.typeCount--)}v.typeCount||(l.splice(g),p.splice(g),d.splice(g))}else for(i in v.events)v.events.hasOwnProperty(i)&&t(e,i,"all")}function i(){this.returnValue=!1}function r(){this.cancelBubble=!0}function n(){this.cancelBubble=!0,this.immediatePropagationStopped=!0}var s="attachEvent"in window&&!("addEventListener"in window),o=s?"attachEvent":"addEventListener",a=s?"detachEvent":"removeEventListener",h=s?"on":"",p=[],l=[],d=[];return{add:function(t,i,r,n){e(t._element,i,r,n)},remove:function(e,i,r,n){t(e._element,i,r,n)},addToElement:e,removeFromElement:t,useAttachEvent:s,_elements:p,_targets:l,_attachedListeners:d}}();$.prototype={getPageXY:function(e,t){return g(e,t,this)},getClientXY:function(e,t){return v(e,t,this)},setEventXY:function(e,t){return d(e,t,this)},pointerOver:function(e,t,i){function r(e,t){e&&A(e,i)&&!R(e,i,i)&&X(e,i,i)&&et(i,t)&&(n.push(e),s.push(i))}if(!this.prepared&&this.mouse){var n=[],s=[],o=this.element;this.addPointer(e),!this.target||!R(this.target,this.element,i)&&X(this.target,this.element,i)&&I(this.target,this.element,this.prepared)||(this.target=null,this.element=null,this.matches=[],this.matchElements=[]);var a=dt.get(i),h=a&&!R(a,i,i)&&X(a,i,i)&&L(a.getAction(e,this,i),a);h=a&&I(a,i,h)?h:null,h?(this.target=a,this.element=i,this.matches=[],this.matchElements=[]):(dt.forEachSelector(r),this.validateSelector(e,n,s)?(this.matches=n,this.matchElements=s,this.pointerHover(e,t,this.matches,this.matchElements),_t.addToElement(i,ht?tt.move:"mousemove",It.pointerHover)):this.target&&(M(o,i)?(this.pointerHover(e,t,this.matches,this.matchElements),_t.addToElement(this.element,ht?tt.move:"mousemove",It.pointerHover)):(this.target=null,this.element=null,this.matches=[],this.matchElements=[])))}},pointerHover:function(e,t,i,r,n,s){var o=this.target;if(!this.prepared&&this.mouse){var a;this.setEventXY(this.curCoords,e),n?a=this.validateSelector(e,n,s):o&&(a=L(o.getAction(this.pointers[0],this,this.element),this.target)),o&&o.options.styleCursor&&(rt.documentElement.style.cursor=a?wt[a]:"")}else this.prepared&&this.checkAndPreventDefault(t,o,this.element)},pointerOut:function(e,t,i){this.prepared||(dt.get(i)||_t.removeFromElement(i,ht?tt.move:"mousemove",It.pointerHover),this.target&&this.target.options.styleCursor&&!this.interacting()&&(rt.documentElement.style.cursor=""))},selectorDown:function(e,t,i,r){function n(e,t,r){var n=it?r.querySelectorAll(t):void 0;A(e,o)&&!R(e,o,i)&&X(e,o,i)&&et(o,t,n)&&(a.matches.push(e),a.matchElements.push(o))}this.pointerIsDown=!0;var s,o=i;if(this.addPointer(e),this.inertiaStatus.active&&this.target.selector)for(;o&&o!==rt;){if(o===this.element&&L(this.target.getAction(e,this,this.element),this.target)===this.prepared)return At(this.inertiaStatus.i),void(this.inertiaStatus.active=!1);o=o.parentNode}if(!this.interacting()){var a=this;if(this.setEventXY(this.curCoords,e),this.matches.length&&this.mouse)s=this.validateSelector(e,this.matches,this.matchElements);else for(;o&&o!==rt&&!s;)this.matches=[],this.matchElements=[],dt.forEachSelector(n),s=this.validateSelector(e,this.matches,this.matchElements),o=o.parentNode;return s?(this.prepared=s,this.pointerDown(e,t,i,r,s)):(this.downTime=(new Date).getTime(),this.downTarget=i,this.downEvent=t,p(this.downPointer,e),l(this.prevCoords,this.curCoords),this.pointerWasMoved=!1,void 0)}},pointerDown:function(e,t,i,r,n){if(!n&&!this.inertiaStatus.active&&this.pointerWasMoved&&this.prepared)return void this.checkAndPreventDefault(t,this.target,this.element);this.pointerIsDown=!0,this.addPointer(e);var s;if(this.pointerIds.length<2&&!this.target||!this.prepared){var o=dt.get(r);o&&!R(o,r,i)&&X(o,r,i)&&(s=L(n||o.getAction(e,this),o,i))&&I(o,r,s)&&(this.target=o,this.element=r)}var a=this.target,h=a&&a.options;if(a&&!this.interacting()){if(s=s||L(n||a.getAction(e,this),a,this.element),this.setEventXY(this.startCoords),!s)return;h.styleCursor&&(rt.documentElement.style.cursor=wt[s]),this.resizeAxes="resizexy"===s?"xy":"resizex"===s?"x":"resizey"===s?"y":"","gesture"===s&&this.pointerIds.length<2&&(s=null),this.prepared=s,this.snapStatus.snappedX=this.snapStatus.snappedY=this.restrictStatus.restrictedX=this.restrictStatus.restrictedY=0/0,this.downTime=(new Date).getTime(),this.downTarget=i,this.downEvent=t,p(this.downPointer,e),this.setEventXY(this.prevCoords),this.pointerWasMoved=!1,this.checkAndPreventDefault(t,a,this.element)}else this.inertiaStatus.active&&this.target.options.inertia.allowResume&&r===this.element&&L(a.getAction(e,this,this.element),a)===this.prepared&&(At(this.inertiaStatus.i),this.inertiaStatus.active=!1,this.checkAndPreventDefault(t,a,this.element))},pointerMove:function(e,t,i,r,n){if(this.pointerIsDown){if(this.setEventXY(this.curCoords,e instanceof q?this.inertiaStatus.startEvent:void 0),this.pointerWasMoved&&!n&&this.curCoords.page.x===this.prevCoords.page.x&&this.curCoords.page.y===this.prevCoords.page.y&&this.curCoords.client.x===this.prevCoords.client.x&&this.curCoords.client.y===this.prevCoords.client.y)return void this.checkAndPreventDefault(t,this.target,this.element);c(this.pointerDelta,this.prevCoords,this.curCoords);var s,o;if(this.pointerWasMoved||(s=this.curCoords.client.x-this.startCoords.client.x,o=this.curCoords.client.y-this.startCoords.client.y,this.pointerWasMoved=pt(s,o)>vt.pointerMoveTolerance),this.prepared){if(this.pointerWasMoved&&(!this.inertiaStatus.active||e instanceof q&&/inertiastart/.test(e.type))){if(!this.interacting()&&(c(this.pointerDelta,this.prevCoords,this.curCoords),"drag"===this.prepared)){var a=Math.abs(s),h=Math.abs(o),p=this.target.options.dragAxis,d=a>h?"x":h>a?"y":"xy";if("xy"!==d&&"xy"!==p&&p!==d){this.prepared=null;for(var u=i;u&&u!==rt;){var g=dt.get(u);if(g&&g!==this.target&&"drag"===g.getAction(this.downPointer,this,u)&&Y(d,g)){this.prepared="drag",this.target=g,this.element=u;break}u=u.parentNode}if(!this.prepared){var v=function(e,t,r){var n=it?r.querySelectorAll(t):void 0;if(e!==this.target)return A(e,i)&&!R(e,u,i)&&X(e,u,i)&&et(u,t,n)&&"drag"===e.getAction(this.downPointer,this,u)&&Y(d,e)&&I(e,u,"drag")?e:void 0};for(u=i;u&&u!==rt;){var m=dt.forEachSelector(v);if(m){this.prepared="drag",this.target=m,this.element=u;break}u=u.parentNode}}}}var f=!!this.prepared&&!this.interacting();if(f&&!I(this.target,this.element,this.prepared))return void this.stop();if(this.prepared&&this.target){var y=this.target,x=k(y,this.prepared)&&(!y.options.snap.endOnly||n),E=_(y,this.prepared)&&(!y.options.restrict.endOnly||n);if(f){var S,w,b=y.getRect(this.element),D=y.options.snap,z=y.options.restrict;b?(this.startOffset.left=this.startCoords.page.x-b.left,this.startOffset.top=this.startCoords.page.y-b.top,this.startOffset.right=b.right-this.startCoords.page.x,this.startOffset.bottom=b.bottom-this.startCoords.page.y,S="width"in b?b.width:b.right-b.left,w="height"in b?b.height:b.bottom-b.top):this.startOffset.left=this.startOffset.top=this.startOffset.right=this.startOffset.bottom=0,b&&D.elementOrigin?(this.snapOffset.x=this.startOffset.left-S*D.elementOrigin.x,this.snapOffset.y=this.startOffset.top-w*D.elementOrigin.y):this.snapOffset.x=this.snapOffset.y=0,b&&z.elementRect?(this.restrictOffset.left=this.startOffset.left-S*z.elementRect.left,this.restrictOffset.top=this.startOffset.top-w*z.elementRect.top,this.restrictOffset.right=this.startOffset.right-S*(1-z.elementRect.right),this.restrictOffset.bottom=this.startOffset.bottom-w*(1-z.elementRect.bottom)):this.restrictOffset.left=this.restrictOffset.top=this.restrictOffset.right=this.restrictOffset.bottom=0}var T=f?this.startCoords.page:this.curCoords.page;x?this.setSnapping(T):this.snapStatus.locked=!1,E?this.setRestriction(T):this.restrictStatus.restricted=!1;var C=(x?this.snapStatus.changed||!this.snapStatus.locked:!0)&&(E?!this.restrictStatus.restricted||this.restrictStatus.restricted&&this.restrictStatus.changed:!0);if(C){var O=/resize/.test(this.prepared)?"resize":this.prepared;if(f){var M=this[O+"Start"](this.downEvent);this.prevEvent=M,this.activeDrops.dropzones=[],this.activeDrops.elements=[],this.activeDrops.rects=[],this.dynamicDrop||this.setActiveDrops(this.element);var P=this.getDropEvents(t,M);P.activate&&this.fireActiveDrops(P.activate),T=this.curCoords.page,x&&this.setSnapping(T),E&&this.setRestriction(T)}this.prevEvent=this[O+"Move"](t)}this.checkAndPreventDefault(t,this.target,this.element)}}l(this.prevCoords,this.curCoords),(this.dragging||this.resizing)&&mt.edgeMove(t)}}},dragStart:function(e){var t=new q(this,e,"drag","start",this.element);return this.dragging=!0,this.target.fire(t),t},dragMove:function(e){var t=this.target,i=new q(this,e,"drag","move",this.element),r=this.element,n=this.getDrop(i,r);this.dropTarget=n.dropzone,this.dropElement=n.element;var s=this.getDropEvents(e,i);return t.fire(i),s.leave&&this.prevDropTarget.fire(s.leave),s.enter&&this.dropTarget.fire(s.enter),s.move&&this.dropTarget.fire(s.move),this.prevDropTarget=this.dropTarget,this.prevDropElement=this.dropElement,i},resizeStart:function(e){var t=new q(this,e,"resize","start",this.element);return this.target.fire(t),this.resizing=!0,t},resizeMove:function(e){var t=new q(this,e,"resize","move",this.element);return this.target.fire(t),t},gestureStart:function(e){var t=new q(this,e,"gesture","start",this.element);return t.ds=0,this.gesture.startDistance=this.gesture.prevDistance=t.distance,this.gesture.startAngle=this.gesture.prevAngle=t.angle,this.gesture.scale=1,this.gesturing=!0,this.target.fire(t),t},gestureMove:function(e){if(!this.pointerIds.length)return this.prevEvent;var t;return t=new q(this,e,"gesture","move",this.element),t.ds=t.scale-this.gesture.scale,this.target.fire(t),this.gesture.prevAngle=t.angle,this.gesture.prevDistance=t.distance,1/0===t.scale||null===t.scale||void 0===t.scale||isNaN(t.scale)||(this.gesture.scale=t.scale),t},pointerUp:function(e,t,i,r){var n,s=this.target,o=s&&s.options,a=o&&o.inertia,h=this.inertiaStatus;if(this.interacting()){if(h.active)return;var d,c,u=(new Date).getTime(),g=!1,v=!1,m=!1,f=k(s,this.prepared)&&o.snap.endOnly,y=_(s,this.prepared)&&o.restrict.endOnly,x=0,E=0;if(this.dragging&&(d="x"===o.dragAxis?Math.abs(this.pointerDelta.client.vx):"y"===o.dragAxis?Math.abs(this.pointerDelta.client.vy):this.pointerDelta.client.speed),g=o.inertiaEnabled&&"gesture"!==this.prepared&&Z(a.actions,this.prepared)&&t!==h.startEvent,v=g&&u-this.curCoords.timeStamp<50&&d>a.minSpeed&&d>a.endSpeed,g&&!v&&(f||y)){var S={};S.snap=S.restrict=S,f&&(this.setSnapping(this.curCoords.page,S),S.locked&&(x+=S.dx,E+=S.dy)),y&&(this.setRestriction(this.curCoords.page,S),S.restricted&&(x+=S.dx,E+=S.dy)),(x||E)&&(m=!0)}if(v||m){if(l(h.upCoords,this.curCoords),this.pointers[0]=h.startEvent=c=new q(this,t,this.prepared,"inertiastart",this.element),h.t0=u,s.fire(h.startEvent),v){h.vx0=this.pointerDelta.client.vx,h.vy0=this.pointerDelta.client.vy,h.v0=d,this.calcInertia(h);var w,b=p({},this.curCoords.page),D=z(s,this.element);if(b.x=b.x+h.xe-D.x,b.y=b.y+h.ye-D.y,w={useStatusXY:!0,x:b.x,y:b.y,dx:0,dy:0,snap:null},w.snap=w,x=E=0,f){var T=this.setSnapping(this.curCoords.page,w);T.locked&&(x+=T.dx,E+=T.dy)}if(y){var C=this.setRestriction(this.curCoords.page,w);C.restricted&&(x+=C.dx,E+=C.dy)}h.modifiedXe+=x,h.modifiedYe+=E,h.i=Pt(this.boundInertiaFrame)}else h.smoothEnd=!0,h.xe=x,h.ye=E,h.sx=h.sy=0,h.i=Pt(this.boundSmoothEndFrame);return void(h.active=!0)}(f||y)&&this.pointerMove(e,t,i,r,!0)}if(this.dragging){n=new q(this,t,"drag","end",this.element);var O=this.element,M=this.getDrop(n,O);this.dropTarget=M.dropzone,this.dropElement=M.element;var P=this.getDropEvents(t,n);P.leave&&this.prevDropTarget.fire(P.leave),P.enter&&this.dropTarget.fire(P.enter),P.drop&&this.dropTarget.fire(P.drop),P.deactivate&&this.fireActiveDrops(P.deactivate),s.fire(n)}else this.resizing?(n=new q(this,t,"resize","end",this.element),s.fire(n)):this.gesturing&&(n=new q(this,t,"gesture","end",this.element),s.fire(n));this.stop(t)},collectDrops:function(e){var i,r=[],n=[];for(e=e||this.element,i=0;ih;h++){var l=o[h];l!==e&&(r.push(s),n.push(l))}}return{dropzones:r,elements:n}},fireActiveDrops:function(e){var t,i,r,n;for(t=0;t1&&ct.splice(Q(ct,this),1)},inertiaFrame:function(){var e=this.inertiaStatus,t=this.target.options.inertia,i=t.resistance,r=(new Date).getTime()/1e3-e.t0;if(rt?(e.sx=O(t,0,e.xe,i),e.sy=O(t,0,e.ye,i),this.pointerMove(e.startEvent,e.startEvent),e.i=Pt(this.boundSmoothEndFrame)):(e.sx=e.xe,e.sy=e.ye,this.pointerMove(e.startEvent,e.startEvent),e.active=!1,e.smoothEnd=!1,this.pointerUp(e.startEvent,e.startEvent))},addPointer:function(e){var t=f(e),i=this.mouse?0:Q(this.pointerIds,t);-1===i?(i=this.pointerIds.length,this.pointerIds.push(t),this.pointers[i]=e):this.pointers[i]=e},removePointer:function(e){var t=f(e),i=this.mouse?0:Q(this.pointerIds,t);-1!==i&&this.pointerIds.splice(i,1)},recordPointer:function(e){if(!this.inertiaStatus.active){var t=this.mouse?0:Q(this.pointerIds,f(e));-1!==t&&(this.pointers[t]=e)}},fireTaps:function(e,t,i,r){var n,s={};p(s,t),p(s,e),s.preventDefault=H,s.stopPropagation=q.prototype.stopPropagation,s.stopImmediatePropagation=q.prototype.stopImmediatePropagation,s.timeStamp=(new Date).getTime(),s.originalEvent=t,s.dt=s.timeStamp-this.downTime,s.type="tap";var o=s.timeStamp-this.tapTime,a=this.prevTap&&"doubletap"!==this.prevTap.type&&this.prevTap.target===s.target&&500>o;for(this.tapTime=s.timeStamp,this.mouse||(Et=this.tapTime),n=0;nr;r++){var s=t[r],o=i[r],a=L(s.getAction(e,this,o),s);if(a&&I(s,o,a))return this.target=s,this.element=o,a}},setSnapping:function(e,t){var i,r,o,a,h,l,d,c,u,g,v=this.target.options.snap,m=v.anchors;if(t=t||this.snapStatus,t.useStatusXY)i={x:t.x,y:t.y};else{var f=z(this.target,this.element);i=p({},e),i.x-=f.x,i.y-=f.y}if(i.x-=this.inertiaStatus.resumeDx,i.y-=this.inertiaStatus.resumeDy,t.realX=i.x,t.realY=i.y,v.range<0&&(v.range=1/0),"path"===v.mode)for(m=[],u=0,g=v.paths.length;g>u;u++){var y=v.paths[u];n(y)&&(y=y(i.x,i.y)),m.push({x:s(y.x)?y.x:i.x,y:s(y.y)?y.y:i.y,range:s(y.range)?y.range:v.range})}if("anchor"!==v.mode&&"path"!==v.mode||!m.length){if("grid"===v.mode){var x=Math.round((i.x-v.gridOffset.x-this.snapOffset.x)/v.grid.x),E=Math.round((i.y-v.gridOffset.y-this.snapOffset.y)/v.grid.y),S=x*v.grid.x+v.gridOffset.x+this.snapOffset.x,w=E*v.grid.y+v.gridOffset.y+this.snapOffset.y;l=S-i.x,d=w-i.y,c=pt(l,d),a=cu;u++){var b=m[u];o=s(b.range)?b.range:v.range,l=b.x-i.x+this.snapOffset.x,d=b.y-i.y+this.snapOffset.y,c=pt(l,d),a=o>c,1/0===o&&r.inRange&&1/0!==r.range&&(a=!1),(!r.anchor||(a?r.inRange&&1/0!==o?c/o$t;$t++){var Ut=Ft[$t];It[Ut]=U(Ut)}dt.indexOfElement=function(e,t){for(var i=0;in.left&&p.xn.top&&p.y=n.left&&c<=n.right&&u>=n.top&&u<=n.bottom}if(s(o)){var v=Math.max(0,Math.min(n.right,d.right)-Math.max(n.left,d.left))*Math.max(0,Math.min(n.bottom,d.bottom)-Math.max(n.top,d.top)),m=v/(d.width*d.height);return m>=o}},dropChecker:function(e){return n(e)?(this.dropCheck=e,this):this.dropCheck},accept:function(e){return t(e)?(this.options.accept=e,this):h(e)?(this.options.accept=e,this):null===e?(delete this.options.accept,this):this.options.accept},resizable:function(e){return i(e)?(this.options.resizable=!0,this.setOnEvents("resize",e),s(e.max)&&(this.options.resizeMax=e.max),s(e.maxPerElement)&&(this.options.resizeMaxPerElement=e.maxPerElement),/^x$|^y$|^xy$/.test(e.axis)?this.options.resizeAxis=e.axis:null===e.axis&&(this.options.resizeAxis=vt.resizeAxis),this):o(e)?(this.options.resizable=e,this):this.options.resizable},resizeable:e,squareResize:function(e){return o(e)?(this.options.squareResize=e,this):null===e?(delete this.options.squareResize,this):this.options.squareResize},gesturable:function(e){return i(e)?(this.options.gesturable=!0,this.setOnEvents("gesture",e),s(e.max)&&(this.options.gestureMax=e.max),s(e.maxPerElement)&&(this.options.gestureMaxPerElement=e.maxPerElement),this):o(e)?(this.options.gesturable=e,this):null===e?(delete this.options.gesturable,this):this.options.gesturable},gestureable:e,autoScroll:function(e){var r=vt.autoScroll;if(i(e)){var n=this.options.autoScroll;return n===r&&(n=this.options.autoScroll={margin:r.margin,distance:r.distance,interval:r.interval,container:r.container}),n.margin=this.validateSetting("autoScroll","margin",e.margin),n.speed=this.validateSetting("autoScroll","speed",e.speed),n.container=t(e.container)||e.container instanceof window.Window?e.container:r.container,this.options.autoScrollEnabled=!0,this.options.autoScroll=n,this}return o(e)?(this.options.autoScrollEnabled=e,this):null===e?(delete this.options.autoScrollEnabled,delete this.options.autoScroll,this):this.options.autoScrollEnabled?this.options.autoScroll:!1},snap:function(e){var t=vt.snap;if(i(e)){var r=this.options.snap;return r===t&&(r={}),r.mode=this.validateSetting("snap","mode",e.mode),r.endOnly=this.validateSetting("snap","endOnly",e.endOnly),r.actions=this.validateSetting("snap","actions",e.actions),r.range=this.validateSetting("snap","range",e.range),r.paths=this.validateSetting("snap","paths",e.paths),r.grid=this.validateSetting("snap","grid",e.grid),r.gridOffset=this.validateSetting("snap","gridOffset",e.gridOffset),r.anchors=this.validateSetting("snap","anchors",e.anchors),r.elementOrigin=this.validateSetting("snap","elementOrigin",e.elementOrigin),this.options.snapEnabled=!0,this.options.snap=r,this}return o(e)?(this.options.snapEnabled=e,this):null===e?(delete this.options.snapEnabled,delete this.options.snap,this):this.options.snapEnabled?this.options.snap:!1},inertia:function(e){var t=vt.inertia;if(i(e)){var r=this.options.inertia;return r===t&&(r=this.options.inertia={resistance:t.resistance,minSpeed:t.minSpeed,endSpeed:t.endSpeed,actions:t.actions,allowResume:t.allowResume,zeroResumeDelta:t.zeroResumeDelta,smoothEndDuration:t.smoothEndDuration}),r.resistance=this.validateSetting("inertia","resistance",e.resistance),r.minSpeed=this.validateSetting("inertia","minSpeed",e.minSpeed),r.endSpeed=this.validateSetting("inertia","endSpeed",e.endSpeed),r.actions=this.validateSetting("inertia","actions",e.actions),r.allowResume=this.validateSetting("inertia","allowResume",e.allowResume),r.zeroResumeDelta=this.validateSetting("inertia","zeroResumeDelta",e.zeroResumeDelta),r.smoothEndDuration=this.validateSetting("inertia","smoothEndDuration",e.smoothEndDuration),this.options.inertiaEnabled=!0,this.options.inertia=r,this}return o(e)?(this.options.inertiaEnabled=e,this):null===e?(delete this.options.inertiaEnabled,delete this.options.inertia,this):this.options.inertiaEnabled?this.options.inertia:!1},getAction:function(e,t,i){var r=this.defaultActionChecker(e,t,i);return this.options.actionChecker&&(r=this.options.actionChecker(e,r,this,i,t)),r},defaultActionChecker:W,actionChecker:function(e){return n(e)?(this.options.actionChecker=e,this):null===e?(delete this.options.actionChecker,this):this.options.actionChecker},getRect:function(e){return e=e||this._element,this.selector&&!t(e)&&(e=this._context.querySelector(this.selector)),x(e)},rectChecker:function(e){return n(e)?(this.getRect=e,this):null===e?(delete this.options.getRect,this):this.getRect},styleCursor:function(e){return o(e)?(this.options.styleCursor=e,this):null===e?(delete this.options.styleCursor,this):this.options.styleCursor},preventDefault:function(e){return o(e)||"auto"===e?(this.options.preventDefault=e,this):null===e?(delete this.options.preventDefault,this):this.options.preventDefault},origin:function(e){return h(e)?(this.options.origin=e,this):i(e)?(this.options.origin=e,this):null===e?(delete this.options.origin,this):this.options.origin},deltaSource:function(e){return"page"===e||"client"===e?(this.options.deltaSource=e,this):null===e?(delete this.options.deltaSource,this):this.options.deltaSource},restrict:function(e){if(void 0===e)return this.options.restrict;if(o(e))vt.restrictEnabled=e;else if(i(e)){var t={};(i(e.drag)||h(e.drag))&&(t.drag=e.drag),(i(e.resize)||h(e.resize))&&(t.resize=e.resize),(i(e.gesture)||h(e.gesture))&&(t.gesture=e.gesture),o(e.endOnly)&&(t.endOnly=e.endOnly),i(e.elementRect)&&(t.elementRect=e.elementRect),this.options.restrictEnabled=!0,this.options.restrict=t}else null===e&&(delete this.options.restrict,delete this.options.restrictEnabled);return this},context:function(){return this._context},_context:rt,ignoreFrom:function(e){return h(e)?(this.options.ignoreFrom=e,this):t(e)?(this.options.ignoreFrom=e,this):null===e?(delete this.options.ignoreFrom,this):this.options.ignoreFrom},allowFrom:function(e){return h(e)?(this.options.allowFrom=e,this):t(e)?(this.options.allowFrom=e,this):null===e?(delete this.options.allowFrom,this):this.options.allowFrom},validateSetting:function(e,n,h){var p=vt[e],l=this.options[e];if(void 0!==p&&void 0!==p[n]){if("objectTypes"in p&&p.objectTypes.test(n))return i(h)?h:n in l&&i(l[n])?l[n]:p[n];if("arrayTypes"in p&&p.arrayTypes.test(n))return r(h)?h:n in l&&r(l[n])?l[n]:p[n];if("stringTypes"in p&&p.stringTypes.test(n))return a(h)?h:n in l&&a(l[n])?l[n]:p[n];if("numberTypes"in p&&p.numberTypes.test(n))return s(h)?h:n in l&&s(l[n])?l[n]:p[n];if("boolTypes"in p&&p.boolTypes.test(n))return o(h)?h:n in l&&o(l[n])?l[n]:p[n];if("elementTypes"in p&&p.elementTypes.test(n))return t(h)?h:n in l&&t(l[n])?l[n]:p[n]}return null},element:function(){return this._element},fire:function(e){if(!e||!e.type||!Z(zt,e.type))return this;var t,i,r,s="on"+e.type,o="";if(e.type in this._iEvents)for(t=this._iEvents[e.type],i=0,r=t.length;r>i&&!e.immediatePropagationStopped;i++)o=t[i].name,t[i](e);if(n(this[s])&&(o=this[s].name,this[s](e)),e.type in Tt&&(t=Tt[e.type]))for(i=0,r=t.length;r>i&&!e.immediatePropagationStopped;i++)o=t[i].name,t[i](e);return this},on:function(e,t,i){if("wheel"===e&&(e=Dt),i=i?!0:!1,Z(zt,e))e in this._iEvents?Z(this._iEvents[e],t)||this._iEvents[e].push(t):this._iEvents[e]=[t];else if(this.selector){gt[e]||(gt[e]={selectors:[],contexts:[],listeners:[]},_t.addToElement(this._context,e,B),_t.addToElement(this._context,e,K,!0));var r,n=gt[e];for(r=n.selectors.length-1;r>=0&&(n.selectors[r]!==this.selector||n.contexts[r]!==this._context);r--);-1===r&&(r=n.selectors.length,n.selectors.push(this.selector),n.contexts.push(this._context),n.listeners.push([])),n.listeners[r].push([t,i])}else _t.add(this,e,t,i);return this},off:function(e,t,i){var r,n=-1;if(i=i?!0:!1,"wheel"===e&&(e=Dt),Z(zt,e))r=this._iEvents[e],r&&-1!==(n=Q(r,t))&&this._iEvents[e].splice(n,1);else if(this.selector){var s=gt[e],o=!1;if(!s)return this;for(n=s.selectors.length-1;n>=0;n--)if(s.selectors[n]===this.selector&&s.contexts[n]===this._context){for(var a=s.listeners[n],h=a.length-1;h>=0;h--){var p=a[h][0],l=a[h][1];if(p===t&&l===i){a.splice(h,1),a.length||(s.selectors.splice(n,1),s.contexts.splice(n,1),s.listeners.splice(n,1),_t.removeFromElement(this._context,e,B),_t.removeFromElement(this._context,e,K,!0),s.selectors.length||(gt[e]=null)),o=!0;break}}if(o)break}}else _t.remove(this,t,i);return this},set:function(e){e&&i(e)||(e={}),this.options=new j(e),this.draggable("draggable"in e?e.draggable:this.options.draggable),this.dropzone("dropzone"in e?e.dropzone:this.options.dropzone),this.resizable("resizable"in e?e.resizable:this.options.resizable),this.gesturable("gesturable"in e?e.gesturable:this.options.gesturable);for(var t=["accept","actionChecker","allowFrom","autoScroll","deltaSource","dropChecker","ignoreFrom","inertia","origin","preventDefault","rectChecker","restrict","snap","styleCursor"],r=0,n=t.length;n>r;r++){var s=t[r];s in e&&this[s](e[s])}return this},unset:function(){if(_t.remove(this,"all"),a(this.selector))for(var e in gt)for(var t=gt[e],i=0;ie;e++){var i=ct[e].currentAction();if(i)return i}return null},V.stop=function(e){for(var t=ct.length-1;t>0;t--)ct[t].stop(e);return V},V.dynamicDrop=function(e){return o(e)?(ut=e,V):ut},V.deltaSource=function(e){return"page"===e||"client"===e?(vt.deltaSource=e,this):vt.deltaSource},V.restrict=function(e){var t=vt.restrict;return void 0===e?vt.restrict:(o(e)?vt.restrictEnabled=e:i(e)?((i(e.drag)||/^parent$|^self$/.test(e.drag))&&(t.drag=e.drag),(i(e.resize)||/^parent$|^self$/.test(e.resize))&&(t.resize=e.resize),(i(e.gesture)||/^parent$|^self$/.test(e.gesture))&&(t.gesture=e.gesture),o(e.endOnly)&&(t.endOnly=e.endOnly),i(e.elementRect)&&(t.elementRect=e.elementRect),vt.restrictEnabled=!0):null===e&&(t.drag=t.resize=t.gesture=null,t.endOnly=!1),this)},V.pointerMoveTolerance=function(e){return s(e)?(vt.pointerMoveTolerance=e,this):vt.pointerMoveTolerance},V.maxInteractions=function(e){return s(e)?(St=e,this):St},ht?(tt=ht===window.MSPointerEvent?{up:"MSPointerUp",down:"MSPointerDown",over:"mouseover",out:"mouseout",move:"MSPointerMove",cancel:"MSPointerCancel"}:{up:"pointerup",down:"pointerdown",over:"pointerover",out:"pointerout",move:"pointermove",cancel:"pointercancel"},_t.add(Xt,tt.up,It.collectTaps),_t.add(Xt,tt.move,It.recordPointer),_t.add(Xt,tt.down,It.selectorDown),_t.add(Xt,tt.move,It.pointerMove),_t.add(Xt,tt.up,It.pointerUp),_t.add(Xt,tt.over,It.pointerOver),_t.add(Xt,tt.out,It.pointerOut),_t.add(Xt,tt.up,It.removePointer),_t.add(Xt,tt.cancel,It.removePointer),_t.add(Xt,tt.move,mt.edgeMove)):(_t.add(Xt,"mouseup",It.collectTaps),_t.add(Xt,"touchend",It.collectTaps),_t.add(Xt,"mousemove",It.recordPointer),_t.add(Xt,"touchmove",It.recordPointer),_t.add(Xt,"mousedown",It.selectorDown),_t.add(Xt,"mousemove",It.pointerMove),_t.add(Xt,"mouseup",It.pointerUp),_t.add(Xt,"mouseover",It.pointerOver),_t.add(Xt,"mouseout",It.pointerOut),_t.add(Xt,"touchstart",It.selectorDown),_t.add(Xt,"touchmove",It.pointerMove),_t.add(Xt,"touchend",It.pointerUp),_t.add(Xt,"touchcancel",It.pointerUp),_t.add(Xt,"touchend",It.removePointer),_t.add(Xt,"touchcancel",It.removePointer),_t.add(Xt,"mousemove",mt.edgeMove),_t.add(Xt,"touchmove",mt.edgeMove)),_t.add(Rt,"blur",J);try{window.frameElement&&(kt._element=window.frameElement.ownerDocument,_t.add(kt,"mouseup",It.pointerUp),_t.add(kt,"touchend",It.pointerUp),_t.add(kt,"touchcancel",It.pointerUp),_t.add(kt,"pointerup",It.pointerUp),_t.add(kt,"MSPointerUp",It.pointerUp),_t.add(Yt,"blur",J))}catch(qt){V.windowParentError=qt}_t.useAttachEvent&&_t.add(Xt,"selectstart",function(e){var t=ct[0];t.currentAction()&&t.checkAndPreventDefault(e)}),Mt in Element.prototype&&n(Element.prototype[Mt])||(it=function(e,t,i){i=i||e.parentNode.querySelectorAll(t);for(var r=0,n=i.length;n>r;r++)if(i[r]===e)return!0;return!1}),function(){for(var e=0,t=["ms","moz","webkit","o"],i=0;i +This package is an internal part of interactjs and is not meant +to be used independently as each update may introduce breaking changes + diff --git a/packages/@interactjs/actions/actions.spec.ts b/packages/@interactjs/actions/actions.spec.ts new file mode 100644 index 000000000..111b791f7 --- /dev/null +++ b/packages/@interactjs/actions/actions.spec.ts @@ -0,0 +1,52 @@ +import type { Scope } from '@interactjs/core/scope' +import * as helpers from '@interactjs/core/tests/_helpers' +import type { ActionName } from '@interactjs/core/types' +import * as pointerUtils from '@interactjs/utils/pointerUtils' + +import actions from './plugin' + +describe('actions integration', () => { + const scope: Scope = helpers.mockScope() + const event = pointerUtils.coordsToEvent(pointerUtils.newCoords()) + const element = scope.document.body + + scope.usePlugin(actions) + + const interactable = scope.interactables.new(element) + // make a dropzone + scope.interactables.new(scope.document.documentElement).dropzone({}) + const interaction1 = scope.interactions.new({}) + + interaction1.pointerDown(event, event, element) + + for (const name in scope.actions.map) { + test(`${name} interaction starts and stops as expected`, () => { + interaction1.start({ name: name as ActionName }, interactable, element) + interaction1.stop() + + expect(() => { + interaction1.interacting() + }).not.toThrow() + + expect(interaction1.interacting()).toBe(false) + }) + } + + const actionNames = Object.keys(scope.actions.map) + + for (const order of [actionNames, [...actionNames].reverse()] as ActionName[][]) { + const interaction2 = scope.interactions.new({}) + + for (const name of order) { + test(`${name} interaction starts, moves and ends as expected`, () => { + expect(() => { + interaction2.start({ name }, interactable, element) + interaction2.pointerMove(event, event, element) + interaction2.pointerUp(event, event, element, element) + }).not.toThrow() + + expect(interaction2.interacting()).toBe(false) + }) + } + } +}) diff --git a/packages/@interactjs/actions/drag/drag.spec.ts b/packages/@interactjs/actions/drag/drag.spec.ts new file mode 100644 index 000000000..6c1a47904 --- /dev/null +++ b/packages/@interactjs/actions/drag/drag.spec.ts @@ -0,0 +1,144 @@ +import type { Interactable } from '@interactjs/core/Interactable' +import type { InteractEvent } from '@interactjs/core/InteractEvent' +import * as helpers from '@interactjs/core/tests/_helpers' +import extend from '@interactjs/utils/extend' +import * as pointerUtils from '@interactjs/utils/pointerUtils' + +import drag from './plugin' + +describe('actions/drag', () => { + test('drag action init', () => { + const scope = helpers.mockScope() + + scope.usePlugin(drag) + + expect(scope.actions.map.drag).toBeTruthy() + expect(scope.actions.methodDict.drag).toBe('draggable') + expect(typeof scope.Interactable.prototype.draggable).toBe('function') + }) + + describe('interactable.draggable method', () => { + const interactable = { + options: { + drag: {}, + }, + draggable: drag.draggable, + setPerAction: () => { + calledSetPerAction = true + }, + setOnEvents: () => { + calledSetOnEvents = true + }, + } as unknown as Interactable + let calledSetPerAction = false + let calledSetOnEvents = false + + test('args types', () => { + expect(interactable.draggable()).toEqual(interactable.options.drag) + + interactable.draggable(true) + expect(interactable.options.drag.enabled).toBe(true) + + interactable.draggable(false) + expect(interactable.options.drag.enabled).toBe(false) + + interactable.draggable({}) + expect(interactable.options.drag.enabled).toBe(true) + expect(calledSetOnEvents).toBe(true) + expect(calledSetPerAction).toBe(true) + + interactable.draggable({ enabled: false }) + expect(interactable.options.drag.enabled).toBe(false) + }) + + const axisSettings = { + lockAxis: ['x', 'y', 'xy', 'start'], + startAxis: ['x', 'y', 'xy'], + } + + for (const axis in axisSettings) { + for (const value of axisSettings[axis]) { + test(`\`${axis}: ${value}\` is set correctly`, () => { + interactable.draggable({ [axis]: value }) + expect(interactable.options.drag[axis]).toBe(value) + }) + } + } + }) + + describe('drag axis', () => { + const scope = helpers.mockScope() + + scope.usePlugin(drag) + + const interaction = scope.interactions.new({}) + const element = {} + const interactable = { + options: { + drag: {}, + }, + target: element, + } as Interactable + const iEvent = { page: {}, client: {}, delta: {}, type: 'dragmove' } as InteractEvent + + const opposites = { x: 'y', y: 'x' } + const eventCoords = { + page: { x: -1, y: -2 }, + client: { x: -3, y: -4 }, + delta: { x: -5, y: -6 }, + timeStamp: 0, + } + const coords = helpers.newCoordsSet() + + resetCoords() + interaction.prepared = { name: 'drag', axis: 'xy' } + interaction.interactable = interactable + + test('xy (any direction)', () => { + scope.fire('interactions:before-action-move', { interaction } as any) + + expect(interaction.coords.start).toEqual(coords.start) + expect(interaction.coords.delta).toEqual(coords.delta) + + scope.fire('interactions:action-move', { iEvent, interaction } as any) + + expect(iEvent.page).toEqual(eventCoords.page) + expect(iEvent.delta).toEqual(eventCoords.delta) + }) + + for (const axis in opposites) { + const opposite = opposites[axis] + + test(`${axis}-axis`, () => { + resetCoords() + interaction.prepared.axis = axis as any + + scope.fire('interactions:action-move', { iEvent, interaction } as any) + + expect(iEvent.delta).toEqual({ + [opposite]: 0, + [axis]: eventCoords.delta[axis], + }) + + expect(iEvent.page).toEqual({ + [opposite]: coords.start.page[opposite], + [axis]: eventCoords.page[axis], + }) + + expect(iEvent.page[axis]).toBe(eventCoords.page[axis]) + + expect(iEvent.client[opposite]).toBe(coords.start.client[opposite]) + expect(iEvent.client[axis]).toBe(eventCoords.client[axis]) + }) + } + + function resetCoords() { + pointerUtils.copyCoords(iEvent, eventCoords) + extend(iEvent.delta, eventCoords.delta) + + for (const prop in coords) { + pointerUtils.copyCoords(interaction.coords[prop], coords[prop]) + } + } + }) +}) diff --git a/packages/@interactjs/actions/drag/plugin.ts b/packages/@interactjs/actions/drag/plugin.ts new file mode 100644 index 000000000..0b4342e37 --- /dev/null +++ b/packages/@interactjs/actions/drag/plugin.ts @@ -0,0 +1,193 @@ +import type { Interactable } from '@interactjs/core/Interactable' +import type { InteractEvent } from '@interactjs/core/InteractEvent' +import type { PerActionDefaults } from '@interactjs/core/options' +import type { Scope, Plugin } from '@interactjs/core/scope' +import type { ListenersArg, OrBoolean } from '@interactjs/core/types' +import is from '@interactjs/utils/is' + +declare module '@interactjs/core/Interactable' { + interface Interactable { + draggable(options: Partial> | boolean): this + draggable(): DraggableOptions + /** + * ```js + * interact(element).draggable({ + * onstart: function (event) {}, + * onmove : function (event) {}, + * onend : function (event) {}, + * + * // the axis in which the first movement must be + * // for the drag sequence to start + * // 'xy' by default - any direction + * startAxis: 'x' || 'y' || 'xy', + * + * // 'xy' by default - don't restrict to one axis (move in any direction) + * // 'x' or 'y' to restrict movement to either axis + * // 'start' to restrict movement to the axis the drag started in + * lockAxis: 'x' || 'y' || 'xy' || 'start', + * + * // max number of drags that can happen concurrently + * // with elements of this Interactable. Infinity by default + * max: Infinity, + * + * // max number of drags that can target the same element+Interactable + * // 1 by default + * maxPerElement: 2 + * }) + * + * var isDraggable = interact('element').draggable(); // true + * ``` + * + * Get or set whether drag actions can be performed on the target + * + * @param options - true/false or An object with event + * listeners to be fired on drag events (object makes the Interactable + * draggable) + */ + draggable(options?: Partial> | boolean): this | DraggableOptions + } +} + +declare module '@interactjs/core/options' { + interface ActionDefaults { + drag: DraggableOptions + } +} + +declare module '@interactjs/core/types' { + interface ActionMap { + drag?: typeof drag + } +} + +export type DragEvent = InteractEvent<'drag'> + +export interface DraggableOptions extends PerActionDefaults { + startAxis?: 'x' | 'y' | 'xy' + lockAxis?: 'x' | 'y' | 'xy' | 'start' + oninertiastart?: ListenersArg + onstart?: ListenersArg + onmove?: ListenersArg + onend?: ListenersArg +} + +function install(scope: Scope) { + const { actions, Interactable, defaults } = scope + + Interactable.prototype.draggable = drag.draggable + + actions.map.drag = drag + actions.methodDict.drag = 'draggable' + + defaults.actions.drag = drag.defaults +} + +function beforeMove({ interaction }) { + if (interaction.prepared.name !== 'drag') return + + const axis = interaction.prepared.axis + + if (axis === 'x') { + interaction.coords.cur.page.y = interaction.coords.start.page.y + interaction.coords.cur.client.y = interaction.coords.start.client.y + + interaction.coords.velocity.client.y = 0 + interaction.coords.velocity.page.y = 0 + } else if (axis === 'y') { + interaction.coords.cur.page.x = interaction.coords.start.page.x + interaction.coords.cur.client.x = interaction.coords.start.client.x + + interaction.coords.velocity.client.x = 0 + interaction.coords.velocity.page.x = 0 + } +} + +function move({ iEvent, interaction }) { + if (interaction.prepared.name !== 'drag') return + + const axis = interaction.prepared.axis + + if (axis === 'x' || axis === 'y') { + const opposite = axis === 'x' ? 'y' : 'x' + + iEvent.page[opposite] = interaction.coords.start.page[opposite] + iEvent.client[opposite] = interaction.coords.start.client[opposite] + iEvent.delta[opposite] = 0 + } +} + +const draggable: Interactable['draggable'] = function draggable( + this: Interactable, + options?: DraggableOptions | boolean, +): any { + if (is.object(options)) { + this.options.drag.enabled = options.enabled !== false + this.setPerAction('drag', options) + this.setOnEvents('drag', options) + + if (/^(xy|x|y|start)$/.test(options.lockAxis)) { + this.options.drag.lockAxis = options.lockAxis + } + if (/^(xy|x|y)$/.test(options.startAxis)) { + this.options.drag.startAxis = options.startAxis + } + + return this + } + + if (is.bool(options)) { + this.options.drag.enabled = options + + return this + } + + return this.options.drag as DraggableOptions +} + +const drag: Plugin = { + id: 'actions/drag', + install, + listeners: { + 'interactions:before-action-move': beforeMove, + 'interactions:action-resume': beforeMove, + + // dragmove + 'interactions:action-move': move, + 'auto-start:check': (arg) => { + const { interaction, interactable, buttons } = arg + const dragOptions = interactable.options.drag + + if ( + !(dragOptions && dragOptions.enabled) || + // check mouseButton setting if the pointer is down + (interaction.pointerIsDown && + /mouse|pointer/.test(interaction.pointerType) && + (buttons & interactable.options.drag.mouseButtons) === 0) + ) { + return undefined + } + + arg.action = { + name: 'drag', + axis: dragOptions.lockAxis === 'start' ? dragOptions.startAxis : dragOptions.lockAxis, + } + + return false + }, + }, + draggable, + beforeMove, + move, + defaults: { + startAxis: 'xy', + lockAxis: 'xy', + } as DraggableOptions, + + getCursor() { + return 'move' + }, + + filterEventType: (type: string) => type.search('drag') === 0, +} + +export default drag diff --git a/packages/@interactjs/actions/drop/DropEvent.spec.ts b/packages/@interactjs/actions/drop/DropEvent.spec.ts new file mode 100644 index 000000000..5da91670e --- /dev/null +++ b/packages/@interactjs/actions/drop/DropEvent.spec.ts @@ -0,0 +1,127 @@ +import type { InteractEvent } from '@interactjs/core/InteractEvent' +import extend from '@interactjs/utils/extend' + +import { DropEvent } from '../drop/DropEvent' + +const dz1: any = { + target: 'dz1', + fire: jest.fn(), +} +const dz2: any = { + target: 'dz2', + fire: jest.fn(), +} +const el1: any = Symbol('el1') +const el2: any = Symbol('el2') +const interactable: any = Symbol('interactable') +const dragElement: any = Symbol('drag-el') + +describe('DropEvent', () => { + describe('constructor', () => { + const interaction: any = { dropState: {} } + const dragEvent = Object.freeze({ + interactable, + _interaction: interaction, + target: dragElement, + timeStamp: 10, + }) as InteractEvent + + extend(interaction.dropState, { + activeDrops: [ + { dropzone: dz1, element: el1 }, + { dropzone: dz2, element: el2 }, + ], + cur: { dropzone: dz1, element: el1 }, + prev: { dropzone: dz2, element: el2 }, + events: {}, + }) + + test('dropmove target, dropzone, relatedTarget props', () => { + const dropmove = new DropEvent(interaction.dropState, dragEvent, 'dropmove') + + expect(dropmove.target).toBe(el1) + expect(dropmove.dropzone).toBe(dz1) + expect(dropmove.relatedTarget).toBe(dragElement) + }) + + test('dragleave target, dropzone, relatedTarget props', () => { + const dragleave = new DropEvent(interaction.dropState, dragEvent, 'dragleave') + + expect(dragleave.target).toBe(el2) + expect(dragleave.dropzone).toBe(dz2) + expect(dragleave.relatedTarget).toBe(dragElement) + }) + }) + + describe('reject', () => { + const interaction: any = { dropState: {} } + const dragEvent = Object.freeze({ + interactable, + _interaction: interaction, + target: dragElement, + timeStamp: 10, + }) as InteractEvent + + test('dropactivate.reject()', () => { + extend(interaction.dropState, { + activeDrops: [ + { dropzone: dz1, element: el1 }, + { dropzone: dz2, element: el2 }, + ], + cur: { dropzone: null, element: null }, + prev: { dropzone: null, element: null }, + events: {}, + }) + + const dropactivate = new DropEvent(interaction.dropState, dragEvent, 'dropactivate') + + dropactivate.dropzone = dz1 + dropactivate.target = el1 + dropactivate.reject() + + // immediate propagation stopped on reject + expect(dropactivate.propagationStopped && dropactivate.immediatePropagationStopped).toBe(true) + + // dropdeactivate is fired on rejected dropzone + expect(dz1.fire).toHaveBeenLastCalledWith(expect.objectContaining({ type: 'dropdeactivate' })) + + // activeDrop of rejected dropactivate event is removed + expect(interaction.dropState.activeDrops).toEqual([{ dropzone: dz2, element: el2 }]) + expect(interaction.dropState.cur).toEqual({ dropzone: null, element: null }) + }) + + test('dropmove.reject()', () => { + extend(interaction.dropState, { + cur: { dropzone: dz1, element: el1 }, + prev: { dropzone: null, element: null }, + events: {}, + }) + + const dropmove = new DropEvent(interaction.dropState, dragEvent, 'dropmove') + + dropmove.reject() + + // dropState.cur remains the same after rejecting non activate event, + expect(interaction.dropState.cur).toEqual({ dropzone: dz1, element: el1 }) + expect(interaction.dropState.rejected).toBe(true) + + // dragleave is fired on rejected dropzone + expect(dz1.fire).toHaveBeenLastCalledWith(expect.objectContaining({ type: 'dragleave' })) + }) + }) + + test('stop[Immediate]Propagation()', () => { + const dropEvent = new DropEvent({ cur: {} } as any, {} as any, 'dragmove') + + expect(dropEvent.propagationStopped || dropEvent.immediatePropagationStopped).toBe(false) + + dropEvent.stopPropagation() + expect(dropEvent.propagationStopped).toBe(true) + expect(dropEvent.immediatePropagationStopped).toBe(false) + + dropEvent.propagationStopped = false + + dropEvent.stopImmediatePropagation() + expect(dropEvent.propagationStopped && dropEvent.immediatePropagationStopped).toBe(true) + }) +}) diff --git a/packages/@interactjs/actions/drop/DropEvent.ts b/packages/@interactjs/actions/drop/DropEvent.ts new file mode 100644 index 000000000..c9e87cb20 --- /dev/null +++ b/packages/@interactjs/actions/drop/DropEvent.ts @@ -0,0 +1,90 @@ +import { BaseEvent } from '@interactjs/core/BaseEvent' +import type { Interactable } from '@interactjs/core/Interactable' +import type { InteractEvent } from '@interactjs/core/InteractEvent' +import type { Element } from '@interactjs/core/types' +import * as arr from '@interactjs/utils/arr' + +import type { DropState } from './plugin' + +export class DropEvent extends BaseEvent<'drag'> { + declare target: Element + dropzone: Interactable + dragEvent: InteractEvent<'drag'> + relatedTarget: Element + draggable: Interactable + propagationStopped = false + immediatePropagationStopped = false + + /** + * Class of events fired on dropzones during drags with acceptable targets. + */ + constructor(dropState: DropState, dragEvent: InteractEvent<'drag'>, type: string) { + super(dragEvent._interaction) + + const { element, dropzone } = type === 'dragleave' ? dropState.prev : dropState.cur + + this.type = type + this.target = element + this.currentTarget = element + this.dropzone = dropzone + this.dragEvent = dragEvent + this.relatedTarget = dragEvent.target + this.draggable = dragEvent.interactable + this.timeStamp = dragEvent.timeStamp + } + + /** + * If this is a `dropactivate` event, the dropzone element will be + * deactivated. + * + * If this is a `dragmove` or `dragenter`, a `dragleave` will be fired on the + * dropzone element and more. + */ + reject() { + const { dropState } = this._interaction + + if ( + this.type !== 'dropactivate' && + (!this.dropzone || dropState.cur.dropzone !== this.dropzone || dropState.cur.element !== this.target) + ) { + return + } + + dropState.prev.dropzone = this.dropzone + dropState.prev.element = this.target + + dropState.rejected = true + dropState.events.enter = null + + this.stopImmediatePropagation() + + if (this.type === 'dropactivate') { + const activeDrops = dropState.activeDrops + const index = arr.findIndex( + activeDrops, + ({ dropzone, element }) => dropzone === this.dropzone && element === this.target, + ) + + dropState.activeDrops.splice(index, 1) + + const deactivateEvent = new DropEvent(dropState, this.dragEvent, 'dropdeactivate') + + deactivateEvent.dropzone = this.dropzone + deactivateEvent.target = this.target + + this.dropzone.fire(deactivateEvent) + } else { + this.dropzone.fire(new DropEvent(dropState, this.dragEvent, 'dragleave')) + } + } + + preventDefault() {} + + stopPropagation() { + this.propagationStopped = true + } + + stopImmediatePropagation() { + this.immediatePropagationStopped = this.propagationStopped = true + } +} diff --git a/packages/@interactjs/actions/drop/drop.spec.ts b/packages/@interactjs/actions/drop/drop.spec.ts new file mode 100644 index 000000000..4b17c6447 --- /dev/null +++ b/packages/@interactjs/actions/drop/drop.spec.ts @@ -0,0 +1,168 @@ +import type Interaction from '@interactjs/core/Interaction' +import * as helpers from '@interactjs/core/tests/_helpers' + +import drop from '../drop/plugin' + +describe('actions/drop', () => { + afterEach(() => { + document.body.innerHTML = '' + }) + + test('options', () => { + const { interactable } = helpers.testEnv({ plugins: [drop] }) + + const funcs = Object.freeze({ + drop() {}, + activate() {}, + deactivate() {}, + dropmove() {}, + dragenter() {}, + dragleave() {}, + }) + + interactable.dropzone({ + listeners: [funcs], + }) + + expect(interactable.events.types.drop[0]).toBe(funcs.drop) + expect(interactable.events.types.dropactivate[0]).toBe(funcs.activate) + expect(interactable.events.types.dropdeactivate[0]).toBe(funcs.deactivate) + expect(interactable.events.types.dropmove[0]).toBe(funcs.dropmove) + expect(interactable.events.types.dragenter[0]).toBe(funcs.dragenter) + expect(interactable.events.types.dragleave[0]).toBe(funcs.dragleave) + }) + + test('dynamicDrop', () => { + const { scope, interactable, down, start, move, interaction } = helpers.testEnv({ plugins: [drop] }) + interactable.draggable({}) + + // no error with dynamicDrop === false + expect(() => { + scope.interactStatic.dynamicDrop(false) + + down() + start({ name: 'drag' }) + move() + interaction.end() + }).not.toThrow() + + // no error with dynamicDrop === true + expect(() => { + scope.interactStatic.dynamicDrop(true) + down() + start({ name: 'drag' }) + move() + interaction.end() + }).not.toThrow() + }) + + test('start', () => { + const { scope, interactable, down, start, move, interaction } = helpers.testEnv({ plugins: [drop] }) + + interactable.draggable({}) + const dropzone = scope.interactables.new('[data-drop]').dropzone({}) + + const [dropEl1, dropEl2, dropEl3] = ['a', 'b', 'c'].map((id) => { + const dropEl = scope.document.createElement('div') + + dropEl.dataset.drop = id + scope.document.body.appendChild(dropEl) + + return dropEl + }) + + // rejet imeediately on activate + dropzone.on('dropactivate', (event) => { + if (event.target === dropEl1 || event.target === dropEl2) { + event.reject() + } + }) + + const onActionsDropStart = jest.fn((arg: { interaction: Interaction }) => { + const activeDrops = [...arg.interaction.dropState!.activeDrops] + // actions/drop:start is fired with all activeDrops + expect(activeDrops.map((activeDrop) => activeDrop.element)).toEqual([dropEl3]) + }) + + scope.addListeners({ 'actions/drop:start': onActionsDropStart }) + + const onDeactivate = jest.fn() + dropzone.on('dropdeactivate', onDeactivate) + + down() + start({ name: 'drag' }) + move() + + expect(onActionsDropStart).toHaveBeenCalledTimes(1) + + // rejected dropzones are removed from activeDrops + expect(interaction.dropState!.activeDrops.map((d) => d.element)).toEqual([dropEl3]) + + // rejected dropzones are deactivated + expect(onDeactivate.mock.calls.map((arg) => arg[0].target)).toEqual([dropEl1, dropEl2]) + + interaction.end() + }) + + test('targeting', () => { + const interactionTarget = document.body.appendChild(document.createElement('div')) + interactionTarget.id = 'target' + const { scope, interactable, down, start, move, up, coords } = helpers.testEnv({ + plugins: [drop], + target: interactionTarget, + }) + + interactable.draggable({}) + + const [dropElA, dropElB, dropElC] = ['a', 'b', 'c'].map((id) => { + const dropEl = scope.document.createElement('div') + + dropEl.dataset.drop = id + scope.document.body.appendChild(dropEl) + + return dropEl + }) + + const onActivate = jest.fn((event) => event.target) + const onDeactivate = jest.fn((event) => event.target) + const onDragenter = jest.fn() + const dropzone = scope.interactables + .new('[data-drop]') + .dropzone({ + checker: () => true, + }) + .on({ dropactivate: onActivate, dropdeactivate: onDeactivate, dragenter: onDragenter }) + + down() + start({ name: 'drag' }) + expect(onActivate.mock.results.map(({ value }) => value)).toEqual([dropElA, dropElB, dropElC]) + expect(onDeactivate.mock.calls).toEqual([]) + expect(onDragenter.mock.calls).toEqual([]) + + coords.page.x++ + move() + + expect(onDragenter.mock.calls.map(([{ target, relatedTarget }]) => [target, relatedTarget])).toEqual([ + [dropElC, interactionTarget], + ]) + + onDragenter.mockClear() + + // only b drop + dropzone.dropzone({ + checker: (_dragEvent, _event, _dropped, _dropzone, dropElement) => dropElement.dataset.drop === 'b', + }) + + coords.page.x++ + move() + + expect(onDragenter.mock.calls.map((args) => [args[0].target, args[0].relatedTarget])).toEqual([ + [dropElB, interactionTarget], + ]) + + up() + + // all dropzones are deactivated + expect(onDeactivate.mock.results.map(({ value }) => value)).toEqual([dropElA, dropElB, dropElC]) + }) +}) diff --git a/packages/@interactjs/actions/drop/plugin.ts b/packages/@interactjs/actions/drop/plugin.ts new file mode 100644 index 000000000..90a9c34e2 --- /dev/null +++ b/packages/@interactjs/actions/drop/plugin.ts @@ -0,0 +1,716 @@ +import type { Interactable } from '@interactjs/core/Interactable' +import type { EventPhase, InteractEvent } from '@interactjs/core/InteractEvent' +import type { Interaction, DoPhaseArg } from '@interactjs/core/Interaction' +import type { PerActionDefaults } from '@interactjs/core/options' +import type { Scope, Plugin } from '@interactjs/core/scope' +import type { Element, PointerEventType, Rect, ListenersArg } from '@interactjs/core/types' +import * as domUtils from '@interactjs/utils/domUtils' +import extend from '@interactjs/utils/extend' +import getOriginXY from '@interactjs/utils/getOriginXY' +import is from '@interactjs/utils/is' +import normalizeListeners from '@interactjs/utils/normalizeListeners' +import * as pointerUtils from '@interactjs/utils/pointerUtils' + +/* eslint-disable import/no-duplicates -- for typescript module augmentations */ +import '../drag/plugin' + +import type { DragEvent } from '../drag/plugin' +import drag from '../drag/plugin' +/* eslint-enable import/no-duplicates */ + +import { DropEvent } from './DropEvent' + +export type DropFunctionChecker = ( + dragEvent: any, // related drag operation + event: any, // touch or mouse EventEmitter + dropped: boolean, // default checker result + dropzone: Interactable, // dropzone interactable + dropElement: Element, // drop zone element + draggable: Interactable, // draggable's Interactable + draggableElement: Element, // dragged element +) => boolean + +export interface DropzoneOptions extends PerActionDefaults { + accept?: + | string + | Element + | (({ dropzone, draggableElement }: { dropzone: Interactable; draggableElement: Element }) => boolean) + // How the overlap is checked on the drop zone + overlap?: 'pointer' | 'center' | number + checker?: DropFunctionChecker + + ondropactivate?: ListenersArg + ondropdeactivate?: ListenersArg + ondragenter?: ListenersArg + ondragleave?: ListenersArg + ondropmove?: ListenersArg + ondrop?: ListenersArg +} + +export interface DropzoneMethod { + (this: Interactable, options: DropzoneOptions | boolean): Interactable + (): DropzoneOptions +} + +declare module '@interactjs/core/Interactable' { + interface Interactable { + /** + * + * ```js + * interact('.drop').dropzone({ + * accept: '.can-drop' || document.getElementById('single-drop'), + * overlap: 'pointer' || 'center' || zeroToOne + * } + * ``` + * + * Returns or sets whether draggables can be dropped onto this target to + * trigger drop events + * + * Dropzones can receive the following events: + * - `dropactivate` and `dropdeactivate` when an acceptable drag starts and ends + * - `dragenter` and `dragleave` when a draggable enters and leaves the dropzone + * - `dragmove` when a draggable that has entered the dropzone is moved + * - `drop` when a draggable is dropped into this dropzone + * + * Use the `accept` option to allow only elements that match the given CSS + * selector or element. The value can be: + * + * - **an Element** - only that element can be dropped into this dropzone. + * - **a string**, - the element being dragged must match it as a CSS selector. + * - **`null`** - accept options is cleared - it accepts any element. + * + * Use the `overlap` option to set how drops are checked for. The allowed + * values are: + * + * - `'pointer'`, the pointer must be over the dropzone (default) + * - `'center'`, the draggable element's center must be over the dropzone + * - a number from 0-1 which is the `(intersection area) / (draggable area)`. + * e.g. `0.5` for drop to happen when half of the area of the draggable is + * over the dropzone + * + * Use the `checker` option to specify a function to check if a dragged element + * is over this Interactable. + * + * @param options - The new options to be set + */ + dropzone(options: DropzoneOptions | boolean): Interactable + /** @returns The current setting */ + dropzone(): DropzoneOptions + + /** + * ```js + * interact(target) + * .dropChecker(function(dragEvent, // related dragmove or dragend event + * event, // TouchEvent/PointerEvent/MouseEvent + * dropped, // bool result of the default checker + * dropzone, // dropzone Interactable + * dropElement, // dropzone elemnt + * draggable, // draggable Interactable + * draggableElement) {// draggable element + * + * return dropped && event.target.hasAttribute('allow-drop') + * } + * ``` + */ + dropCheck( + dragEvent: InteractEvent, + event: PointerEventType, + draggable: Interactable, + draggableElement: Element, + dropElemen: Element, + rect: any, + ): boolean + } +} + +declare module '@interactjs/core/Interaction' { + interface Interaction { + dropState?: DropState + } +} + +declare module '@interactjs/core/InteractEvent' { + interface InteractEvent { + /** @internal */ + prevDropzone?: Interactable + dropzone?: Interactable + dragEnter?: Element + dragLeave?: Element + } +} + +declare module '@interactjs/core/options' { + interface ActionDefaults { + drop: DropzoneOptions + } +} + +declare module '@interactjs/core/scope' { + interface Scope { + dynamicDrop?: boolean + } + + interface SignalArgs { + 'actions/drop:start': DropSignalArg + 'actions/drop:move': DropSignalArg + 'actions/drop:end': DropSignalArg + } +} + +declare module '@interactjs/core/types' { + interface ActionMap { + drop?: typeof drop + } +} + +declare module '@interactjs/core/InteractStatic' { + interface InteractStatic { + /** + * Returns or sets whether the dimensions of dropzone elements are calculated + * on every dragmove or only on dragstart for the default dropChecker + * + * @param {boolean} [newValue] True to check on each move. False to check only + * before start + * @return {boolean | interact} The current setting or interact + */ + dynamicDrop: (newValue?: boolean) => boolean | this + } +} + +interface DropSignalArg { + interaction: Interaction<'drag'> + dragEvent: DragEvent +} + +export interface ActiveDrop { + dropzone: Interactable + element: Element + rect: Rect +} + +export interface DropState { + cur: { + // the dropzone a drag target might be dropped into + dropzone: Interactable + // the element at the time of checking + element: Element + } + prev: { + // the dropzone that was recently dragged away from + dropzone: Interactable + // the element at the time of checking + element: Element + } + // wheather the potential drop was rejected from a listener + rejected: boolean + // the drop events related to the current drag event + events: FiredDropEvents + activeDrops: ActiveDrop[] +} + +function install(scope: Scope) { + const { actions, interactStatic: interact, Interactable, defaults } = scope + + scope.usePlugin(drag) + + Interactable.prototype.dropzone = function (this: Interactable, options) { + return dropzoneMethod(this, options) + } as Interactable['dropzone'] + + Interactable.prototype.dropCheck = function ( + this: Interactable, + dragEvent, + event, + draggable, + draggableElement, + dropElement, + rect, + ) { + return dropCheckMethod(this, dragEvent, event, draggable, draggableElement, dropElement, rect) + } + + interact.dynamicDrop = function (newValue?: boolean) { + if (is.bool(newValue)) { + // if (dragging && scope.dynamicDrop !== newValue && !newValue) { + // calcRects(dropzones) + // } + + scope.dynamicDrop = newValue + + return interact + } + return scope.dynamicDrop! + } + + extend(actions.phaselessTypes, { + dragenter: true, + dragleave: true, + dropactivate: true, + dropdeactivate: true, + dropmove: true, + drop: true, + }) + actions.methodDict.drop = 'dropzone' + + scope.dynamicDrop = false + + defaults.actions.drop = drop.defaults +} + +function collectDropzones({ interactables }: Scope, draggableElement: Element) { + const drops: ActiveDrop[] = [] + + // collect all dropzones and their elements which qualify for a drop + for (const dropzone of interactables.list) { + if (!dropzone.options.drop.enabled) { + continue + } + + const accept = dropzone.options.drop.accept + + // test the draggable draggableElement against the dropzone's accept setting + if ( + (is.element(accept) && accept !== draggableElement) || + (is.string(accept) && !domUtils.matchesSelector(draggableElement, accept)) || + (is.func(accept) && !accept({ dropzone, draggableElement })) + ) { + continue + } + + for (const dropzoneElement of dropzone.getAllElements()) { + if (dropzoneElement !== draggableElement) { + drops.push({ + dropzone, + element: dropzoneElement, + rect: dropzone.getRect(dropzoneElement), + }) + } + } + } + + return drops +} + +function fireActivationEvents(activeDrops: ActiveDrop[], event: DropEvent) { + // loop through all active dropzones and trigger event + for (const { dropzone, element } of activeDrops.slice()) { + event.dropzone = dropzone + + // set current element as event target + event.target = element + dropzone.fire(event) + event.propagationStopped = event.immediatePropagationStopped = false + } +} + +// return a new array of possible drops. getActiveDrops should always be +// called when a drag has just started or a drag event happens while +// dynamicDrop is true +function getActiveDrops(scope: Scope, dragElement: Element) { + // get dropzones and their elements that could receive the draggable + const activeDrops = collectDropzones(scope, dragElement) + + for (const activeDrop of activeDrops) { + activeDrop.rect = activeDrop.dropzone.getRect(activeDrop.element) + } + + return activeDrops +} + +function getDrop( + { dropState, interactable: draggable, element: dragElement }: Interaction, + dragEvent, + pointerEvent, +) { + const validDrops: Element[] = [] + + // collect all dropzones and their elements which qualify for a drop + for (const { dropzone, element: dropzoneElement, rect } of dropState.activeDrops) { + const isValid = dropzone.dropCheck( + dragEvent, + pointerEvent, + draggable!, + dragElement!, + dropzoneElement, + rect, + ) + validDrops.push(isValid ? dropzoneElement : null) + } + + // get the most appropriate dropzone based on DOM depth and order + const dropIndex = domUtils.indexOfDeepestElement(validDrops) + + return dropState!.activeDrops[dropIndex] || null +} + +function getDropEvents(interaction: Interaction, _pointerEvent, dragEvent: DragEvent) { + const dropState = interaction.dropState! + const dropEvents: Record = { + enter: null, + leave: null, + activate: null, + deactivate: null, + move: null, + drop: null, + } + + if (dragEvent.type === 'dragstart') { + dropEvents.activate = new DropEvent(dropState, dragEvent, 'dropactivate') + + dropEvents.activate.target = null as never + dropEvents.activate.dropzone = null as never + } + if (dragEvent.type === 'dragend') { + dropEvents.deactivate = new DropEvent(dropState, dragEvent, 'dropdeactivate') + + dropEvents.deactivate.target = null as never + dropEvents.deactivate.dropzone = null as never + } + + if (dropState.rejected) { + return dropEvents + } + + if (dropState.cur.element !== dropState.prev.element) { + // if there was a previous dropzone, create a dragleave event + if (dropState.prev.dropzone) { + dropEvents.leave = new DropEvent(dropState, dragEvent, 'dragleave') + + dragEvent.dragLeave = dropEvents.leave.target = dropState.prev.element + dragEvent.prevDropzone = dropEvents.leave.dropzone = dropState.prev.dropzone + } + // if dropzone is not null, create a dragenter event + if (dropState.cur.dropzone) { + dropEvents.enter = new DropEvent(dropState, dragEvent, 'dragenter') + + dragEvent.dragEnter = dropState.cur.element + dragEvent.dropzone = dropState.cur.dropzone + } + } + + if (dragEvent.type === 'dragend' && dropState.cur.dropzone) { + dropEvents.drop = new DropEvent(dropState, dragEvent, 'drop') + + dragEvent.dropzone = dropState.cur.dropzone + dragEvent.relatedTarget = dropState.cur.element + } + if (dragEvent.type === 'dragmove' && dropState.cur.dropzone) { + dropEvents.move = new DropEvent(dropState, dragEvent, 'dropmove') + + dragEvent.dropzone = dropState.cur.dropzone + } + + return dropEvents +} + +type FiredDropEvents = Partial< + Record<'leave' | 'enter' | 'move' | 'drop' | 'activate' | 'deactivate', DropEvent> +> + +function fireDropEvents(interaction: Interaction, events: FiredDropEvents) { + const dropState = interaction.dropState! + const { activeDrops, cur, prev } = dropState + + if (events.leave) { + prev.dropzone.fire(events.leave) + } + if (events.enter) { + cur.dropzone.fire(events.enter) + } + if (events.move) { + cur.dropzone.fire(events.move) + } + if (events.drop) { + cur.dropzone.fire(events.drop) + } + + if (events.deactivate) { + fireActivationEvents(activeDrops, events.deactivate) + } + + dropState.prev.dropzone = cur.dropzone + dropState.prev.element = cur.element +} + +function onEventCreated({ interaction, iEvent, event }: DoPhaseArg<'drag', EventPhase>, scope: Scope) { + if (iEvent.type !== 'dragmove' && iEvent.type !== 'dragend') { + return + } + + const dropState = interaction.dropState! + + if (scope.dynamicDrop) { + dropState.activeDrops = getActiveDrops(scope, interaction.element!) + } + + const dragEvent = iEvent + const dropResult = getDrop(interaction, dragEvent, event) + + // update rejected status + dropState.rejected = + dropState.rejected && + !!dropResult && + dropResult.dropzone === dropState.cur.dropzone && + dropResult.element === dropState.cur.element + + dropState.cur.dropzone = dropResult && dropResult.dropzone + dropState.cur.element = dropResult && dropResult.element + + dropState.events = getDropEvents(interaction, event, dragEvent) +} + +function dropzoneMethod(interactable: Interactable): DropzoneOptions +function dropzoneMethod(interactable: Interactable, options: DropzoneOptions | boolean): Interactable +function dropzoneMethod(interactable: Interactable, options?: DropzoneOptions | boolean) { + if (is.object(options)) { + interactable.options.drop.enabled = options.enabled !== false + + if (options.listeners) { + const normalized = normalizeListeners(options.listeners) + // rename 'drop' to '' as it will be prefixed with 'drop' + const corrected = Object.keys(normalized).reduce((acc, type) => { + const correctedType = /^(enter|leave)/.test(type) + ? `drag${type}` + : /^(activate|deactivate|move)/.test(type) + ? `drop${type}` + : type + + acc[correctedType] = normalized[type] + + return acc + }, {}) + + const prevListeners = interactable.options.drop.listeners + prevListeners && interactable.off(prevListeners) + + interactable.on(corrected) + interactable.options.drop.listeners = corrected + } + + if (is.func(options.ondrop)) { + interactable.on('drop', options.ondrop) + } + if (is.func(options.ondropactivate)) { + interactable.on('dropactivate', options.ondropactivate) + } + if (is.func(options.ondropdeactivate)) { + interactable.on('dropdeactivate', options.ondropdeactivate) + } + if (is.func(options.ondragenter)) { + interactable.on('dragenter', options.ondragenter) + } + if (is.func(options.ondragleave)) { + interactable.on('dragleave', options.ondragleave) + } + if (is.func(options.ondropmove)) { + interactable.on('dropmove', options.ondropmove) + } + + if (/^(pointer|center)$/.test(options.overlap as string)) { + interactable.options.drop.overlap = options.overlap + } else if (is.number(options.overlap)) { + interactable.options.drop.overlap = Math.max(Math.min(1, options.overlap), 0) + } + if ('accept' in options) { + interactable.options.drop.accept = options.accept + } + if ('checker' in options) { + interactable.options.drop.checker = options.checker + } + + return interactable + } + + if (is.bool(options)) { + interactable.options.drop.enabled = options + + return interactable + } + + return interactable.options.drop +} + +function dropCheckMethod( + interactable: Interactable, + dragEvent: InteractEvent, + event: PointerEventType, + draggable: Interactable, + draggableElement: Element, + dropElement: Element, + rect: any, +) { + let dropped = false + + // if the dropzone has no rect (eg. display: none) + // call the custom dropChecker or just return false + if (!(rect = rect || interactable.getRect(dropElement))) { + return interactable.options.drop.checker + ? interactable.options.drop.checker( + dragEvent, + event, + dropped, + interactable, + dropElement, + draggable, + draggableElement, + ) + : false + } + + const dropOverlap = interactable.options.drop.overlap + + if (dropOverlap === 'pointer') { + const origin = getOriginXY(draggable, draggableElement, 'drag') + const page = pointerUtils.getPageXY(dragEvent) + + page.x += origin.x + page.y += origin.y + + const horizontal = page.x > rect.left && page.x < rect.right + const vertical = page.y > rect.top && page.y < rect.bottom + + dropped = horizontal && vertical + } + + const dragRect = draggable.getRect(draggableElement) + + if (dragRect && dropOverlap === 'center') { + const cx = dragRect.left + dragRect.width / 2 + const cy = dragRect.top + dragRect.height / 2 + + dropped = cx >= rect.left && cx <= rect.right && cy >= rect.top && cy <= rect.bottom + } + + if (dragRect && is.number(dropOverlap)) { + const overlapArea = + Math.max(0, Math.min(rect.right, dragRect.right) - Math.max(rect.left, dragRect.left)) * + Math.max(0, Math.min(rect.bottom, dragRect.bottom) - Math.max(rect.top, dragRect.top)) + + const overlapRatio = overlapArea / (dragRect.width * dragRect.height) + + dropped = overlapRatio >= dropOverlap + } + + if (interactable.options.drop.checker) { + dropped = interactable.options.drop.checker( + dragEvent, + event, + dropped, + interactable, + dropElement, + draggable, + draggableElement, + ) + } + + return dropped +} + +const drop: Plugin = { + id: 'actions/drop', + install, + listeners: { + 'interactions:before-action-start': ({ interaction }) => { + if (interaction.prepared.name !== 'drag') { + return + } + + interaction.dropState = { + cur: { + dropzone: null, + element: null, + }, + prev: { + dropzone: null, + element: null, + }, + rejected: null, + events: null, + activeDrops: [], + } + }, + + 'interactions:after-action-start': ( + { interaction, event, iEvent: dragEvent }: DoPhaseArg<'drag', EventPhase>, + scope, + ) => { + if (interaction.prepared.name !== 'drag') { + return + } + + const dropState = interaction.dropState! + + // reset active dropzones + dropState.activeDrops = [] + dropState.events = {} + dropState.activeDrops = getActiveDrops(scope, interaction.element!) + dropState.events = getDropEvents(interaction, event, dragEvent) + + if (dropState.events.activate) { + fireActivationEvents(dropState.activeDrops, dropState.events.activate) + scope.fire('actions/drop:start', { interaction, dragEvent }) + } + }, + + 'interactions:action-move': onEventCreated, + + 'interactions:after-action-move': ( + { interaction, iEvent: dragEvent }: DoPhaseArg<'drag', EventPhase>, + scope, + ) => { + if (interaction.prepared.name !== 'drag') { + return + } + + const dropState = interaction.dropState! + fireDropEvents(interaction, dropState.events) + + scope.fire('actions/drop:move', { interaction, dragEvent }) + dropState.events = {} + }, + + 'interactions:action-end': (arg: DoPhaseArg<'drag', EventPhase>, scope) => { + if (arg.interaction.prepared.name !== 'drag') { + return + } + + const { interaction, iEvent: dragEvent } = arg + + onEventCreated(arg, scope) + fireDropEvents(interaction, interaction.dropState!.events) + scope.fire('actions/drop:end', { interaction, dragEvent }) + }, + + 'interactions:stop': ({ interaction }) => { + if (interaction.prepared.name !== 'drag') { + return + } + + const { dropState } = interaction + + if (dropState) { + dropState.activeDrops = null as never + dropState.events = null as never + dropState.cur.dropzone = null as never + dropState.cur.element = null as never + dropState.prev.dropzone = null as never + dropState.prev.element = null as never + dropState.rejected = false + } + }, + }, + getActiveDrops, + getDrop, + getDropEvents, + fireDropEvents, + + filterEventType: (type: string) => type.search('drag') === 0 || type.search('drop') === 0, + + defaults: { + enabled: false, + accept: null as never, + overlap: 'pointer', + } as DropzoneOptions, +} + +export default drop diff --git a/packages/@interactjs/actions/gesture/gesture.spec.ts b/packages/@interactjs/actions/gesture/gesture.spec.ts new file mode 100644 index 000000000..97c4d0b1e --- /dev/null +++ b/packages/@interactjs/actions/gesture/gesture.spec.ts @@ -0,0 +1,202 @@ +import type { Scope } from '@interactjs/core/scope' +import * as helpers from '@interactjs/core/tests/_helpers' +import extend from '@interactjs/utils/extend' +import { coordsToEvent, newCoords } from '@interactjs/utils/pointerUtils' + +import type { GestureEvent } from './plugin' +import gesture from './plugin' + +function getGestureProps(event: GestureEvent) { + return helpers.getProps(event, ['type', 'angle', 'distance', 'scale', 'ds', 'da']) +} + +describe('actions/gesture', () => { + test('action init', () => { + const scope: Scope = helpers.mockScope() + + scope.usePlugin(gesture) + + expect(scope.actions.map.gesture).toBeTruthy() + expect(scope.actions.methodDict.gesture).toBe('gesturable') + expect(scope.Interactable.prototype.gesturable).toBeInstanceOf(Function) + }) + + test('interactable.gesturable() method', () => { + const rect = Object.freeze({ top: 100, left: 200, bottom: 300, right: 400 }) + const { + scope, + interaction, + interactable, + target: element, + coords, + down, + start, + move, + } = helpers.testEnv({ + plugins: [gesture], + rect, + }) + const events: GestureEvent[] = [] + const event2 = coordsToEvent(newCoords()) + event2.coords.pointerId = 2 + + scope.usePlugin(gesture) + + interactable.rectChecker(() => ({ ...rect })) + interactable.gesturable(true) + interactable.on('gesturestart gesturemove gestureend', (event: GestureEvent) => { + events.push(event) + }) + interaction.pointerType = 'touch' + + // 0 โžก 1 + extend(coords.page, { x: 0, y: 0 }) + extend(event2.coords.page, { x: 100, y: 0 }) + + const checkArg = { + action: null, + interactable, + interaction, + element, + rect, + buttons: 0, + } + + down() + + scope.fire('auto-start:check', checkArg) + // not allowed with 1 pointer + expect(checkArg.action).toBeFalsy() + + interaction.pointerDown(event2, event2, element) + scope.fire('auto-start:check', checkArg) + // allowed with 2 pointers + expect(checkArg.action).toBeTruthy() + + start({ name: 'gesture' }) + + // start interaction properties are correct, + expect(interaction.gesture).toEqual({ + angle: 0, + distance: 100, + scale: 1, + startAngle: 0, + startDistance: 100, + }) + + // start event properties are correct, + expect(getGestureProps(events[0])).toEqual({ + type: 'gesturestart', + angle: 0, + distance: 100, + scale: 1, + ds: 0, + da: 0, + }) + + // 0 + // โฌ‡ + // 1 + extend(event2.coords.page, { x: 0, y: 50 }) + interaction.pointerMove(event2, event2, element) + + // move interaction properties are correct, + expect(interaction.gesture).toEqual({ + angle: 90, + distance: 50, + scale: 0.5, + startAngle: 0, + startDistance: 100, + }) + + // move event properties are correct, + expect(getGestureProps(events[1])).toEqual({ + type: 'gesturemove', + angle: 90, + distance: 50, + scale: 0.5, + ds: -0.5, + da: 90, + }) + + // 1 โฌ… 0 + extend(coords.page, { x: 50, y: 50 }) + move() + + // move interaction properties are correct, + expect(interaction.gesture).toEqual({ + angle: 180, + distance: 50, + scale: 0.5, + startAngle: 0, + startDistance: 100, + }) + + // move event properties are correct, + expect(getGestureProps(events[2])).toEqual({ + type: 'gesturemove', + angle: 180, + distance: 50, + scale: 0.5, + ds: 0, + da: 90, + }) + + interaction.pointerUp(event2, event2, element, element) + + // move interaction properties are correct, + expect(interaction.gesture).toEqual({ + angle: 180, + distance: 50, + scale: 0.5, + startAngle: 0, + startDistance: 100, + }) + + // end event properties are correct, + expect(getGestureProps(events[3])).toEqual({ + type: 'gestureend', + angle: 180, + distance: 50, + scale: 0.5, + ds: 0, + da: 0, + }) + + // 0 + // โฌ‡ + // 1 + interaction.pointerDown(event2, event2, element) + extend(coords.page, { x: 0, y: -150 }) + checkArg.action = null + scope.fire('auto-start:check', checkArg) + interaction.pointerMove(event2, event2, element) + + // not allowed with re-added second pointers + expect(checkArg.action).toBeTruthy() + + interaction.start({ name: 'gesture' }, interactable, element) + + // move interaction properties are correct, + expect(interaction.gesture).toEqual({ + angle: 90, + distance: 200, + scale: 1, + startAngle: 90, + startDistance: 200, + }) + + // second start event properties are correct, + expect(getGestureProps(events[4])).toEqual({ + type: 'gesturestart', + angle: 90, + distance: 200, + scale: 1, + ds: 0, + da: 0, + }) + + // correct number of events fired + expect(events).toHaveLength(5) + }) +}) diff --git a/packages/@interactjs/actions/gesture/plugin.ts b/packages/@interactjs/actions/gesture/plugin.ts new file mode 100644 index 000000000..efc8ad2e8 --- /dev/null +++ b/packages/@interactjs/actions/gesture/plugin.ts @@ -0,0 +1,206 @@ +import type { Interactable } from '@interactjs/core/Interactable' +import type { InteractEvent, EventPhase } from '@interactjs/core/InteractEvent' +import type { Interaction, DoPhaseArg } from '@interactjs/core/Interaction' +import type { PerActionDefaults } from '@interactjs/core/options' +import type { Scope, Plugin } from '@interactjs/core/scope' +import type { Rect, PointerType, ListenersArg, OrBoolean } from '@interactjs/core/types' +import is from '@interactjs/utils/is' +import * as pointerUtils from '@interactjs/utils/pointerUtils' + +declare module '@interactjs/core/Interaction' { + interface Interaction { + gesture?: { + angle: number // angle from first to second touch + distance: number + scale: number // gesture.distance / gesture.startDistance + startAngle: number // angle of line joining two touches + startDistance: number // distance between two touches of touchStart + } + } +} + +declare module '@interactjs/core/Interactable' { + interface Interactable { + gesturable(options: Partial> | boolean): this + gesturable(): GesturableOptions + /** + * ```js + * interact(element).gesturable({ + * onstart: function (event) {}, + * onmove : function (event) {}, + * onend : function (event) {}, + * + * // limit multiple gestures. + * // See the explanation in {@link Interactable.draggable} example + * max: Infinity, + * maxPerElement: 1, + * }) + * + * var isGestureable = interact(element).gesturable() + * ``` + * + * Gets or sets whether multitouch gestures can be performed on the target + * + * @param options - true/false or An object with event listeners to be fired on gesture events (makes the Interactable gesturable) + * @returns A boolean indicating if this can be the target of gesture events, or this Interactable + */ + gesturable(options?: Partial> | boolean): this | GesturableOptions + } +} + +declare module '@interactjs/core/options' { + interface ActionDefaults { + gesture: GesturableOptions + } +} + +declare module '@interactjs/core/types' { + interface ActionMap { + gesture?: typeof gesture + } +} + +export interface GesturableOptions extends PerActionDefaults { + onstart?: ListenersArg + onmove?: ListenersArg + onend?: ListenersArg +} + +export interface GestureEvent extends InteractEvent<'gesture'> { + distance: number + angle: number + da: number // angle change + scale: number // ratio of distance start to current event + ds: number // scale change + box: Rect // enclosing box of all points + touches: PointerType[] +} + +export interface GestureSignalArg extends DoPhaseArg<'gesture', EventPhase> { + iEvent: GestureEvent + interaction: Interaction<'gesture'> +} + +function install(scope: Scope) { + const { actions, Interactable, defaults } = scope + + Interactable.prototype.gesturable = function ( + this: InstanceType, + options: GesturableOptions | boolean, + ) { + if (is.object(options)) { + this.options.gesture.enabled = options.enabled !== false + this.setPerAction('gesture', options) + this.setOnEvents('gesture', options) + + return this + } + + if (is.bool(options)) { + this.options.gesture.enabled = options + + return this + } + + return this.options.gesture as GesturableOptions + } as Interactable['gesturable'] + + actions.map.gesture = gesture + actions.methodDict.gesture = 'gesturable' + + defaults.actions.gesture = gesture.defaults +} + +function updateGestureProps({ interaction, iEvent, phase }: GestureSignalArg) { + if (interaction.prepared.name !== 'gesture') return + + const pointers = interaction.pointers.map((p) => p.pointer) + const starting = phase === 'start' + const ending = phase === 'end' + const deltaSource = interaction.interactable.options.deltaSource + + iEvent.touches = [pointers[0], pointers[1]] + + if (starting) { + iEvent.distance = pointerUtils.touchDistance(pointers, deltaSource) + iEvent.box = pointerUtils.touchBBox(pointers) + iEvent.scale = 1 + iEvent.ds = 0 + iEvent.angle = pointerUtils.touchAngle(pointers, deltaSource) + iEvent.da = 0 + + interaction.gesture.startDistance = iEvent.distance + interaction.gesture.startAngle = iEvent.angle + } else if (ending || interaction.pointers.length < 2) { + const prevEvent = interaction.prevEvent as GestureEvent + + iEvent.distance = prevEvent.distance + iEvent.box = prevEvent.box + iEvent.scale = prevEvent.scale + iEvent.ds = 0 + iEvent.angle = prevEvent.angle + iEvent.da = 0 + } else { + iEvent.distance = pointerUtils.touchDistance(pointers, deltaSource) + iEvent.box = pointerUtils.touchBBox(pointers) + iEvent.scale = iEvent.distance / interaction.gesture.startDistance + iEvent.angle = pointerUtils.touchAngle(pointers, deltaSource) + + iEvent.ds = iEvent.scale - interaction.gesture.scale + iEvent.da = iEvent.angle - interaction.gesture.angle + } + + interaction.gesture.distance = iEvent.distance + interaction.gesture.angle = iEvent.angle + + if (is.number(iEvent.scale) && iEvent.scale !== Infinity && !isNaN(iEvent.scale)) { + interaction.gesture.scale = iEvent.scale + } +} + +const gesture: Plugin = { + id: 'actions/gesture', + before: ['actions/drag', 'actions/resize'], + install, + listeners: { + 'interactions:action-start': updateGestureProps, + 'interactions:action-move': updateGestureProps, + 'interactions:action-end': updateGestureProps, + + 'interactions:new': ({ interaction }) => { + interaction.gesture = { + angle: 0, + distance: 0, + scale: 1, + startAngle: 0, + startDistance: 0, + } + }, + + 'auto-start:check': (arg) => { + if (arg.interaction.pointers.length < 2) { + return undefined + } + + const gestureOptions = arg.interactable.options.gesture + + if (!(gestureOptions && gestureOptions.enabled)) { + return undefined + } + + arg.action = { name: 'gesture' } + + return false + }, + }, + + defaults: {}, + + getCursor() { + return '' + }, + + filterEventType: (type: string) => type.search('gesture') === 0, +} + +export default gesture diff --git a/packages/@interactjs/actions/package.json b/packages/@interactjs/actions/package.json new file mode 100644 index 000000000..9cd1f1e6b --- /dev/null +++ b/packages/@interactjs/actions/package.json @@ -0,0 +1,27 @@ +{ + "name": "@interactjs/actions", + "version": "1.10.27", + "main": "index", + "module": "index", + "type": "module", + "repository": { + "type": "git", + "url": "https://github.com/taye/interact.js.git", + "directory": "packages/@interactjs/actions" + }, + "peerDependencies": { + "@interactjs/core": "1.10.27", + "@interactjs/utils": "1.10.27" + }, + "optionalDependencies": { + "@interactjs/interact": "1.10.27" + }, + "publishConfig": { + "access": "public" + }, + "sideEffects": [ + "**/index.js", + "**/index.prod.js" + ], + "license": "MIT" +} diff --git a/packages/@interactjs/actions/plugin.ts b/packages/@interactjs/actions/plugin.ts new file mode 100644 index 000000000..2e4805b2f --- /dev/null +++ b/packages/@interactjs/actions/plugin.ts @@ -0,0 +1,23 @@ +import type { Scope } from '@interactjs/core/scope' + +/* eslint-disable import/no-duplicates -- for typescript module augmentations */ +import './drag/plugin' +import './drop/plugin' +import './gesture/plugin' +import './resize/plugin' + +import drag from './drag/plugin' +import drop from './drop/plugin' +import gesture from './gesture/plugin' +import resize from './resize/plugin' +/* eslint-enable import/no-duplicates */ + +export default { + id: 'actions', + install(scope: Scope) { + scope.usePlugin(gesture) + scope.usePlugin(resize) + scope.usePlugin(drag) + scope.usePlugin(drop) + }, +} diff --git a/packages/@interactjs/actions/resize/plugin.ts b/packages/@interactjs/actions/resize/plugin.ts new file mode 100644 index 000000000..83c6f60e1 --- /dev/null +++ b/packages/@interactjs/actions/resize/plugin.ts @@ -0,0 +1,522 @@ +import type { Interactable } from '@interactjs/core/Interactable' +import type { EventPhase, InteractEvent } from '@interactjs/core/InteractEvent' +import type { Interaction } from '@interactjs/core/Interaction' +import type { PerActionDefaults } from '@interactjs/core/options' +import type { Scope, Plugin } from '@interactjs/core/scope' +import type { + ActionName, + ActionProps, + EdgeOptions, + FullRect, + ListenersArg, + OrBoolean, + Point, + Rect, +} from '@interactjs/core/types' +import * as dom from '@interactjs/utils/domUtils' +import extend from '@interactjs/utils/extend' +import is from '@interactjs/utils/is' + +export type EdgeName = 'top' | 'left' | 'bottom' | 'right' + +declare module '@interactjs/core/Interactable' { + interface Interactable { + resizable(): ResizableOptions + resizable(options: Partial> | boolean): this + /** + * ```js + * interact(element).resizable({ + * onstart: function (event) {}, + * onmove : function (event) {}, + * onend : function (event) {}, + * + * edges: { + * top : true, // Use pointer coords to check for resize. + * left : false, // Disable resizing from left edge. + * bottom: '.resize-s',// Resize if pointer target matches selector + * right : handleEl // Resize if pointer target is the given Element + * }, + * + * // Width and height can be adjusted independently. When `true`, width and + * // height are adjusted at a 1:1 ratio. + * square: false, + * + * // Width and height can be adjusted independently. When `true`, width and + * // height maintain the aspect ratio they had when resizing started. + * preserveAspectRatio: false, + * + * // a value of 'none' will limit the resize rect to a minimum of 0x0 + * // 'negate' will allow the rect to have negative width/height + * // 'reposition' will keep the width/height positive by swapping + * // the top and bottom edges and/or swapping the left and right edges + * invert: 'none' || 'negate' || 'reposition' + * + * // limit multiple resizes. + * // See the explanation in the {@link Interactable.draggable} example + * max: Infinity, + * maxPerElement: 1, + * }) + * + * var isResizeable = interact(element).resizable() + * ``` + * + * Gets or sets whether resize actions can be performed on the target + * + * @param options - true/false or An object with event + * listeners to be fired on resize events (object makes the Interactable + * resizable) + * @returns A boolean indicating if this can be the + * target of resize elements, or this Interactable + */ + resizable(options?: Partial> | boolean): this | ResizableOptions + } +} + +declare module '@interactjs/core/Interaction' { + interface Interaction { + resizeAxes: 'x' | 'y' | 'xy' + styleCursor(newValue: boolean): this + styleCursor(): boolean + resizeStartAspectRatio: number + } +} + +declare module '@interactjs/core/options' { + interface ActionDefaults { + resize: ResizableOptions + } +} + +declare module '@interactjs/core/types' { + interface ActionMap { + resize?: typeof resize + } +} + +export interface ResizableOptions extends PerActionDefaults { + square?: boolean + preserveAspectRatio?: boolean + edges?: EdgeOptions | null + axis?: 'x' | 'y' | 'xy' // deprecated + invert?: 'none' | 'negate' | 'reposition' + margin?: number + squareResize?: boolean + oninertiastart?: ListenersArg + onstart?: ListenersArg + onmove?: ListenersArg + onend?: ListenersArg +} + +export interface ResizeEvent

extends InteractEvent<'resize', P> { + deltaRect?: FullRect + edges?: ActionProps['edges'] +} + +function install(scope: Scope) { + const { + actions, + browser, + Interactable, // tslint:disable-line no-shadowed-variable + defaults, + } = scope + + // Less Precision with touch input + + resize.cursors = initCursors(browser) + resize.defaultMargin = browser.supportsTouch || browser.supportsPointerEvent ? 20 : 10 + + Interactable.prototype.resizable = function (this: Interactable, options: ResizableOptions | boolean) { + return resizable(this, options, scope) + } as Interactable['resizable'] + + actions.map.resize = resize + actions.methodDict.resize = 'resizable' + + defaults.actions.resize = resize.defaults +} + +function resizeChecker(arg) { + const { interaction, interactable, element, rect, buttons } = arg + + if (!rect) { + return undefined + } + + const page = extend({}, interaction.coords.cur.page) + const resizeOptions = interactable.options.resize + + if ( + !(resizeOptions && resizeOptions.enabled) || + // check mouseButton setting if the pointer is down + (interaction.pointerIsDown && + /mouse|pointer/.test(interaction.pointerType) && + (buttons & resizeOptions.mouseButtons) === 0) + ) { + return undefined + } + + // if using resize.edges + if (is.object(resizeOptions.edges)) { + const resizeEdges = { + left: false, + right: false, + top: false, + bottom: false, + } + + for (const edge in resizeEdges) { + resizeEdges[edge] = checkResizeEdge( + edge, + resizeOptions.edges[edge], + page, + interaction._latestPointer.eventTarget, + element, + rect, + resizeOptions.margin || resize.defaultMargin, + ) + } + + resizeEdges.left = resizeEdges.left && !resizeEdges.right + resizeEdges.top = resizeEdges.top && !resizeEdges.bottom + + if (resizeEdges.left || resizeEdges.right || resizeEdges.top || resizeEdges.bottom) { + arg.action = { + name: 'resize', + edges: resizeEdges, + } + } + } else { + const right = resizeOptions.axis !== 'y' && page.x > rect.right - resize.defaultMargin + const bottom = resizeOptions.axis !== 'x' && page.y > rect.bottom - resize.defaultMargin + + if (right || bottom) { + arg.action = { + name: 'resize', + axes: (right ? 'x' : '') + (bottom ? 'y' : ''), + } + } + } + + return arg.action ? false : undefined +} + +function resizable(interactable: Interactable, options: OrBoolean | boolean, scope: Scope) { + if (is.object(options)) { + interactable.options.resize.enabled = options.enabled !== false + interactable.setPerAction('resize', options) + interactable.setOnEvents('resize', options) + + if (is.string(options.axis) && /^x$|^y$|^xy$/.test(options.axis)) { + interactable.options.resize.axis = options.axis + } else if (options.axis === null) { + interactable.options.resize.axis = scope.defaults.actions.resize.axis + } + + if (is.bool(options.preserveAspectRatio)) { + interactable.options.resize.preserveAspectRatio = options.preserveAspectRatio + } else if (is.bool(options.square)) { + interactable.options.resize.square = options.square + } + + return interactable + } + if (is.bool(options)) { + interactable.options.resize.enabled = options + + return interactable + } + return interactable.options.resize +} + +function checkResizeEdge( + name: string, + value: any, + page: Point, + element: Node, + interactableElement: Element, + rect: Rect, + margin: number, +) { + // false, '', undefined, null + if (!value) { + return false + } + + // true value, use pointer coords and element rect + if (value === true) { + // if dimensions are negative, "switch" edges + const width = is.number(rect.width) ? rect.width : rect.right - rect.left + const height = is.number(rect.height) ? rect.height : rect.bottom - rect.top + + // don't use margin greater than half the relevent dimension + margin = Math.min(margin, Math.abs((name === 'left' || name === 'right' ? width : height) / 2)) + + if (width < 0) { + if (name === 'left') { + name = 'right' + } else if (name === 'right') { + name = 'left' + } + } + if (height < 0) { + if (name === 'top') { + name = 'bottom' + } else if (name === 'bottom') { + name = 'top' + } + } + + if (name === 'left') { + const edge = width >= 0 ? rect.left : rect.right + return page.x < edge + margin + } + if (name === 'top') { + const edge = height >= 0 ? rect.top : rect.bottom + return page.y < edge + margin + } + + if (name === 'right') { + return page.x > (width >= 0 ? rect.right : rect.left) - margin + } + if (name === 'bottom') { + return page.y > (height >= 0 ? rect.bottom : rect.top) - margin + } + } + + // the remaining checks require an element + if (!is.element(element)) { + return false + } + + return is.element(value) + ? // the value is an element to use as a resize handle + value === element + : // otherwise check if element matches value as selector + dom.matchesUpTo(element, value, interactableElement) +} + +/* eslint-disable multiline-ternary */ +// eslint-disable-next-line @typescript-eslint/consistent-type-imports +function initCursors(browser: typeof import('@interactjs/utils/browser').default) { + return browser.isIe9 + ? { + x: 'e-resize', + y: 's-resize', + xy: 'se-resize', + + top: 'n-resize', + left: 'w-resize', + bottom: 's-resize', + right: 'e-resize', + topleft: 'se-resize', + bottomright: 'se-resize', + topright: 'ne-resize', + bottomleft: 'ne-resize', + } + : { + x: 'ew-resize', + y: 'ns-resize', + xy: 'nwse-resize', + + top: 'ns-resize', + left: 'ew-resize', + bottom: 'ns-resize', + right: 'ew-resize', + topleft: 'nwse-resize', + bottomright: 'nwse-resize', + topright: 'nesw-resize', + bottomleft: 'nesw-resize', + } +} +/* eslint-enable multiline-ternary */ + +function start({ iEvent, interaction }: { iEvent: InteractEvent; interaction: Interaction }) { + if (interaction.prepared.name !== 'resize' || !interaction.prepared.edges) { + return + } + + const resizeEvent = iEvent as ResizeEvent + const rect = interaction.rect + + interaction._rects = { + start: extend({}, rect), + corrected: extend({}, rect), + previous: extend({}, rect), + delta: { + left: 0, + right: 0, + width: 0, + top: 0, + bottom: 0, + height: 0, + }, + } + + resizeEvent.edges = interaction.prepared.edges + resizeEvent.rect = interaction._rects.corrected + resizeEvent.deltaRect = interaction._rects.delta +} + +function move({ iEvent, interaction }: { iEvent: InteractEvent; interaction: Interaction }) { + if (interaction.prepared.name !== 'resize' || !interaction.prepared.edges) return + + const resizeEvent = iEvent as ResizeEvent + const resizeOptions = interaction.interactable.options.resize + const invert = resizeOptions.invert + const invertible = invert === 'reposition' || invert === 'negate' + + const current = interaction.rect + const { start: startRect, corrected, delta: deltaRect, previous } = interaction._rects + + extend(previous, corrected) + + if (invertible) { + // if invertible, copy the current rect + extend(corrected, current) + + if (invert === 'reposition') { + // swap edge values if necessary to keep width/height positive + if (corrected.top > corrected.bottom) { + const swap = corrected.top + + corrected.top = corrected.bottom + corrected.bottom = swap + } + if (corrected.left > corrected.right) { + const swap = corrected.left + + corrected.left = corrected.right + corrected.right = swap + } + } + } else { + // if not invertible, restrict to minimum of 0x0 rect + corrected.top = Math.min(current.top, startRect.bottom) + corrected.bottom = Math.max(current.bottom, startRect.top) + corrected.left = Math.min(current.left, startRect.right) + corrected.right = Math.max(current.right, startRect.left) + } + + corrected.width = corrected.right - corrected.left + corrected.height = corrected.bottom - corrected.top + + for (const edge in corrected) { + deltaRect[edge] = corrected[edge] - previous[edge] + } + + resizeEvent.edges = interaction.prepared.edges + resizeEvent.rect = corrected + resizeEvent.deltaRect = deltaRect +} + +function end({ iEvent, interaction }: { iEvent: InteractEvent; interaction: Interaction }) { + if (interaction.prepared.name !== 'resize' || !interaction.prepared.edges) return + + const resizeEvent = iEvent as ResizeEvent + + resizeEvent.edges = interaction.prepared.edges + resizeEvent.rect = interaction._rects.corrected + resizeEvent.deltaRect = interaction._rects.delta +} + +function updateEventAxes({ + iEvent, + interaction, +}: { + iEvent: InteractEvent + interaction: Interaction +}) { + if (interaction.prepared.name !== 'resize' || !interaction.resizeAxes) return + + const options = interaction.interactable.options + const resizeEvent = iEvent as ResizeEvent + + if (options.resize.square) { + if (interaction.resizeAxes === 'y') { + resizeEvent.delta.x = resizeEvent.delta.y + } else { + resizeEvent.delta.y = resizeEvent.delta.x + } + resizeEvent.axes = 'xy' + } else { + resizeEvent.axes = interaction.resizeAxes + + if (interaction.resizeAxes === 'x') { + resizeEvent.delta.y = 0 + } else if (interaction.resizeAxes === 'y') { + resizeEvent.delta.x = 0 + } + } +} + +const resize: Plugin = { + id: 'actions/resize', + before: ['actions/drag'], + install, + listeners: { + 'interactions:new': ({ interaction }) => { + interaction.resizeAxes = 'xy' + }, + + 'interactions:action-start': (arg) => { + start(arg) + updateEventAxes(arg) + }, + 'interactions:action-move': (arg) => { + move(arg) + updateEventAxes(arg) + }, + 'interactions:action-end': end, + 'auto-start:check': resizeChecker, + }, + + defaults: { + square: false, + preserveAspectRatio: false, + axis: 'xy', + + // use default margin + margin: NaN, + + // object with props left, right, top, bottom which are + // true/false values to resize when the pointer is over that edge, + // CSS selectors to match the handles for each direction + // or the Elements for each handle + edges: null, + + // a value of 'none' will limit the resize rect to a minimum of 0x0 + // 'negate' will alow the rect to have negative width/height + // 'reposition' will keep the width/height positive by swapping + // the top and bottom edges and/or swapping the left and right edges + invert: 'none', + } as ResizableOptions, + + cursors: null as ReturnType, + + getCursor({ edges, axis, name }: ActionProps) { + const cursors = resize.cursors + let result: string = null + + if (axis) { + result = cursors[name + axis] + } else if (edges) { + let cursorKey = '' + + for (const edge of ['top', 'bottom', 'left', 'right']) { + if (edges[edge]) { + cursorKey += edge + } + } + + result = cursors[cursorKey] + } + + return result + }, + + filterEventType: (type: string) => type.search('resize') === 0, + + defaultMargin: null as number, +} + +export default resize diff --git a/packages/@interactjs/actions/resize/resize.spec.ts b/packages/@interactjs/actions/resize/resize.spec.ts new file mode 100644 index 000000000..fe5016eba --- /dev/null +++ b/packages/@interactjs/actions/resize/resize.spec.ts @@ -0,0 +1,143 @@ +import * as helpers from '@interactjs/core/tests/_helpers' + +import type { ResizeEvent } from './plugin' +import resize from './plugin' + +const { ltrbwh } = helpers + +describe('actions/resize', () => { + test('action init', () => { + const { scope } = helpers.testEnv({ + plugins: [resize], + }) + + expect(scope.actions.map.resize).toBeTruthy() + expect(scope.actions.methodDict.resize).toBe('resizable') + expect(scope.Interactable.prototype.resizable).toEqual(expect.any(Function)) + }) + + test('checker', () => { + const rect = Object.freeze({ left: 0, top: 0, right: 10, bottom: 10, width: 10, height: 10 }) + const { scope, interactable, interaction, coords, target, down, start, move } = helpers.testEnv({ + plugins: [resize], + rect, + }) + + const element = target as HTMLElement + const checkArg = { + action: null, + interactable, + interaction, + element, + rect, + buttons: 0, + } + + interactable.resizable({ + edges: { left: true, top: true, right: true, bottom: true }, + // use margin greater than width and height + margin: Infinity, + }) + + // resize top left + down() + scope.fire('auto-start:check', checkArg) + expect(checkArg.action).toEqual({ + name: 'resize', + edges: { left: true, top: true, right: false, bottom: false }, + }) + + // resize top right + coords.page.x = 10 + move() + + scope.fire('auto-start:check', checkArg) + expect(checkArg.action).toEqual({ + name: 'resize', + edges: { left: false, top: true, right: true, bottom: false }, + }) + + // resize bottom right + coords.page.y = 10 + move() + + scope.fire('auto-start:check', checkArg) + expect(checkArg.action).toEqual({ + name: 'resize', + edges: { left: false, top: false, right: true, bottom: true }, + }) + + const zeroRect = { left: 0, top: 0, right: 0, bottom: 0, width: 0, height: 0 } + let resizeEvent: ResizeEvent = null + + interactable.on('resizestart resizemove resizeend', (e) => { + resizeEvent = e + }) + + coords.page.x = rect.right + coords.page.y = rect.bottom + down() + start({ name: 'resize', edges: { bottom: true, right: true } }) + + // sets starting correct interaction._rects + expect(interaction._rects).toEqual({ + start: rect, + corrected: rect, + previous: rect, + delta: zeroRect, + }) + // sets starting correct interaction.rect + expect(interaction.rect).toEqual(rect) + // resizestart event has extra resize props + expect(hasResizeProps(resizeEvent)).toBe(true) + + coords.page.x = -100 + coords.page.y = -200 + resizeEvent = null + move() + + // `invert: 'none'` interaction._rects are correct + expect(interaction._rects).toEqual({ + start: rect, + corrected: zeroRect, + previous: rect, + delta: ltrbwh(0, 0, -rect.width, -rect.bottom, -rect.width, -rect.height), + }) + // `invert: 'none'` interaction.rect is correct + expect(interaction.rect).toEqual(ltrbwh(0, 0, -100, -200, -100, -200)) + // resizemove event has extra resize props + expect(hasResizeProps(resizeEvent)).toBe(true) + + interactable.options.resize.invert = 'reposition' + interaction.move() + + // `invert: 'reposition'` interaction._rects + expect(interaction._rects).toEqual({ + start: rect, + corrected: ltrbwh(-100, -200, 0, 0, 100, 200), + previous: interaction._rects.previous, // not testing previous + delta: ltrbwh(-100, -200, 0, 0, 100, 200), + }) + + interaction.move() + interactable.options.resize.invert = 'negate' + interaction.move() + + // invert: 'negate' interaction._rects + expect(interaction._rects).toEqual({ + start: rect, + corrected: ltrbwh(0, 0, -100, -200, -100, -200), + previous: interaction._rects.previous, // not testing previous + delta: ltrbwh(100, 200, -100, -200, -200, -400), + }) + + resizeEvent = null + interaction.end() + // resizeend event has extra resize props + expect(hasResizeProps(resizeEvent)).toBe(true) + }) +}) + +function hasResizeProps(event: ResizeEvent) { + return !!(event.deltaRect && event.rect && event.edges) +} diff --git a/packages/@interactjs/auto-scroll/README.md b/packages/@interactjs/auto-scroll/README.md new file mode 100644 index 000000000..549491910 --- /dev/null +++ b/packages/@interactjs/auto-scroll/README.md @@ -0,0 +1,5 @@ +

+This package is an internal part of interactjs and is not meant +to be used independently as each update may introduce breaking changes +

diff --git a/packages/@interactjs/auto-scroll/package.json b/packages/@interactjs/auto-scroll/package.json new file mode 100644 index 000000000..4716aa77f --- /dev/null +++ b/packages/@interactjs/auto-scroll/package.json @@ -0,0 +1,26 @@ +{ + "name": "@interactjs/auto-scroll", + "version": "1.10.27", + "main": "index", + "module": "index", + "type": "module", + "repository": { + "type": "git", + "url": "https://github.com/taye/interact.js.git", + "directory": "packages/@interactjs/auto-scroll" + }, + "peerDependencies": { + "@interactjs/utils": "1.10.27" + }, + "optionalDependencies": { + "@interactjs/interact": "1.10.27" + }, + "publishConfig": { + "access": "public" + }, + "sideEffects": [ + "**/index.js", + "**/index.prod.js" + ], + "license": "MIT" +} diff --git a/packages/@interactjs/auto-scroll/plugin.ts b/packages/@interactjs/auto-scroll/plugin.ts new file mode 100644 index 000000000..c89870695 --- /dev/null +++ b/packages/@interactjs/auto-scroll/plugin.ts @@ -0,0 +1,280 @@ +import type { Interactable } from '@interactjs/core/Interactable' +import type Interaction from '@interactjs/core/Interaction' +import type { Scope, Plugin } from '@interactjs/core/scope' +import type { ActionName, PointerType } from '@interactjs/core/types' +import * as domUtils from '@interactjs/utils/domUtils' +import is from '@interactjs/utils/is' +import raf from '@interactjs/utils/raf' +import { getStringOptionResult } from '@interactjs/utils/rect' +import { getWindow } from '@interactjs/utils/window' + +declare module '@interactjs/core/scope' { + interface Scope { + autoScroll: typeof autoScroll + } +} + +declare module '@interactjs/core/Interaction' { + interface Interaction { + autoScroll?: typeof autoScroll + } +} + +declare module '@interactjs/core/options' { + interface PerActionDefaults { + autoScroll?: AutoScrollOptions + } +} + +export interface AutoScrollOptions { + container?: Window | HTMLElement | string + margin?: number + distance?: number + interval?: number + speed?: number + enabled?: boolean +} + +function install(scope: Scope) { + const { defaults, actions } = scope + + scope.autoScroll = autoScroll + autoScroll.now = () => scope.now() + + actions.phaselessTypes.autoscroll = true + defaults.perAction.autoScroll = autoScroll.defaults +} + +const autoScroll = { + defaults: { + enabled: false, + margin: 60, + + // the item that is scrolled (Window or HTMLElement) + container: null as AutoScrollOptions['container'], + + // the scroll speed in pixels per second + speed: 300, + } as AutoScrollOptions, + + now: Date.now, + + interaction: null as Interaction | null, + i: 0, // the handle returned by window.setInterval + + // Direction each pulse is to scroll in + x: 0, + y: 0, + + isScrolling: false, + prevTime: 0, + margin: 0, + speed: 0, + + start(interaction: Interaction) { + autoScroll.isScrolling = true + raf.cancel(autoScroll.i) + + interaction.autoScroll = autoScroll + autoScroll.interaction = interaction + autoScroll.prevTime = autoScroll.now() + autoScroll.i = raf.request(autoScroll.scroll) + }, + + stop() { + autoScroll.isScrolling = false + if (autoScroll.interaction) { + autoScroll.interaction.autoScroll = null + } + raf.cancel(autoScroll.i) + }, + + // scroll the window by the values in scroll.x/y + scroll() { + const { interaction } = autoScroll + const { interactable, element } = interaction + const actionName = interaction.prepared.name + const options = interactable.options[actionName].autoScroll + const container = getContainer(options.container, interactable, element) + const now = autoScroll.now() + // change in time in seconds + const dt = (now - autoScroll.prevTime) / 1000 + // displacement + const s = options.speed * dt + + if (s >= 1) { + const scrollBy = { + x: autoScroll.x * s, + y: autoScroll.y * s, + } + + if (scrollBy.x || scrollBy.y) { + const prevScroll = getScroll(container) + + if (is.window(container)) { + container.scrollBy(scrollBy.x, scrollBy.y) + } else if (container) { + container.scrollLeft += scrollBy.x + container.scrollTop += scrollBy.y + } + + const curScroll = getScroll(container) + const delta = { + x: curScroll.x - prevScroll.x, + y: curScroll.y - prevScroll.y, + } + + if (delta.x || delta.y) { + interactable.fire({ + type: 'autoscroll', + target: element, + interactable, + delta, + interaction, + container, + }) + } + } + + autoScroll.prevTime = now + } + + if (autoScroll.isScrolling) { + raf.cancel(autoScroll.i) + autoScroll.i = raf.request(autoScroll.scroll) + } + }, + check(interactable: Interactable, actionName: ActionName) { + const options = interactable.options + + return options[actionName].autoScroll?.enabled + }, + onInteractionMove({ + interaction, + pointer, + }: { + interaction: Interaction + pointer: PointerType + }) { + if ( + !(interaction.interacting() && autoScroll.check(interaction.interactable, interaction.prepared.name)) + ) { + return + } + + if (interaction.simulation) { + autoScroll.x = autoScroll.y = 0 + return + } + + let top: boolean + let right: boolean + let bottom: boolean + let left: boolean + + const { interactable, element } = interaction + const actionName = interaction.prepared.name + const options = interactable.options[actionName].autoScroll + const container = getContainer(options.container, interactable, element) + + if (is.window(container)) { + left = pointer.clientX < autoScroll.margin + top = pointer.clientY < autoScroll.margin + right = pointer.clientX > container.innerWidth - autoScroll.margin + bottom = pointer.clientY > container.innerHeight - autoScroll.margin + } else { + const rect = domUtils.getElementClientRect(container) + + left = pointer.clientX < rect.left + autoScroll.margin + top = pointer.clientY < rect.top + autoScroll.margin + right = pointer.clientX > rect.right - autoScroll.margin + bottom = pointer.clientY > rect.bottom - autoScroll.margin + } + + autoScroll.x = right ? 1 : left ? -1 : 0 + autoScroll.y = bottom ? 1 : top ? -1 : 0 + + if (!autoScroll.isScrolling) { + // set the autoScroll properties to those of the target + autoScroll.margin = options.margin + autoScroll.speed = options.speed + + autoScroll.start(interaction) + } + }, +} + +export function getContainer(value: any, interactable: Interactable, element: Element) { + return ( + (is.string(value) ? getStringOptionResult(value, interactable, element) : value) || getWindow(element) + ) +} + +export function getScroll(container: any) { + if (is.window(container)) { + container = window.document.body + } + + return { x: container.scrollLeft, y: container.scrollTop } +} + +export function getScrollSize(container: any) { + if (is.window(container)) { + container = window.document.body + } + + return { x: container.scrollWidth, y: container.scrollHeight } +} + +export function getScrollSizeDelta( + { + interaction, + element, + }: { + interaction: Partial> + element: Element + }, + func: any, +) { + const scrollOptions = interaction && interaction.interactable.options[interaction.prepared.name].autoScroll + + if (!scrollOptions || !scrollOptions.enabled) { + func() + return { x: 0, y: 0 } + } + + const scrollContainer = getContainer(scrollOptions.container, interaction.interactable, element) + + const prevSize = getScroll(scrollContainer) + func() + const curSize = getScroll(scrollContainer) + + return { + x: curSize.x - prevSize.x, + y: curSize.y - prevSize.y, + } +} + +const autoScrollPlugin: Plugin = { + id: 'auto-scroll', + install, + listeners: { + 'interactions:new': ({ interaction }) => { + interaction.autoScroll = null + }, + + 'interactions:destroy': ({ interaction }) => { + interaction.autoScroll = null + autoScroll.stop() + if (autoScroll.interaction) { + autoScroll.interaction = null + } + }, + + 'interactions:stop': autoScroll.stop, + + 'interactions:action-move': (arg: any) => autoScroll.onInteractionMove(arg), + }, +} + +export default autoScrollPlugin diff --git a/packages/@interactjs/auto-start/InteractableMethods.ts b/packages/@interactjs/auto-start/InteractableMethods.ts new file mode 100644 index 000000000..acd7c424d --- /dev/null +++ b/packages/@interactjs/auto-start/InteractableMethods.ts @@ -0,0 +1,219 @@ +import type { Interactable } from '@interactjs/core/Interactable' +import type { Interaction } from '@interactjs/core/Interaction' +import type { Scope } from '@interactjs/core/scope' +import type { ActionProps, PointerType, PointerEventType, Element } from '@interactjs/core/types' +import is from '@interactjs/utils/is' +import { warnOnce } from '@interactjs/utils/misc' + +declare module '@interactjs/core/Interactable' { + interface Interactable { + getAction: ( + this: Interactable, + pointer: PointerType, + event: PointerEventType, + interaction: Interaction, + element: Element, + ) => ActionProps | null + styleCursor(newValue: boolean): this + styleCursor(): boolean + /** + * Returns or sets whether the the cursor should be changed depending on the + * action that would be performed if the mouse were pressed and dragged. + * + * @param {boolean} [newValue] + * @return {boolean | Interactable} The current setting or this Interactable + */ + styleCursor(newValue?: boolean): boolean | this + actionChecker(checker: Function): Interactable + actionChecker(): Function + /** + * ```js + * interact('.resize-drag') + * .resizable(true) + * .draggable(true) + * .actionChecker(function (pointer, event, action, interactable, element, interaction) { + * + * if (interact.matchesSelector(event.target, '.drag-handle')) { + * // force drag with handle target + * action.name = drag + * } + * else { + * // resize from the top and right edges + * action.name = 'resize' + * action.edges = { top: true, right: true } + * } + * + * return action + * }) + * ``` + * + * Returns or sets the function used to check action to be performed on + * pointerDown + * + * @param checker - A function which takes a pointer event, + * defaultAction string, interactable, element and interaction as parameters + * and returns an object with name property 'drag' 'resize' or 'gesture' and + * optionally an `edges` object with boolean 'top', 'left', 'bottom' and right + * props. + * @returns The checker function or this Interactable + */ + actionChecker(checker?: Function): Interactable | Function + /** @returns This interactable */ + ignoreFrom(newValue: string | Element | null): Interactable + /** @returns The current ignoreFrom value */ + ignoreFrom(): string | Element | null + /** + * If the target of the `mousedown`, `pointerdown` or `touchstart` event or any + * of it's parents match the given CSS selector or Element, no + * drag/resize/gesture is started. + * + * @deprecated + * Don't use this method. Instead set the `ignoreFrom` option for each action + * or for `pointerEvents` + * + * ```js + * interact(targett) + * .draggable({ + * ignoreFrom: 'input, textarea, a[href]'', + * }) + * .pointerEvents({ + * ignoreFrom: '[no-pointer]', + * }) + * ``` + * Interactable + */ + ignoreFrom( + /** a CSS selector string, an Element or `null` to not ignore any elements */ + newValue?: string | Element | null, + ): Interactable | string | Element | null + allowFrom(): boolean + /** + * + * A drag/resize/gesture is started only If the target of the `mousedown`, + * `pointerdown` or `touchstart` event or any of it's parents match the given + * CSS selector or Element. + * + * @deprecated + * Don't use this method. Instead set the `allowFrom` option for each action + * or for `pointerEvents` + * + * ```js + * interact(targett) + * .resizable({ + * allowFrom: '.resize-handle', + * .pointerEvents({ + * allowFrom: '.handle',, + * }) + * ``` + * + * @param {string | Element | null} [newValue] + * @return {string | Element | object} The current allowFrom value or this + * Interactable + */ + allowFrom( + /** A CSS selector string, an Element or `null` to allow from any element */ + newValue: string | Element | null, + ): Interactable + } +} + +function install(scope: Scope) { + const { + Interactable, // tslint:disable-line no-shadowed-variable + } = scope + + Interactable.prototype.getAction = function getAction( + this: Interactable, + pointer: PointerType, + event: PointerEventType, + interaction: Interaction, + element: Element, + ): ActionProps { + const action = defaultActionChecker(this, event, interaction, element, scope) + + if (this.options.actionChecker) { + return this.options.actionChecker(pointer, event, action, this, element, interaction) + } + + return action + } + + Interactable.prototype.ignoreFrom = warnOnce(function (this: Interactable, newValue) { + return this._backCompatOption('ignoreFrom', newValue) + }, 'Interactable.ignoreFrom() has been deprecated. Use Interactble.draggable({ignoreFrom: newValue}).') + + Interactable.prototype.allowFrom = warnOnce(function (this: Interactable, newValue) { + return this._backCompatOption('allowFrom', newValue) + }, 'Interactable.allowFrom() has been deprecated. Use Interactble.draggable({allowFrom: newValue}).') + + Interactable.prototype.actionChecker = actionChecker + + Interactable.prototype.styleCursor = styleCursor +} + +function defaultActionChecker( + interactable: Interactable, + event: PointerEventType, + interaction: Interaction, + element: Element, + scope: Scope, +) { + const rect = interactable.getRect(element) + const buttons = + (event as MouseEvent).buttons || + { + 0: 1, + 1: 4, + 3: 8, + 4: 16, + }[(event as MouseEvent).button as 0 | 1 | 3 | 4] + const arg = { + action: null, + interactable, + interaction, + element, + rect, + buttons, + } + + scope.fire('auto-start:check', arg) + + return arg.action +} + +function styleCursor(this: Interactable, newValue?: boolean) { + if (is.bool(newValue)) { + this.options.styleCursor = newValue + + return this + } + + if (newValue === null) { + delete this.options.styleCursor + + return this + } + + return this.options.styleCursor +} + +function actionChecker(this: Interactable, checker?: any) { + if (is.func(checker)) { + this.options.actionChecker = checker + + return this + } + + if (checker === null) { + delete this.options.actionChecker + + return this + } + + return this.options.actionChecker +} + +export default { + id: 'auto-start/interactableMethods', + install, +} diff --git a/packages/@interactjs/auto-start/README.md b/packages/@interactjs/auto-start/README.md new file mode 100644 index 000000000..549491910 --- /dev/null +++ b/packages/@interactjs/auto-start/README.md @@ -0,0 +1,5 @@ +

+This package is an internal part of interactjs and is not meant +to be used independently as each update may introduce breaking changes +

diff --git a/packages/@interactjs/auto-start/autoStart.spec.ts b/packages/@interactjs/auto-start/autoStart.spec.ts new file mode 100644 index 000000000..96f3434b8 --- /dev/null +++ b/packages/@interactjs/auto-start/autoStart.spec.ts @@ -0,0 +1,80 @@ +import drag from '@interactjs/actions/drag/plugin' +import * as helpers from '@interactjs/core/tests/_helpers' + +import autoStart from './base' + +test('autoStart', () => { + window.PointerEvent = null + + document.body.innerHTML = ` + +
+ ` + + Object.assign(document.body.style) + + const { + interaction, + interactable, + event, + coords, + target: element, + down, + } = helpers.testEnv({ + plugins: [autoStart, drag], + target: document.getElementById('target'), + }) + + interactable.draggable(true) + interaction.pointerType = coords.pointerType = 'mouse' + coords.buttons = 1 + + down() + + // prepares action + expect(interaction.prepared).toEqual({ name: 'drag', axis: 'xy', edges: undefined }) + + // set interaction.rect + expect(interaction.rect).toEqual( + helpers.getProps(element.getBoundingClientRect(), ['top', 'left', 'bottom', 'right', 'width', 'height']), + ) + + // sets drag cursor + expect(element.style.cursor).toBe('move') + + const cursorChecker = jest.fn(() => 'pointer') + + interactable.draggable({ + cursorChecker, + }) + + interaction.pointerDown(event, event, element) + + // calls cursorChecker with expected args + expect(cursorChecker).toHaveBeenCalledWith( + { name: 'drag', axis: 'xy', edges: undefined }, + interactable, + element, + false, + ) + + interaction.pointerDown(event, event, element) + // uses cursorChecker value + expect(element.style.cursor).toBe('pointer') + + coords.page.x += 10 + coords.client.x += 10 + interaction.pointerMove(event, event, element) + // down -> move starts action + expect(interaction._interacting).toBe(true) + + // calls cursorChecker with true for interacting arg + expect(cursorChecker).toHaveBeenCalledWith( + { name: 'drag', axis: 'xy', edges: undefined }, + interactable, + element, + true, + ) +}) diff --git a/packages/@interactjs/auto-start/base.ts b/packages/@interactjs/auto-start/base.ts new file mode 100644 index 000000000..442225457 --- /dev/null +++ b/packages/@interactjs/auto-start/base.ts @@ -0,0 +1,418 @@ +import type { Interactable } from '@interactjs/core/Interactable' +import type { Interaction } from '@interactjs/core/Interaction' +import type { Scope, SignalArgs, Plugin } from '@interactjs/core/scope' +import type { + CursorChecker, + PointerType, + PointerEventType, + Element, + ActionName, + ActionProps, +} from '@interactjs/core/types' +import * as domUtils from '@interactjs/utils/domUtils' +import extend from '@interactjs/utils/extend' +import is from '@interactjs/utils/is' +import { copyAction } from '@interactjs/utils/misc' + +/* eslint-disable import/no-duplicates -- for typescript module augmentations */ +import './InteractableMethods' +import InteractableMethods from './InteractableMethods' +/* eslint-enable import/no-duplicates */ + +declare module '@interactjs/core/InteractStatic' { + export interface InteractStatic { + /** + * Returns or sets the maximum number of concurrent interactions allowed. By + * default only 1 interaction is allowed at a time (for backwards + * compatibility). To allow multiple interactions on the same Interactables and + * elements, you need to enable it in the draggable, resizable and gesturable + * `'max'` and `'maxPerElement'` options. + * + * @param {number} [newValue] Any number. newValue <= 0 means no interactions. + */ + maxInteractions: (newValue: any) => any + } +} + +declare module '@interactjs/core/scope' { + interface Scope { + autoStart: AutoStart + } + + interface SignalArgs { + 'autoStart:before-start': Omit & { + interaction: Interaction + } + 'autoStart:prepared': { interaction: Interaction } + 'auto-start:check': CheckSignalArg + } +} + +declare module '@interactjs/core/options' { + interface BaseDefaults { + actionChecker?: any + cursorChecker?: any + styleCursor?: any + } + + interface PerActionDefaults { + manualStart?: boolean + max?: number + maxPerElement?: number + allowFrom?: string | Element + ignoreFrom?: string | Element + cursorChecker?: CursorChecker + + // only allow left button by default + // see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons#Return_value + // TODO: docst + mouseButtons?: 0 | 1 | 2 | 4 | 8 | 16 + } +} + +interface CheckSignalArg { + interactable: Interactable + interaction: Interaction + element: Element + action: ActionProps + buttons: number +} + +export interface AutoStart { + // Allow this many interactions to happen simultaneously + maxInteractions: number + withinInteractionLimit: typeof withinInteractionLimit + cursorElement: Element +} + +function install(scope: Scope) { + const { interactStatic: interact, defaults } = scope + + scope.usePlugin(InteractableMethods) + + defaults.base.actionChecker = null + defaults.base.styleCursor = true + + extend(defaults.perAction, { + manualStart: false, + max: Infinity, + maxPerElement: 1, + allowFrom: null, + ignoreFrom: null, + + // only allow left button by default + // see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons#Return_value + mouseButtons: 1, + }) + + interact.maxInteractions = (newValue: number) => maxInteractions(newValue, scope) + + scope.autoStart = { + // Allow this many interactions to happen simultaneously + maxInteractions: Infinity, + withinInteractionLimit, + cursorElement: null, + } +} + +function prepareOnDown( + { interaction, pointer, event, eventTarget }: SignalArgs['interactions:down'], + scope: Scope, +) { + if (interaction.interacting()) return + + const actionInfo = getActionInfo(interaction, pointer, event, eventTarget, scope) + prepare(interaction, actionInfo, scope) +} + +function prepareOnMove( + { interaction, pointer, event, eventTarget }: SignalArgs['interactions:move'], + scope: Scope, +) { + if (interaction.pointerType !== 'mouse' || interaction.pointerIsDown || interaction.interacting()) return + + const actionInfo = getActionInfo(interaction, pointer, event, eventTarget as Element, scope) + prepare(interaction, actionInfo, scope) +} + +function startOnMove(arg: SignalArgs['interactions:move'], scope: Scope) { + const { interaction } = arg + + if ( + !interaction.pointerIsDown || + interaction.interacting() || + !interaction.pointerWasMoved || + !interaction.prepared.name + ) { + return + } + + scope.fire('autoStart:before-start', arg) + + const { interactable } = interaction + const actionName = (interaction as Interaction).prepared.name + + if (actionName && interactable) { + // check manualStart and interaction limit + if ( + interactable.options[actionName].manualStart || + !withinInteractionLimit(interactable, interaction.element, interaction.prepared, scope) + ) { + interaction.stop() + } else { + interaction.start(interaction.prepared, interactable, interaction.element) + setInteractionCursor(interaction, scope) + } + } +} + +function clearCursorOnStop({ interaction }: { interaction: Interaction }, scope: Scope) { + const { interactable } = interaction + + if (interactable && interactable.options.styleCursor) { + setCursor(interaction.element, '', scope) + } +} + +// Check if the current interactable supports the action. +// If so, return the validated action. Otherwise, return null +function validateAction( + action: ActionProps, + interactable: Interactable, + element: Element, + eventTarget: Node, + scope: Scope, +) { + if ( + interactable.testIgnoreAllow(interactable.options[action.name], element, eventTarget) && + interactable.options[action.name].enabled && + withinInteractionLimit(interactable, element, action, scope) + ) { + return action + } + + return null +} + +function validateMatches( + interaction: Interaction, + pointer: PointerType, + event: PointerEventType, + matches: Interactable[], + matchElements: Element[], + eventTarget: Node, + scope: Scope, +) { + for (let i = 0, len = matches.length; i < len; i++) { + const match = matches[i] + const matchElement = matchElements[i] + const matchAction = match.getAction(pointer, event, interaction, matchElement) + + if (!matchAction) { + continue + } + + const action = validateAction(matchAction, match, matchElement, eventTarget, scope) + + if (action) { + return { + action, + interactable: match, + element: matchElement, + } + } + } + + return { action: null, interactable: null, element: null } +} + +function getActionInfo( + interaction: Interaction, + pointer: PointerType, + event: PointerEventType, + eventTarget: Node, + scope: Scope, +) { + let matches: Interactable[] = [] + let matchElements: Element[] = [] + + let element = eventTarget as Element + + function pushMatches(interactable: Interactable) { + matches.push(interactable) + matchElements.push(element) + } + + while (is.element(element)) { + matches = [] + matchElements = [] + + scope.interactables.forEachMatch(element, pushMatches) + + const actionInfo = validateMatches( + interaction, + pointer, + event, + matches, + matchElements, + eventTarget, + scope, + ) + + if (actionInfo.action && !actionInfo.interactable.options[actionInfo.action.name].manualStart) { + return actionInfo + } + + element = domUtils.parentNode(element) as Element + } + + return { action: null, interactable: null, element: null } +} + +function prepare( + interaction: Interaction, + { + action, + interactable, + element, + }: { + action: ActionProps + interactable: Interactable + element: Element + }, + scope: Scope, +) { + action = action || { name: null } + + interaction.interactable = interactable + interaction.element = element + copyAction(interaction.prepared, action) + + interaction.rect = interactable && action.name ? interactable.getRect(element) : null + + setInteractionCursor(interaction, scope) + + scope.fire('autoStart:prepared', { interaction }) +} + +function withinInteractionLimit( + interactable: Interactable, + element: Element, + action: ActionProps, + scope: Scope, +) { + const options = interactable.options + const maxActions = options[action.name].max + const maxPerElement = options[action.name].maxPerElement + const autoStartMax = scope.autoStart.maxInteractions + let activeInteractions = 0 + let interactableCount = 0 + let elementCount = 0 + + // no actions if any of these values == 0 + if (!(maxActions && maxPerElement && autoStartMax)) { + return false + } + + for (const interaction of scope.interactions.list) { + const otherAction = interaction.prepared.name + + if (!interaction.interacting()) { + continue + } + + activeInteractions++ + + if (activeInteractions >= autoStartMax) { + return false + } + + if (interaction.interactable !== interactable) { + continue + } + + interactableCount += otherAction === action.name ? 1 : 0 + + if (interactableCount >= maxActions) { + return false + } + + if (interaction.element === element) { + elementCount++ + + if (otherAction === action.name && elementCount >= maxPerElement) { + return false + } + } + } + + return autoStartMax > 0 +} + +function maxInteractions(newValue: any, scope: Scope) { + if (is.number(newValue)) { + scope.autoStart.maxInteractions = newValue + + return this + } + + return scope.autoStart.maxInteractions +} + +function setCursor(element: Element, cursor: string, scope: Scope) { + const { cursorElement: prevCursorElement } = scope.autoStart + + if (prevCursorElement && prevCursorElement !== element) { + prevCursorElement.style.cursor = '' + } + + element.ownerDocument.documentElement.style.cursor = cursor + element.style.cursor = cursor + scope.autoStart.cursorElement = cursor ? element : null +} + +function setInteractionCursor(interaction: Interaction, scope: Scope) { + const { interactable, element, prepared } = interaction + + if (!(interaction.pointerType === 'mouse' && interactable && interactable.options.styleCursor)) { + // clear previous target element cursor + if (scope.autoStart.cursorElement) { + setCursor(scope.autoStart.cursorElement, '', scope) + } + + return + } + + let cursor = '' + + if (prepared.name) { + const cursorChecker = interactable.options[prepared.name].cursorChecker + + if (is.func(cursorChecker)) { + cursor = cursorChecker(prepared, interactable, element, interaction._interacting) + } else { + cursor = scope.actions.map[prepared.name].getCursor(prepared) + } + } + + setCursor(interaction.element, cursor || '', scope) +} + +const autoStart: Plugin = { + id: 'auto-start/base', + before: ['actions'], + install, + listeners: { + 'interactions:down': prepareOnDown, + 'interactions:move': (arg, scope) => { + prepareOnMove(arg, scope) + startOnMove(arg, scope) + }, + 'interactions:stop': clearCursorOnStop, + }, + maxInteractions, + withinInteractionLimit, + validateAction, +} + +export default autoStart diff --git a/packages/@interactjs/auto-start/dragAxis.ts b/packages/@interactjs/auto-start/dragAxis.ts new file mode 100644 index 000000000..60abbf5e4 --- /dev/null +++ b/packages/@interactjs/auto-start/dragAxis.ts @@ -0,0 +1,86 @@ +import type { Interactable } from '@interactjs/core/Interactable' +import type Interaction from '@interactjs/core/Interaction' +import type { SignalArgs, Scope } from '@interactjs/core/scope' +import type { ActionName, Element } from '@interactjs/core/types' +import { parentNode } from '@interactjs/utils/domUtils' +import is from '@interactjs/utils/is' + +import autoStart from './base' + +function beforeStart({ interaction, eventTarget, dx, dy }: SignalArgs['interactions:move'], scope: Scope) { + if (interaction.prepared.name !== 'drag') return + + // check if a drag is in the correct axis + const absX = Math.abs(dx) + const absY = Math.abs(dy) + const targetOptions = interaction.interactable.options.drag + const startAxis = targetOptions.startAxis + const currentAxis = absX > absY ? 'x' : absX < absY ? 'y' : 'xy' + + interaction.prepared.axis = + targetOptions.lockAxis === 'start' + ? (currentAxis[0] as 'x' | 'y') // always lock to one axis even if currentAxis === 'xy' + : targetOptions.lockAxis + + // if the movement isn't in the startAxis of the interactable + if (currentAxis !== 'xy' && startAxis !== 'xy' && startAxis !== currentAxis) { + // cancel the prepared action + ;(interaction as Interaction).prepared.name = null + + // then try to get a drag from another ineractable + let element = eventTarget as Element + + const getDraggable = function (interactable: Interactable): Interactable | void { + if (interactable === interaction.interactable) return + + const options = interaction.interactable.options.drag + + if (!options.manualStart && interactable.testIgnoreAllow(options, element, eventTarget)) { + const action = interactable.getAction( + interaction.downPointer, + interaction.downEvent, + interaction, + element, + ) + + if ( + action && + action.name === 'drag' && + checkStartAxis(currentAxis, interactable) && + autoStart.validateAction(action, interactable, element, eventTarget, scope) + ) { + return interactable + } + } + } + + // check all interactables + while (is.element(element)) { + const interactable = scope.interactables.forEachMatch(element, getDraggable) + + if (interactable) { + ;(interaction as Interaction).prepared.name = 'drag' + interaction.interactable = interactable + interaction.element = element + break + } + + element = parentNode(element) as Element + } + } +} + +function checkStartAxis(startAxis: string, interactable: Interactable) { + if (!interactable) { + return false + } + + const thisAxis = interactable.options.drag.startAxis + + return startAxis === 'xy' || thisAxis === 'xy' || thisAxis === startAxis +} + +export default { + id: 'auto-start/dragAxis', + listeners: { 'autoStart:before-start': beforeStart }, +} diff --git a/packages/@interactjs/auto-start/hold.spec.ts b/packages/@interactjs/auto-start/hold.spec.ts new file mode 100644 index 000000000..8485c5449 --- /dev/null +++ b/packages/@interactjs/auto-start/hold.spec.ts @@ -0,0 +1,30 @@ +import * as helpers from '@interactjs/core/tests/_helpers' + +import hold from './hold' + +test('autoStart/hold', () => { + const { scope } = helpers.testEnv({ plugins: [hold] }) + + // sets scope.defaults.perAction.hold + expect(scope.defaults.perAction.hold).toBe(0) + // backwards compatible "delay" alias. + expect(scope.defaults.perAction.delay).toBe(0) + + const holdDuration = 1000 + const actionName = 'TEST_ACTION' + const interaction: any = { + interactable: { options: { [actionName]: { hold: holdDuration } } }, + prepared: { name: actionName }, + } + + // gets holdDuration + expect(hold.getHoldDuration(interaction)).toBe(holdDuration) + + const delayDuration = 500 + + interaction.interactable.options[actionName].delay = delayDuration + delete interaction.interactable.options[actionName].hold + + // gets holdDuration from "delay" value + expect(hold.getHoldDuration(interaction)).toBe(delayDuration) +}) diff --git a/packages/@interactjs/auto-start/hold.ts b/packages/@interactjs/auto-start/hold.ts new file mode 100644 index 000000000..33a0b09ed --- /dev/null +++ b/packages/@interactjs/auto-start/hold.ts @@ -0,0 +1,79 @@ +import type Interaction from '@interactjs/core/Interaction' +import type { Scope, Plugin } from '@interactjs/core/scope' + +/* eslint-disable import/no-duplicates -- for typescript module augmentations */ +import './base' +import basePlugin from './base' +/* eslint-enable */ + +declare module '@interactjs/core/options' { + interface PerActionDefaults { + hold?: number + delay?: number + } +} + +declare module '@interactjs/core/Interaction' { + interface Interaction { + autoStartHoldTimer?: any + } +} + +function install(scope: Scope) { + const { defaults } = scope + + scope.usePlugin(basePlugin) + + defaults.perAction.hold = 0 + defaults.perAction.delay = 0 +} + +function getHoldDuration(interaction: Interaction) { + const actionName = interaction.prepared && interaction.prepared.name + + if (!actionName) { + return null + } + + const options = interaction.interactable.options + + return options[actionName].hold || options[actionName].delay +} + +const hold: Plugin = { + id: 'auto-start/hold', + install, + listeners: { + 'interactions:new': ({ interaction }) => { + interaction.autoStartHoldTimer = null + }, + + 'autoStart:prepared': ({ interaction }) => { + const hold = getHoldDuration(interaction) + + if (hold > 0) { + interaction.autoStartHoldTimer = setTimeout(() => { + interaction.start(interaction.prepared, interaction.interactable, interaction.element) + }, hold) + } + }, + + 'interactions:move': ({ interaction, duplicate }) => { + if (interaction.autoStartHoldTimer && interaction.pointerWasMoved && !duplicate) { + clearTimeout(interaction.autoStartHoldTimer) + interaction.autoStartHoldTimer = null + } + }, + + // prevent regular down->move autoStart + 'autoStart:before-start': ({ interaction }) => { + const holdDuration = getHoldDuration(interaction) + + if (holdDuration > 0) { + interaction.prepared.name = null + } + }, + }, + getHoldDuration, +} +export default hold diff --git a/packages/@interactjs/auto-start/package.json b/packages/@interactjs/auto-start/package.json new file mode 100644 index 000000000..d564b1df4 --- /dev/null +++ b/packages/@interactjs/auto-start/package.json @@ -0,0 +1,27 @@ +{ + "name": "@interactjs/auto-start", + "version": "1.10.27", + "main": "index", + "module": "index", + "type": "module", + "repository": { + "type": "git", + "url": "https://github.com/taye/interact.js.git", + "directory": "packages/@interactjs/auto-start" + }, + "peerDependencies": { + "@interactjs/core": "1.10.27", + "@interactjs/utils": "1.10.27" + }, + "optionalDependencies": { + "@interactjs/interact": "1.10.27" + }, + "publishConfig": { + "access": "public" + }, + "sideEffects": [ + "**/index.js", + "**/index.prod.js" + ], + "license": "MIT" +} diff --git a/packages/@interactjs/auto-start/plugin.ts b/packages/@interactjs/auto-start/plugin.ts new file mode 100644 index 000000000..7549f8155 --- /dev/null +++ b/packages/@interactjs/auto-start/plugin.ts @@ -0,0 +1,20 @@ +import type { Scope } from '@interactjs/core/scope' + +/* eslint-disable import/no-duplicates -- for typescript module augmentations */ +import './base' +import './dragAxis' +import './hold' + +import autoStart from './base' +import dragAxis from './dragAxis' +import hold from './hold' +/* eslint-enable import/no-duplicates */ + +export default { + id: 'auto-start', + install(scope: Scope) { + scope.usePlugin(autoStart) + scope.usePlugin(hold) + scope.usePlugin(dragAxis) + }, +} diff --git a/packages/@interactjs/core/BaseEvent.ts b/packages/@interactjs/core/BaseEvent.ts new file mode 100644 index 000000000..9f04f8c87 --- /dev/null +++ b/packages/@interactjs/core/BaseEvent.ts @@ -0,0 +1,50 @@ +import type { Interactable } from '@interactjs/core/Interactable' +import type { Interaction, InteractionProxy } from '@interactjs/core/Interaction' +import type { ActionName } from '@interactjs/core/types' + +export class BaseEvent { + declare type: string + declare target: EventTarget + declare currentTarget: Node + declare interactable: Interactable + /** @internal */ + declare _interaction: Interaction + declare timeStamp: number + immediatePropagationStopped = false + propagationStopped = false + + constructor(interaction: Interaction) { + this._interaction = interaction + } + + preventDefault() {} + + /** + * Don't call any other listeners (even on the current target) + */ + stopPropagation() { + this.propagationStopped = true + } + + /** + * Don't call listeners on the remaining targets + */ + stopImmediatePropagation() { + this.immediatePropagationStopped = this.propagationStopped = true + } +} + +// defined outside of class definition to avoid assignment of undefined during +// construction +export interface BaseEvent { + interaction: InteractionProxy +} + +// getters and setters defined here to support typescript 3.6 and below which +// don't support getter and setters in .d.ts files +Object.defineProperty(BaseEvent.prototype, 'interaction', { + get(this: BaseEvent) { + return this._interaction._proxy + }, + set(this: BaseEvent) {}, +}) diff --git a/packages/@interactjs/core/Eventable.spec.ts b/packages/@interactjs/core/Eventable.spec.ts new file mode 100644 index 000000000..d100f0316 --- /dev/null +++ b/packages/@interactjs/core/Eventable.spec.ts @@ -0,0 +1,35 @@ +import { Eventable } from './Eventable' + +test('core/Eventable', () => { + const eventable = new Eventable() + const type = 'TEST' + + const testEvent = { + type, + immediatePropagationStopped: false, + } + let firedEvent: any + const listener = (event: any) => { + firedEvent = event + } + + eventable.on(type, listener) + eventable.fire(testEvent) + + // on'd listener is called + expect(firedEvent).toBe(testEvent) + + firedEvent = undefined + eventable.off(type, listener) + eventable.fire(testEvent) + + // off'd listener is not called + expect(firedEvent).toBeUndefined() + + testEvent.immediatePropagationStopped = true + eventable.on(type, listener) + eventable.fire(testEvent) + + // listener is not called with immediatePropagationStopped + expect(firedEvent).toBeUndefined() +}) diff --git a/packages/@interactjs/core/Eventable.ts b/packages/@interactjs/core/Eventable.ts new file mode 100644 index 000000000..aa88122bb --- /dev/null +++ b/packages/@interactjs/core/Eventable.ts @@ -0,0 +1,76 @@ +import * as arr from '@interactjs/utils/arr' +import extend from '@interactjs/utils/extend' +import type { NormalizedListeners } from '@interactjs/utils/normalizeListeners' +import normalize from '@interactjs/utils/normalizeListeners' + +import type { Listener, ListenersArg, Rect } from '@interactjs/core/types' + +function fireUntilImmediateStopped(event: any, listeners: Listener[]) { + for (const listener of listeners) { + if (event.immediatePropagationStopped) { + break + } + + listener(event) + } +} + +export class Eventable { + options: any + types: NormalizedListeners = {} + propagationStopped = false + immediatePropagationStopped = false + global: any + + constructor(options?: { [index: string]: any }) { + this.options = extend({}, options || {}) + } + + fire(event: T) { + let listeners: Listener[] + const global = this.global + + // Interactable#on() listeners + // tslint:disable no-conditional-assignment + if ((listeners = this.types[event.type])) { + fireUntilImmediateStopped(event, listeners) + } + + // interact.on() listeners + if (!event.propagationStopped && global && (listeners = global[event.type])) { + fireUntilImmediateStopped(event, listeners) + } + } + + on(type: string, listener: ListenersArg) { + const listeners = normalize(type, listener) + + for (type in listeners) { + this.types[type] = arr.merge(this.types[type] || [], listeners[type]) + } + } + + off(type: string, listener: ListenersArg) { + const listeners = normalize(type, listener) + + for (type in listeners) { + const eventList = this.types[type] + + if (!eventList || !eventList.length) { + continue + } + + for (const subListener of listeners[type]) { + const index = eventList.indexOf(subListener) + + if (index !== -1) { + eventList.splice(index, 1) + } + } + } + } + + getRect(_element: Element): Rect { + return null + } +} diff --git a/packages/@interactjs/core/InteractEvent.ts b/packages/@interactjs/core/InteractEvent.ts new file mode 100644 index 000000000..054bead00 --- /dev/null +++ b/packages/@interactjs/core/InteractEvent.ts @@ -0,0 +1,266 @@ +import extend from '@interactjs/utils/extend' +import getOriginXY from '@interactjs/utils/getOriginXY' +import hypot from '@interactjs/utils/hypot' + +import type { Point, FullRect, PointerEventType, Element, ActionName } from '@interactjs/core/types' + +import { BaseEvent } from './BaseEvent' +import type { Interaction } from './Interaction' +import { defaults } from './options' + +export type EventPhase = keyof PhaseMap + +export interface PhaseMap { + start: true + move: true + end: true +} + +// defined outside of class definition to avoid assignment of undefined during +// construction +export interface InteractEvent { + pageX: number + pageY: number + + clientX: number + clientY: number + + dx: number + dy: number + + velocityX: number + velocityY: number +} + +export class InteractEvent< + T extends ActionName = never, + P extends EventPhase = EventPhase, +> extends BaseEvent { + declare target: Element + declare currentTarget: Element + relatedTarget: Element | null = null + screenX?: number + screenY?: number + button: number + buttons: number + ctrlKey: boolean + shiftKey: boolean + altKey: boolean + metaKey: boolean + page: Point + client: Point + delta: Point + rect: FullRect + x0: number + y0: number + t0: number + dt: number + duration: number + clientX0: number + clientY0: number + velocity: Point + speed: number + swipe: ReturnType['getSwipe']> + // resize + axes?: 'x' | 'y' | 'xy' + /** @internal */ + preEnd?: boolean + + constructor( + interaction: Interaction, + event: PointerEventType, + actionName: T, + phase: P, + element: Element, + preEnd?: boolean, + type?: string, + ) { + super(interaction) + + element = element || interaction.element + + const target = interaction.interactable + const deltaSource = (((target && target.options) || defaults) as any).deltaSource as 'page' | 'client' + const origin = getOriginXY(target, element, actionName) + const starting = phase === 'start' + const ending = phase === 'end' + const prevEvent = starting ? this : interaction.prevEvent + const coords = starting + ? interaction.coords.start + : ending + ? { page: prevEvent.page, client: prevEvent.client, timeStamp: interaction.coords.cur.timeStamp } + : interaction.coords.cur + + this.page = extend({}, coords.page) + this.client = extend({}, coords.client) + this.rect = extend({}, interaction.rect) + this.timeStamp = coords.timeStamp + + if (!ending) { + this.page.x -= origin.x + this.page.y -= origin.y + + this.client.x -= origin.x + this.client.y -= origin.y + } + + this.ctrlKey = event.ctrlKey + this.altKey = event.altKey + this.shiftKey = event.shiftKey + this.metaKey = event.metaKey + this.button = (event as MouseEvent).button + this.buttons = (event as MouseEvent).buttons + this.target = element + this.currentTarget = element + this.preEnd = preEnd + this.type = type || actionName + (phase || '') + this.interactable = target + + this.t0 = starting ? interaction.pointers[interaction.pointers.length - 1].downTime : prevEvent.t0 + + this.x0 = interaction.coords.start.page.x - origin.x + this.y0 = interaction.coords.start.page.y - origin.y + this.clientX0 = interaction.coords.start.client.x - origin.x + this.clientY0 = interaction.coords.start.client.y - origin.y + + if (starting || ending) { + this.delta = { x: 0, y: 0 } + } else { + this.delta = { + x: this[deltaSource].x - prevEvent[deltaSource].x, + y: this[deltaSource].y - prevEvent[deltaSource].y, + } + } + + this.dt = interaction.coords.delta.timeStamp + this.duration = this.timeStamp - this.t0 + + // velocity and speed in pixels per second + this.velocity = extend({}, interaction.coords.velocity[deltaSource]) + this.speed = hypot(this.velocity.x, this.velocity.y) + + this.swipe = ending || phase === 'inertiastart' ? this.getSwipe() : null + } + + getSwipe() { + const interaction = this._interaction + + if (interaction.prevEvent.speed < 600 || this.timeStamp - interaction.prevEvent.timeStamp > 150) { + return null + } + + let angle = (180 * Math.atan2(interaction.prevEvent.velocityY, interaction.prevEvent.velocityX)) / Math.PI + const overlap = 22.5 + + if (angle < 0) { + angle += 360 + } + + const left = 135 - overlap <= angle && angle < 225 + overlap + const up = 225 - overlap <= angle && angle < 315 + overlap + + const right = !left && (315 - overlap <= angle || angle < 45 + overlap) + const down = !up && 45 - overlap <= angle && angle < 135 + overlap + + return { + up, + down, + left, + right, + angle, + speed: interaction.prevEvent.speed, + velocity: { + x: interaction.prevEvent.velocityX, + y: interaction.prevEvent.velocityY, + }, + } + } + + preventDefault() {} + + /** + * Don't call listeners on the remaining targets + */ + stopImmediatePropagation() { + this.immediatePropagationStopped = this.propagationStopped = true + } + + /** + * Don't call any other listeners (even on the current target) + */ + stopPropagation() { + this.propagationStopped = true + } +} + +// getters and setters defined here to support typescript 3.6 and below which +// don't support getter and setters in .d.ts files +Object.defineProperties(InteractEvent.prototype, { + pageX: { + get() { + return this.page.x + }, + set(value) { + this.page.x = value + }, + }, + pageY: { + get() { + return this.page.y + }, + set(value) { + this.page.y = value + }, + }, + + clientX: { + get() { + return this.client.x + }, + set(value) { + this.client.x = value + }, + }, + clientY: { + get() { + return this.client.y + }, + set(value) { + this.client.y = value + }, + }, + + dx: { + get() { + return this.delta.x + }, + set(value) { + this.delta.x = value + }, + }, + dy: { + get() { + return this.delta.y + }, + set(value) { + this.delta.y = value + }, + }, + + velocityX: { + get() { + return this.velocity.x + }, + set(value) { + this.velocity.x = value + }, + }, + velocityY: { + get() { + return this.velocity.y + }, + set(value) { + this.velocity.y = value + }, + }, +}) diff --git a/packages/@interactjs/core/InteractStatic.ts b/packages/@interactjs/core/InteractStatic.ts new file mode 100644 index 000000000..29db82f63 --- /dev/null +++ b/packages/@interactjs/core/InteractStatic.ts @@ -0,0 +1,247 @@ +import browser from '@interactjs/utils/browser' +import * as domUtils from '@interactjs/utils/domUtils' +import is from '@interactjs/utils/is' +import isNonNativeEvent from '@interactjs/utils/isNonNativeEvent' +import { warnOnce } from '@interactjs/utils/misc' +import * as pointerUtils from '@interactjs/utils/pointerUtils' + +import type { Scope, Plugin } from '@interactjs/core/scope' +import type { Context, EventTypes, Listener, ListenersArg, Target } from '@interactjs/core/types' + +import type { Interactable } from './Interactable' +import type { Options } from './options' + +/** + * ```js + * interact('#draggable').draggable(true) + * + * var rectables = interact('rect') + * rectables + * .gesturable(true) + * .on('gesturemove', function (event) { + * // ... + * }) + * ``` + * + * The methods of this variable can be used to set elements as interactables + * and also to change various default settings. + * + * Calling it as a function and passing an element or a valid CSS selector + * string returns an Interactable object which has various methods to configure + * it. + * + * @param {Element | string} target The HTML or SVG Element to interact with + * or CSS selector + * @return {Interactable} + */ +export interface InteractStatic { + (target: Target, options?: Options): Interactable + getPointerAverage: typeof pointerUtils.pointerAverage + getTouchBBox: typeof pointerUtils.touchBBox + getTouchDistance: typeof pointerUtils.touchDistance + getTouchAngle: typeof pointerUtils.touchAngle + getElementRect: typeof domUtils.getElementRect + getElementClientRect: typeof domUtils.getElementClientRect + matchesSelector: typeof domUtils.matchesSelector + closest: typeof domUtils.closest + /** @internal */ globalEvents: any + version: string + /** @internal */ scope: Scope + /** + * Use a plugin + */ + use( + plugin: Plugin, + options?: { + [key: string]: any + }, + ): any + /** + * Check if an element or selector has been set with the `interact(target)` + * function + * + * @return {boolean} Indicates if the element or CSS selector was previously + * passed to interact + */ + isSet( + /* The Element or string being searched for */ + target: Target, + options?: any, + ): boolean + on(type: string | EventTypes, listener: ListenersArg, options?: object): any + off(type: EventTypes, listener: any, options?: object): any + debug(): any + /** + * Whether or not the browser supports touch input + */ + supportsTouch(): boolean + /** + * Whether or not the browser supports PointerEvents + */ + supportsPointerEvent(): boolean + /** + * Cancels all interactions (end events are not fired) + */ + stop(): InteractStatic + /** + * Returns or sets the distance the pointer must be moved before an action + * sequence occurs. This also affects tolerance for tap events. + */ + pointerMoveTolerance( + /** The movement from the start position must be greater than this value */ + newValue?: number, + ): InteractStatic | number + addDocument(doc: Document, options?: object): void + removeDocument(doc: Document): void +} + +export function createInteractStatic(scope: Scope): InteractStatic { + const interact = ((target: Target, options: Options) => { + let interactable = scope.interactables.getExisting(target, options) + + if (!interactable) { + interactable = scope.interactables.new(target, options) + interactable.events.global = interact.globalEvents + } + + return interactable + }) as InteractStatic + + // expose the functions used to calculate multi-touch properties + interact.getPointerAverage = pointerUtils.pointerAverage + interact.getTouchBBox = pointerUtils.touchBBox + interact.getTouchDistance = pointerUtils.touchDistance + interact.getTouchAngle = pointerUtils.touchAngle + + interact.getElementRect = domUtils.getElementRect + interact.getElementClientRect = domUtils.getElementClientRect + interact.matchesSelector = domUtils.matchesSelector + interact.closest = domUtils.closest + + interact.globalEvents = {} as any + + // eslint-disable-next-line no-undef + interact.version = process.env.npm_package_version + interact.scope = scope + interact.use = function (plugin, options) { + this.scope.usePlugin(plugin, options) + + return this + } + + interact.isSet = function (target: Target, options?: { context?: Context }): boolean { + return !!this.scope.interactables.get(target, options && options.context) + } + + interact.on = warnOnce(function on(type: string | EventTypes, listener: ListenersArg, options?: object) { + if (is.string(type) && type.search(' ') !== -1) { + type = type.trim().split(/ +/) + } + + if (is.array(type)) { + for (const eventType of type as any[]) { + this.on(eventType, listener, options) + } + + return this + } + + if (is.object(type)) { + for (const prop in type) { + this.on(prop, (type as any)[prop], listener) + } + + return this + } + + // if it is an InteractEvent type, add listener to globalEvents + if (isNonNativeEvent(type, this.scope.actions)) { + // if this type of event was never bound + if (!this.globalEvents[type]) { + this.globalEvents[type] = [listener] + } else { + this.globalEvents[type].push(listener) + } + } + // If non InteractEvent type, addEventListener to document + else { + this.scope.events.add(this.scope.document, type, listener as Listener, { options }) + } + + return this + }, 'The interact.on() method is being deprecated') + + interact.off = warnOnce(function off(type: EventTypes, listener: any, options?: object) { + if (is.string(type) && type.search(' ') !== -1) { + type = type.trim().split(/ +/) + } + + if (is.array(type)) { + for (const eventType of type) { + this.off(eventType, listener, options) + } + + return this + } + + if (is.object(type)) { + for (const prop in type) { + this.off(prop, type[prop], listener) + } + + return this + } + + if (isNonNativeEvent(type, this.scope.actions)) { + let index: number + + if (type in this.globalEvents && (index = this.globalEvents[type].indexOf(listener)) !== -1) { + this.globalEvents[type].splice(index, 1) + } + } else { + this.scope.events.remove(this.scope.document, type, listener, options) + } + + return this + }, 'The interact.off() method is being deprecated') + + interact.debug = function () { + return this.scope + } + + interact.supportsTouch = function () { + return browser.supportsTouch + } + + interact.supportsPointerEvent = function () { + return browser.supportsPointerEvent + } + + interact.stop = function () { + for (const interaction of this.scope.interactions.list) { + interaction.stop() + } + + return this + } + + interact.pointerMoveTolerance = function (newValue?: number) { + if (is.number(newValue)) { + this.scope.interactions.pointerMoveTolerance = newValue + + return this + } + + return this.scope.interactions.pointerMoveTolerance + } + + interact.addDocument = function (doc: Document, options?: object) { + this.scope.addDocument(doc, options) + } + + interact.removeDocument = function (doc: Document) { + this.scope.removeDocument(doc) + } + + return interact +} diff --git a/packages/@interactjs/core/Interactable.spec.ts b/packages/@interactjs/core/Interactable.spec.ts new file mode 100644 index 000000000..72ba16da2 --- /dev/null +++ b/packages/@interactjs/core/Interactable.spec.ts @@ -0,0 +1,192 @@ +import drag from '@interactjs/actions/drag/plugin' + +import * as helpers from './tests/_helpers' + +describe('core/Interactable', () => { + test('Interactable copies and extends defaults', () => { + const scope = helpers.mockScope() as any + const { defaults } = scope + + scope.actions.methodDict = { test: 'testize' } + + scope.Interactable.prototype.testize = function (options: any) { + this.setPerAction('test', options) + } + + defaults.actions.test = { + fromDefault: { a: 1, b: 2 }, + specified: { c: 1, d: 2 }, + } + + const specified = { specified: 'parent' } + + const div = scope.document.createElement('div') + const interactable = scope.interactables.new(div, { test: specified }) + + // specified options are properly set + expect(interactable.options.test.specified).toEqual(specified.specified) + // default options are properly set + expect(interactable.options.test.fromDefault).toEqual(defaults.actions.test.fromDefault) + // defaults are not aliased + expect(interactable.options.test.fromDefault).not.toBe(defaults.actions.test.fromDefault) + + defaults.actions.test.fromDefault.c = 3 + // modifying defaults does not affect constructed interactables + expect('c' in interactable.options.test.fromDefault).not.toBe(true) + div.remove() + }) + + test('Interactable unset correctly', () => { + const scope = helpers.mockScope() + + const div = scope.document.createElement('div') + const interactable = scope.interactables.new(div) + + expect(div[scope.id]).toHaveLength(1) + + interactable.unset() + + // clears target mapping + expect(div[scope.id]).toHaveLength(0) + + div.remove() + }) + + test('Interactable copies and extends per action defaults', () => { + const scope = helpers.mockScope() + const { defaults } = scope + + scope.actions.methodDict = { test: 'testize' } as any + ;(scope.Interactable.prototype as any).testize = function (options: any) { + this.setPerAction('test', options) + } + ;(defaults.perAction as any).testOption = { + fromDefault: { a: 1, b: 2 }, + specified: null, + } + ;(defaults.actions as any).test = { testOption: (defaults.perAction as any).testOption } + + const div = scope.document.createElement('div') + const interactable = scope.interactables.new(div, {}) + ;(interactable as any).testize({ testOption: { specified: 'parent' } }) + + // specified options are properly set + expect((interactable.options as any).test).toEqual({ + enabled: false, + origin: { x: 0, y: 0 }, + + testOption: { + fromDefault: { a: 1, b: 2 }, + specified: 'parent', + }, + }) + // default options are properly set + expect((interactable.options as any).test.testOption.fromDefault).toEqual( + (defaults.perAction as any).testOption.fromDefault, + ) + // defaults are not aliased + expect((interactable.options as any).test.testOption.fromDefault).not.toBe( + (defaults.perAction as any).testOption.fromDefault, + ) + ;(defaults.perAction as any).testOption.fromDefault.c = 3 + // modifying defaults does not affect constructed interactables + expect('c' in (interactable.options as any).test.testOption.fromDefault).toBe(false) + div.remove() + }) + + test('Interactable.updatePerActionListeners', () => { + const scope = helpers.mockScope() + + let fired: any[] = [] + function addToFired(event: any) { + fired.push(event) + } + + scope.actions.methodDict = { test: 'testize' } as any + ;(scope.Interactable.prototype as any).testize = function (options: any) { + this.setPerAction('test', options) + } + ;(scope.defaults.actions as any).test = {} + + const interactable = scope.interactables.new('target') + + interactable.setPerAction('test' as any, { + listeners: [ + { + start: addToFired, + move: addToFired, + end: addToFired, + }, + ], + }) + + interactable.fire({ type: 'teststart' }) + expect(fired.map((e) => e.type)).toEqual(['teststart']) + + interactable.fire({ type: 'testmove' }) + expect(fired.map((e) => e.type)).toEqual(['teststart', 'testmove']) + + interactable.fire({ type: 'testnotadded' }) + expect(fired.map((e) => e.type)).toEqual(['teststart', 'testmove']) + + interactable.fire({ type: 'testend' }) + expect(fired.map((e) => e.type)).toEqual(['teststart', 'testmove', 'testend']) + + fired = [] + interactable.setPerAction('test' as any, { + listeners: [{ start: addToFired }], + }) + + interactable.fire({ type: 'teststart' }) + interactable.fire({ type: 'testmove' }) + interactable.fire({ type: 'testend' }) + expect(fired.map((e) => e.type)).toEqual(['teststart']) + + fired = [] + interactable.setPerAction('test' as any, { + listeners: null, + }) + + interactable.fire({ type: 'teststart' }) + interactable.fire({ type: 'testmove' }) + interactable.fire({ type: 'testend' }) + expect(fired).toEqual([]) + }) + + test('Interactable.{on,off}', () => { + const { interactable: elInteractable, interact, target: element } = helpers.testEnv({ plugins: [drag] }) + + let fired: Array<{ type: any }> = [] + const listener = (e: { type: any }) => fired.push(e) + const selectorInteractable = interact('html') + + elInteractable.on('dragstart click', listener) + selectorInteractable.on('dragstart click change', listener) + + elInteractable.fire({ type: 'dragstart' }) + expect(fired).toHaveLength(1) + expect(fired[0].type).toBe('dragstart') + + elInteractable.off('dragstart', listener) + fired = [] + elInteractable.fire({ type: 'dragstart' }) + expect(fired).toEqual([]) + + element.click() + expect(fired.map((e) => e.type)).toEqual(['click', 'click']) + + selectorInteractable.off('click', listener) + fired = [] + element.click() + expect(fired.map((e) => e.type)).toEqual(['click']) + + fired = [] + selectorInteractable.fire({ type: 'dragstart' }) + expect(fired.map((e) => e.type)).toEqual(['dragstart']) + + selectorInteractable.off('dragstart', listener) + fired = [] + selectorInteractable.fire({ type: 'dragstart' }) + expect(fired).toEqual([]) + }) +}) diff --git a/packages/@interactjs/core/Interactable.ts b/packages/@interactjs/core/Interactable.ts new file mode 100644 index 000000000..9616423d9 --- /dev/null +++ b/packages/@interactjs/core/Interactable.ts @@ -0,0 +1,500 @@ +/* eslint-disable no-dupe-class-members */ +import * as arr from '@interactjs/utils/arr' +import browser from '@interactjs/utils/browser' +import clone from '@interactjs/utils/clone' +import { getElementRect, matchesUpTo, nodeContains, trySelector } from '@interactjs/utils/domUtils' +import extend from '@interactjs/utils/extend' +import is from '@interactjs/utils/is' +import isNonNativeEvent from '@interactjs/utils/isNonNativeEvent' +import normalizeListeners from '@interactjs/utils/normalizeListeners' +import { getWindow } from '@interactjs/utils/window' + +import type { Scope } from '@interactjs/core/scope' +import type { + ActionMap, + ActionMethod, + ActionName, + Actions, + Context, + Element, + EventTypes, + Listeners, + ListenersArg, + OrBoolean, + Target, +} from '@interactjs/core/types' + +import { Eventable } from './Eventable' +import type { ActionDefaults, Defaults, OptionsArg, PerActionDefaults, Options } from './options' + +type IgnoreValue = string | Element | boolean +type DeltaSource = 'page' | 'client' + +const enum OnOffMethod { + On, + Off, +} + +/** + * ```ts + * const interactable = interact('.cards') + * .draggable({ + * listeners: { move: event => console.log(event.type, event.pageX, event.pageY) } + * }) + * .resizable({ + * listeners: { move: event => console.log(event.rect) }, + * modifiers: [interact.modifiers.restrictEdges({ outer: 'parent' })] + * }) + * ``` + */ +export class Interactable implements Partial { + /** @internal */ get _defaults(): Defaults { + return { + base: {}, + perAction: {}, + actions: {} as ActionDefaults, + } + } + + readonly target: Target + /** @internal */ readonly options!: Required + /** @internal */ readonly _actions: Actions + /** @internal */ readonly events = new Eventable() + /** @internal */ readonly _context: Context + /** @internal */ readonly _win: Window + /** @internal */ readonly _doc: Document + /** @internal */ readonly _scopeEvents: Scope['events'] + + constructor( + target: Target, + options: any, + defaultContext: Document | Element, + scopeEvents: Scope['events'], + ) { + this._actions = options.actions + this.target = target + this._context = options.context || defaultContext + this._win = getWindow(trySelector(target) ? this._context : target) + this._doc = this._win.document + this._scopeEvents = scopeEvents + + this.set(options) + } + + setOnEvents(actionName: ActionName, phases: NonNullable) { + if (is.func(phases.onstart)) { + this.on(`${actionName}start`, phases.onstart) + } + if (is.func(phases.onmove)) { + this.on(`${actionName}move`, phases.onmove) + } + if (is.func(phases.onend)) { + this.on(`${actionName}end`, phases.onend) + } + if (is.func(phases.oninertiastart)) { + this.on(`${actionName}inertiastart`, phases.oninertiastart) + } + + return this + } + + updatePerActionListeners(actionName: ActionName, prev: Listeners | undefined, cur: Listeners | undefined) { + const actionFilter = (this._actions.map[actionName] as { filterEventType?: (type: string) => boolean }) + ?.filterEventType + const filter = (type: string) => + (actionFilter == null || actionFilter(type)) && isNonNativeEvent(type, this._actions) + + if (is.array(prev) || is.object(prev)) { + this._onOff(OnOffMethod.Off, actionName, prev, undefined, filter) + } + + if (is.array(cur) || is.object(cur)) { + this._onOff(OnOffMethod.On, actionName, cur, undefined, filter) + } + } + + setPerAction(actionName: ActionName, options: OrBoolean) { + const defaults = this._defaults + + // for all the default per-action options + for (const optionName_ in options) { + const optionName = optionName_ as keyof PerActionDefaults + const actionOptions = this.options[actionName] + const optionValue: any = options[optionName] + + // remove old event listeners and add new ones + if (optionName === 'listeners') { + this.updatePerActionListeners(actionName, actionOptions.listeners, optionValue as Listeners) + } + + // if the option value is an array + if (is.array(optionValue)) { + ;(actionOptions[optionName] as any) = arr.from(optionValue) + } + // if the option value is an object + else if (is.plainObject(optionValue)) { + // copy the object + ;(actionOptions[optionName] as any) = extend( + actionOptions[optionName] || ({} as any), + clone(optionValue), + ) + + // set anabled field to true if it exists in the defaults + if ( + is.object(defaults.perAction[optionName]) && + 'enabled' in (defaults.perAction[optionName] as any) + ) { + ;(actionOptions[optionName] as any).enabled = optionValue.enabled !== false + } + } + // if the option value is a boolean and the default is an object + else if (is.bool(optionValue) && is.object(defaults.perAction[optionName])) { + ;(actionOptions[optionName] as any).enabled = optionValue + } + // if it's anything else, do a plain assignment + else { + ;(actionOptions[optionName] as any) = optionValue + } + } + } + + /** + * The default function to get an Interactables bounding rect. Can be + * overridden using {@link Interactable.rectChecker}. + * + * @param {Element} [element] The element to measure. + * @return {Rect} The object's bounding rectangle. + */ + getRect(element: Element) { + element = element || (is.element(this.target) ? this.target : null) + + if (is.string(this.target)) { + element = element || this._context.querySelector(this.target) + } + + return getElementRect(element) + } + + /** + * Returns or sets the function used to calculate the interactable's + * element's rectangle + * + * @param {function} [checker] A function which returns this Interactable's + * bounding rectangle. See {@link Interactable.getRect} + * @return {function | object} The checker function or this Interactable + */ + rectChecker(): (element: Element) => any | null + rectChecker(checker: (element: Element) => any): this + rectChecker(checker?: (element: Element) => any) { + if (is.func(checker)) { + this.getRect = (element) => { + const rect = extend({}, checker.apply(this, element)) + + if (!(('width' in rect) as unknown)) { + rect.width = rect.right - rect.left + rect.height = rect.bottom - rect.top + } + + return rect + } + + return this + } + + if (checker === null) { + delete (this as Partial).getRect + + return this + } + + return this.getRect + } + + /** @internal */ + _backCompatOption(optionName: keyof Options, newValue: any) { + if (trySelector(newValue) || is.object(newValue)) { + ;(this.options[optionName] as any) = newValue + + for (const action in this._actions.map) { + ;(this.options[action as keyof ActionMap] as any)[optionName] = newValue + } + + return this + } + + return this.options[optionName] + } + + /** + * Gets or sets the origin of the Interactable's element. The x and y + * of the origin will be subtracted from action event coordinates. + * + * @param {Element | object | string} [origin] An HTML or SVG Element whose + * rect will be used, an object eg. { x: 0, y: 0 } or string 'parent', 'self' + * or any CSS selector + * + * @return {object} The current origin or this Interactable + */ + origin(newValue: any) { + return this._backCompatOption('origin', newValue) + } + + /** + * Returns or sets the mouse coordinate types used to calculate the + * movement of the pointer. + * + * @param {string} [newValue] Use 'client' if you will be scrolling while + * interacting; Use 'page' if you want autoScroll to work + * @return {string | object} The current deltaSource or this Interactable + */ + deltaSource(): DeltaSource + deltaSource(newValue: DeltaSource): this + deltaSource(newValue?: DeltaSource) { + if (newValue === 'page' || newValue === 'client') { + this.options.deltaSource = newValue + + return this + } + + return this.options.deltaSource + } + + /** @internal */ + getAllElements(): Element[] { + const { target } = this + + if (is.string(target)) { + return Array.from(this._context.querySelectorAll(target)) + } + + if (is.func(target) && (target as any).getAllElements) { + return (target as any).getAllElements() + } + + return is.element(target) ? [target] : [] + } + + /** + * Gets the selector context Node of the Interactable. The default is + * `window.document`. + * + * @return {Node} The context Node of this Interactable + */ + context() { + return this._context + } + + inContext(element: Document | Node) { + return this._context === element.ownerDocument || nodeContains(this._context, element) + } + + /** @internal */ + testIgnoreAllow( + this: Interactable, + options: { ignoreFrom?: IgnoreValue; allowFrom?: IgnoreValue }, + targetNode: Node, + eventTarget: Node, + ) { + return ( + !this.testIgnore(options.ignoreFrom, targetNode, eventTarget) && + this.testAllow(options.allowFrom, targetNode, eventTarget) + ) + } + + /** @internal */ + testAllow(this: Interactable, allowFrom: IgnoreValue | undefined, targetNode: Node, element: Node) { + if (!allowFrom) { + return true + } + + if (!is.element(element)) { + return false + } + + if (is.string(allowFrom)) { + return matchesUpTo(element, allowFrom, targetNode) + } else if (is.element(allowFrom)) { + return nodeContains(allowFrom, element) + } + + return false + } + + /** @internal */ + testIgnore(this: Interactable, ignoreFrom: IgnoreValue | undefined, targetNode: Node, element: Node) { + if (!ignoreFrom || !is.element(element)) { + return false + } + + if (is.string(ignoreFrom)) { + return matchesUpTo(element, ignoreFrom, targetNode) + } else if (is.element(ignoreFrom)) { + return nodeContains(ignoreFrom, element) + } + + return false + } + + /** + * Calls listeners for the given InteractEvent type bound globally + * and directly to this Interactable + * + * @param {InteractEvent} iEvent The InteractEvent object to be fired on this + * Interactable + * @return {Interactable} this Interactable + */ + fire(iEvent: E) { + this.events.fire(iEvent) + + return this + } + + /** @internal */ + _onOff( + method: OnOffMethod, + typeArg: EventTypes, + listenerArg?: ListenersArg | null, + options?: any, + filter?: (type: string) => boolean, + ) { + if (is.object(typeArg) && !is.array(typeArg)) { + options = listenerArg + listenerArg = null + } + + const listeners = normalizeListeners(typeArg, listenerArg, filter) + + for (let type in listeners) { + if (type === 'wheel') { + type = browser.wheelEvent + } + + for (const listener of listeners[type]) { + // if it is an action event type + if (isNonNativeEvent(type, this._actions)) { + this.events[method === OnOffMethod.On ? 'on' : 'off'](type, listener) + } + // delegated event + else if (is.string(this.target)) { + this._scopeEvents[method === OnOffMethod.On ? 'addDelegate' : 'removeDelegate']( + this.target, + this._context, + type, + listener, + options, + ) + } + // remove listener from this Interactable's element + else { + this._scopeEvents[method === OnOffMethod.On ? 'add' : 'remove']( + this.target, + type, + listener, + options, + ) + } + } + } + + return this + } + + /** + * Binds a listener for an InteractEvent, pointerEvent or DOM event. + * + * @param {string | array | object} types The types of events to listen + * for + * @param {function | array | object} [listener] The event listener function(s) + * @param {object | boolean} [options] options object or useCapture flag for + * addEventListener + * @return {Interactable} This Interactable + */ + on(types: EventTypes, listener?: ListenersArg, options?: any) { + return this._onOff(OnOffMethod.On, types, listener, options) + } + + /** + * Removes an InteractEvent, pointerEvent or DOM event listener. + * + * @param {string | array | object} types The types of events that were + * listened for + * @param {function | array | object} [listener] The event listener function(s) + * @param {object | boolean} [options] options object or useCapture flag for + * removeEventListener + * @return {Interactable} This Interactable + */ + off(types: string | string[] | EventTypes, listener?: ListenersArg, options?: any) { + return this._onOff(OnOffMethod.Off, types, listener, options) + } + + /** + * Reset the options of this Interactable + * + * @param {object} options The new settings to apply + * @return {object} This Interactable + */ + set(options: OptionsArg) { + const defaults = this._defaults + + if (!is.object(options)) { + options = {} + } + + ;(this.options as Required) = clone(defaults.base) as Required + + for (const actionName_ in this._actions.methodDict) { + const actionName = actionName_ as ActionName + const methodName = this._actions.methodDict[actionName] + + this.options[actionName] = {} + this.setPerAction(actionName, extend(extend({}, defaults.perAction), defaults.actions[actionName])) + ;(this[methodName] as ActionMethod)(options[actionName]) + } + + for (const setting in options) { + if (setting === 'getRect') { + this.rectChecker(options.getRect) + continue + } + + if (is.func((this as any)[setting])) { + ;(this as any)[setting](options[setting as keyof typeof options]) + } + } + + return this + } + + /** + * Remove this interactable from the list of interactables and remove it's + * action capabilities and event listeners + */ + unset() { + if (is.string(this.target)) { + // remove delegated events + for (const type in this._scopeEvents.delegatedEvents) { + const delegated = this._scopeEvents.delegatedEvents[type] + + for (let i = delegated.length - 1; i >= 0; i--) { + const { selector, context, listeners } = delegated[i] + + if (selector === this.target && context === this._context) { + delegated.splice(i, 1) + } + + for (let l = listeners.length - 1; l >= 0; l--) { + this._scopeEvents.removeDelegate( + this.target, + this._context, + type, + listeners[l][0], + listeners[l][1], + ) + } + } + } + } else { + this._scopeEvents.remove(this.target, 'all') + } + } +} diff --git a/packages/@interactjs/core/InteractableSet.ts b/packages/@interactjs/core/InteractableSet.ts new file mode 100644 index 000000000..f353ae44d --- /dev/null +++ b/packages/@interactjs/core/InteractableSet.ts @@ -0,0 +1,119 @@ +import * as arr from '@interactjs/utils/arr' +import * as domUtils from '@interactjs/utils/domUtils' +import extend from '@interactjs/utils/extend' +import is from '@interactjs/utils/is' + +import type { Interactable } from '@interactjs/core/Interactable' +import type { OptionsArg, Options } from '@interactjs/core/options' +import type { Scope } from '@interactjs/core/scope' +import type { Target } from '@interactjs/core/types' + +declare module '@interactjs/core/scope' { + interface SignalArgs { + 'interactable:new': { + interactable: Interactable + target: Target + options: OptionsArg + win: Window + } + } +} + +export class InteractableSet { + // all set interactables + list: Interactable[] = [] + + selectorMap: { + [selector: string]: Interactable[] + } = {} + + scope: Scope + + constructor(scope: Scope) { + this.scope = scope + scope.addListeners({ + 'interactable:unset': ({ interactable }) => { + const { target } = interactable + const interactablesOnTarget: Interactable[] = is.string(target) + ? this.selectorMap[target] + : (target as any)[this.scope.id] + + const targetIndex = arr.findIndex(interactablesOnTarget, (i) => i === interactable) + interactablesOnTarget.splice(targetIndex, 1) + }, + }) + } + + new(target: Target, options?: any): Interactable { + options = extend(options || {}, { + actions: this.scope.actions, + }) + const interactable = new this.scope.Interactable(target, options, this.scope.document, this.scope.events) + + this.scope.addDocument(interactable._doc) + this.list.push(interactable) + + if (is.string(target)) { + if (!this.selectorMap[target]) { + this.selectorMap[target] = [] + } + this.selectorMap[target].push(interactable) + } else { + if (!(interactable.target as any)[this.scope.id]) { + Object.defineProperty(target, this.scope.id, { + value: [], + configurable: true, + }) + } + + ;(target as any)[this.scope.id].push(interactable) + } + + this.scope.fire('interactable:new', { + target, + options, + interactable, + win: this.scope._win, + }) + + return interactable + } + + getExisting(target: Target, options?: Options) { + const context = (options && options.context) || this.scope.document + const isSelector = is.string(target) + const interactablesOnTarget: Interactable[] = isSelector + ? this.selectorMap[target as string] + : (target as any)[this.scope.id] + + if (!interactablesOnTarget) return undefined + + return arr.find( + interactablesOnTarget, + (interactable) => + interactable._context === context && (isSelector || interactable.inContext(target as any)), + ) + } + + forEachMatch(node: Node, callback: (interactable: Interactable) => T): T | void { + for (const interactable of this.list) { + let ret: T + + if ( + (is.string(interactable.target) + ? // target is a selector and the element matches + is.element(node) && domUtils.matchesSelector(node, interactable.target) + : // target is the element + node === interactable.target) && + // the element is in context + interactable.inContext(node) + ) { + ret = callback(interactable) + } + + if (ret !== undefined) { + return ret + } + } + } +} diff --git a/packages/@interactjs/core/Interaction.spec.ts b/packages/@interactjs/core/Interaction.spec.ts new file mode 100644 index 000000000..7dcb806f7 --- /dev/null +++ b/packages/@interactjs/core/Interaction.spec.ts @@ -0,0 +1,493 @@ +import drag from '@interactjs/actions/drag/plugin' +import drop from '@interactjs/actions/drop/plugin' +import autoStart from '@interactjs/auto-start/base' +import extend from '@interactjs/utils/extend' +import * as pointerUtils from '@interactjs/utils/pointerUtils' + +import type { PointerType } from '@interactjs/core/types' + +import type { EventPhase } from './InteractEvent' +import { InteractEvent } from './InteractEvent' +import { Interaction } from './Interaction' +import * as helpers from './tests/_helpers' + +describe('core/Interaction', () => { + test('constructor', () => { + const testType = 'test' + const dummyScopeFire = () => {} + const interaction = new Interaction({ + pointerType: testType, + scopeFire: dummyScopeFire, + }) + const zeroCoords = { + page: { x: 0, y: 0 }, + client: { x: 0, y: 0 }, + timeStamp: 0, + } + + // scopeFire option is set assigned to interaction._scopeFire + expect(interaction._scopeFire).toBe(dummyScopeFire) + + expect(interaction.prepared).toEqual(expect.any(Object)) + expect(interaction.downPointer).toEqual(expect.any(Object)) + + // `interaction.coords.${coordField} set to zero` + expect(interaction.coords).toEqual({ + start: zeroCoords, + cur: zeroCoords, + prev: zeroCoords, + delta: zeroCoords, + velocity: zeroCoords, + }) + + // interaction.pointerType is set + expect(interaction.pointerType).toBe(testType) + // interaction.pointers is initially an empty array + expect(interaction.pointers).toEqual([]) + // false properties + expect(interaction).toMatchObject({ pointerIsDown: false, pointerWasMoved: false, _interacting: false }) + expect(interaction.pointerType).not.toBe('mouse') + }) + + test('Interaction destroy', () => { + const { interaction } = helpers.testEnv() + const pointer = { pointerId: 10 } as any + const event = {} as any + + interaction.updatePointer(pointer, event, null) + + interaction.destroy() + + // interaction._latestPointer.pointer is null + expect(interaction._latestPointer.pointer).toBeNull() + + // interaction._latestPointer.event is null + expect(interaction._latestPointer.event).toBeNull() + + // interaction._latestPointer.eventTarget is null + expect(interaction._latestPointer.eventTarget).toBeNull() + }) + + test('Interaction.getPointerIndex', () => { + const { interaction } = helpers.testEnv() + + interaction.pointers = [2, 4, 5, 0, -1].map((id) => ({ id })) as any + + interaction.pointers.forEach(({ id }, index) => { + expect(interaction.getPointerIndex({ pointerId: id } as any)).toBe(index) + }) + }) + + describe('Interaction.updatePointer', () => { + test('no existing pointers', () => { + const { interaction } = helpers.testEnv() + const pointer = { pointerId: 10 } as any + const event = {} as any + + const ret = interaction.updatePointer(pointer, event, null) + + // interaction.pointers == [{ pointer, ... }] + expect(interaction.pointers).toEqual([ + { + id: pointer.pointerId, + pointer, + event, + downTime: null, + downTarget: null, + }, + ]) + // new pointer index is returned + expect(ret).toBe(0) + }) + + test('new pointer with exisiting pointer', () => { + const { interaction } = helpers.testEnv() + const existing: any = { pointerId: 0 } + const event: any = {} + + interaction.updatePointer(existing, event, null) + + const newPointer: any = { pointerId: 10 } + const ret = interaction.updatePointer(newPointer, event, null) + + // interaction.pointers == [{ pointer: existing, ... }, { pointer: newPointer, ... }] + expect(interaction.pointers).toEqual([ + { + id: existing.pointerId, + pointer: existing, + event, + downTime: null, + downTarget: null, + }, + { + id: newPointer.pointerId, + pointer: newPointer, + event, + downTime: null, + downTarget: null, + }, + ]) + + // second pointer index is 1 + expect(ret).toBe(1) + }) + + test('update existing pointers', () => { + const { interaction } = helpers.testEnv() + + const oldPointers = [-3, 10, 2].map((pointerId) => ({ pointerId })) + const newPointers = oldPointers.map((pointer) => ({ ...pointer, new: true })) + + oldPointers.forEach((pointer: any) => interaction.updatePointer(pointer, pointer, null)) + newPointers.forEach((pointer: any) => interaction.updatePointer(pointer, pointer, null)) + + // number of pointers is unchanged + expect(interaction.pointers).toHaveLength(oldPointers.length) + + interaction.pointers.forEach((pointerInfo, i) => { + // `pointer[${i}].id is the same` + expect(pointerInfo.id).toBe(oldPointers[i].pointerId) + // `new pointer ${i} !== old pointer object` + expect(pointerInfo.pointer).not.toBe(oldPointers[i]) + }) + }) + }) + + test('Interaction.removePointer', () => { + const { interaction } = helpers.testEnv() + const ids = [0, 1, 2, 3] + const removals = [ + { id: 0, remain: [1, 2, 3], message: 'first of 4' }, + { id: 2, remain: [1, 3], message: 'middle of 3' }, + { id: 3, remain: [1], message: 'last of 2' }, + { id: 1, remain: [], message: 'final' }, + ] + + ids.forEach((pointerId) => interaction.updatePointer({ pointerId } as any, {} as any, null)) + + for (const removal of removals) { + interaction.removePointer({ pointerId: removal.id } as PointerType, null) + + // `${removal.message} - remaining interaction.pointers is correct` + expect(interaction.pointers.map((p) => p.id)).toEqual(removal.remain) + } + }) + + test('Interaction.pointer{Down,Move,Up} updatePointer', () => { + const { scope, interaction } = helpers.testEnv() + const eventTarget: any = {} + const pointer: any = { + target: eventTarget, + pointerId: 0, + } + let info: any = {} + + scope.addListeners({ + 'interactions:update-pointer': (arg) => { + info.updated = arg.pointerInfo + }, + 'interactions:remove-pointer': (arg) => { + info.removed = arg.pointerInfo + }, + }) + + interaction.coords.cur.timeStamp = 0 + const commonPointerInfo: any = { + id: 0, + pointer, + event: pointer, + downTime: null, + downTarget: null, + } + + interaction.pointerDown(pointer, pointer, eventTarget) + // interaction.pointerDown updates pointer + expect(info.updated).toEqual({ + ...commonPointerInfo, + downTime: interaction.coords.cur.timeStamp, + downTarget: eventTarget, + }) + // interaction.pointerDown doesn't remove pointer + expect(info.removed).toBeUndefined() + interaction.removePointer(pointer, null) + info = {} + + interaction.pointerMove(pointer, pointer, eventTarget) + // interaction.pointerMove updates pointer + expect(info.updated).toEqual(commonPointerInfo) + // interaction.pointerMove doesn't remove pointer + expect(info.removed).toBeUndefined() + info = {} + + interaction.pointerUp(pointer, pointer, eventTarget, null) + // interaction.pointerUp doesn't update existing pointer + expect(info.updated).toBeUndefined() + info = {} + + interaction.pointerUp(pointer, pointer, eventTarget, null) + // interaction.pointerUp updates non existing pointer + expect(info.updated).toEqual(commonPointerInfo) + // interaction.pointerUp also removes pointer + expect(info.removed).toEqual(commonPointerInfo) + info = {} + }) + + test('Interaction.pointerDown', () => { + const { interaction, scope, coords, event, target } = helpers.testEnv() + let signalArg: any + + const coordsSet = helpers.newCoordsSet() + scope.now = () => coords.timeStamp + + extend(coords, { + target, + type: 'down', + }) + + const signalListener = (arg: any) => { + signalArg = arg + } + + scope.addListeners({ + 'interactions:down': signalListener, + }) + + const pointerCoords: any = { page: {}, client: {} } + pointerUtils.setCoords(pointerCoords, [event], event.timeStamp) + + for (const prop in coordsSet) { + pointerUtils.copyCoords( + interaction.coords[prop as keyof typeof coordsSet], + coordsSet[prop as keyof typeof coordsSet], + ) + } + + // downPointer is initially empty + expect(interaction.downPointer).toEqual({} as any) + + // test while interacting + interaction._interacting = true + interaction.pointerDown(event, event, target) + + // downEvent is not updated + expect(interaction.downEvent).toBeNull() + // pointer is added + expect(interaction.pointers).toEqual([ + { + id: event.pointerId, + event, + pointer: event, + downTime: 0, + downTarget: target, + }, + ]) + + // downPointer is updated + expect(interaction.downPointer).not.toEqual({} as any) + + // coords.start are not modified + expect(interaction.coords.start).toEqual(coordsSet.start) + // coords.prev are not modified + expect(interaction.coords.prev).toEqual(coordsSet.prev) + + // coords.cur *are* modified + expect(interaction.coords.cur).toEqual(helpers.getProps(event, ['page', 'client', 'timeStamp'])) + + // pointerIsDown + expect(interaction.pointerIsDown).toBe(true) + // !pointerWasMoved + expect(interaction.pointerWasMoved).toBe(false) + + // pointer in down signal arg + expect(signalArg.pointer).toBe(event) + // event in down signal arg + expect(signalArg.event).toBe(event) + // eventTarget in down signal arg + expect(signalArg.eventTarget).toBe(target) + // pointerIndex in down signal arg + expect(signalArg.pointerIndex).toBe(0) + + // test while not interacting + interaction._interacting = false + // reset pointerIsDown + interaction.pointerIsDown = false + // pretend pointer was moved + interaction.pointerWasMoved = true + // reset signalArg object + signalArg = undefined + + interaction.removePointer(event, null) + interaction.pointerDown(event, event, target) + + // timeStamp is assigned with new Date.getTime() + // don't let it cause deepEaual to fail + pointerCoords.timeStamp = interaction.coords.start.timeStamp + + // downEvent is updated + expect(interaction.downEvent).toBe(event) + + // interaction.pointers is updated + expect(interaction.pointers).toEqual([ + { + id: event.pointerId, + event, + pointer: event, + downTime: pointerCoords.timeStamp, + downTarget: target, + }, + ]) + + // coords.start are set to pointer + expect(interaction.coords.start).toEqual(pointerCoords) + // coords.cur are set to pointer + expect(interaction.coords.cur).toEqual(pointerCoords) + // coords.prev are set to pointer + expect(interaction.coords.prev).toEqual(pointerCoords) + + // down signal was fired again + expect(signalArg).toBeInstanceOf(Object) + // pointerIsDown + expect(interaction.pointerIsDown).toBe(true) + // pointerWasMoved should always change to false + expect(interaction.pointerWasMoved).toBe(false) + }) + + test('Interaction.start', () => { + const { + interaction, + interactable, + scope, + event, + target: element, + down, + stop, + } = helpers.testEnv({ + plugins: [drag], + }) + const action = { name: 'drag' } as const + + interaction.start(action, interactable, element) + // do nothing if !pointerIsDown + expect(interaction.prepared.name).toBeNull() + + // pointers is still empty + interaction.pointerIsDown = true + interaction.start(action, interactable, element) + // do nothing if too few pointers are down + expect(interaction.prepared.name).toBeNull() + + down() + + interaction._interacting = true + interaction.start(action, interactable, element) + // do nothing if already interacting + expect(interaction.prepared.name).toBeNull() + + interaction._interacting = false + + interactable.options[action.name] = { enabled: false } + interaction.start(action, interactable, element) + // do nothing if action is not enabled + expect(interaction.prepared.name).toBeNull() + interactable.options[action.name] = { enabled: true } + + let signalArg: any + + // let interactingInStartListener + const signalListener = (arg: any) => { + signalArg = arg + // interactingInStartListener = arg.interaction.interacting() + } + + scope.addListeners({ + 'interactions:action-start': signalListener, + }) + interaction.start(action, interactable, element) + + // action is prepared + expect(interaction.prepared.name).toBe(action.name) + // interaction.interactable is updated + expect(interaction.interactable).toBe(interactable) + // interaction.element is updated + expect(interaction.element).toBe(element) + + // t.assert(interactingInStartListener, 'interaction is interacting during action-start signal') + // interaction is interacting after start method + expect(interaction.interacting()).toBe(true) + // interaction in signal arg + expect(signalArg.interaction).toBe(interaction) + // event (interaction.downEvent) in signal arg + expect(signalArg.event).toBe(event) + + stop() + }) + + test('interaction move() and stop() from start event', () => { + const { interaction, interactable, target, down } = helpers.testEnv({ plugins: [drag, drop, autoStart] }) + + let stoppedBeforeStartFired: boolean + + interactable.draggable({ + listeners: { + start(event) { + stoppedBeforeStartFired = interaction._stopped + + // interaction.move() doesn't throw from start event + expect(() => event.interaction.move()).not.toThrow() + + // interaction.stop() doesn't throw from start event + expect(() => event.interaction.stop()).not.toThrow() + }, + }, + }) + + down() + interaction.start({ name: 'drag' }, interactable, target as HTMLElement) + + // !interaction._stopped in start listener + expect(stoppedBeforeStartFired).toBe(false) + // interaction can be stopped from start event listener + expect(interaction.interacting()).toBe(false) + // interaction._stopped after stop() in start listener + expect(interaction._stopped).toBe(true) + }) + + test('Interaction createPreparedEvent', () => { + const { interaction, interactable, target } = helpers.testEnv() + + const action = { name: 'resize' } as const + const phase = 'TEST_PHASE' as EventPhase + + interaction.prepared = action + interaction.interactable = interactable + interaction.element = target + interaction.prevEvent = { page: {}, client: {}, velocity: {} } as any + + const iEvent = interaction._createPreparedEvent({} as any, phase) + + expect(iEvent).toBeInstanceOf(InteractEvent) + + expect(iEvent.type).toBe(action.name + phase) + + expect(iEvent.interactable).toBe(interactable) + + expect(iEvent.target).toBe(interactable.target) + }) + + test('Interaction fireEvent', () => { + const { interaction, interactable } = helpers.testEnv() + const iEvent = {} as InteractEvent + + // this method should be called from actions.firePrepared + interactable.fire = jest.fn() + + interaction.interactable = interactable + interaction._fireEvent(iEvent) + + // target interactable's fire method is called + expect(interactable.fire).toHaveBeenCalledWith(iEvent) + + // interaction.prevEvent is updated + expect(interaction.prevEvent).toBe(iEvent) + }) +}) diff --git a/packages/@interactjs/core/Interaction.ts b/packages/@interactjs/core/Interaction.ts new file mode 100644 index 000000000..94fd81625 --- /dev/null +++ b/packages/@interactjs/core/Interaction.ts @@ -0,0 +1,643 @@ +import * as arr from '@interactjs/utils/arr' +import extend from '@interactjs/utils/extend' +import hypot from '@interactjs/utils/hypot' +import { warnOnce, copyAction } from '@interactjs/utils/misc' +import * as pointerUtils from '@interactjs/utils/pointerUtils' +import * as rectUtils from '@interactjs/utils/rect' + +import type { + Element, + EdgeOptions, + PointerEventType, + PointerType, + FullRect, + CoordsSet, + ActionName, + ActionProps, +} from '@interactjs/core/types' + +import type { Interactable } from './Interactable' +import type { EventPhase } from './InteractEvent' +import { InteractEvent } from './InteractEvent' +import type { ActionDefaults } from './options' +import { PointerInfo } from './PointerInfo' +import type { Scope } from './scope' + +export enum _ProxyValues { + interactable = '', + element = '', + prepared = '', + pointerIsDown = '', + pointerWasMoved = '', + _proxy = '', +} + +export enum _ProxyMethods { + start = '', + move = '', + end = '', + stop = '', + interacting = '', +} + +export type PointerArgProps = { + pointer: PointerType + event: PointerEventType + eventTarget: Node + pointerIndex: number + pointerInfo: PointerInfo + interaction: Interaction +} & T + +export interface DoPhaseArg { + event: PointerEventType + phase: EventPhase + interaction: Interaction + iEvent: InteractEvent + preEnd?: boolean + type?: string +} + +export type DoAnyPhaseArg = DoPhaseArg + +declare module '@interactjs/core/scope' { + interface SignalArgs { + 'interactions:new': { interaction: Interaction } + 'interactions:down': PointerArgProps<{ + type: 'down' + }> + 'interactions:move': PointerArgProps<{ + type: 'move' + dx: number + dy: number + duplicate: boolean + }> + 'interactions:up': PointerArgProps<{ + type: 'up' + curEventTarget: EventTarget + }> + 'interactions:cancel': SignalArgs['interactions:up'] & { + type: 'cancel' + curEventTarget: EventTarget + } + 'interactions:update-pointer': PointerArgProps<{ + down: boolean + }> + 'interactions:remove-pointer': PointerArgProps + 'interactions:blur': { interaction: Interaction; event: Event; type: 'blur' } + 'interactions:before-action-start': Omit + 'interactions:action-start': DoAnyPhaseArg + 'interactions:after-action-start': DoAnyPhaseArg + 'interactions:before-action-move': Omit + 'interactions:action-move': DoAnyPhaseArg + 'interactions:after-action-move': DoAnyPhaseArg + 'interactions:before-action-end': Omit + 'interactions:action-end': DoAnyPhaseArg + 'interactions:after-action-end': DoAnyPhaseArg + 'interactions:stop': { interaction: Interaction } + } +} + +export type InteractionProxy = Pick< + Interaction, + Exclude +> + +let idCounter = 0 + +export class Interaction { + /** current interactable being interacted with */ + interactable: Interactable | null = null + + /** the target element of the interactable */ + element: Element | null = null + rect: FullRect | null = null + /** @internal */ + _rects?: { + start: FullRect + corrected: FullRect + previous: FullRect + delta: FullRect + } + /** @internal */ + edges: EdgeOptions | null = null + + /** @internal */ + _scopeFire: Scope['fire'] + + // action that's ready to be fired on next move event + prepared: ActionProps = { + name: null, + axis: null, + edges: null, + } + + pointerType: string + + /** @internal keep track of added pointers */ + pointers: PointerInfo[] = [] + + /** @internal pointerdown/mousedown/touchstart event */ + downEvent: PointerEventType | null = null + + /** @internal */ downPointer: PointerType = {} as PointerType + + /** @internal */ + _latestPointer: { + pointer: PointerType + event: PointerEventType + eventTarget: Node + } = { + pointer: null, + event: null, + eventTarget: null, + } + + /** @internal */ prevEvent: InteractEvent = null + + pointerIsDown = false + pointerWasMoved = false + /** @internal */ _interacting = false + /** @internal */ _ending = false + /** @internal */ _stopped = true + /** @internal */ _proxy: InteractionProxy + + /** @internal */ simulation = null + + /** @internal */ get pointerMoveTolerance() { + return 1 + } + + doMove = warnOnce(function (this: Interaction, signalArg: any) { + this.move(signalArg) + }, 'The interaction.doMove() method has been renamed to interaction.move()') + + coords: CoordsSet = { + // Starting InteractEvent pointer coordinates + start: pointerUtils.newCoords(), + // Previous native pointer move event coordinates + prev: pointerUtils.newCoords(), + // current native pointer move event coordinates + cur: pointerUtils.newCoords(), + // Change in coordinates and time of the pointer + delta: pointerUtils.newCoords(), + // pointer velocity + velocity: pointerUtils.newCoords(), + } + + /** @internal */ readonly _id: number = idCounter++ + + constructor({ pointerType, scopeFire }: { pointerType?: string; scopeFire: Scope['fire'] }) { + this._scopeFire = scopeFire + this.pointerType = pointerType + + const that = this + + this._proxy = {} as InteractionProxy + + for (const key in _ProxyValues) { + Object.defineProperty(this._proxy, key, { + get() { + return that[key] + }, + }) + } + + for (const key in _ProxyMethods) { + Object.defineProperty(this._proxy, key, { + value: (...args: any[]) => that[key](...args), + }) + } + + this._scopeFire('interactions:new', { interaction: this }) + } + + pointerDown(pointer: PointerType, event: PointerEventType, eventTarget: Node) { + const pointerIndex = this.updatePointer(pointer, event, eventTarget, true) + const pointerInfo = this.pointers[pointerIndex] + + this._scopeFire('interactions:down', { + pointer, + event, + eventTarget, + pointerIndex, + pointerInfo, + type: 'down', + interaction: this as unknown as Interaction, + }) + } + + /** + * ```js + * interact(target) + * .draggable({ + * // disable the default drag start by down->move + * manualStart: true + * }) + * // start dragging after the user holds the pointer down + * .on('hold', function (event) { + * var interaction = event.interaction + * + * if (!interaction.interacting()) { + * interaction.start({ name: 'drag' }, + * event.interactable, + * event.currentTarget) + * } + * }) + * ``` + * + * Start an action with the given Interactable and Element as tartgets. The + * action must be enabled for the target Interactable and an appropriate + * number of pointers must be held down - 1 for drag/resize, 2 for gesture. + * + * Use it with `interactable.able({ manualStart: false })` to always + * [start actions manually](https://github.com/taye/interact.js/issues/114) + * + * @param action - The action to be performed - drag, resize, etc. + * @param target - The Interactable to target + * @param element - The DOM Element to target + * @returns Whether the interaction was successfully started + */ + start(action: ActionProps, interactable: Interactable, element: Element): boolean { + if ( + this.interacting() || + !this.pointerIsDown || + this.pointers.length < (action.name === 'gesture' ? 2 : 1) || + !interactable.options[action.name as keyof ActionDefaults].enabled + ) { + return false + } + + copyAction(this.prepared, action) + + this.interactable = interactable + this.element = element + this.rect = interactable.getRect(element) + this.edges = this.prepared.edges + ? extend({}, this.prepared.edges) + : { left: true, right: true, top: true, bottom: true } + this._stopped = false + this._interacting = + this._doPhase({ + interaction: this, + event: this.downEvent, + phase: 'start', + }) && !this._stopped + + return this._interacting + } + + pointerMove(pointer: PointerType, event: PointerEventType, eventTarget: Node) { + if (!this.simulation && !(this.modification && this.modification.endResult)) { + this.updatePointer(pointer, event, eventTarget, false) + } + + const duplicateMove = + this.coords.cur.page.x === this.coords.prev.page.x && + this.coords.cur.page.y === this.coords.prev.page.y && + this.coords.cur.client.x === this.coords.prev.client.x && + this.coords.cur.client.y === this.coords.prev.client.y + + let dx: number + let dy: number + + // register movement greater than pointerMoveTolerance + if (this.pointerIsDown && !this.pointerWasMoved) { + dx = this.coords.cur.client.x - this.coords.start.client.x + dy = this.coords.cur.client.y - this.coords.start.client.y + + this.pointerWasMoved = hypot(dx, dy) > this.pointerMoveTolerance + } + + const pointerIndex = this.getPointerIndex(pointer) + const signalArg = { + pointer, + pointerIndex, + pointerInfo: this.pointers[pointerIndex], + event, + type: 'move' as const, + eventTarget, + dx, + dy, + duplicate: duplicateMove, + interaction: this as unknown as Interaction, + } + + if (!duplicateMove) { + // set pointer coordinate, time changes and velocity + pointerUtils.setCoordVelocity(this.coords.velocity, this.coords.delta) + } + + this._scopeFire('interactions:move', signalArg) + + if (!duplicateMove && !this.simulation) { + // if interacting, fire an 'action-move' signal etc + if (this.interacting()) { + signalArg.type = null + this.move(signalArg) + } + + if (this.pointerWasMoved) { + pointerUtils.copyCoords(this.coords.prev, this.coords.cur) + } + } + } + + /** + * ```js + * interact(target) + * .draggable(true) + * .on('dragmove', function (event) { + * if (someCondition) { + * // change the snap settings + * event.interactable.draggable({ snap: { targets: [] }}) + * // fire another move event with re-calculated snap + * event.interaction.move() + * } + * }) + * ``` + * + * Force a move of the current action at the same coordinates. Useful if + * snap/restrict has been changed and you want a movement with the new + * settings. + */ + move(signalArg?: any) { + if (!signalArg || !signalArg.event) { + pointerUtils.setZeroCoords(this.coords.delta) + } + + signalArg = extend( + { + pointer: this._latestPointer.pointer, + event: this._latestPointer.event, + eventTarget: this._latestPointer.eventTarget, + interaction: this, + }, + signalArg || {}, + ) + + signalArg.phase = 'move' + + this._doPhase(signalArg) + } + + /** + * @internal + * End interact move events and stop auto-scroll unless simulation is running + */ + pointerUp(pointer: PointerType, event: PointerEventType, eventTarget: Node, curEventTarget: EventTarget) { + let pointerIndex = this.getPointerIndex(pointer) + + if (pointerIndex === -1) { + pointerIndex = this.updatePointer(pointer, event, eventTarget, false) + } + + const type = /cancel$/i.test(event.type) ? 'cancel' : 'up' + + this._scopeFire(`interactions:${type}` as 'interactions:up' | 'interactions:cancel', { + pointer, + pointerIndex, + pointerInfo: this.pointers[pointerIndex], + event, + eventTarget, + type: type as any, + curEventTarget, + interaction: this as unknown as Interaction, + }) + + if (!this.simulation) { + this.end(event) + } + + this.removePointer(pointer, event) + } + + /** @internal */ + documentBlur(event: Event) { + this.end(event as any) + this._scopeFire('interactions:blur', { + event, + type: 'blur', + interaction: this as unknown as Interaction, + }) + } + + /** + * ```js + * interact(target) + * .draggable(true) + * .on('move', function (event) { + * if (event.pageX > 1000) { + * // end the current action + * event.interaction.end() + * // stop all further listeners from being called + * event.stopImmediatePropagation() + * } + * }) + * ``` + */ + end(event?: PointerEventType) { + this._ending = true + event = event || this._latestPointer.event + let endPhaseResult: boolean + + if (this.interacting()) { + endPhaseResult = this._doPhase({ + event, + interaction: this, + phase: 'end', + }) + } + + this._ending = false + + if (endPhaseResult === true) { + this.stop() + } + } + + currentAction() { + return this._interacting ? this.prepared.name : null + } + + interacting() { + return this._interacting + } + + stop() { + this._scopeFire('interactions:stop', { interaction: this }) + + this.interactable = this.element = null + + this._interacting = false + this._stopped = true + this.prepared.name = this.prevEvent = null + } + + /** @internal */ + getPointerIndex(pointer: any) { + const pointerId = pointerUtils.getPointerId(pointer) + + // mouse and pen interactions may have only one pointer + return this.pointerType === 'mouse' || this.pointerType === 'pen' + ? this.pointers.length - 1 + : arr.findIndex(this.pointers, (curPointer) => curPointer.id === pointerId) + } + + /** @internal */ + getPointerInfo(pointer: any) { + return this.pointers[this.getPointerIndex(pointer)] + } + + /** @internal */ + updatePointer(pointer: PointerType, event: PointerEventType, eventTarget: Node, down?: boolean) { + const id = pointerUtils.getPointerId(pointer) + let pointerIndex = this.getPointerIndex(pointer) + let pointerInfo = this.pointers[pointerIndex] + + down = down === false ? false : down || /(down|start)$/i.test(event.type) + + if (!pointerInfo) { + pointerInfo = new PointerInfo(id, pointer, event, null, null) + + pointerIndex = this.pointers.length + this.pointers.push(pointerInfo) + } else { + pointerInfo.pointer = pointer + } + + pointerUtils.setCoords( + this.coords.cur, + this.pointers.map((p) => p.pointer), + this._now(), + ) + pointerUtils.setCoordDeltas(this.coords.delta, this.coords.prev, this.coords.cur) + + if (down) { + this.pointerIsDown = true + + pointerInfo.downTime = this.coords.cur.timeStamp + pointerInfo.downTarget = eventTarget + pointerUtils.pointerExtend(this.downPointer, pointer) + + if (!this.interacting()) { + pointerUtils.copyCoords(this.coords.start, this.coords.cur) + pointerUtils.copyCoords(this.coords.prev, this.coords.cur) + + this.downEvent = event + this.pointerWasMoved = false + } + } + + this._updateLatestPointer(pointer, event, eventTarget) + + this._scopeFire('interactions:update-pointer', { + pointer, + event, + eventTarget, + down, + pointerInfo, + pointerIndex, + interaction: this as unknown as Interaction, + }) + + return pointerIndex + } + + /** @internal */ + removePointer(pointer: PointerType, event: PointerEventType) { + const pointerIndex = this.getPointerIndex(pointer) + + if (pointerIndex === -1) return + + const pointerInfo = this.pointers[pointerIndex] + + this._scopeFire('interactions:remove-pointer', { + pointer, + event, + eventTarget: null, + pointerIndex, + pointerInfo, + interaction: this as unknown as Interaction, + }) + + this.pointers.splice(pointerIndex, 1) + this.pointerIsDown = false + } + + /** @internal */ + _updateLatestPointer(pointer: PointerType, event: PointerEventType, eventTarget: Node) { + this._latestPointer.pointer = pointer + this._latestPointer.event = event + this._latestPointer.eventTarget = eventTarget + } + + destroy() { + this._latestPointer.pointer = null + this._latestPointer.event = null + this._latestPointer.eventTarget = null + } + + /** @internal */ + _createPreparedEvent

( + event: PointerEventType, + phase: P, + preEnd?: boolean, + type?: string, + ) { + return new InteractEvent(this, event, this.prepared.name, phase, this.element, preEnd, type) + } + + /** @internal */ + _fireEvent

(iEvent: InteractEvent) { + this.interactable?.fire(iEvent) + + if (!this.prevEvent || iEvent.timeStamp >= this.prevEvent.timeStamp) { + this.prevEvent = iEvent + } + } + + /** @internal */ + _doPhase

( + signalArg: Omit, 'iEvent'> & { iEvent?: InteractEvent }, + ) { + const { event, phase, preEnd, type } = signalArg + const { rect } = this + + if (rect && phase === 'move') { + // update the rect changes due to pointer move + rectUtils.addEdges(this.edges, rect, this.coords.delta[this.interactable.options.deltaSource]) + + rect.width = rect.right - rect.left + rect.height = rect.bottom - rect.top + } + + const beforeResult = this._scopeFire(`interactions:before-action-${phase}` as any, signalArg) + + if (beforeResult === false) { + return false + } + + const iEvent = (signalArg.iEvent = this._createPreparedEvent(event, phase, preEnd, type)) + + this._scopeFire(`interactions:action-${phase}` as any, signalArg) + + if (phase === 'start') { + this.prevEvent = iEvent + } + + this._fireEvent(iEvent) + + this._scopeFire(`interactions:after-action-${phase}` as any, signalArg) + + return true + } + + /** @internal */ + _now() { + return Date.now() + } +} + +export default Interaction +export { PointerInfo } diff --git a/packages/@interactjs/core/NativeTypes.ts b/packages/@interactjs/core/NativeTypes.ts new file mode 100644 index 000000000..d4f6b385a --- /dev/null +++ b/packages/@interactjs/core/NativeTypes.ts @@ -0,0 +1,3 @@ +export const NativePointerEvent = null as unknown as InstanceType +export type NativeEventTarget = EventTarget +export type NativeElement = Element diff --git a/packages/@interactjs/core/PointerInfo.ts b/packages/@interactjs/core/PointerInfo.ts new file mode 100644 index 000000000..6a9dd11a9 --- /dev/null +++ b/packages/@interactjs/core/PointerInfo.ts @@ -0,0 +1,17 @@ +import type { PointerEventType, PointerType } from '@interactjs/core/types' + +export class PointerInfo { + id: number + pointer: PointerType + event: PointerEventType + downTime: number + downTarget: Node + + constructor(id: number, pointer: PointerType, event: PointerEventType, downTime: number, downTarget: Node) { + this.id = id + this.pointer = pointer + this.event = event + this.downTime = downTime + this.downTarget = downTarget + } +} diff --git a/packages/@interactjs/core/README.md b/packages/@interactjs/core/README.md new file mode 100644 index 000000000..549491910 --- /dev/null +++ b/packages/@interactjs/core/README.md @@ -0,0 +1,5 @@ +

+This package is an internal part of interactjs and is not meant +to be used independently as each update may introduce breaking changes +

diff --git a/packages/@interactjs/core/events.ts b/packages/@interactjs/core/events.ts new file mode 100644 index 000000000..3a4ac9a31 --- /dev/null +++ b/packages/@interactjs/core/events.ts @@ -0,0 +1,341 @@ +import * as arr from '@interactjs/utils/arr' +import * as domUtils from '@interactjs/utils/domUtils' +import is from '@interactjs/utils/is' +import pExtend from '@interactjs/utils/pointerExtend' +import * as pointerUtils from '@interactjs/utils/pointerUtils' + +import type { Scope } from '@interactjs/core/scope' +import type { Element } from '@interactjs/core/types' + +import type { NativeEventTarget } from './NativeTypes' + +declare module '@interactjs/core/scope' { + interface Scope { + events: ReturnType + } +} + +interface EventOptions { + capture: boolean + passive: boolean +} + +type PartialEventTarget = Partial + +type ListenerEntry = { func: (event: Event | FakeEvent) => any; options: EventOptions } + +function install(scope: Scope) { + const targets: Array<{ + eventTarget: PartialEventTarget + events: { [type: string]: ListenerEntry[] } + }> = [] + + const delegatedEvents: { + [type: string]: Array<{ + selector: string + context: Node + listeners: ListenerEntry[] + }> + } = {} + const documents: Document[] = [] + + const eventsMethods = { + add, + remove, + + addDelegate, + removeDelegate, + + delegateListener, + delegateUseCapture, + delegatedEvents, + documents, + + targets, + + supportsOptions: false, + supportsPassive: false, + } + + // check if browser supports passive events and options arg + scope.document?.createElement('div').addEventListener('test', null, { + get capture() { + return (eventsMethods.supportsOptions = true) + }, + get passive() { + return (eventsMethods.supportsPassive = true) + }, + }) + + scope.events = eventsMethods + + function add( + eventTarget: PartialEventTarget, + type: string, + listener: ListenerEntry['func'], + optionalArg?: boolean | EventOptions, + ) { + if (!eventTarget.addEventListener) return + + const options = getOptions(optionalArg) + let target = arr.find(targets, (t) => t.eventTarget === eventTarget) + + if (!target) { + target = { + eventTarget, + events: {}, + } + + targets.push(target) + } + + if (!target.events[type]) { + target.events[type] = [] + } + + if (!arr.find(target.events[type], (l) => l.func === listener && optionsMatch(l.options, options))) { + eventTarget.addEventListener( + type, + listener as any, + eventsMethods.supportsOptions ? options : options.capture, + ) + target.events[type].push({ func: listener, options }) + } + } + + function remove( + eventTarget: PartialEventTarget, + type: string, + listener?: 'all' | ListenerEntry['func'], + optionalArg?: boolean | EventOptions, + ) { + if (!eventTarget.addEventListener || !eventTarget.removeEventListener) return + + const targetIndex = arr.findIndex(targets, (t) => t.eventTarget === eventTarget) + const target = targets[targetIndex] + + if (!target || !target.events) { + return + } + + if (type === 'all') { + for (type in target.events) { + if (target.events.hasOwnProperty(type)) { + remove(eventTarget, type, 'all') + } + } + return + } + + let typeIsEmpty = false + const typeListeners = target.events[type] + + if (typeListeners) { + if (listener === 'all') { + for (let i = typeListeners.length - 1; i >= 0; i--) { + const entry = typeListeners[i] + remove(eventTarget, type, entry.func, entry.options) + } + return + } else { + const options = getOptions(optionalArg) + + for (let i = 0; i < typeListeners.length; i++) { + const entry = typeListeners[i] + if (entry.func === listener && optionsMatch(entry.options, options)) { + eventTarget.removeEventListener( + type, + listener as any, + eventsMethods.supportsOptions ? options : options.capture, + ) + typeListeners.splice(i, 1) + + if (typeListeners.length === 0) { + delete target.events[type] + typeIsEmpty = true + } + + break + } + } + } + } + + if (typeIsEmpty && !Object.keys(target.events).length) { + targets.splice(targetIndex, 1) + } + } + + function addDelegate( + selector: string, + context: Node, + type: string, + listener: ListenerEntry['func'], + optionalArg?: any, + ) { + const options = getOptions(optionalArg) + if (!delegatedEvents[type]) { + delegatedEvents[type] = [] + + // add delegate listener functions + for (const doc of documents) { + add(doc, type, delegateListener) + add(doc, type, delegateUseCapture, true) + } + } + + const delegates = delegatedEvents[type] + let delegate = arr.find(delegates, (d) => d.selector === selector && d.context === context) + + if (!delegate) { + delegate = { selector, context, listeners: [] } + delegates.push(delegate) + } + + delegate.listeners.push({ func: listener, options }) + } + + function removeDelegate( + selector: string, + context: Document | Element, + type: string, + listener?: ListenerEntry['func'], + optionalArg?: any, + ) { + const options = getOptions(optionalArg) + const delegates = delegatedEvents[type] + let matchFound = false + let index: number + + if (!delegates) return + + // count from last index of delegated to 0 + for (index = delegates.length - 1; index >= 0; index--) { + const cur = delegates[index] + // look for matching selector and context Node + if (cur.selector === selector && cur.context === context) { + const { listeners } = cur + + // each item of the listeners array is an array: [function, capture, passive] + for (let i = listeners.length - 1; i >= 0; i--) { + const entry = listeners[i] + + // check if the listener functions and capture and passive flags match + if (entry.func === listener && optionsMatch(entry.options, options)) { + // remove the listener from the array of listeners + listeners.splice(i, 1) + + // if all listeners for this target have been removed + // remove the target from the delegates array + if (!listeners.length) { + delegates.splice(index, 1) + + // remove delegate function from context + remove(context, type, delegateListener) + remove(context, type, delegateUseCapture, true) + } + + // only remove one listener + matchFound = true + break + } + } + + if (matchFound) { + break + } + } + } + } + + // bound to the interactable context when a DOM event + // listener is added to a selector interactable + function delegateListener(event: Event | FakeEvent, optionalArg?: any) { + const options = getOptions(optionalArg) + const fakeEvent = new FakeEvent(event as Event) + const delegates = delegatedEvents[event.type] + const [eventTarget] = pointerUtils.getEventTargets(event as Event) + let element: Node = eventTarget + + // climb up document tree looking for selector matches + while (is.element(element)) { + for (let i = 0; i < delegates.length; i++) { + const cur = delegates[i] + const { selector, context } = cur + + if ( + domUtils.matchesSelector(element, selector) && + domUtils.nodeContains(context, eventTarget) && + domUtils.nodeContains(context, element) + ) { + const { listeners } = cur + + fakeEvent.currentTarget = element + + for (const entry of listeners) { + if (optionsMatch(entry.options, options)) { + entry.func(fakeEvent) + } + } + } + } + + element = domUtils.parentNode(element) + } + } + + function delegateUseCapture(this: Element, event: Event | FakeEvent) { + return delegateListener.call(this, event, true) + } + + // for type inferrence + return eventsMethods +} + +class FakeEvent implements Partial { + currentTarget: Node + originalEvent: Event + type: string + + constructor(originalEvent: Event) { + this.originalEvent = originalEvent + // duplicate the event so that currentTarget can be changed + pExtend(this, originalEvent) + } + + preventOriginalDefault() { + this.originalEvent.preventDefault() + } + + stopPropagation() { + this.originalEvent.stopPropagation() + } + + stopImmediatePropagation() { + this.originalEvent.stopImmediatePropagation() + } +} + +function getOptions(param: { [index: string]: any } | boolean): { capture: boolean; passive: boolean } { + if (!is.object(param)) { + return { capture: !!param, passive: false } + } + + return { + capture: !!param.capture, + passive: !!param.passive, + } +} + +function optionsMatch(a: Partial | boolean, b: Partial) { + if (a === b) return true + + if (typeof a === 'boolean') return !!b.capture === a && !!b.passive === false + + return !!a.capture === !!b.capture && !!a.passive === !!b.passive +} + +export default { + id: 'events', + install, +} diff --git a/packages/@interactjs/core/interactablePreventDefault.spec.ts b/packages/@interactjs/core/interactablePreventDefault.spec.ts new file mode 100644 index 000000000..85fe20808 --- /dev/null +++ b/packages/@interactjs/core/interactablePreventDefault.spec.ts @@ -0,0 +1,28 @@ +import drag from '@interactjs/actions/drag/plugin' +import autoStart from '@interactjs/auto-start/base' + +import interactablePreventDefault from './interactablePreventDefault' +import * as helpers from './tests/_helpers' + +test('core/interactablePreventDefault', () => { + window.PointerEvent = null + + const { scope, interactable } = helpers.testEnv({ + plugins: [interactablePreventDefault, autoStart, drag], + }) + + const { MouseEvent, Event } = scope.window as any + + interactable.draggable({}) + + const mouseDown: MouseEvent = new MouseEvent('mousedown', { bubbles: true }) + const nativeDragStart: Event = new Event('dragstart', { bubbles: true }) + + nativeDragStart.preventDefault = jest.fn() + + scope.document.body.dispatchEvent(mouseDown) + scope.document.body.dispatchEvent(nativeDragStart) + + // native dragstart is prevented on interactable + expect(nativeDragStart.preventDefault).toHaveBeenCalledTimes(1) +}) diff --git a/packages/@interactjs/core/interactablePreventDefault.ts b/packages/@interactjs/core/interactablePreventDefault.ts new file mode 100644 index 000000000..f43d48dc0 --- /dev/null +++ b/packages/@interactjs/core/interactablePreventDefault.ts @@ -0,0 +1,124 @@ +import { matchesSelector, nodeContains } from '@interactjs/utils/domUtils' +import is from '@interactjs/utils/is' +import { getWindow } from '@interactjs/utils/window' + +import type { Interactable } from '@interactjs/core/Interactable' +import type Interaction from '@interactjs/core/Interaction' +import type { Scope } from '@interactjs/core/scope' +import type { PointerEventType } from '@interactjs/core/types' + +type PreventDefaultValue = 'always' | 'never' | 'auto' + +declare module '@interactjs/core/Interactable' { + interface Interactable { + preventDefault(newValue: PreventDefaultValue): this + preventDefault(): PreventDefaultValue + /** + * Returns or sets whether to prevent the browser's default behaviour in + * response to pointer events. Can be set to: + * - `'always'` to always prevent + * - `'never'` to never prevent + * - `'auto'` to let interact.js try to determine what would be best + * + * @param newValue - `'always'`, `'never'` or `'auto'` + * @returns The current setting or this Interactable + */ + preventDefault(newValue?: PreventDefaultValue): PreventDefaultValue | this + checkAndPreventDefault(event: Event): void + } +} + +const preventDefault = function preventDefault(this: Interactable, newValue?: PreventDefaultValue) { + if (/^(always|never|auto)$/.test(newValue)) { + this.options.preventDefault = newValue + return this + } + + if (is.bool(newValue)) { + this.options.preventDefault = newValue ? 'always' : 'never' + return this + } + + return this.options.preventDefault +} as Interactable['preventDefault'] + +function checkAndPreventDefault(interactable: Interactable, scope: Scope, event: Event) { + const setting = interactable.options.preventDefault + + if (setting === 'never') return + + if (setting === 'always') { + event.preventDefault() + return + } + + // setting === 'auto' + + // if the browser supports passive event listeners and isn't running on iOS, + // don't preventDefault of touch{start,move} events. CSS touch-action and + // user-select should be used instead of calling event.preventDefault(). + if (scope.events.supportsPassive && /^touch(start|move)$/.test(event.type)) { + const doc = getWindow(event.target).document + const docOptions = scope.getDocOptions(doc) + + if (!(docOptions && docOptions.events) || docOptions.events.passive !== false) { + return + } + } + + // don't preventDefault of pointerdown events + if (/^(mouse|pointer|touch)*(down|start)/i.test(event.type)) { + return + } + + // don't preventDefault on editable elements + if ( + is.element(event.target) && + matchesSelector(event.target, 'input,select,textarea,[contenteditable=true],[contenteditable=true] *') + ) { + return + } + + event.preventDefault() +} + +function onInteractionEvent({ interaction, event }: { interaction: Interaction; event: PointerEventType }) { + if (interaction.interactable) { + interaction.interactable.checkAndPreventDefault(event as Event) + } +} + +export function install(scope: Scope) { + const { Interactable } = scope + + Interactable.prototype.preventDefault = preventDefault + + Interactable.prototype.checkAndPreventDefault = function (event) { + return checkAndPreventDefault(this, scope, event) + } + + // prevent native HTML5 drag on interact.js target elements + scope.interactions.docEvents.push({ + type: 'dragstart', + listener(event) { + for (const interaction of scope.interactions.list) { + if ( + interaction.element && + (interaction.element === event.target || nodeContains(interaction.element, event.target)) + ) { + interaction.interactable.checkAndPreventDefault(event) + return + } + } + }, + }) +} + +export default { + id: 'core/interactablePreventDefault', + install, + listeners: ['down', 'move', 'up', 'cancel'].reduce((acc, eventType) => { + acc[`interactions:${eventType}`] = onInteractionEvent + return acc + }, {} as any), +} diff --git a/packages/@interactjs/core/interactionFinder.spec.ts b/packages/@interactjs/core/interactionFinder.spec.ts new file mode 100644 index 000000000..da987b0c0 --- /dev/null +++ b/packages/@interactjs/core/interactionFinder.spec.ts @@ -0,0 +1,61 @@ +import finder from './interactionFinder' +import * as helpers from './tests/_helpers' + +test('modifiers/snap', () => { + const { interactable, event, coords, scope } = helpers.testEnv() + + const { body } = scope.document + + const { list } = scope.interactions + const details = { + pointer: event, + get pointerId(): number { + return details.pointer.pointerId + }, + get pointerType(): string { + return details.pointer.pointerType + }, + eventType: null as string, + eventTarget: body, + curEventTarget: scope.document, + scope, + } + + scope.interactions.new({ pointerType: 'touch' }) + scope.interactions.new({ pointerType: 'mouse' }) + + coords.pointerType = 'mouse' + list[0].pointerType = 'mouse' + list[2]._interacting = true + + // [pointerType: mouse] skips inactive mouse and touch interaction + expect(list.indexOf(finder.search(details))).toBe(2) + + list[2]._interacting = false + + // [pointerType: mouse] returns first idle mouse interaction + expect(list.indexOf(finder.search(details))).toBe(0) + + coords.pointerId = 4 + list[1].pointerDown({ ...event } as any, { ...event } as any, body) + coords.pointerType = 'touch' + + // [pointerType: touch] gets interaction with pointerId + expect(list.indexOf(finder.search(details))).toBe(1) + + coords.pointerId = 5 + + // `[pointerType: touch] returns idle touch interaction without matching pointerId and existing touch interaction has pointer and no target` + expect(list.indexOf(finder.search(details))).toBe(1) + + interactable.options.gesture = { enabled: false } + list[1].interactable = interactable + + // `[pointerType: touch] no result without matching pointerId and existing touch interaction has a pointer and target not gesturable` + expect(list.indexOf(finder.search(details))).toBe(-1) + + interactable.options.gesture = { enabled: true } + + // `[pointerType: touch] returns idle touch interaction with gesturable target and existing pointer` + expect(list.indexOf(finder.search(details))).toBe(1) +}) diff --git a/packages/@interactjs/core/interactionFinder.ts b/packages/@interactjs/core/interactionFinder.ts new file mode 100644 index 000000000..ff56b6dc6 --- /dev/null +++ b/packages/@interactjs/core/interactionFinder.ts @@ -0,0 +1,144 @@ +import * as dom from '@interactjs/utils/domUtils' + +import type Interaction from '@interactjs/core/Interaction' +import type { Scope } from '@interactjs/core/scope' +import type { PointerType } from '@interactjs/core/types' + +export interface SearchDetails { + pointer: PointerType + pointerId: number + pointerType: string + eventType: string + eventTarget: EventTarget + curEventTarget: EventTarget + scope: Scope +} + +const finder = { + methodOrder: ['simulationResume', 'mouseOrPen', 'hasPointer', 'idle'] as const, + + search(details: SearchDetails) { + for (const method of finder.methodOrder) { + const interaction = finder[method](details) + + if (interaction) { + return interaction + } + } + + return null + }, + + // try to resume simulation with a new pointer + simulationResume({ pointerType, eventType, eventTarget, scope }: SearchDetails) { + if (!/down|start/i.test(eventType)) { + return null + } + + for (const interaction of scope.interactions.list) { + let element = eventTarget as Node + + if ( + interaction.simulation && + interaction.simulation.allowResume && + interaction.pointerType === pointerType + ) { + while (element) { + // if the element is the interaction element + if (element === interaction.element) { + return interaction + } + element = dom.parentNode(element) + } + } + } + + return null + }, + + // if it's a mouse or pen interaction + mouseOrPen({ pointerId, pointerType, eventType, scope }: SearchDetails) { + if (pointerType !== 'mouse' && pointerType !== 'pen') { + return null + } + + let firstNonActive + + for (const interaction of scope.interactions.list) { + if (interaction.pointerType === pointerType) { + // if it's a down event, skip interactions with running simulations + if (interaction.simulation && !hasPointerId(interaction, pointerId)) { + continue + } + + // if the interaction is active, return it immediately + if (interaction.interacting()) { + return interaction + } + // otherwise save it and look for another active interaction + else if (!firstNonActive) { + firstNonActive = interaction + } + } + } + + // if no active mouse interaction was found use the first inactive mouse + // interaction + if (firstNonActive) { + return firstNonActive + } + + // find any mouse or pen interaction. + // ignore the interaction if the eventType is a *down, and a simulation + // is active + for (const interaction of scope.interactions.list) { + if (interaction.pointerType === pointerType && !(/down/i.test(eventType) && interaction.simulation)) { + return interaction + } + } + + return null + }, + + // get interaction that has this pointer + hasPointer({ pointerId, scope }: SearchDetails) { + for (const interaction of scope.interactions.list) { + if (hasPointerId(interaction, pointerId)) { + return interaction + } + } + + return null + }, + + // get first idle interaction with a matching pointerType + idle({ pointerType, scope }: SearchDetails) { + for (const interaction of scope.interactions.list) { + // if there's already a pointer held down + if (interaction.pointers.length === 1) { + const target = interaction.interactable + // don't add this pointer if there is a target interactable and it + // isn't gesturable + if (target && !(target.options.gesture && target.options.gesture.enabled)) { + continue + } + } + // maximum of 2 pointers per interaction + else if (interaction.pointers.length >= 2) { + continue + } + + if (!interaction.interacting() && pointerType === interaction.pointerType) { + return interaction + } + } + + return null + }, +} + +function hasPointerId(interaction: Interaction, pointerId: number) { + return interaction.pointers.some(({ id }) => id === pointerId) +} + +export default finder diff --git a/packages/@interactjs/core/interactions.spec.ts b/packages/@interactjs/core/interactions.spec.ts new file mode 100644 index 000000000..b3dbf7997 --- /dev/null +++ b/packages/@interactjs/core/interactions.spec.ts @@ -0,0 +1,85 @@ +import Interaction from './Interaction' +import interactions from './interactions' +import * as helpers from './tests/_helpers' + +describe('core/interactions', () => { + test('interactions', () => { + let scope = helpers.mockScope() + + const interaction = scope.interactions.new({ pointerType: 'TEST' }) + + // new Interaction is pushed to scope.interactions + expect(scope.interactions.list[0]).toBe(interaction) + // interactions object added to scope + expect(scope.interactions).toBeInstanceOf(Object) + + const listeners = scope.interactions.listeners + + expect(interactions.methodNames.every((m: string) => typeof listeners[m] === 'function')).toBe(true) + + scope = helpers.mockScope() + + const newInteraction = scope.interactions.new({}) + + expect(typeof scope.interactions).toBe('object') + expect(typeof scope.interactions.new).toBe('function') + expect(newInteraction instanceof Interaction).toBe(true) + expect(typeof newInteraction._scopeFire).toBe('function') + + expect(scope.actions).toBeInstanceOf(Object) + expect(scope.actions.map).toEqual({}) + expect(scope.actions.methodDict).toEqual({}) + }) + + test('interactions document event options', () => { + const { scope } = helpers.testEnv() + const doc = scope.document + + let options = {} + scope.browser = { isIOS: false } as any + scope.fire('scope:add-document', { doc, scope, options } as any) + + // no doc options.event.passive is added when not iOS + expect(options).toEqual({}) + + options = {} + + scope.browser.isIOS = true + scope.fire('scope:add-document', { doc, scope, options } as any) + + // doc options.event.passive is set to false for iOS + expect(options).toEqual({ events: { passive: false } }) + }) + + test('interactions removes pointers on targeting removed elements', () => { + const { interaction, scope } = helpers.testEnv() + + const { PointerEvent } = scope.window as any + const div1 = scope.document.body.appendChild(scope.document.createElement('div')) + const div2 = scope.document.body.appendChild(scope.document.createElement('div')) + + const touch1Init = { bubbles: true, pointerType: 'touch', pointerId: 1, target: div1 } + const touch2Init = { bubbles: true, pointerType: 'touch', pointerId: 2, target: div2 } + + interaction.pointerType = 'touch' + div1.dispatchEvent(new PointerEvent('pointerdown', touch1Init)) + div1.dispatchEvent(new PointerEvent('pointermove', touch1Init)) + + expect(scope.interactions.list).toHaveLength(1) + + // down pointer added to interaction + expect(interaction.pointers).toHaveLength(1) + // _latestPointer target is down target + expect(interaction._latestPointer.eventTarget).toBe(div1) + + div1.remove() + + div2.dispatchEvent(new TouchEvent('touchstart', touch2Init)) + + // interaction with removed element is reused for new pointer + expect(scope.interactions.list).toEqual([interaction]) + + // pointer on removed element is removed from existing interaction and new pointerdown is added + expect(interaction.pointers).toHaveLength(1) + }) +}) diff --git a/packages/@interactjs/core/interactions.ts b/packages/@interactjs/core/interactions.ts new file mode 100644 index 000000000..77cb71fd8 --- /dev/null +++ b/packages/@interactjs/core/interactions.ts @@ -0,0 +1,289 @@ +import browser from '@interactjs/utils/browser' +import domObjects from '@interactjs/utils/domObjects' +import { nodeContains } from '@interactjs/utils/domUtils' +import * as pointerUtils from '@interactjs/utils/pointerUtils' + +import type { Scope, SignalArgs, Plugin } from '@interactjs/core/scope' +import type { ActionName, Listener } from '@interactjs/core/types' + +/* eslint-disable import/no-duplicates -- for typescript module augmentations */ +import './interactablePreventDefault' +import interactablePreventDefault from './interactablePreventDefault' +import InteractionBase from './Interaction' +/* eslint-enable import/no-duplicates */ +import type { SearchDetails } from './interactionFinder' +import finder from './interactionFinder' + +declare module '@interactjs/core/scope' { + interface Scope { + Interaction: typeof InteractionBase + interactions: { + new: (options: any) => InteractionBase + list: Array> + listeners: { [type: string]: Listener } + docEvents: Array<{ type: string; listener: Listener }> + pointerMoveTolerance: number + } + prevTouchTime: number + } + + interface SignalArgs { + 'interactions:find': { + interaction: InteractionBase + searchDetails: SearchDetails + } + } +} + +const methodNames = [ + 'pointerDown', + 'pointerMove', + 'pointerUp', + 'updatePointer', + 'removePointer', + 'windowBlur', +] + +function install(scope: Scope) { + const listeners = {} as any + + for (const method of methodNames) { + listeners[method] = doOnInteractions(method, scope) + } + + const pEventTypes = browser.pEventTypes + let docEvents: typeof scope.interactions.docEvents + + if (domObjects.PointerEvent) { + docEvents = [ + { type: pEventTypes.down, listener: releasePointersOnRemovedEls }, + { type: pEventTypes.down, listener: listeners.pointerDown }, + { type: pEventTypes.move, listener: listeners.pointerMove }, + { type: pEventTypes.up, listener: listeners.pointerUp }, + { type: pEventTypes.cancel, listener: listeners.pointerUp }, + ] + } else { + docEvents = [ + { type: 'mousedown', listener: listeners.pointerDown }, + { type: 'mousemove', listener: listeners.pointerMove }, + { type: 'mouseup', listener: listeners.pointerUp }, + + { type: 'touchstart', listener: releasePointersOnRemovedEls }, + { type: 'touchstart', listener: listeners.pointerDown }, + { type: 'touchmove', listener: listeners.pointerMove }, + { type: 'touchend', listener: listeners.pointerUp }, + { type: 'touchcancel', listener: listeners.pointerUp }, + ] + } + + docEvents.push({ + type: 'blur', + listener(event) { + for (const interaction of scope.interactions.list) { + interaction.documentBlur(event) + } + }, + }) + + // for ignoring browser's simulated mouse events + scope.prevTouchTime = 0 + + scope.Interaction = class extends InteractionBase { + get pointerMoveTolerance() { + return scope.interactions.pointerMoveTolerance + } + + set pointerMoveTolerance(value) { + scope.interactions.pointerMoveTolerance = value + } + + _now() { + return scope.now() + } + } + + scope.interactions = { + // all active and idle interactions + list: [], + new(options: { pointerType?: string; scopeFire?: Scope['fire'] }) { + options.scopeFire = (name, arg) => scope.fire(name, arg) + + const interaction = new scope.Interaction(options as Required) + + scope.interactions.list.push(interaction) + return interaction + }, + listeners, + docEvents, + pointerMoveTolerance: 1, + } + + function releasePointersOnRemovedEls() { + // for all inactive touch interactions with pointers down + for (const interaction of scope.interactions.list) { + if (!interaction.pointerIsDown || interaction.pointerType !== 'touch' || interaction._interacting) { + continue + } + + // if a pointer is down on an element that is no longer in the DOM tree + for (const pointer of interaction.pointers) { + if (!scope.documents.some(({ doc }) => nodeContains(doc, pointer.downTarget))) { + // remove the pointer from the interaction + interaction.removePointer(pointer.pointer, pointer.event) + } + } + } + } + + scope.usePlugin(interactablePreventDefault) +} + +function doOnInteractions(method: string, scope: Scope) { + return function (event: Event) { + const interactions = scope.interactions.list + + const pointerType = pointerUtils.getPointerType(event) + const [eventTarget, curEventTarget] = pointerUtils.getEventTargets(event) + const matches: any[] = [] // [ [pointer, interaction], ...] + + if (/^touch/.test(event.type)) { + scope.prevTouchTime = scope.now() + + // @ts-expect-error + for (const changedTouch of event.changedTouches) { + const pointer = changedTouch + const pointerId = pointerUtils.getPointerId(pointer) + const searchDetails: SearchDetails = { + pointer, + pointerId, + pointerType, + eventType: event.type, + eventTarget, + curEventTarget, + scope, + } + const interaction = getInteraction(searchDetails) + + matches.push([ + searchDetails.pointer, + searchDetails.eventTarget, + searchDetails.curEventTarget, + interaction, + ]) + } + } else { + let invalidPointer = false + + if (!browser.supportsPointerEvent && /mouse/.test(event.type)) { + // ignore mouse events while touch interactions are active + for (let i = 0; i < interactions.length && !invalidPointer; i++) { + invalidPointer = interactions[i].pointerType !== 'mouse' && interactions[i].pointerIsDown + } + + // try to ignore mouse events that are simulated by the browser + // after a touch event + invalidPointer = + invalidPointer || + scope.now() - scope.prevTouchTime < 500 || + // on iOS and Firefox Mobile, MouseEvent.timeStamp is zero if simulated + event.timeStamp === 0 + } + + if (!invalidPointer) { + const searchDetails = { + pointer: event as PointerEvent, + pointerId: pointerUtils.getPointerId(event as PointerEvent), + pointerType, + eventType: event.type, + curEventTarget, + eventTarget, + scope, + } + + const interaction = getInteraction(searchDetails) + + matches.push([ + searchDetails.pointer, + searchDetails.eventTarget, + searchDetails.curEventTarget, + interaction, + ]) + } + } + + // eslint-disable-next-line no-shadow + for (const [pointer, eventTarget, curEventTarget, interaction] of matches) { + interaction[method](pointer, event, eventTarget, curEventTarget) + } + } +} + +function getInteraction(searchDetails: SearchDetails) { + const { pointerType, scope } = searchDetails + + const foundInteraction = finder.search(searchDetails) + const signalArg = { interaction: foundInteraction, searchDetails } + + scope.fire('interactions:find', signalArg) + + return signalArg.interaction || scope.interactions.new({ pointerType }) +} + +function onDocSignal( + { doc, scope, options }: SignalArgs[T], + eventMethodName: 'add' | 'remove', +) { + const { + interactions: { docEvents }, + events, + } = scope + const eventMethod = events[eventMethodName] + + if (scope.browser.isIOS && !options.events) { + options.events = { passive: false } + } + + // delegate event listener + for (const eventType in events.delegatedEvents) { + eventMethod(doc, eventType, events.delegateListener) + eventMethod(doc, eventType, events.delegateUseCapture, true) + } + + const eventOptions = options && options.events + + for (const { type, listener } of docEvents) { + eventMethod(doc, type, listener, eventOptions) + } +} + +const interactions: Plugin = { + id: 'core/interactions', + install, + listeners: { + 'scope:add-document': (arg) => onDocSignal(arg, 'add'), + 'scope:remove-document': (arg) => onDocSignal(arg, 'remove'), + 'interactable:unset': ({ interactable }, scope) => { + // Stop and destroy related interactions when an Interactable is unset + for (let i = scope.interactions.list.length - 1; i >= 0; i--) { + const interaction = scope.interactions.list[i] + + if (interaction.interactable !== interactable) { + continue + } + + interaction.stop() + scope.fire('interactions:destroy', { interaction }) + interaction.destroy() + + if (scope.interactions.list.length > 2) { + scope.interactions.list.splice(i, 1) + } + } + }, + }, + onDocSignal, + doOnInteractions, + methodNames, +} + +export default interactions diff --git a/packages/@interactjs/core/options.ts b/packages/@interactjs/core/options.ts new file mode 100644 index 000000000..553829939 --- /dev/null +++ b/packages/@interactjs/core/options.ts @@ -0,0 +1,46 @@ +import type { Point, Listeners, OrBoolean, Element, Rect } from '@interactjs/core/types' + +export interface Defaults { + base: BaseDefaults + perAction: PerActionDefaults + actions: ActionDefaults +} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ActionDefaults {} + +export interface BaseDefaults { + preventDefault?: 'always' | 'never' | 'auto' + deltaSource?: 'page' | 'client' + context?: Node + getRect?: (element: Element) => Rect +} + +export interface PerActionDefaults { + enabled?: boolean + origin?: Point | string | Element + listeners?: Listeners + allowFrom?: string | Element + ignoreFrom?: string | Element +} + +export type Options = Partial & + Partial & { + [P in keyof ActionDefaults]?: Partial + } + +export interface OptionsArg extends BaseDefaults, OrBoolean> {} + +export const defaults: Defaults = { + base: { + preventDefault: 'auto', + deltaSource: 'page', + }, + + perAction: { + enabled: false, + origin: { x: 0, y: 0 }, + }, + + actions: {} as ActionDefaults, +} diff --git a/packages/@interactjs/core/package.json b/packages/@interactjs/core/package.json new file mode 100644 index 000000000..94f22274c --- /dev/null +++ b/packages/@interactjs/core/package.json @@ -0,0 +1,23 @@ +{ + "name": "@interactjs/core", + "version": "1.10.27", + "main": "index", + "module": "index", + "type": "module", + "repository": { + "type": "git", + "url": "https://github.com/taye/interact.js.git", + "directory": "packages/@interactjs/core" + }, + "peerDependencies": { + "@interactjs/utils": "1.10.27" + }, + "publishConfig": { + "access": "public" + }, + "sideEffects": [ + "**/index.js", + "**/index.prod.js" + ], + "license": "MIT" +} diff --git a/packages/@interactjs/core/scope.spec.ts b/packages/@interactjs/core/scope.spec.ts new file mode 100644 index 000000000..36793236c --- /dev/null +++ b/packages/@interactjs/core/scope.spec.ts @@ -0,0 +1,50 @@ +import type { ActionName } from '@interactjs/core/types' + +import * as helpers from './tests/_helpers' + +describe('core/scope', () => { + test('usePlugin', () => { + const { scope } = helpers.testEnv() + + const plugin1 = { id: '1', listeners: {} } + const plugin2 = { id: '2', listeners: {} } + const plugin3 = { id: '3', listeners: {}, before: ['2'] } + const plugin4 = { id: '4', listeners: {}, before: ['2', '3'] } + + const initialListeners = scope.listenerMaps.map((l) => l.id) + + scope.usePlugin(plugin1) + scope.usePlugin(plugin2) + scope.usePlugin(plugin3) + scope.usePlugin(plugin4) + + expect(scope.listenerMaps.map((l) => l.id)).toEqual([...initialListeners, '1', '4', '3', '2']) + }) + + test('interactable unset', () => { + const { scope, interactable, interaction, event } = helpers.testEnv() + + const interactable2 = scope.interactables.new('x') + + expect(scope.interactables.list).toContain(interactable) + expect(scope.interactables.list).toContain(interactable2) + ;(interactable.options as any).test = { enabled: true } + interaction.pointerDown(event, event, scope.document.body) + interaction.start({ name: 'test' as ActionName }, interactable, scope.document.body) + + const started = interaction._interacting + interactable.unset() + const stopped = !interaction._interacting + + expect(scope.interactables.list).not.toContain(interactable) + expect(scope.interactables.list).toContain(interactable2) + + // interaction is stopped on interactable.unset() + expect(started && stopped).toBe(true) + + // repeated call to unset + interactable.unset() + expect(scope.interactables.list).not.toContain(interactable) + expect(scope.interactables.list).toContain(interactable2) + }) +}) diff --git a/packages/@interactjs/core/scope.ts b/packages/@interactjs/core/scope.ts new file mode 100644 index 000000000..284c188ec --- /dev/null +++ b/packages/@interactjs/core/scope.ts @@ -0,0 +1,288 @@ +import browser from '@interactjs/utils/browser' +import clone from '@interactjs/utils/clone' +import domObjects from '@interactjs/utils/domObjects' +import extend from '@interactjs/utils/extend' +import is from '@interactjs/utils/is' +import raf from '@interactjs/utils/raf' +import * as win from '@interactjs/utils/window' + +import type Interaction from '@interactjs/core/Interaction' + +import { Eventable } from './Eventable' +/* eslint-disable import/no-duplicates -- for typescript module augmentations */ +import './events' +import './interactions' +import events from './events' +import { Interactable as InteractableBase } from './Interactable' +import { InteractableSet } from './InteractableSet' +import { InteractEvent } from './InteractEvent' +import interactions from './interactions' +/* eslint-enable import/no-duplicates */ +import { createInteractStatic } from './InteractStatic' +import type { OptionsArg } from './options' +import { defaults } from './options' +import type { Actions } from './types' + +export interface SignalArgs { + 'scope:add-document': DocSignalArg + 'scope:remove-document': DocSignalArg + 'interactable:unset': { interactable: InteractableBase } + 'interactable:set': { interactable: InteractableBase; options: OptionsArg } + 'interactions:destroy': { interaction: Interaction } +} + +export type ListenerName = keyof SignalArgs + +export type ListenerMap = { + [P in ListenerName]?: (arg: SignalArgs[P], scope: Scope, signalName: P) => void | boolean +} + +interface DocSignalArg { + doc: Document + window: Window + scope: Scope + options: Record +} + +export interface Plugin { + [key: string]: any + id?: string + listeners?: ListenerMap + before?: string[] + install?(scope: Scope, options?: any): void +} + +/** @internal */ +export class Scope { + id = `__interact_scope_${Math.floor(Math.random() * 100)}` + isInitialized = false + listenerMaps: Array<{ + map: ListenerMap + id?: string + }> = [] + + browser = browser + defaults = clone(defaults) as typeof defaults + Eventable = Eventable + actions: Actions = { + map: {}, + phases: { + start: true, + move: true, + end: true, + }, + methodDict: {} as any, + phaselessTypes: {}, + } + + interactStatic = createInteractStatic(this) + InteractEvent = InteractEvent + Interactable: typeof InteractableBase + interactables = new InteractableSet(this) + + // main window + _win!: Window + + // main document + document!: Document + + // main window + window!: Window + + // all documents being listened to + documents: Array<{ doc: Document; options: any }> = [] + + _plugins: { + list: Plugin[] + map: { [id: string]: Plugin } + } = { + list: [], + map: {}, + } + + constructor() { + const scope = this + + this.Interactable = class extends InteractableBase { + get _defaults() { + return scope.defaults + } + + set(this: T, options: OptionsArg) { + super.set(options) + + scope.fire('interactable:set', { + options, + interactable: this, + }) + + return this + } + + unset(this: InteractableBase) { + super.unset() + + const index = scope.interactables.list.indexOf(this) + if (index < 0) return + + scope.interactables.list.splice(index, 1) + scope.fire('interactable:unset', { interactable: this }) + } + } + } + + addListeners(map: ListenerMap, id?: string) { + this.listenerMaps.push({ id, map }) + } + + fire(name: T, arg: SignalArgs[T]): void | false { + for (const { + map: { [name]: listener }, + } of this.listenerMaps) { + if (!!listener && listener(arg as any, this, name as never) === false) { + return false + } + } + } + + onWindowUnload = (event: BeforeUnloadEvent) => this.removeDocument(event.target as Document) + + init(window: Window | typeof globalThis) { + return this.isInitialized ? this : initScope(this, window) + } + + pluginIsInstalled(plugin: Plugin) { + const { id } = plugin + return id ? !!this._plugins.map[id] : this._plugins.list.indexOf(plugin) !== -1 + } + + usePlugin(plugin: Plugin, options?: { [key: string]: any }) { + if (!this.isInitialized) { + return this + } + + if (this.pluginIsInstalled(plugin)) { + return this + } + + if (plugin.id) { + this._plugins.map[plugin.id] = plugin + } + this._plugins.list.push(plugin) + + if (plugin.install) { + plugin.install(this, options) + } + + if (plugin.listeners && plugin.before) { + let index = 0 + const len = this.listenerMaps.length + const before = plugin.before.reduce((acc, id) => { + acc[id] = true + acc[pluginIdRoot(id)] = true + return acc + }, {}) + + for (; index < len; index++) { + const otherId = this.listenerMaps[index].id + + if (otherId && (before[otherId] || before[pluginIdRoot(otherId)])) { + break + } + } + + this.listenerMaps.splice(index, 0, { id: plugin.id, map: plugin.listeners }) + } else if (plugin.listeners) { + this.listenerMaps.push({ id: plugin.id, map: plugin.listeners }) + } + + return this + } + + addDocument(doc: Document, options?: any): void | false { + // do nothing if document is already known + if (this.getDocIndex(doc) !== -1) { + return false + } + + const window = win.getWindow(doc) + + options = options ? extend({}, options) : {} + + this.documents.push({ doc, options }) + this.events.documents.push(doc) + + // don't add an unload event for the main document + // so that the page may be cached in browser history + if (doc !== this.document) { + this.events.add(window, 'unload', this.onWindowUnload) + } + + this.fire('scope:add-document', { doc, window, scope: this, options }) + } + + removeDocument(doc: Document) { + const index = this.getDocIndex(doc) + + const window = win.getWindow(doc) + const options = this.documents[index].options + + this.events.remove(window, 'unload', this.onWindowUnload) + + this.documents.splice(index, 1) + this.events.documents.splice(index, 1) + + this.fire('scope:remove-document', { doc, window, scope: this, options }) + } + + getDocIndex(doc: Document) { + for (let i = 0; i < this.documents.length; i++) { + if (this.documents[i].doc === doc) { + return i + } + } + + return -1 + } + + getDocOptions(doc: Document) { + const docIndex = this.getDocIndex(doc) + + return docIndex === -1 ? null : this.documents[docIndex].options + } + + now() { + return (((this.window as any).Date as typeof Date) || Date).now() + } +} + +// Keep Scope class internal, but expose minimal interface to avoid broken types when Scope is stripped out +export interface Scope { + fire(name: T, arg: SignalArgs[T]): void | false +} + +/** @internal */ +export function initScope(scope: Scope, window: Window | typeof globalThis) { + scope.isInitialized = true + + if (is.window(window)) { + win.init(window) + } + + domObjects.init(window) + browser.init(window) + raf.init(window) + + // @ts-expect-error + scope.window = window + scope.document = window.document + + scope.usePlugin(interactions) + scope.usePlugin(events) + + return scope +} + +function pluginIdRoot(id: string) { + return id && id.replace(/\/.*$/, '') +} diff --git a/packages/@interactjs/core/tests/_helpers.ts b/packages/@interactjs/core/tests/_helpers.ts new file mode 100644 index 000000000..1c647ec6e --- /dev/null +++ b/packages/@interactjs/core/tests/_helpers.ts @@ -0,0 +1,156 @@ +import extend from '@interactjs/utils/extend' +import is from '@interactjs/utils/is' +import * as pointerUtils from '@interactjs/utils/pointerUtils' + +import type { PointerType, Rect, Target, ActionName, ActionProps } from '@interactjs/core/types' + +import type { Plugin } from '../scope' +import { Scope } from '../scope' + +let counter = 0 + +export function unique() { + return counter++ +} + +export function uniqueProps(obj: any) { + for (const prop in obj) { + if (!obj.hasOwnProperty(prop)) { + continue + } + + if (is.object(obj)) { + uniqueProps(obj[prop]) + } else { + obj[prop] = counter++ + } + } +} + +export function newCoordsSet(n = 0) { + return { + start: { + page: { x: n++, y: n++ }, + client: { x: n++, y: n++ }, + timeStamp: n++, + }, + cur: { + page: { x: n++, y: n++ }, + client: { x: n++, y: n++ }, + timeStamp: n++, + }, + prev: { + page: { x: n++, y: n++ }, + client: { x: n++, y: n++ }, + timeStamp: n++, + }, + delta: { + page: { x: n++, y: n++ }, + client: { x: n++, y: n++ }, + timeStamp: n++, + }, + velocity: { + page: { x: n++, y: n++ }, + client: { x: n++, y: n++ }, + timeStamp: n++, + }, + } +} + +export function newPointer(n = 50) { + return { + pointerId: n++, + pageX: n++, + pageY: n++, + clientX: n++, + clientY: n++, + } as PointerType +} + +export function mockScope({ document = window.document } = {} as any) { + const window = document.defaultView + + const scope = new Scope().init(window) + + extend(scope.actions.phaselessTypes, { teststart: true, testmove: true, testend: true }) + + return scope +} + +export function getProps(src: T, props: readonly K[]) { + return props.reduce( + (acc, prop) => { + if (prop in src) { + acc[prop] = src[prop] + } + + return acc + }, + {} as Pick, + ) +} + +export function testEnv({ + plugins = [], + target, + rect, + document = window.document, +}: { + plugins?: Plugin[] + target?: T + rect?: Rect + document?: Document +} = {}) { + const scope = mockScope({ document }) + + for (const plugin of plugins) { + scope.usePlugin(plugin) + } + + if (!target) { + ;(target as unknown as HTMLElement) = scope.document.body + } + + const interaction = scope.interactions.new({}) + const interactable = scope.interactables.new(target) + const coords: pointerUtils.MockCoords = pointerUtils.newCoords() + + coords.target = target + const event = pointerUtils.coordsToEvent(coords) + + if (rect) { + interactable.rectChecker(() => ({ ...rect })) + } + + return { + scope, + interaction, + target: target as T extends undefined ? HTMLElement : T, + interactable, + coords, + event, + interact: scope.interactStatic, + start: (action: ActionProps) => + interaction.start(action, interactable, target as HTMLElement), + stop: () => interaction.stop(), + down: () => interaction.pointerDown(event, event, target as HTMLElement), + move: (force?: boolean) => + force ? interaction.move() : interaction.pointerMove(event, event, target as HTMLElement), + up: () => interaction.pointerUp(event, event, target as HTMLElement, target as HTMLElement), + } +} + +export function timeout(n: number) { + return new Promise((resolve) => setTimeout(resolve, n)) +} + +export function ltrbwh( + left: number, + top: number, + right: number, + bottom: number, + width: number, + height: number, +) { + return { left, top, right, bottom, width, height } +} diff --git a/packages/@interactjs/core/types.ts b/packages/@interactjs/core/types.ts new file mode 100644 index 000000000..3d65d508c --- /dev/null +++ b/packages/@interactjs/core/types.ts @@ -0,0 +1,145 @@ +import type Interaction from '@interactjs/core/Interaction' + +import type { Interactable } from './Interactable' +import type { PhaseMap, InteractEvent } from './InteractEvent' +import type { NativePointerEvent as NativePointerEvent_ } from './NativeTypes' + +export type OrBoolean = { + [P in keyof T]: T[P] | boolean +} + +export type Element = HTMLElement | SVGElement +export type Context = Document | Element +export type EventTarget = Window | Document | Element +export type Target = EventTarget | string + +export interface Point { + x: number + y: number +} + +export interface Size { + width: number + height: number +} + +export interface Rect { + top: number + left: number + bottom: number + right: number + width?: number + height?: number +} + +export type FullRect = Required + +export type RectFunction = (...args: T) => Rect | Element + +export type RectResolvable = Rect | string | Element | RectFunction + +export type Dimensions = Point & Size + +export interface CoordsSetMember { + page: Point + client: Point + timeStamp: number +} + +export interface CoordsSet { + cur: CoordsSetMember + prev: CoordsSetMember + start: CoordsSetMember + delta: CoordsSetMember + velocity: CoordsSetMember +} + +export interface HasGetRect { + getRect(element: Element): Rect +} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ActionMap {} + +export type ActionName = keyof ActionMap + +export interface Actions { + map: ActionMap + phases: PhaseMap + methodDict: Record + phaselessTypes: { [type: string]: true } +} + +export interface ActionProps { + name: T + axis?: 'x' | 'y' | 'xy' | null + edges?: EdgeOptions | null +} + +export interface InertiaOption { + resistance?: number + minSpeed?: number + endSpeed?: number + allowResume?: boolean + smoothEndDuration?: number +} +export type InertiaOptions = InertiaOption | boolean + +export interface EdgeOptions { + top?: boolean | string | Element + left?: boolean | string | Element + bottom?: boolean | string | Element + right?: boolean | string | Element +} + +export type CursorChecker = ( + action: ActionProps, + interactable: Interactable, + element: Element, + interacting: boolean, +) => string + +export interface ActionMethod { + (this: Interactable): T + (this: Interactable, options?: Partial> | boolean): typeof this +} + +export interface OptionMethod { + (this: Interactable): T + // eslint-disable-next-line no-undef + (this: Interactable, options: T): typeof this +} + +export type ActionChecker = ( + pointerEvent: any, + defaultAction: string, + interactable: Interactable, + element: Element, + interaction: Interaction, +) => ActionProps + +export type OriginFunction = (target: Element) => Rect + +export interface PointerEventsOptions { + holdDuration?: number + allowFrom?: string + ignoreFrom?: string + origin?: Rect | Point | string | Element | OriginFunction +} + +export type RectChecker = (element: Element) => Rect + +export type NativePointerEventType = typeof NativePointerEvent_ +export type PointerEventType = MouseEvent | TouchEvent | Partial | InteractEvent +export type PointerType = MouseEvent | Touch | Partial | InteractEvent + +export type EventTypes = string | ListenerMap | Array + +export type Listener = (...args: any[]) => any +export type Listeners = ListenerMap | ListenerMap[] +export type ListenersArg = Listener | ListenerMap | Array +export interface ListenerMap { + [index: string]: ListenersArg | ListenersArg[] +} + +export type ArrayElementType = T extends Array ? P : never diff --git a/packages/@interactjs/dev-tools/README.md b/packages/@interactjs/dev-tools/README.md new file mode 100644 index 000000000..549491910 --- /dev/null +++ b/packages/@interactjs/dev-tools/README.md @@ -0,0 +1,5 @@ +

+This package is an internal part of interactjs and is not meant +to be used independently as each update may introduce breaking changes +

diff --git a/packages/@interactjs/dev-tools/babel-plugin-prod.js b/packages/@interactjs/dev-tools/babel-plugin-prod.js new file mode 100644 index 000000000..c3bbffd38 --- /dev/null +++ b/packages/@interactjs/dev-tools/babel-plugin-prod.js @@ -0,0 +1,59 @@ +/* global process, __dirname */ +const path = require('path') + +const PROD_EXT = '.prod' + +function fixImportSource ({ node: { source } }, { filename }) { + if (shouldIgnoreImport(source)) return + + let resolvedShort = '' + + try { + const paths = [filename && path.dirname(filename), __dirname, process.cwd()].filter((p) => !!p) + + const resolved = require.resolve(source.value, { paths }) + const resolvedWithoutScopePath = resolved.replace(/.*[\\/]@interactjs[\\/]/, '') + + resolvedShort = path + .join('@interactjs', resolvedWithoutScopePath) + // windows path to posix + .replace(/\\/g, '/') + source.value = resolvedShort.replace(/(\.js)?$/, PROD_EXT) + } catch (e) {} +} + +function babelPluginInteractjsProd () { + if (process.env.NODE_ENV === 'development') { + // eslint-disable-next-line no-console + console.warn( + "[@interactjs/dev-tools] You're using the production plugin in the development environment. You might lose out on some helpful hints!", + ) + } + + return { + visitor: { + ImportDeclaration: fixImportSource, + ExportNamedDeclaration: fixImportSource, + ExportAllDeclaration: fixImportSource, + ExportDefaultSpecifier: fixImportSource, + }, + } +} + +function shouldIgnoreImport (source) { + return ( + !source || + // only change @interactjs scoped imports + !source.value.startsWith('@interactjs/') || + // ignore imports of prod files + source.value.endsWith(PROD_EXT) || + source.value.endsWith(PROD_EXT + '.js') + ) +} + +module.exports = babelPluginInteractjsProd + +Object.assign(module.exports, { + default: babelPluginInteractjsProd, + fixImportSource, +}) diff --git a/packages/@interactjs/dev-tools/babel-plugin-prod.spec.ts b/packages/@interactjs/dev-tools/babel-plugin-prod.spec.ts new file mode 100644 index 000000000..59e49daf3 --- /dev/null +++ b/packages/@interactjs/dev-tools/babel-plugin-prod.spec.ts @@ -0,0 +1,90 @@ +/** @jest-environment node */ +import * as babel from '@babel/core' +import proposalExportDefaultFrom from '@babel/plugin-proposal-export-default-from' + +import babelPluginProd, { fixImportSource } from './babel-plugin-prod' + +describe('@dev-tools/prod/babel-plugin-prod', () => { + const filename = require.resolve('@interactjs/_dev/test/fixtures/babelPluginProject/index.js') + + const cases = [ + { + module: 'x', + expected: 'x', + message: 'non @interact/* package unchanged', + }, + { + module: 'interact', + expected: 'interact', + message: 'unscoped interact import unchanged', + }, + { + module: '@interactjs/NONEXISTENT_PACKAGE', + expected: '@interactjs/NONEXISTENT_PACKAGE', + message: 'missing package unchanged', + }, + { + module: '@interactjs/a/NONEXISTENT_MODULE', + expected: '@interactjs/a/NONEXISTENT_MODULE', + message: 'import of missing module unchanged', + }, + { + module: '@interactjs/a', + expected: '@interactjs/a/package-main-file.prod', + message: 'package main module', + }, + { + module: '@interactjs/a/a', + expected: '@interactjs/a/a.prod', + message: 'package root-level non index module', + }, + { + module: '@interactjs/a/b', + expected: '@interactjs/a/b/index.prod', + message: 'nested index module', + }, + { + module: '@interactjs/a/b/b', + expected: '@interactjs/a/b/b.prod', + message: 'package nested non index module', + }, + ] + + for (const { module, expected, message } of cases) { + // eslint-disable-next-line jest/valid-title + test(message, () => { + const source = { value: module } + + fixImportSource({ node: { source } }, { filename }) + + expect(source.value).toBe(expected) + }) + } + + test('transforms code when used in babel config', () => { + expect( + babel.transform( + [ + 'import "@interactjs/a/a";', + 'import a, { b } from "@interactjs/a/a";', + 'export b from "@interactjs/a/a";', + 'export * from "@interactjs/a/a";', + ].join('\n'), + { + babelrc: false, + configFile: false, + plugins: [babelPluginProd, [proposalExportDefaultFrom, { loose: true }]], + filename, + sourceType: 'module', + }, + ).code, + ).toEqual( + [ + 'import "@interactjs/a/a.prod";', + 'import a, { b } from "@interactjs/a/a.prod";', + 'export { default as b } from "@interactjs/a/a.prod";', + 'export * from "@interactjs/a/a.prod";', + ].join('\n'), + ) + }) +}) diff --git a/packages/@interactjs/dev-tools/devTools.spec.ts b/packages/@interactjs/dev-tools/devTools.spec.ts new file mode 100644 index 000000000..09a0546e1 --- /dev/null +++ b/packages/@interactjs/dev-tools/devTools.spec.ts @@ -0,0 +1,118 @@ +import drag from '@interactjs/actions/drag/plugin' +import resize from '@interactjs/actions/resize/plugin' +import * as helpers from '@interactjs/core/tests/_helpers' + +import type { Check, Logger } from './plugin' +import devTools from './plugin' + +const { checks, links, prefix } = devTools +const checkMap = checks.reduce( + (acc, check) => { + acc[check.name] = check + return acc + }, + {} as { [name: string]: Check }, +) + +test('devTools', () => { + const devToolsWithLogger = { + install: (s) => + s.usePlugin(devTools, { + logger: { + warn(...args: any[]) { + log(args, 'warn') + }, + log(...args: any[]) { + log(args, 'log') + }, + error(...args: any[]) { + log(args, 'error') + }, + }, + }), + } + + const { + scope, + interaction, + interactable, + target: element, + down, + start, + move, + stop, + } = helpers.testEnv({ + plugins: [devToolsWithLogger, drag, resize], + }) + + const logs: Array<{ args: any[]; type: keyof Logger }> = [] + + function log(args: any, type: any) { + logs.push({ args, type }) + } + + scope.usePlugin(drag) + scope.usePlugin(resize) + + interactable.draggable(true).resizable({ onmove: () => {} }) + + down() + start({ name: 'drag' }) + // warning about missing touchAction + expect(logs[0]).toEqual({ + args: [prefix + checkMap.touchAction.text, element, links.touchAction], + type: 'warn', + }) + + // warning about missing move listeners + expect(logs[1]).toEqual({ args: [prefix + checkMap.noListeners.text, 'drag', interactable], type: 'warn' }) + + stop() + + // resolve touchAction + element.style.touchAction = 'none' + // resolve missing listeners + interactable.on('dragmove', () => {}) + + interaction.start({ name: 'resize' }, interactable, element) + move() + stop() + + // warning about resizing without "box-sizing: none" + expect(logs[2]).toEqual({ + args: [prefix + checkMap.boxSizing.text, element, links.boxSizing], + type: 'warn', + }) + + logs.splice(0) + + interaction.start({ name: 'resize' }, interactable, element) + move() + stop() + + interactable.options.devTools.ignore = { boxSizing: true } + + interaction.start({ name: 'resize' }, interactable, element) + move() + stop() + + // warning removed with options.devTools.ignore + expect(logs).toHaveLength(1) + + logs.splice(0) + + // resolve boxSizing + interactable.options.devTools.ignore = {} + element.style.boxSizing = 'border-box' + + interaction.start({ name: 'resize' }, interactable, element) + move(true) + stop() + + interaction.start({ name: 'drag' }, interactable, element) + move() + stop() + + // no warnings when issues are resolved + expect(logs).toHaveLength(0) +}) diff --git a/packages/@interactjs/dev-tools/package.json b/packages/@interactjs/dev-tools/package.json new file mode 100644 index 000000000..dd02c9895 --- /dev/null +++ b/packages/@interactjs/dev-tools/package.json @@ -0,0 +1,31 @@ +{ + "name": "@interactjs/dev-tools", + "version": "1.10.27", + "main": "index", + "module": "index", + "type": "module", + "repository": { + "type": "git", + "url": "https://github.com/taye/interact.js.git", + "directory": "packages/@interactjs/dev-tools" + }, + "peerDependencies": { + "@interactjs/modifiers": "1.10.27", + "@interactjs/utils": "1.10.27" + }, + "optionalDependencies": { + "@interactjs/interact": "1.10.27", + "vue": "3" + }, + "devDependencies": { + "vue": "next" + }, + "publishConfig": { + "access": "public" + }, + "sideEffects": [ + "**/index.js", + "**/index.prod.js" + ], + "license": "MIT" +} diff --git a/packages/@interactjs/dev-tools/plugin.ts b/packages/@interactjs/dev-tools/plugin.ts new file mode 100644 index 000000000..df20c3587 --- /dev/null +++ b/packages/@interactjs/dev-tools/plugin.ts @@ -0,0 +1,195 @@ +import type Interaction from '@interactjs/core/Interaction' +import type { Scope, Plugin } from '@interactjs/core/scope' +import type { Element, OptionMethod } from '@interactjs/core/types' +import domObjects from '@interactjs/utils/domObjects' +import { parentNode } from '@interactjs/utils/domUtils' +import extend from '@interactjs/utils/extend' +import is from '@interactjs/utils/is' +import isNonNativeEvent from '@interactjs/utils/isNonNativeEvent' +import normalizeListeners from '@interactjs/utils/normalizeListeners' +import * as win from '@interactjs/utils/window' + +declare module '@interactjs/core/scope' { + interface Scope { + logger: Logger + } +} + +declare module '@interactjs/core/options' { + interface BaseDefaults { + devTools?: DevToolsOptions + } +} + +declare module '@interactjs/core/Interactable' { + interface Interactable { + devTools: OptionMethod + } +} + +export interface DevToolsOptions { + ignore: { [P in keyof typeof CheckName]?: boolean } +} + +export interface Logger { + warn: (...args: any[]) => void + error: (...args: any[]) => void + log: (...args: any[]) => void +} + +export interface Check { + name: CheckName + text: string + perform: (interaction: Interaction) => boolean + getInfo: (interaction: Interaction) => any[] +} + +enum CheckName { + touchAction = 'touchAction', + boxSizing = 'boxSizing', + noListeners = 'noListeners', +} + +const prefix = '[interact.js] ' +const links = { + touchAction: 'https://developer.mozilla.org/en-US/docs/Web/CSS/touch-action', + boxSizing: 'https://developer.mozilla.org/en-US/docs/Web/CSS/box-sizing', +} + +// eslint-disable-next-line no-undef +const isProduction = process.env.NODE_ENV === 'production' + +function install(scope: Scope, { logger }: { logger?: Logger } = {}) { + const { Interactable, defaults } = scope + + scope.logger = logger || console + + defaults.base.devTools = { + ignore: {}, + } + + Interactable.prototype.devTools = function (options?: object) { + if (options) { + extend(this.options.devTools, options) + return this + } + + return this.options.devTools + } + + // can't set native events on non string targets without `addEventListener` prop + const { _onOff } = Interactable.prototype + Interactable.prototype._onOff = function (method, typeArg, listenerArg, options, filter) { + if (is.string(this.target) || this.target.addEventListener) { + return _onOff.call(this, method, typeArg, listenerArg, options, filter) + } + + if (is.object(typeArg) && !is.array(typeArg)) { + options = listenerArg + listenerArg = null + } + + const normalizedListeners = normalizeListeners(typeArg, listenerArg, filter) + + for (const type in normalizedListeners) { + if (isNonNativeEvent(type, scope.actions)) continue + scope.logger.warn( + prefix + + `Can't add native "${type}" event listener to target without \`addEventListener(type, listener, options)\` prop.`, + ) + } + + return _onOff.call(this, method, normalizedListeners, options) + } +} + +const checks: Check[] = [ + { + name: CheckName.touchAction, + perform({ element }) { + return !!element && !parentHasStyle(element, 'touchAction', /pan-|pinch|none/) + }, + getInfo({ element }) { + return [element, links.touchAction] + }, + text: 'Consider adding CSS "touch-action: none" to this element\n', + }, + + { + name: CheckName.boxSizing, + perform(interaction) { + const { element } = interaction + + return ( + interaction.prepared.name === 'resize' && + element instanceof domObjects.HTMLElement && + !hasStyle(element, 'boxSizing', /border-box/) + ) + }, + text: 'Consider adding CSS "box-sizing: border-box" to this resizable element', + getInfo({ element }) { + return [element, links.boxSizing] + }, + }, + + { + name: CheckName.noListeners, + perform(interaction) { + const actionName = interaction.prepared.name + const moveListeners = interaction.interactable?.events.types[`${actionName}move`] || [] + + return !moveListeners.length + }, + getInfo(interaction) { + return [interaction.prepared.name, interaction.interactable] + }, + text: 'There are no listeners set for this action', + }, +] + +function hasStyle(element: HTMLElement, prop: keyof CSSStyleDeclaration, styleRe: RegExp) { + const value = element.style[prop] || win.window.getComputedStyle(element)[prop] + return styleRe.test((value || '').toString()) +} + +function parentHasStyle(element: Element, prop: keyof CSSStyleDeclaration, styleRe: RegExp) { + let parent = element as HTMLElement + + while (is.element(parent)) { + if (hasStyle(parent, prop, styleRe)) { + return true + } + + parent = parentNode(parent) as HTMLElement + } + + return false +} + +const id = 'dev-tools' +const defaultExport: Plugin = isProduction + ? { id, install: () => {} } + : { + id, + install, + listeners: { + 'interactions:action-start': ({ interaction }, scope) => { + for (const check of checks) { + const options = interaction.interactable && interaction.interactable.options + + if ( + !(options && options.devTools && options.devTools.ignore[check.name]) && + check.perform(interaction) + ) { + scope.logger.warn(prefix + check.text, ...check.getInfo(interaction)) + } + } + }, + }, + checks, + CheckName, + links, + prefix, + } + +export default defaultExport diff --git a/packages/@interactjs/dev-tools/visualizer/plugin.stub.ts b/packages/@interactjs/dev-tools/visualizer/plugin.stub.ts new file mode 100644 index 000000000..b1c6ea436 --- /dev/null +++ b/packages/@interactjs/dev-tools/visualizer/plugin.stub.ts @@ -0,0 +1 @@ +export default {} diff --git a/packages/@interactjs/dev-tools/visualizer/plugin.ts b/packages/@interactjs/dev-tools/visualizer/plugin.ts new file mode 100644 index 000000000..b1c6ea436 --- /dev/null +++ b/packages/@interactjs/dev-tools/visualizer/plugin.ts @@ -0,0 +1 @@ +export default {} diff --git a/packages/@interactjs/dev-tools/visualizer/visualizer.spec.stub.ts b/packages/@interactjs/dev-tools/visualizer/visualizer.spec.stub.ts new file mode 100644 index 000000000..d8ae0b116 --- /dev/null +++ b/packages/@interactjs/dev-tools/visualizer/visualizer.spec.stub.ts @@ -0,0 +1 @@ +test.skip('visualizer', () => {}) diff --git a/packages/@interactjs/dev-tools/visualizer/visualizer.spec.ts b/packages/@interactjs/dev-tools/visualizer/visualizer.spec.ts new file mode 100644 index 000000000..d8ae0b116 --- /dev/null +++ b/packages/@interactjs/dev-tools/visualizer/visualizer.spec.ts @@ -0,0 +1 @@ +test.skip('visualizer', () => {}) diff --git a/packages/@interactjs/dev-tools/visualizer/vueModules.stub.ts b/packages/@interactjs/dev-tools/visualizer/vueModules.stub.ts new file mode 100644 index 000000000..336ce12bb --- /dev/null +++ b/packages/@interactjs/dev-tools/visualizer/vueModules.stub.ts @@ -0,0 +1 @@ +export {} diff --git a/packages/@interactjs/dev-tools/visualizer/vueModules.ts b/packages/@interactjs/dev-tools/visualizer/vueModules.ts new file mode 100644 index 000000000..336ce12bb --- /dev/null +++ b/packages/@interactjs/dev-tools/visualizer/vueModules.ts @@ -0,0 +1 @@ +export {} diff --git a/packages/@interactjs/inertia/README.md b/packages/@interactjs/inertia/README.md new file mode 100644 index 000000000..549491910 --- /dev/null +++ b/packages/@interactjs/inertia/README.md @@ -0,0 +1,5 @@ +

+This package is an internal part of interactjs and is not meant +to be used independently as each update may introduce breaking changes +

diff --git a/packages/@interactjs/inertia/inertia.spec.ts b/packages/@interactjs/inertia/inertia.spec.ts new file mode 100644 index 000000000..b52075d99 --- /dev/null +++ b/packages/@interactjs/inertia/inertia.spec.ts @@ -0,0 +1,180 @@ +import drag from '@interactjs/actions/drag/plugin' +import type { EventPhase, InteractEvent } from '@interactjs/core/InteractEvent' +import * as helpers from '@interactjs/core/tests/_helpers' +import extend from '@interactjs/utils/extend' + +import inertia from './plugin' + +test('inertia', () => { + const { scope, interaction, down, start, move, up, interactable, coords } = helpers.testEnv({ + plugins: [inertia, drag], + rect: { left: 0, top: 0, bottom: 100, right: 100 }, + }) + + const state = interaction.inertia + const modifierChange = 5 + const changeModifier = { + options: { endOnly: false }, + methods: { + set({ coords: modifierCoords, phase }: any) { + modifierCoords.x = modifierChange + modifierCoords.y = modifierChange + modifierCallPhases.push(phase) + }, + }, + } + + let fired: Array> = [] + let modifierCallPhases: EventPhase[] = [] + + coords.client = coords.page + scope.now = () => coords.timeStamp + interactable.draggable({ inertia: true }).on( + Object.keys(scope.actions.phases).map((p) => `drag${p}`), + (e) => fired.push(e), + ) + + // test inertia without modifiers or throw + downStartMoveUp({ x: 100, y: 0, dt: 1000 }) + // { modifiers: [] } && !thrown: inertia is not activated + expect(state.active).toBe(false) + + // test inertia without modifiers and with throw + downStartMoveUp({ x: 100, y: 0, dt: 10 }) + // thrown: inertia is activated + expect(state.active && state.timeout).toBeTruthy() + + interactable.draggable({ modifiers: [changeModifier as any] }) + + // test inertia with { endOnly: false } modifier and with throw + downStartMoveUp({ x: 100, y: 0, dt: 10 }) + // { endOnly: false } && thrown: modifier is called from pointerUp inertia calc and phase + expect(modifierCallPhases).toEqual(['move', 'inertiastart', 'inertiastart']) + // { endOnly: false } && thrown: move, inertiastart, and end InteractEvents are modified + expect(fired.map(({ page, type }) => ({ ...page, type }))).toEqual([ + { x: 0, y: 0, type: 'dragstart' }, + { x: modifierChange, y: modifierChange, type: 'dragmove' }, + { x: modifierChange, y: modifierChange, type: 'draginertiastart' }, + ]) + + // test inertia with { endOnly: true } modifier and with throw + changeModifier.options.endOnly = true + downStartMoveUp({ x: 100, y: 0, dt: 10 }) + // { endOnly: true } && thrown: modifier is called from pointerUp inertia calc + expect(modifierCallPhases).toEqual(['inertiastart']) + // { endOnly: true } && thrown: inertia target coords are correct + expect(state.modifiedOffset).toEqual({ + // modified target minus move coords + x: modifierChange - 100, + y: modifierChange - 0, + }) + + // test smoothEnd with { endOnly: false } modifier + changeModifier.options.endOnly = false + downStartMoveUp({ x: 1, y: 0, dt: 1000 }) + // { endOnly: false } && !thrown: inertia smoothEnd is not activated + expect(state.active).toBe(false) + // { endOnly: false } && !thrown: modifier is called from pointerUp + expect(modifierCallPhases).toEqual(['move', 'inertiastart']) + + // test smoothEnd with { endOnly: true } modifier + changeModifier.options.endOnly = true + downStartMoveUp({ x: 1, y: 0, dt: 1000 }) + // { endOnly: true } && !thrown: inertia smoothEnd is activated + expect(state.active).toBe(true) + // { endOnly: true } && !thrown: modifier is called from pointerUp smooth end check + expect(modifierCallPhases).toEqual(['inertiastart']) + + interactable.draggable({ + modifiers: [ + { + options: { endOnly: true }, + methods: { + set({ coords: modifiedCoords, phase }) { + extend(modifiedCoords, { x: 300, y: 400 }) + modifierCallPhases.push(phase) + }, + }, + enable: null, + disable: null, + }, + ], + }) + + downStartMoveUp({ x: 50, y: 70, dt: 1000 }) + coords.timeStamp = 100 + expect(state.targetOffset).toEqual({ x: 250, y: 330 }) + + extend(coords.page, { x: 50, y: 100 }) + down() + // inertia is stopped on resume + expect(interaction._interacting && !state.active).toBe(true) + // interaction coords are updated to down coords on resume + expect({ coords: interaction.coords.cur.page, rect: interaction.rect }).toEqual({ + coords: coords.page, + rect: { left: 50, top: 70, right: 150, bottom: 170, width: 100, height: 100 }, + }) + // action resume event coords are set correctly + expect(lastEvent().page).toEqual(coords.page) + + move() + // interaction coords are correct on duplicate move after resume + expect({ coords: interaction.coords.cur.page, rect: interaction.rect }).toEqual({ + coords: coords.page, + rect: { left: 50, top: 70, right: 150, bottom: 170, width: 100, height: 100 }, + }) + // second release inertia target is not the modified target + // second release inertia target is not the pointer event coords + // action move event coords on duplicate move after resume is correct + expect(lastEvent().page).toEqual(coords.page) + + extend(coords.page, { x: 200, y: 250 }) + move() + up() + expect(state.targetOffset).not.toEqual(coords.page) + expect(state.targetOffset).not.toEqual({ x: 300, y: 400 }) + // inertiastart is fired at non preEnd modified coords + expect(helpers.getProps(lastEvent(), ['type', 'page', 'rect'] as const)).toEqual({ + type: 'draginertiastart', + page: coords.page, + rect: { left: 200, top: 220, right: 300, bottom: 320, width: 100, height: 100 }, + }) + + down() + extend(coords.page, { x: 150, y: 400 }) + move() + // interaction coords after second resume are correct + expect({ coords: interaction.coords.cur.page, rect: interaction.rect }).toEqual({ + coords: coords.page, + rect: { left: 150, top: 370, right: 250, bottom: 470, width: 100, height: 100 }, + }) + // action move event after second resume is fired at non preEnd modified coords + expect(helpers.getProps(lastEvent(), ['type', 'page', 'rect'] as const)).toEqual({ + type: 'dragmove', + page: coords.page, + rect: { left: 150, top: 370, right: 250, bottom: 470, width: 100, height: 100 }, + }) + + interaction.stop() + + function downStartMoveUp({ x, y, dt }: any) { + fired = [] + modifierCallPhases = [] + coords.timeStamp = 0 + interaction.stop() + + Object.assign(coords.page, { x: 0, y: 0 }) + down() + + start({ name: 'drag' }) + + Object.assign(coords.page, { x, y }) + coords.timeStamp = dt + move() + up() + } + + function lastEvent() { + return fired[fired.length - 1] + } +}) diff --git a/packages/@interactjs/inertia/package.json b/packages/@interactjs/inertia/package.json new file mode 100644 index 000000000..bdc64987b --- /dev/null +++ b/packages/@interactjs/inertia/package.json @@ -0,0 +1,31 @@ +{ + "name": "@interactjs/inertia", + "version": "1.10.27", + "main": "index", + "module": "index", + "type": "module", + "repository": { + "type": "git", + "url": "https://github.com/taye/interact.js.git", + "directory": "packages/@interactjs/inertia" + }, + "dependencies": { + "@interactjs/offset": "1.10.27" + }, + "peerDependencies": { + "@interactjs/core": "1.10.27", + "@interactjs/modifiers": "1.10.27", + "@interactjs/utils": "1.10.27" + }, + "optionalDependencies": { + "@interactjs/interact": "1.10.27" + }, + "publishConfig": { + "access": "public" + }, + "sideEffects": [ + "**/index.js", + "**/index.prod.js" + ], + "license": "MIT" +} diff --git a/packages/@interactjs/inertia/plugin.ts b/packages/@interactjs/inertia/plugin.ts new file mode 100644 index 000000000..1d7c72e52 --- /dev/null +++ b/packages/@interactjs/inertia/plugin.ts @@ -0,0 +1,434 @@ +import type { Interaction, DoPhaseArg } from '@interactjs/core/Interaction' +import type { Scope, SignalArgs, Plugin } from '@interactjs/core/scope' +import type { ActionName, Point, PointerEventType } from '@interactjs/core/types' +/* eslint-disable import/no-duplicates -- for typescript module augmentations */ +import '@interactjs/modifiers/base' +import '@interactjs/offset/plugin' +import * as modifiers from '@interactjs/modifiers/base' +import { Modification } from '@interactjs/modifiers/Modification' +import type { ModifierArg } from '@interactjs/modifiers/types' +import offset from '@interactjs/offset/plugin' +/* eslint-enable import/no-duplicates */ +import * as dom from '@interactjs/utils/domUtils' +import hypot from '@interactjs/utils/hypot' +import is from '@interactjs/utils/is' +import { copyCoords } from '@interactjs/utils/pointerUtils' +import raf from '@interactjs/utils/raf' + +declare module '@interactjs/core/InteractEvent' { + interface PhaseMap { + resume?: true + inertiastart?: true + } +} + +declare module '@interactjs/core/Interaction' { + interface Interaction { + inertia?: InertiaState + } +} + +declare module '@interactjs/core/options' { + interface PerActionDefaults { + inertia?: { + enabled?: boolean + resistance?: number // the lambda in exponential decay + minSpeed?: number // target speed must be above this for inertia to start + endSpeed?: number // the speed at which inertia is slow enough to stop + allowResume?: true // allow resuming an action in inertia phase + smoothEndDuration?: number // animate to snap/restrict endOnly if there's no inertia + } + } +} + +declare module '@interactjs/core/scope' { + interface SignalArgs { + 'interactions:before-action-inertiastart': Omit, 'iEvent'> + 'interactions:action-inertiastart': DoPhaseArg + 'interactions:after-action-inertiastart': DoPhaseArg + 'interactions:before-action-resume': Omit, 'iEvent'> + 'interactions:action-resume': DoPhaseArg + 'interactions:after-action-resume': DoPhaseArg + } +} + +function install(scope: Scope) { + const { defaults } = scope + + scope.usePlugin(offset) + scope.usePlugin(modifiers.default) + scope.actions.phases.inertiastart = true + scope.actions.phases.resume = true + + defaults.perAction.inertia = { + enabled: false, + resistance: 10, // the lambda in exponential decay + minSpeed: 100, // target speed must be above this for inertia to start + endSpeed: 10, // the speed at which inertia is slow enough to stop + allowResume: true, // allow resuming an action in inertia phase + smoothEndDuration: 300, // animate to snap/restrict endOnly if there's no inertia + } +} + +export class InertiaState { + active = false + isModified = false + smoothEnd = false + allowResume = false + + modification!: Modification + modifierCount = 0 + modifierArg!: ModifierArg + + startCoords!: Point + t0 = 0 + v0 = 0 + + te = 0 + targetOffset!: Point + modifiedOffset!: Point + currentOffset!: Point + + lambda_v0? = 0 // eslint-disable-line camelcase + one_ve_v0? = 0 // eslint-disable-line camelcase + timeout!: number + readonly interaction: Interaction + + constructor(interaction: Interaction) { + this.interaction = interaction + } + + start(event: PointerEventType) { + const { interaction } = this + const options = getOptions(interaction) + + if (!options || !options.enabled) { + return false + } + + const { client: velocityClient } = interaction.coords.velocity + const pointerSpeed = hypot(velocityClient.x, velocityClient.y) + const modification = this.modification || (this.modification = new Modification(interaction)) + + modification.copyFrom(interaction.modification) + + this.t0 = interaction._now() + this.allowResume = options.allowResume + this.v0 = pointerSpeed + this.currentOffset = { x: 0, y: 0 } + this.startCoords = interaction.coords.cur.page + + this.modifierArg = modification.fillArg({ + pageCoords: this.startCoords, + preEnd: true, + phase: 'inertiastart', + }) + + const thrown = + this.t0 - interaction.coords.cur.timeStamp < 50 && + pointerSpeed > options.minSpeed && + pointerSpeed > options.endSpeed + + if (thrown) { + this.startInertia() + } else { + modification.result = modification.setAll(this.modifierArg) + + if (!modification.result.changed) { + return false + } + + this.startSmoothEnd() + } + + // force modification change + interaction.modification.result.rect = null + + // bring inertiastart event to the target coords + interaction.offsetBy(this.targetOffset) + interaction._doPhase({ + interaction, + event, + phase: 'inertiastart', + }) + interaction.offsetBy({ x: -this.targetOffset.x, y: -this.targetOffset.y }) + // force modification change + interaction.modification.result.rect = null + + this.active = true + interaction.simulation = this + + return true + } + + startInertia() { + const startVelocity = this.interaction.coords.velocity.client + const options = getOptions(this.interaction) + const lambda = options.resistance + const inertiaDur = -Math.log(options.endSpeed / this.v0) / lambda + + this.targetOffset = { + x: (startVelocity.x - inertiaDur) / lambda, + y: (startVelocity.y - inertiaDur) / lambda, + } + + this.te = inertiaDur + this.lambda_v0 = lambda / this.v0 + this.one_ve_v0 = 1 - options.endSpeed / this.v0 + + const { modification, modifierArg } = this + + modifierArg.pageCoords = { + x: this.startCoords.x + this.targetOffset.x, + y: this.startCoords.y + this.targetOffset.y, + } + + modification.result = modification.setAll(modifierArg) + + if (modification.result.changed) { + this.isModified = true + this.modifiedOffset = { + x: this.targetOffset.x + modification.result.delta.x, + y: this.targetOffset.y + modification.result.delta.y, + } + } + + this.onNextFrame(() => this.inertiaTick()) + } + + startSmoothEnd() { + this.smoothEnd = true + this.isModified = true + this.targetOffset = { + x: this.modification.result.delta.x, + y: this.modification.result.delta.y, + } + + this.onNextFrame(() => this.smoothEndTick()) + } + + onNextFrame(tickFn: () => void) { + this.timeout = raf.request(() => { + if (this.active) { + tickFn() + } + }) + } + + inertiaTick() { + const { interaction } = this + const options = getOptions(interaction) + const lambda = options.resistance + const t = (interaction._now() - this.t0) / 1000 + + if (t < this.te) { + const progress = 1 - (Math.exp(-lambda * t) - this.lambda_v0) / this.one_ve_v0 + let newOffset: Point + + if (this.isModified) { + newOffset = getQuadraticCurvePoint( + 0, + 0, + this.targetOffset.x, + this.targetOffset.y, + this.modifiedOffset.x, + this.modifiedOffset.y, + progress, + ) + } else { + newOffset = { + x: this.targetOffset.x * progress, + y: this.targetOffset.y * progress, + } + } + + const delta = { x: newOffset.x - this.currentOffset.x, y: newOffset.y - this.currentOffset.y } + + this.currentOffset.x += delta.x + this.currentOffset.y += delta.y + + interaction.offsetBy(delta) + interaction.move() + + this.onNextFrame(() => this.inertiaTick()) + } else { + interaction.offsetBy({ + x: this.modifiedOffset.x - this.currentOffset.x, + y: this.modifiedOffset.y - this.currentOffset.y, + }) + + this.end() + } + } + + smoothEndTick() { + const { interaction } = this + const t = interaction._now() - this.t0 + const { smoothEndDuration: duration } = getOptions(interaction) + + if (t < duration) { + const newOffset = { + x: easeOutQuad(t, 0, this.targetOffset.x, duration), + y: easeOutQuad(t, 0, this.targetOffset.y, duration), + } + const delta = { + x: newOffset.x - this.currentOffset.x, + y: newOffset.y - this.currentOffset.y, + } + + this.currentOffset.x += delta.x + this.currentOffset.y += delta.y + + interaction.offsetBy(delta) + interaction.move({ skipModifiers: this.modifierCount }) + + this.onNextFrame(() => this.smoothEndTick()) + } else { + interaction.offsetBy({ + x: this.targetOffset.x - this.currentOffset.x, + y: this.targetOffset.y - this.currentOffset.y, + }) + + this.end() + } + } + + resume({ pointer, event, eventTarget }: SignalArgs['interactions:down']) { + const { interaction } = this + + // undo inertia changes to interaction coords + interaction.offsetBy({ + x: -this.currentOffset.x, + y: -this.currentOffset.y, + }) + + // update pointer at pointer down position + interaction.updatePointer(pointer, event, eventTarget, true) + + // fire resume signals and event + interaction._doPhase({ + interaction, + event, + phase: 'resume', + }) + copyCoords(interaction.coords.prev, interaction.coords.cur) + + this.stop() + } + + end() { + this.interaction.move() + this.interaction.end() + this.stop() + } + + stop() { + this.active = this.smoothEnd = false + this.interaction.simulation = null + raf.cancel(this.timeout) + } +} + +function start({ interaction, event }: DoPhaseArg) { + if (!interaction._interacting || interaction.simulation) { + return null + } + + const started = interaction.inertia.start(event) + + // prevent action end if inertia or smoothEnd + return started ? false : null +} + +// Check if the down event hits the current inertia target +// control should be return to the user +function resume(arg: SignalArgs['interactions:down']) { + const { interaction, eventTarget } = arg + const state = interaction.inertia + + if (!state.active) return + + let element = eventTarget as Node + + // climb up the DOM tree from the event target + while (is.element(element)) { + // if interaction element is the current inertia target element + if (element === interaction.element) { + state.resume(arg) + break + } + + element = dom.parentNode(element) + } +} + +function stop({ interaction }: { interaction: Interaction }) { + const state = interaction.inertia + + if (state.active) { + state.stop() + } +} + +function getOptions({ interactable, prepared }: Interaction) { + return interactable && interactable.options && prepared.name && interactable.options[prepared.name].inertia +} + +const inertia: Plugin = { + id: 'inertia', + before: ['modifiers', 'actions'], + install, + listeners: { + 'interactions:new': ({ interaction }) => { + interaction.inertia = new InertiaState(interaction) + }, + + 'interactions:before-action-end': start, + 'interactions:down': resume, + 'interactions:stop': stop, + + 'interactions:before-action-resume': (arg) => { + const { modification } = arg.interaction + + modification.stop(arg) + modification.start(arg, arg.interaction.coords.cur.page) + modification.applyToInteraction(arg) + }, + + 'interactions:before-action-inertiastart': (arg) => arg.interaction.modification.setAndApply(arg), + 'interactions:action-resume': modifiers.addEventModifiers, + 'interactions:action-inertiastart': modifiers.addEventModifiers, + 'interactions:after-action-inertiastart': (arg) => + arg.interaction.modification.restoreInteractionCoords(arg), + 'interactions:after-action-resume': (arg) => arg.interaction.modification.restoreInteractionCoords(arg), + }, +} + +// http://stackoverflow.com/a/5634528/2280888 +function _getQBezierValue(t: number, p1: number, p2: number, p3: number) { + const iT = 1 - t + return iT * iT * p1 + 2 * iT * t * p2 + t * t * p3 +} + +function getQuadraticCurvePoint( + startX: number, + startY: number, + cpX: number, + cpY: number, + endX: number, + endY: number, + position: number, +) { + return { + x: _getQBezierValue(position, startX, cpX, endX), + y: _getQBezierValue(position, startY, cpY, endY), + } +} + +// http://gizma.com/easing/ +function easeOutQuad(t: number, b: number, c: number, d: number) { + t /= d + return -c * t * (t - 2) + b +} + +export default inertia diff --git a/packages/@interactjs/interact/README.md b/packages/@interactjs/interact/README.md new file mode 100644 index 000000000..549491910 --- /dev/null +++ b/packages/@interactjs/interact/README.md @@ -0,0 +1,5 @@ +

+This package is an internal part of interactjs and is not meant +to be used independently as each update may introduce breaking changes +

diff --git a/packages/@interactjs/interact/index.ts b/packages/@interactjs/interact/index.ts new file mode 100644 index 000000000..0dfec7314 --- /dev/null +++ b/packages/@interactjs/interact/index.ts @@ -0,0 +1,10 @@ +import { Scope } from '@interactjs/core/scope' + +const scope = new Scope() + +const interact = scope.interactStatic + +export default interact + +const _global = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : this +scope.init(_global) diff --git a/packages/@interactjs/interact/interact.spec.ts b/packages/@interactjs/interact/interact.spec.ts new file mode 100644 index 000000000..dbcb11f14 --- /dev/null +++ b/packages/@interactjs/interact/interact.spec.ts @@ -0,0 +1,93 @@ +import { Scope } from '@interactjs/core/scope' + +const makeIframeDoc = () => { + const iframe = document.body.appendChild(document.createElement('iframe')) + return iframe.contentWindow.document +} +test('interact export', () => { + const scope = new Scope() + const interact = scope.interactStatic + + scope.init(window) + + const interactable1 = interact('selector') + // interact function returns Interactable instance + expect(interactable1).toBeInstanceOf(scope.Interactable) + // same interactable is returned with same target and context + expect(interact('selector')).toBe(interactable1) + // new interactables are added to list + expect(scope.interactables.list).toHaveLength(1) + + interactable1.unset() + // unset interactables are removed + expect(scope.interactables.list).toHaveLength(0) + // unset interactions are removed + expect(scope.interactions.list).toHaveLength(0) + + const doc1 = document + const doc2 = makeIframeDoc() + const results = ( + [ + ['repeat', doc1], + ['repeat', doc2], + [doc1, doc1], + [doc2.body, doc2], + ] as const + ).reduce((acc, [target, context]) => { + const interactable = interact(target, { context }) + + // unique contexts make unique interactables with identical targets + expect(acc.some((e) => e.interactable === interactable)).toBe(false) + + acc.push({ interactable, target, context }) + return acc + }, []) + + for (const { interactable, target, context } of results) { + // interactions.get returns correct result with identical targets and different contexts + expect(scope.interactables.getExisting(target, { context })).toBe(interactable) + } + + const doc3 = makeIframeDoc() + + const prevDocCount = scope.documents.length + + interact.addDocument(doc3, { events: { passive: false } }) + // interact.addDocument() adds to scope with options + expect(scope.documents[prevDocCount]).toEqual({ doc: doc3, options: { events: { passive: false } } }) + + interact.removeDocument(doc3) + // interact.removeDocument() removes document from scope + expect(scope.documents).toHaveLength(prevDocCount) + + scope.interactables.list.forEach((i) => i.unset()) + + const plugin1 = { + id: 'test-1', + install() { + plugin1.count++ + }, + count: 0, + } + const plugin2 = { + id: '', + install() { + plugin2.count++ + }, + count: 0, + } + + interact.use(plugin1) + interact.use(plugin2) + + // new plugin install methods are called + expect([plugin1.count, plugin2.count]).toEqual([1, 1]) + + interact.use({ ...plugin1 }) + // different plugin object with same id not installed + expect([plugin1.count, plugin2.count]).toEqual([1, 1]) + + interact.use(plugin2) + // plugin without id not re-installed + expect([plugin1.count, plugin2.count]).toEqual([1, 1]) +}) diff --git a/packages/@interactjs/interact/package.json b/packages/@interactjs/interact/package.json new file mode 100644 index 000000000..6045ebbb8 --- /dev/null +++ b/packages/@interactjs/interact/package.json @@ -0,0 +1,21 @@ +{ + "name": "@interactjs/interact", + "version": "1.10.27", + "main": "index", + "module": "index", + "type": "module", + "repository": { + "type": "git", + "url": "https://github.com/taye/interact.js.git", + "directory": "packages/@interactjs/interact" + }, + "dependencies": { + "@interactjs/core": "1.10.27", + "@interactjs/utils": "1.10.27" + }, + "publishConfig": { + "access": "public" + }, + "sideEffects": false, + "license": "MIT" +} diff --git a/packages/@interactjs/interactjs/index.stub.ts b/packages/@interactjs/interactjs/index.stub.ts new file mode 100644 index 000000000..7401706b2 --- /dev/null +++ b/packages/@interactjs/interactjs/index.stub.ts @@ -0,0 +1,58 @@ +/* eslint-disable import/no-duplicates -- for typescript module augmentations */ +import '@interactjs/actions/plugin' +import '@interactjs/auto-scroll/plugin' +import '@interactjs/auto-start/plugin' +import '@interactjs/core/interactablePreventDefault' +import '@interactjs/dev-tools/plugin' +import '@interactjs/inertia/plugin' +import '@interactjs/interact' +import '@interactjs/modifiers/plugin' +import '@interactjs/offset/plugin' +import '@interactjs/pointer-events/plugin' +import '@interactjs/reflow/plugin' + +import actions from '@interactjs/actions/plugin' +import autoScroll from '@interactjs/auto-scroll/plugin' +import autoStart from '@interactjs/auto-start/plugin' +import interactablePreventDefault from '@interactjs/core/interactablePreventDefault' +import devTools from '@interactjs/dev-tools/plugin' +import inertia from '@interactjs/inertia/plugin' +import interact from '@interactjs/interact' +import modifiers from '@interactjs/modifiers/plugin' +import offset from '@interactjs/offset/plugin' +import pointerEvents from '@interactjs/pointer-events/plugin' +import reflow from '@interactjs/reflow/plugin' +/* eslint-enable import/no-duplicates */ + +interact.use(interactablePreventDefault) + +interact.use(offset) + +// pointerEvents +interact.use(pointerEvents) + +// inertia +interact.use(inertia) + +// snap, resize, etc. +interact.use(modifiers) + +// autoStart, hold +interact.use(autoStart) + +// drag and drop, resize, gesture +interact.use(actions) + +// autoScroll +interact.use(autoScroll) + +// reflow +interact.use(reflow) + +// eslint-disable-next-line no-undef +if (process.env.NODE_ENV !== 'production') { + interact.use(devTools) +} + +export default interact +;(interact as any).default = interact diff --git a/packages/@interactjs/interactjs/index.ts b/packages/@interactjs/interactjs/index.ts new file mode 100644 index 000000000..7401706b2 --- /dev/null +++ b/packages/@interactjs/interactjs/index.ts @@ -0,0 +1,58 @@ +/* eslint-disable import/no-duplicates -- for typescript module augmentations */ +import '@interactjs/actions/plugin' +import '@interactjs/auto-scroll/plugin' +import '@interactjs/auto-start/plugin' +import '@interactjs/core/interactablePreventDefault' +import '@interactjs/dev-tools/plugin' +import '@interactjs/inertia/plugin' +import '@interactjs/interact' +import '@interactjs/modifiers/plugin' +import '@interactjs/offset/plugin' +import '@interactjs/pointer-events/plugin' +import '@interactjs/reflow/plugin' + +import actions from '@interactjs/actions/plugin' +import autoScroll from '@interactjs/auto-scroll/plugin' +import autoStart from '@interactjs/auto-start/plugin' +import interactablePreventDefault from '@interactjs/core/interactablePreventDefault' +import devTools from '@interactjs/dev-tools/plugin' +import inertia from '@interactjs/inertia/plugin' +import interact from '@interactjs/interact' +import modifiers from '@interactjs/modifiers/plugin' +import offset from '@interactjs/offset/plugin' +import pointerEvents from '@interactjs/pointer-events/plugin' +import reflow from '@interactjs/reflow/plugin' +/* eslint-enable import/no-duplicates */ + +interact.use(interactablePreventDefault) + +interact.use(offset) + +// pointerEvents +interact.use(pointerEvents) + +// inertia +interact.use(inertia) + +// snap, resize, etc. +interact.use(modifiers) + +// autoStart, hold +interact.use(autoStart) + +// drag and drop, resize, gesture +interact.use(actions) + +// autoScroll +interact.use(autoScroll) + +// reflow +interact.use(reflow) + +// eslint-disable-next-line no-undef +if (process.env.NODE_ENV !== 'production') { + interact.use(devTools) +} + +export default interact +;(interact as any).default = interact diff --git a/packages/@interactjs/interactjs/package.json b/packages/@interactjs/interactjs/package.json new file mode 100644 index 000000000..e7be2440f --- /dev/null +++ b/packages/@interactjs/interactjs/package.json @@ -0,0 +1,38 @@ +{ + "name": "@interactjs/interactjs", + "version": "1.10.27", + "main": "index", + "module": "index", + "type": "module", + "repository": { + "type": "git", + "url": "https://github.com/taye/interact.js.git" + }, + "dependencies": { + "@interactjs/actions": "1.10.27", + "@interactjs/arrange": "1.10.27", + "@interactjs/auto-scroll": "1.10.27", + "@interactjs/auto-start": "1.10.27", + "@interactjs/clone": "1.10.27", + "@interactjs/core": "1.10.27", + "@interactjs/dev-tools": "1.10.27", + "@interactjs/feedback": "1.10.27", + "@interactjs/inertia": "1.10.27", + "@interactjs/interact": "1.10.27", + "@interactjs/modifiers": "1.10.27", + "@interactjs/multi-target": "1.10.27", + "@interactjs/offset": "1.10.27", + "@interactjs/pointer-events": "1.10.27", + "@interactjs/react": "1.10.27", + "@interactjs/rebound": "1.10.27", + "@interactjs/symbol-tree": "1.10.27", + "@interactjs/reflow": "1.10.27", + "@interactjs/utils": "1.10.27", + "@interactjs/vue": "1.10.27" + }, + "publishConfig": { + "access": "public" + }, + "sideEffects": false, + "license": "MIT" +} diff --git a/packages/@interactjs/modifiers/Modification.ts b/packages/@interactjs/modifiers/Modification.ts new file mode 100644 index 000000000..974d7ab49 --- /dev/null +++ b/packages/@interactjs/modifiers/Modification.ts @@ -0,0 +1,421 @@ +import type { EventPhase } from '@interactjs/core/InteractEvent' +import type { Interaction, DoAnyPhaseArg } from '@interactjs/core/Interaction' +import type { EdgeOptions, FullRect, Point, Rect } from '@interactjs/core/types' +import clone from '@interactjs/utils/clone' +import extend from '@interactjs/utils/extend' +import * as rectUtils from '@interactjs/utils/rect' + +import type { Modifier, ModifierArg, ModifierState } from './types' + +export interface ModificationResult { + delta: Point + rectDelta: Rect + coords: Point + rect: FullRect + eventProps: any[] + changed: boolean +} + +interface MethodArg { + phase: EventPhase + pageCoords: Point + rect: FullRect + coords: Point + preEnd?: boolean + skipModifiers?: number +} + +export class Modification { + states: ModifierState[] = [] + startOffset: Rect = { left: 0, right: 0, top: 0, bottom: 0 } + startDelta!: Point + result!: ModificationResult + endResult!: Point + startEdges!: EdgeOptions + edges: EdgeOptions + readonly interaction: Readonly + + constructor(interaction: Interaction) { + this.interaction = interaction + this.result = createResult() + this.edges = { + left: false, + right: false, + top: false, + bottom: false, + } + } + + start({ phase }: { phase: EventPhase }, pageCoords: Point) { + const { interaction } = this + const modifierList = getModifierList(interaction) + this.prepareStates(modifierList) + + this.startEdges = extend({}, interaction.edges) + this.edges = extend({}, this.startEdges) + this.startOffset = getRectOffset(interaction.rect, pageCoords) + this.startDelta = { x: 0, y: 0 } + + const arg = this.fillArg({ + phase, + pageCoords, + preEnd: false, + }) + + this.result = createResult() + this.startAll(arg) + + const result = (this.result = this.setAll(arg)) + + return result + } + + fillArg(arg: Partial) { + const { interaction } = this + + arg.interaction = interaction + arg.interactable = interaction.interactable + arg.element = interaction.element + arg.rect ||= interaction.rect + arg.edges ||= this.startEdges + arg.startOffset = this.startOffset + + return arg as ModifierArg + } + + startAll(arg: MethodArg & Partial) { + for (const state of this.states) { + if (state.methods.start) { + arg.state = state + state.methods.start(arg as ModifierArg) + } + } + } + + setAll(arg: MethodArg & Partial): ModificationResult { + const { phase, preEnd, skipModifiers, rect: unmodifiedRect, edges: unmodifiedEdges } = arg + + arg.coords = extend({}, arg.pageCoords) + arg.rect = extend({}, unmodifiedRect) + arg.edges = extend({}, unmodifiedEdges) + + const states = skipModifiers ? this.states.slice(skipModifiers) : this.states + + const newResult = createResult(arg.coords, arg.rect) + + for (const state of states) { + const { options } = state + const lastModifierCoords = extend({}, arg.coords) + let returnValue = null + + if (state.methods?.set && this.shouldDo(options, preEnd, phase)) { + arg.state = state + returnValue = state.methods.set(arg as ModifierArg) + + rectUtils.addEdges(arg.edges, arg.rect, { + x: arg.coords.x - lastModifierCoords.x, + y: arg.coords.y - lastModifierCoords.y, + }) + } + + newResult.eventProps.push(returnValue) + } + + extend(this.edges, arg.edges) + + newResult.delta.x = arg.coords.x - arg.pageCoords.x + newResult.delta.y = arg.coords.y - arg.pageCoords.y + + newResult.rectDelta.left = arg.rect.left - unmodifiedRect.left + newResult.rectDelta.right = arg.rect.right - unmodifiedRect.right + newResult.rectDelta.top = arg.rect.top - unmodifiedRect.top + newResult.rectDelta.bottom = arg.rect.bottom - unmodifiedRect.bottom + + const prevCoords = this.result.coords + const prevRect = this.result.rect + + if (prevCoords && prevRect) { + const rectChanged = + newResult.rect.left !== prevRect.left || + newResult.rect.right !== prevRect.right || + newResult.rect.top !== prevRect.top || + newResult.rect.bottom !== prevRect.bottom + + newResult.changed = + rectChanged || prevCoords.x !== newResult.coords.x || prevCoords.y !== newResult.coords.y + } + + return newResult + } + + applyToInteraction(arg: { phase: EventPhase; rect?: Rect }) { + const { interaction } = this + const { phase } = arg + const curCoords = interaction.coords.cur + const startCoords = interaction.coords.start + const { result, startDelta } = this + const curDelta = result.delta + + if (phase === 'start') { + extend(this.startDelta, result.delta) + } + + for (const [coordsSet, delta] of [ + [startCoords, startDelta], + [curCoords, curDelta], + ] as const) { + coordsSet.page.x += delta.x + coordsSet.page.y += delta.y + coordsSet.client.x += delta.x + coordsSet.client.y += delta.y + } + + const { rectDelta } = this.result + const rect = arg.rect || interaction.rect + + rect.left += rectDelta.left + rect.right += rectDelta.right + rect.top += rectDelta.top + rect.bottom += rectDelta.bottom + + rect.width = rect.right - rect.left + rect.height = rect.bottom - rect.top + } + + setAndApply( + arg: Partial & { + phase: EventPhase + preEnd?: boolean + skipModifiers?: number + modifiedCoords?: Point + }, + ): void | false { + const { interaction } = this + const { phase, preEnd, skipModifiers } = arg + + const result = this.setAll( + this.fillArg({ + preEnd, + phase, + pageCoords: arg.modifiedCoords || interaction.coords.cur.page, + }), + ) + + this.result = result + + // don't fire an action move if a modifier would keep the event in the same + // cordinates as before + if ( + !result.changed && + (!skipModifiers || skipModifiers < this.states.length) && + interaction.interacting() + ) { + return false + } + + if (arg.modifiedCoords) { + const { page } = interaction.coords.cur + const adjustment = { + x: arg.modifiedCoords.x - page.x, + y: arg.modifiedCoords.y - page.y, + } + + result.coords.x += adjustment.x + result.coords.y += adjustment.y + result.delta.x += adjustment.x + result.delta.y += adjustment.y + } + + this.applyToInteraction(arg) + } + + beforeEnd(arg: Omit & { state?: ModifierState }): void | false { + const { interaction, event } = arg + const states = this.states + + if (!states || !states.length) { + return + } + + let doPreend = false + + for (const state of states) { + arg.state = state + const { options, methods } = state + + const endPosition = methods.beforeEnd && methods.beforeEnd(arg as unknown as ModifierArg) + + if (endPosition) { + this.endResult = endPosition + return false + } + + doPreend = doPreend || (!doPreend && this.shouldDo(options, true, arg.phase, true)) + } + + if (doPreend) { + // trigger a final modified move before ending + interaction.move({ event, preEnd: true }) + } + } + + stop(arg: { interaction: Interaction }) { + const { interaction } = arg + + if (!this.states || !this.states.length) { + return + } + + const modifierArg: Partial = extend( + { + states: this.states, + interactable: interaction.interactable, + element: interaction.element, + rect: null, + }, + arg, + ) + + this.fillArg(modifierArg) + + for (const state of this.states) { + modifierArg.state = state + + if (state.methods.stop) { + state.methods.stop(modifierArg as ModifierArg) + } + } + + this.states = null + this.endResult = null + } + + prepareStates(modifierList: Modifier[]) { + this.states = [] + + for (let index = 0; index < modifierList.length; index++) { + const { options, methods, name } = modifierList[index] + + this.states.push({ + options, + methods, + index, + name, + }) + } + + return this.states + } + + restoreInteractionCoords({ interaction: { coords, rect, modification } }: { interaction: Interaction }) { + if (!modification.result) return + + const { startDelta } = modification + const { delta: curDelta, rectDelta } = modification.result + + const coordsAndDeltas = [ + [coords.start, startDelta], + [coords.cur, curDelta], + ] + + for (const [coordsSet, delta] of coordsAndDeltas as any) { + coordsSet.page.x -= delta.x + coordsSet.page.y -= delta.y + coordsSet.client.x -= delta.x + coordsSet.client.y -= delta.y + } + + rect.left -= rectDelta.left + rect.right -= rectDelta.right + rect.top -= rectDelta.top + rect.bottom -= rectDelta.bottom + } + + shouldDo(options, preEnd?: boolean, phase?: string, requireEndOnly?: boolean) { + if ( + // ignore disabled modifiers + !options || + options.enabled === false || + // check if we require endOnly option to fire move before end + (requireEndOnly && !options.endOnly) || + // don't apply endOnly modifiers when not ending + (options.endOnly && !preEnd) || + // check if modifier should run be applied on start + (phase === 'start' && !options.setStart) + ) { + return false + } + + return true + } + + copyFrom(other: Modification) { + this.startOffset = other.startOffset + this.startDelta = other.startDelta + this.startEdges = other.startEdges + this.edges = other.edges + this.states = other.states.map((s) => clone(s) as ModifierState) + this.result = createResult(extend({}, other.result.coords), extend({}, other.result.rect)) + } + + destroy() { + for (const prop in this) { + this[prop] = null + } + } +} + +function createResult(coords?: Point, rect?: FullRect): ModificationResult { + return { + rect, + coords, + delta: { x: 0, y: 0 }, + rectDelta: { + left: 0, + right: 0, + top: 0, + bottom: 0, + }, + eventProps: [], + changed: true, + } +} + +function getModifierList(interaction) { + const actionOptions = interaction.interactable.options[interaction.prepared.name] + const actionModifiers = actionOptions.modifiers + + if (actionModifiers && actionModifiers.length) { + return actionModifiers + } + + return ['snap', 'snapSize', 'snapEdges', 'restrict', 'restrictEdges', 'restrictSize'] + .map((type) => { + const options = actionOptions[type] + + return ( + options && + options.enabled && { + options, + methods: options._methods, + } + ) + }) + .filter((m) => !!m) +} + +export function getRectOffset(rect, coords) { + return rect + ? { + left: coords.x - rect.left, + top: coords.y - rect.top, + right: rect.right - coords.x, + bottom: rect.bottom - coords.y, + } + : { + left: 0, + top: 0, + right: 0, + bottom: 0, + } +} diff --git a/packages/@interactjs/modifiers/README.md b/packages/@interactjs/modifiers/README.md new file mode 100644 index 000000000..549491910 --- /dev/null +++ b/packages/@interactjs/modifiers/README.md @@ -0,0 +1,5 @@ +

+This package is an internal part of interactjs and is not meant +to be used independently as each update may introduce breaking changes +

diff --git a/packages/@interactjs/modifiers/all.ts b/packages/@interactjs/modifiers/all.ts new file mode 100644 index 000000000..9883af48e --- /dev/null +++ b/packages/@interactjs/modifiers/all.ts @@ -0,0 +1,29 @@ +/* eslint-disable n/no-extraneous-import, import/no-unresolved */ +import aspectRatio from './aspectRatio' +import avoid from './avoid/avoid' +import restrictEdges from './restrict/edges' +import restrict from './restrict/pointer' +import restrictRect from './restrict/rect' +import restrictSize from './restrict/size' +import rubberband from './rubberband/rubberband' +import snapEdges from './snap/edges' +import snap from './snap/pointer' +import snapSize from './snap/size' +import spring from './spring/spring' +import transform from './transform/transform' + +export default { + aspectRatio, + restrictEdges, + restrict, + restrictRect, + restrictSize, + snapEdges, + snap, + snapSize, + + spring, + avoid, + transform, + rubberband, +} diff --git a/packages/@interactjs/modifiers/aspectRatio.spec.ts b/packages/@interactjs/modifiers/aspectRatio.spec.ts new file mode 100644 index 000000000..7c0c834e4 --- /dev/null +++ b/packages/@interactjs/modifiers/aspectRatio.spec.ts @@ -0,0 +1,90 @@ +import resize from '@interactjs/actions/resize/plugin' +import * as helpers from '@interactjs/core/tests/_helpers' +import type { FullRect, EdgeOptions } from '@interactjs/core/types' + +import type { AspectRatioOptions } from './aspectRatio' +import aspectRatio from './aspectRatio' +import modifiersBase from './base' +import restrictSize from './restrict/size' + +const { ltrbwh } = helpers + +test('modifiers/aspectRatio', () => { + const rect = Object.freeze({ left: 0, top: 0, right: 10, bottom: 20, width: 10, height: 20 }) + const { interactable, interaction, event, coords, target } = helpers.testEnv({ + plugins: [modifiersBase, resize], + rect, + }) + + coords.client = coords.page + + const options: AspectRatioOptions = {} + let lastRect: FullRect = null + + interactable.resizable({ + edges: { left: true, top: true, right: true, bottom: true }, + modifiers: [aspectRatio(options)], + listeners: { + move(e) { + lastRect = e.rect + }, + }, + }) + + options.equalDelta = true + downStartMoveUp({ x: 2, y: 4.33, edges: { left: true, top: true } }) + // `equalDelta: true, 1 { left: true, top: true } + expect(lastRect).toEqual(ltrbwh(2, 2, 10, 20, 8, 18)) + + downStartMoveUp({ x: 30, y: 2, edges: { bottom: true } }) + // equalDelta: true, 2, edges: { bottom: true } + expect(lastRect).toEqual(ltrbwh(0, 0, 12, 22, 12, 22)) + + options.equalDelta = false + options.ratio = 2 + downStartMoveUp({ x: -5, y: 2, edges: { left: true } }) + // equalDelta: false, ratio: 2, edges: left + expect(lastRect).toEqual(ltrbwh(-5, 12.5, 10, 20, 15, 7.5)) + + // combine with restrictSize + options.modifiers = [ + restrictSize({ + max: { width: 20, height: 20 }, + }), + ] + options.equalDelta = false + options.ratio = 2 + + downStartMoveUp({ x: 20, y: 0, edges: { right: true } }) + // restrictSize with critical primary edge + expect(lastRect).toEqual(ltrbwh(0, 0, 20, 10, 20, 10)) + + downStartMoveUp({ x: 20, y: 20, edges: { bottom: true } }) + // restrictSize with critical secondary edge + expect(lastRect).toEqual(ltrbwh(0, 0, 20, 10, 20, 10)) + + options.ratio = 0.5 + downStartMoveUp({ x: 5, y: -5, edges: { left: true, bottom: true } }) + // equalDelta: false, ratio: 2, edges: left & bottom + expect(lastRect).toEqual(ltrbwh(5, 0, 10, 10, 5, 10)) + + downStartMoveUp({ x: -5, y: -5, edges: { right: true, top: true } }) + // equalDelta: false, ratio: 2, edges: right & top + expect(lastRect).toEqual(ltrbwh(0, 10, 5, 20, 5, 10)) + + function downStartMoveUp({ x, y, edges }: { x: number; y: number; edges: EdgeOptions }) { + coords.timeStamp = 0 + interaction.stop() + lastRect = null + + Object.assign(coords.page, { x: 0, y: 0 }) + interaction.pointerDown(event, event, target) + + interaction.start({ name: 'resize', edges }, interactable, target) + + Object.assign(coords.page, { x, y }) + interaction.pointerMove(event, event, target) + interaction.pointerMove(event, event, target) + interaction.pointerUp(event, event, target, target) + } +}) diff --git a/packages/@interactjs/modifiers/aspectRatio.ts b/packages/@interactjs/modifiers/aspectRatio.ts new file mode 100644 index 000000000..65523313e --- /dev/null +++ b/packages/@interactjs/modifiers/aspectRatio.ts @@ -0,0 +1,179 @@ +/** + * @module modifiers/aspectRatio + * + * @description + * This modifier forces elements to be resized with a specified dx/dy ratio. + * + * ```js + * interact(target).resizable({ + * modifiers: [ + * interact.modifiers.snapSize({ + * targets: [ interact.snappers.grid({ x: 20, y: 20 }) ], + * }), + * interact.aspectRatio({ ratio: 'preserve' }), + * ], + * }); + * ``` + */ + +import type { Point, Rect, EdgeOptions } from '@interactjs/core/types' +import extend from '@interactjs/utils/extend' +import { addEdges } from '@interactjs/utils/rect' + +import { makeModifier } from './base' +import { Modification } from './Modification' +import type { Modifier, ModifierModule, ModifierState } from './types' + +export interface AspectRatioOptions { + ratio?: number | 'preserve' + equalDelta?: boolean + modifiers?: Modifier[] + enabled?: boolean +} + +export type AspectRatioState = ModifierState< + AspectRatioOptions, + { + startCoords: Point + startRect: Rect + linkedEdges: EdgeOptions + ratio: number + equalDelta: boolean + xIsPrimaryAxis: boolean + edgeSign: { + x: number + y: number + } + subModification: Modification + } +> + +const aspectRatio: ModifierModule = { + start(arg) { + const { state, rect, edges, pageCoords: coords } = arg + let { ratio, enabled } = state.options + const { equalDelta, modifiers } = state.options + + if (ratio === 'preserve') { + ratio = rect.width / rect.height + } + + state.startCoords = extend({}, coords) + state.startRect = extend({}, rect) + state.ratio = ratio + state.equalDelta = equalDelta + + const linkedEdges = (state.linkedEdges = { + top: edges.top || (edges.left && !edges.bottom), + left: edges.left || (edges.top && !edges.right), + bottom: edges.bottom || (edges.right && !edges.top), + right: edges.right || (edges.bottom && !edges.left), + }) + + state.xIsPrimaryAxis = !!(edges.left || edges.right) + + if (state.equalDelta) { + const sign = (linkedEdges.left ? 1 : -1) * (linkedEdges.top ? 1 : -1) + state.edgeSign = { + x: sign, + y: sign, + } + } else { + state.edgeSign = { + x: linkedEdges.left ? -1 : 1, + y: linkedEdges.top ? -1 : 1, + } + } + + if (enabled !== false) { + extend(edges, linkedEdges) + } + + if (!modifiers?.length) return + + const subModification = new Modification(arg.interaction) + + subModification.copyFrom(arg.interaction.modification) + subModification.prepareStates(modifiers) + + state.subModification = subModification + subModification.startAll({ ...arg }) + }, + + set(arg) { + const { state, rect, coords } = arg + const { linkedEdges } = state + const initialCoords = extend({}, coords) + const aspectMethod = state.equalDelta ? setEqualDelta : setRatio + + extend(arg.edges, linkedEdges) + aspectMethod(state, state.xIsPrimaryAxis, coords, rect) + + if (!state.subModification) { + return null + } + + const correctedRect = extend({}, rect) + + addEdges(linkedEdges, correctedRect, { + x: coords.x - initialCoords.x, + y: coords.y - initialCoords.y, + }) + + const result = state.subModification.setAll({ + ...arg, + rect: correctedRect, + edges: linkedEdges, + pageCoords: coords, + prevCoords: coords, + prevRect: correctedRect, + }) + + const { delta } = result + + if (result.changed) { + const xIsCriticalAxis = Math.abs(delta.x) > Math.abs(delta.y) + + // do aspect modification again with critical edge axis as primary + aspectMethod(state, xIsCriticalAxis, result.coords, result.rect) + extend(coords, result.coords) + } + + return result.eventProps + }, + + defaults: { + ratio: 'preserve', + equalDelta: false, + modifiers: [], + enabled: false, + }, +} + +function setEqualDelta({ startCoords, edgeSign }: AspectRatioState, xIsPrimaryAxis: boolean, coords: Point) { + if (xIsPrimaryAxis) { + coords.y = startCoords.y + (coords.x - startCoords.x) * edgeSign.y + } else { + coords.x = startCoords.x + (coords.y - startCoords.y) * edgeSign.x + } +} + +function setRatio( + { startRect, startCoords, ratio, edgeSign }: AspectRatioState, + xIsPrimaryAxis: boolean, + coords: Point, + rect: Rect, +) { + if (xIsPrimaryAxis) { + const newHeight = rect.width / ratio + + coords.y = startCoords.y + (newHeight - startRect.height) * edgeSign.y + } else { + const newWidth = rect.height * ratio + + coords.x = startCoords.x + (newWidth - startRect.width) * edgeSign.x + } +} + +export default makeModifier(aspectRatio, 'aspectRatio') +export { aspectRatio } diff --git a/packages/@interactjs/modifiers/avoid/avoid.stub.ts b/packages/@interactjs/modifiers/avoid/avoid.stub.ts new file mode 100644 index 000000000..80c8a7126 --- /dev/null +++ b/packages/@interactjs/modifiers/avoid/avoid.stub.ts @@ -0,0 +1 @@ +export { default } from '../noop' diff --git a/packages/@interactjs/modifiers/avoid/avoid.ts b/packages/@interactjs/modifiers/avoid/avoid.ts new file mode 100644 index 000000000..80c8a7126 --- /dev/null +++ b/packages/@interactjs/modifiers/avoid/avoid.ts @@ -0,0 +1 @@ +export { default } from '../noop' diff --git a/packages/@interactjs/modifiers/base.spec.ts b/packages/@interactjs/modifiers/base.spec.ts new file mode 100644 index 000000000..6b9849f5d --- /dev/null +++ b/packages/@interactjs/modifiers/base.spec.ts @@ -0,0 +1,150 @@ +import * as helpers from '@interactjs/core/tests/_helpers' +import type { ActionName, Element } from '@interactjs/core/types' +import extend from '@interactjs/utils/extend' +import is from '@interactjs/utils/is' + +import modifiersBase from './base' + +test('modifiers/base', () => { + const { scope, target, interaction, interactable, coords, event } = helpers.testEnv({ + plugins: [modifiersBase], + }) + + // modifiers prop is added new Interaction + expect(is.object(interaction.modification)).toBe(true) + + coords.client = coords.page + + const testAction = { name: 'test' as ActionName } + const element = target as Element + const startCoords = { x: 100, y: 200 } + const moveCoords = { x: 400, y: 500 } + const options: any = { target: { x: 100, y: 100 }, setStart: true } + let firedEvents: any[] = [] + + interactable.rectChecker(() => ({ top: 0, left: 0, bottom: 50, right: 50 })) + interactable.on('teststart testmove testend', (e) => firedEvents.push(e)) + + extend(coords.page, startCoords) + interaction.pointerDown(event, event, element) + ;(interactable.options as any).test = { + enabled: true, + modifiers: [ + { + options, + methods: targetModifier, + }, + ], + } + + interaction.start(testAction, interactable, element) + + // modifier methods.start() was called + expect(options.started).toBe(true) + + // modifier methods.set() was called + expect(options.setted).toBe(true) + + // start event coords are modified + expect(interaction.prevEvent.page).toEqual(options.target) + + // interaction.coords.start are restored after action start phase + expect(interaction.coords.start.page).toEqual(startCoords) + + // interaction.coords.cur are restored after action start phase + expect(interaction.coords.cur.page).toEqual(startCoords) + + extend(coords.page, moveCoords) + interaction.pointerMove(event, event, element) + + // interaction.coords.cur are restored after action move phase + expect(interaction.coords.cur.page).toEqual(moveCoords) + + // interaction.coords.start are restored after action move phase + expect(interaction.coords.start.page).toEqual(startCoords) + + // move event start coords are modified + expect({ x: interaction.prevEvent.x0, y: interaction.prevEvent.y0 }).toEqual({ x: 100, y: 100 }) + + firedEvents = [] + scope.interactions.pointerMoveTolerance = 0 + interaction.pointerMove(event, event, element) + // duplicate result coords are ignored + expect(firedEvents).toHaveLength(0) + + interaction.stop() + + // modifier methods.stop() was called + expect(options.stopped).toBe(true) + + // don't set start + options.setStart = null + // add second modifier + ;(interactable.options as any).test.modifiers.push({ + options, + methods: doubleModifier, + }) + + extend(coords.page, startCoords) + interaction.pointerDown(event, event, element) + interaction.start(testAction, interactable, element) + + // modifier methods.set() was not called on start phase without options.setStart + expect(options.setted).toBeUndefined() + + // start event coords are not modified without options.setStart + expect(interaction.prevEvent.page).toEqual({ x: 100, y: 200 }) + + // interaction.coords.start are not modified without options.setStart + expect(interaction.coords.start.page).toEqual({ x: 100, y: 200 }) + + extend(coords.page, moveCoords) + interaction.pointerMove(event, event, element) + + // move event coords are modified by all modifiers + expect(interaction.prevEvent.page).toEqual({ x: 200, y: 200 }) + + interaction.pointerMove(event, event, element) + + expect(() => { + interaction._scopeFire('interactions:action-resume', { + interaction, + phase: 'resume', + iEvent: {} as any, + event, + }) + }).not.toThrow() + + interaction.stop() + + interaction.pointerUp(event, event, element, element) + // interaction coords after stopping are as expected + expect(interaction.coords.cur.page).toEqual(moveCoords) +}) + +const targetModifier = { + start({ state }: any) { + state.options.started = true + }, + set({ state, coords }: any) { + const { target } = state.options + + coords.x = target.x + coords.y = target.y + + state.options.setted = true + }, + stop({ state }: any) { + state.options.stopped = true + delete state.options.started + delete state.options.setted + }, +} + +const doubleModifier = { + start() {}, + set({ coords }: any) { + coords.x *= 2 + coords.y *= 2 + }, +} diff --git a/packages/@interactjs/modifiers/base.ts b/packages/@interactjs/modifiers/base.ts new file mode 100644 index 000000000..573b2874f --- /dev/null +++ b/packages/@interactjs/modifiers/base.ts @@ -0,0 +1,144 @@ +import type { InteractEvent } from '@interactjs/core/InteractEvent' +import type Interaction from '@interactjs/core/Interaction' +import type { Plugin } from '@interactjs/core/scope' + +import { Modification } from './Modification' +import type { Modifier, ModifierModule, ModifierState } from './types' + +declare module '@interactjs/core/Interaction' { + interface Interaction { + modification?: Modification + } +} + +declare module '@interactjs/core/InteractEvent' { + interface InteractEvent { + modifiers?: Array<{ + name: string + [key: string]: any + }> + } +} + +declare module '@interactjs/core/options' { + interface PerActionDefaults { + modifiers?: Modifier[] + } +} + +export function makeModifier< + Defaults extends { enabled?: boolean }, + State extends ModifierState, + Name extends string, + Result, +>(module: ModifierModule, name?: Name) { + const { defaults } = module + const methods = { + start: module.start, + set: module.set, + beforeEnd: module.beforeEnd, + stop: module.stop, + } + + const modifier = (_options?: Partial) => { + const options = (_options || {}) as Defaults + + options.enabled = options.enabled !== false + + // add missing defaults to options + for (const prop in defaults) { + if (!(prop in options)) { + ;(options as any)[prop] = defaults[prop] + } + } + + const m: Modifier = { + options, + methods, + name, + enable: () => { + options.enabled = true + return m + }, + disable: () => { + options.enabled = false + return m + }, + } + + return m + } + + if (name && typeof name === 'string') { + // for backwrads compatibility + modifier._defaults = defaults + modifier._methods = methods + } + + return modifier +} + +export function addEventModifiers({ + iEvent, + interaction, +}: { + iEvent: InteractEvent + interaction: Interaction +}) { + const result = interaction.modification!.result + + if (result) { + iEvent.modifiers = result.eventProps + } +} + +const modifiersBase: Plugin = { + id: 'modifiers/base', + before: ['actions'], + install: (scope) => { + scope.defaults.perAction.modifiers = [] + }, + listeners: { + 'interactions:new': ({ interaction }) => { + interaction.modification = new Modification(interaction) + }, + + 'interactions:before-action-start': (arg) => { + const { interaction } = arg + const modification = arg.interaction.modification! + + modification.start(arg, interaction.coords.start.page) + interaction.edges = modification.edges + modification.applyToInteraction(arg) + }, + + 'interactions:before-action-move': (arg) => { + const { interaction } = arg + const { modification } = interaction + const ret = modification.setAndApply(arg) + interaction.edges = modification.edges + + return ret + }, + + 'interactions:before-action-end': (arg) => { + const { interaction } = arg + const { modification } = interaction + const ret = modification.beforeEnd(arg) + interaction.edges = modification.startEdges + + return ret + }, + + 'interactions:action-start': addEventModifiers, + 'interactions:action-move': addEventModifiers, + 'interactions:action-end': addEventModifiers, + + 'interactions:after-action-start': (arg) => arg.interaction.modification.restoreInteractionCoords(arg), + 'interactions:after-action-move': (arg) => arg.interaction.modification.restoreInteractionCoords(arg), + + 'interactions:stop': (arg) => arg.interaction.modification.stop(arg), + }, +} + +export default modifiersBase diff --git a/packages/@interactjs/modifiers/noop.ts b/packages/@interactjs/modifiers/noop.ts new file mode 100644 index 000000000..491dc8057 --- /dev/null +++ b/packages/@interactjs/modifiers/noop.ts @@ -0,0 +1,7 @@ +import type { ModifierFunction } from './types' + +const noop = (() => {}) as unknown as ModifierFunction + +noop._defaults = {} + +export default noop diff --git a/packages/@interactjs/modifiers/package.json b/packages/@interactjs/modifiers/package.json new file mode 100644 index 000000000..8fbc25301 --- /dev/null +++ b/packages/@interactjs/modifiers/package.json @@ -0,0 +1,31 @@ +{ + "name": "@interactjs/modifiers", + "version": "1.10.27", + "main": "index", + "module": "index", + "type": "module", + "repository": { + "type": "git", + "url": "https://github.com/taye/interact.js.git", + "directory": "packages/@interactjs/modifiers" + }, + "dependencies": { + "@interactjs/snappers": "1.10.27" + }, + "peerDependencies": { + "@interactjs/core": "1.10.27", + "@interactjs/rebound": "1.10.27", + "@interactjs/utils": "1.10.27" + }, + "optionalDependencies": { + "@interactjs/interact": "1.10.27" + }, + "publishConfig": { + "access": "public" + }, + "sideEffects": [ + "**/index.js", + "**/index.prod.js" + ], + "license": "MIT" +} diff --git a/packages/@interactjs/modifiers/plugin.ts b/packages/@interactjs/modifiers/plugin.ts new file mode 100644 index 000000000..9a3e9944b --- /dev/null +++ b/packages/@interactjs/modifiers/plugin.ts @@ -0,0 +1,38 @@ +import type { Plugin } from '@interactjs/core/scope' +import snappers from '@interactjs/snappers/plugin' + +/* eslint-disable import/no-duplicates -- for typescript module augmentations */ +import './all' +import './base' + +import all from './all' +import base from './base' +/* eslint-enable import/no-duplicates */ + +declare module '@interactjs/core/InteractStatic' { + export interface InteractStatic { + modifiers: typeof all + } +} + +const modifiers: Plugin = { + id: 'modifiers', + install(scope) { + const { interactStatic: interact } = scope + + scope.usePlugin(base) + scope.usePlugin(snappers) + + interact.modifiers = all + + // for backwrads compatibility + for (const type in all) { + const { _defaults, _methods } = all[type as keyof typeof all] + + ;(_defaults as any)._methods = _methods + ;(scope.defaults.perAction as any)[type] = _defaults + } + }, +} + +export default modifiers diff --git a/packages/@interactjs/modifiers/restrict/edges.spec.ts b/packages/@interactjs/modifiers/restrict/edges.spec.ts new file mode 100644 index 000000000..869003cba --- /dev/null +++ b/packages/@interactjs/modifiers/restrict/edges.spec.ts @@ -0,0 +1,69 @@ +import * as helpers from '@interactjs/core/tests/_helpers' + +import { restrictEdges } from '../restrict/edges' + +test('restrictEdges', () => { + const { interaction } = helpers.testEnv() + const edges = { top: true, bottom: true, left: true, right: true } + interaction.prepared = {} as any + interaction.prepared.edges = edges + interaction._rects = {} as any + interaction._rects.corrected = { x: 10, y: 20, width: 300, height: 200 } as any + interaction._interacting = true + + const options: any = { enabled: true } + const coords = { x: 40, y: 40 } + const offset = { top: 0, left: 0, bottom: 0, right: 0 } + const state = { options, offset } + const arg = { interaction, edges, state } as any + + arg.coords = { ...coords } + + // outer restriction + options.outer = { top: 100, left: 100, bottom: 200, right: 200 } + restrictEdges.set(arg) + + // outer restriction is applied correctly + expect(arg.coords).toEqual({ x: coords.y + 60, y: coords.y + 60 }) + + arg.coords = { ...coords } + + // inner restriction + options.outer = null + options.inner = { top: 0, left: 0, bottom: 10, right: 10 } + restrictEdges.set(arg) + + // inner restriction is applied correctly + expect(arg.coords).toEqual({ x: coords.x - 40, y: coords.y - 40 }) + + // offset + Object.assign(offset, { + top: 100, + left: 100, + bottom: 200, + right: 200, + }) + arg.coords = { ...coords } + + options.outer = { top: 100, left: 100, bottom: 200, right: 200 } + options.inner = null + restrictEdges.set(arg) + + // outer restriction is applied correctly with offset + expect(arg.coords).toEqual({ x: coords.x + 160, y: coords.x + 160 }) + + // start + interaction.modification = {} as any + arg.startOffset = { top: 5, left: 10, bottom: -8, right: -16 } + interaction.interactable = { + getRect() { + return { top: 500, left: 900 } + }, + } as any + + options.offset = 'self' + restrictEdges.start(arg) + + // start gets x/y from selector string + expect(arg.state.offset).toEqual({ top: 505, left: 910, bottom: 508, right: 916 }) +}) diff --git a/packages/@interactjs/modifiers/restrict/edges.ts b/packages/@interactjs/modifiers/restrict/edges.ts new file mode 100644 index 000000000..876ae5fc1 --- /dev/null +++ b/packages/@interactjs/modifiers/restrict/edges.ts @@ -0,0 +1,115 @@ +// This modifier adds the options.resize.restrictEdges setting which sets min and +// max for the top, left, bottom and right edges of the target being resized. +// +// interact(target).resize({ +// edges: { top: true, left: true }, +// restrictEdges: { +// inner: { top: 200, left: 200, right: 400, bottom: 400 }, +// outer: { top: 0, left: 0, right: 600, bottom: 600 }, +// }, +// }) + +import type { Point, Rect } from '@interactjs/core/types' +import extend from '@interactjs/utils/extend' +import * as rectUtils from '@interactjs/utils/rect' + +import { makeModifier } from '../base' +import type { ModifierArg, ModifierState } from '../types' + +import type { RestrictOptions } from './pointer' +import { getRestrictionRect } from './pointer' + +export interface RestrictEdgesOptions { + inner: RestrictOptions['restriction'] + outer: RestrictOptions['restriction'] + offset?: RestrictOptions['offset'] + endOnly: boolean + enabled?: boolean +} + +export type RestrictEdgesState = ModifierState< + RestrictEdgesOptions, + { + inner: Rect + outer: Rect + offset: RestrictEdgesOptions['offset'] + } +> + +const noInner = { top: +Infinity, left: +Infinity, bottom: -Infinity, right: -Infinity } +const noOuter = { top: -Infinity, left: -Infinity, bottom: +Infinity, right: +Infinity } + +function start({ interaction, startOffset, state }: ModifierArg) { + const { options } = state + let offset: Point + + if (options) { + const offsetRect = getRestrictionRect(options.offset, interaction, interaction.coords.start.page) + + offset = rectUtils.rectToXY(offsetRect) + } + + offset = offset || { x: 0, y: 0 } + + state.offset = { + top: offset.y + startOffset.top, + left: offset.x + startOffset.left, + bottom: offset.y - startOffset.bottom, + right: offset.x - startOffset.right, + } +} + +function set({ coords, edges, interaction, state }: ModifierArg) { + const { offset, options } = state + + if (!edges) { + return + } + + const page = extend({}, coords) + const inner = getRestrictionRect(options.inner, interaction, page) || ({} as Rect) + const outer = getRestrictionRect(options.outer, interaction, page) || ({} as Rect) + + fixRect(inner, noInner) + fixRect(outer, noOuter) + + if (edges.top) { + coords.y = Math.min(Math.max(outer.top + offset.top, page.y), inner.top + offset.top) + } else if (edges.bottom) { + coords.y = Math.max(Math.min(outer.bottom + offset.bottom, page.y), inner.bottom + offset.bottom) + } + if (edges.left) { + coords.x = Math.min(Math.max(outer.left + offset.left, page.x), inner.left + offset.left) + } else if (edges.right) { + coords.x = Math.max(Math.min(outer.right + offset.right, page.x), inner.right + offset.right) + } +} + +function fixRect(rect: Rect, defaults: Rect) { + for (const edge of ['top', 'left', 'bottom', 'right']) { + if (!(edge in rect)) { + rect[edge] = defaults[edge] + } + } + + return rect +} + +const defaults: RestrictEdgesOptions = { + inner: null, + outer: null, + offset: null, + endOnly: false, + enabled: false, +} + +const restrictEdges = { + noInner, + noOuter, + start, + set, + defaults, +} + +export default makeModifier(restrictEdges, 'restrictEdges') +export { restrictEdges } diff --git a/packages/@interactjs/modifiers/restrict/pointer.spec.ts b/packages/@interactjs/modifiers/restrict/pointer.spec.ts new file mode 100644 index 000000000..9a4cad0be --- /dev/null +++ b/packages/@interactjs/modifiers/restrict/pointer.spec.ts @@ -0,0 +1,49 @@ +import * as helpers from '@interactjs/core/tests/_helpers' + +import { restrict } from '../restrict/pointer' + +test('restrict larger than restriction', () => { + const edges = { left: 0, top: 0, right: 200, bottom: 200 } + const rect = { ...edges, width: 200, height: 200 } + const { interaction } = helpers.testEnv({ rect }) + + const restriction = { left: 100, top: 50, right: 150, bottom: 150 } + const options = { + ...restrict.defaults, + restriction: null as any, + elementRect: { left: 0, top: 0, right: 1, bottom: 1 }, + } + const state = { options, offset: null as any } + const arg: any = { + interaction, + state, + rect, + startOffset: rect, + coords: { x: 0, y: 0 }, + pageCoords: { x: 0, y: 0 }, + } + + options.restriction = () => null as any + expect(() => { + restrict.start(arg as any) + restrict.set(arg as any) + }).not.toThrow() + + options.restriction = restriction + restrict.start(arg as any) + + arg.coords = { x: 0, y: 0 } + restrict.set(arg) + // allows top and left edge values to be lower than the restriction + expect(arg.coords).toEqual({ x: 0, y: 0 }) + + arg.coords = { x: restriction.left + 10, y: restriction.top + 10 } + restrict.set(arg) + // keeps the top left edge values lower than the restriction + expect(arg.coords).toEqual({ x: restriction.left - rect.left, y: restriction.top - rect.top }) + + arg.coords = { x: restriction.right - rect.right - 10, y: restriction.bottom - rect.right - 10 } + restrict.set(arg) + // keeps the bottom right edge values higher than the restriction + expect(arg.coords).toEqual({ x: restriction.right - rect.right, y: restriction.bottom - rect.right }) +}) diff --git a/packages/@interactjs/modifiers/restrict/pointer.ts b/packages/@interactjs/modifiers/restrict/pointer.ts new file mode 100644 index 000000000..8394e9ed2 --- /dev/null +++ b/packages/@interactjs/modifiers/restrict/pointer.ts @@ -0,0 +1,112 @@ +import type Interaction from '@interactjs/core/Interaction' +import type { RectResolvable, Rect, Point } from '@interactjs/core/types' +import extend from '@interactjs/utils/extend' +import is from '@interactjs/utils/is' +import * as rectUtils from '@interactjs/utils/rect' + +import { makeModifier } from '../base' +import type { ModifierArg, ModifierModule, ModifierState } from '../types' + +export interface RestrictOptions { + // where to drag over + restriction: RectResolvable<[number, number, Interaction]> + // what part of self is allowed to drag over + elementRect: Rect + offset: Rect + // restrict just before the end drag + endOnly: boolean + enabled?: boolean +} + +export type RestrictState = ModifierState< + RestrictOptions, + { + offset: Rect + } +> + +function start({ rect, startOffset, state, interaction, pageCoords }: ModifierArg) { + const { options } = state + const { elementRect } = options + const offset: Rect = extend( + { + left: 0, + top: 0, + right: 0, + bottom: 0, + }, + options.offset || {}, + ) + + if (rect && elementRect) { + const restriction = getRestrictionRect(options.restriction, interaction, pageCoords) + + if (restriction) { + const widthDiff = restriction.right - restriction.left - rect.width + const heightDiff = restriction.bottom - restriction.top - rect.height + + if (widthDiff < 0) { + offset.left += widthDiff + offset.right += widthDiff + } + if (heightDiff < 0) { + offset.top += heightDiff + offset.bottom += heightDiff + } + } + + offset.left += startOffset.left - rect.width * elementRect.left + offset.top += startOffset.top - rect.height * elementRect.top + + offset.right += startOffset.right - rect.width * (1 - elementRect.right) + offset.bottom += startOffset.bottom - rect.height * (1 - elementRect.bottom) + } + + state.offset = offset +} + +function set({ coords, interaction, state }: ModifierArg) { + const { options, offset } = state + + const restriction = getRestrictionRect(options.restriction, interaction, coords) + + if (!restriction) return + + const rect = rectUtils.xywhToTlbr(restriction) + + coords.x = Math.max(Math.min(rect.right - offset.right, coords.x), rect.left + offset.left) + coords.y = Math.max(Math.min(rect.bottom - offset.bottom, coords.y), rect.top + offset.top) +} + +export function getRestrictionRect( + value: RectResolvable<[number, number, Interaction]>, + interaction: Interaction, + coords?: Point, +) { + if (is.func(value)) { + return rectUtils.resolveRectLike(value, interaction.interactable, interaction.element, [ + coords.x, + coords.y, + interaction, + ]) + } else { + return rectUtils.resolveRectLike(value, interaction.interactable, interaction.element) + } +} + +const defaults: RestrictOptions = { + restriction: null, + elementRect: null, + offset: null, + endOnly: false, + enabled: false, +} + +const restrict: ModifierModule = { + start, + set, + defaults, +} + +export default makeModifier(restrict, 'restrict') +export { restrict } diff --git a/packages/@interactjs/modifiers/restrict/rect.ts b/packages/@interactjs/modifiers/restrict/rect.ts new file mode 100644 index 000000000..637d96cbe --- /dev/null +++ b/packages/@interactjs/modifiers/restrict/rect.ts @@ -0,0 +1,24 @@ +import extend from '@interactjs/utils/extend' + +import { makeModifier } from '../base' + +import { restrict } from './pointer' + +const defaults = extend( + { + get elementRect() { + return { top: 0, left: 0, bottom: 1, right: 1 } + }, + set elementRect(_) {}, + }, + restrict.defaults, +) + +const restrictRect = { + start: restrict.start, + set: restrict.set, + defaults, +} + +export default makeModifier(restrictRect, 'restrictRect') +export { restrictRect } diff --git a/packages/@interactjs/modifiers/restrict/size.spec.ts b/packages/@interactjs/modifiers/restrict/size.spec.ts new file mode 100644 index 000000000..d1da412e0 --- /dev/null +++ b/packages/@interactjs/modifiers/restrict/size.spec.ts @@ -0,0 +1,71 @@ +import type { ResizeEvent } from '@interactjs/actions/resize/plugin' +import resize from '@interactjs/actions/resize/plugin' +import * as helpers from '@interactjs/core/tests/_helpers' +import extend from '@interactjs/utils/extend' +import * as rectUtils from '@interactjs/utils/rect' + +import modifiersBase from '../base' + +import restrictSize from './size' + +test('restrictSize', () => { + const rect = rectUtils.xywhToTlbr({ left: 0, top: 0, right: 200, bottom: 300 }) + const { interaction, interactable, coords, down, start, move } = helpers.testEnv({ + plugins: [modifiersBase, resize], + rect, + }) + const edges = { left: true, top: true } + const action: any = { name: 'resize', edges } + const options = { + min: { width: 60, height: 50 } as any, + max: { width: 300, height: 350 } as any, + } + let latestEvent: ResizeEvent = null + + interactable + .resizable({ + modifiers: [restrictSize(options)], + }) + .on('resizestart resizemove resizeend', (e) => { + latestEvent = e + }) + + down() + start(action) + + extend(coords.page, { x: -50, y: -40 }) + move() + // within both min and max + expect(latestEvent.page).toEqual(coords.page) + + extend(coords.page, { x: -200, y: -300 }) + move() + + // outside max + expect(latestEvent.page).toEqual({ x: -100, y: -50 }) + + extend(coords.page, { x: 250, y: 320 }) + move() + + // outside min + expect(latestEvent.page).toEqual({ x: 140, y: 250 }) + + // min and max function restrictions + let minFuncArgs: any[] + let maxFuncArgs: any[] + + options.min = (...args: any[]) => { + minFuncArgs = args + } + options.max = (...args: any[]) => { + maxFuncArgs = args + } + + move() + + // correct args are passed to min function restriction + expect(minFuncArgs).toEqual([coords.page.x, coords.page.y, interaction]) + + // correct args are passed to max function restriction + expect(maxFuncArgs).toEqual([coords.page.x, coords.page.y, interaction]) +}) diff --git a/packages/@interactjs/modifiers/restrict/size.ts b/packages/@interactjs/modifiers/restrict/size.ts new file mode 100644 index 000000000..e91ff46b8 --- /dev/null +++ b/packages/@interactjs/modifiers/restrict/size.ts @@ -0,0 +1,89 @@ +import type { Point, Rect, Size } from '@interactjs/core/types' +import extend from '@interactjs/utils/extend' +import * as rectUtils from '@interactjs/utils/rect' + +import { makeModifier } from '../base' +import type { ModifierArg, ModifierState } from '../types' + +import type { RestrictEdgesState } from './edges' +import { restrictEdges } from './edges' +import type { RestrictOptions } from './pointer' +import { getRestrictionRect } from './pointer' + +const noMin = { width: -Infinity, height: -Infinity } +const noMax = { width: +Infinity, height: +Infinity } + +export interface RestrictSizeOptions { + min?: Size | Point | RestrictOptions['restriction'] + max?: Size | Point | RestrictOptions['restriction'] + endOnly: boolean + enabled?: boolean +} + +function start(arg: ModifierArg) { + return restrictEdges.start(arg) +} + +export type RestrictSizeState = RestrictEdgesState & + ModifierState< + RestrictSizeOptions & { inner: Rect; outer: Rect }, + { + min: Rect + max: Rect + } + > + +function set(arg: ModifierArg) { + const { interaction, state, rect, edges } = arg + const { options } = state + + if (!edges) { + return + } + + const minSize = + rectUtils.tlbrToXywh(getRestrictionRect(options.min as any, interaction, arg.coords)) || noMin + const maxSize = + rectUtils.tlbrToXywh(getRestrictionRect(options.max as any, interaction, arg.coords)) || noMax + + state.options = { + endOnly: options.endOnly, + inner: extend({}, restrictEdges.noInner), + outer: extend({}, restrictEdges.noOuter), + } + + if (edges.top) { + state.options.inner.top = rect.bottom - minSize.height + state.options.outer.top = rect.bottom - maxSize.height + } else if (edges.bottom) { + state.options.inner.bottom = rect.top + minSize.height + state.options.outer.bottom = rect.top + maxSize.height + } + if (edges.left) { + state.options.inner.left = rect.right - minSize.width + state.options.outer.left = rect.right - maxSize.width + } else if (edges.right) { + state.options.inner.right = rect.left + minSize.width + state.options.outer.right = rect.left + maxSize.width + } + + restrictEdges.set(arg) + + state.options = options +} + +const defaults: RestrictSizeOptions = { + min: null, + max: null, + endOnly: false, + enabled: false, +} + +const restrictSize = { + start, + set, + defaults, +} + +export default makeModifier(restrictSize, 'restrictSize') +export { restrictSize } diff --git a/packages/@interactjs/modifiers/rubberband/rubberband.stub.ts b/packages/@interactjs/modifiers/rubberband/rubberband.stub.ts new file mode 100644 index 000000000..80c8a7126 --- /dev/null +++ b/packages/@interactjs/modifiers/rubberband/rubberband.stub.ts @@ -0,0 +1 @@ +export { default } from '../noop' diff --git a/packages/@interactjs/modifiers/rubberband/rubberband.ts b/packages/@interactjs/modifiers/rubberband/rubberband.ts new file mode 100644 index 000000000..80c8a7126 --- /dev/null +++ b/packages/@interactjs/modifiers/rubberband/rubberband.ts @@ -0,0 +1 @@ +export { default } from '../noop' diff --git a/packages/@interactjs/modifiers/snap/edges.spec.ts b/packages/@interactjs/modifiers/snap/edges.spec.ts new file mode 100644 index 000000000..45dc6b5b7 --- /dev/null +++ b/packages/@interactjs/modifiers/snap/edges.spec.ts @@ -0,0 +1,50 @@ +import * as helpers from '@interactjs/core/tests/_helpers' +import type { EdgeOptions } from '@interactjs/core/types' + +import { snapEdges } from '../snap/edges' + +test('modifiers/snap/edges', () => { + const rect = { top: 0, left: 0, bottom: 100, right: 100 } + const { interaction, interactable } = helpers.testEnv({ rect }) + interaction.interactable = interactable + interaction._interacting = true + + const target0 = Object.freeze({ + left: 50, + right: 150, + top: 0, + bottom: 100, + }) + const options = { + targets: [{ ...target0 }], + range: Infinity, + } + const pageCoords = Object.freeze({ x: 0, y: 0 }) + const arg = { + interaction, + // resize from top left + edges: { top: true, left: true } as EdgeOptions, + interactable: interaction.interactable, + state: null as any, + pageCoords, + coords: { ...pageCoords }, + offset: [{ x: 0, y: 0 }], + } + + arg.state = { options } + snapEdges.start!(arg as any) + snapEdges.set!(arg as any) + + // modified coords are correct + expect(arg.coords).toEqual({ x: target0.left, y: target0.top }) + + // resize from bottom right + arg.edges = { bottom: true, right: true } + + arg.state = { options } + snapEdges.start!(arg as any) + snapEdges.set!(arg as any) + + // modified coord are correct + expect(arg.coords).toEqual({ x: target0.right, y: target0.bottom }) +}) diff --git a/packages/@interactjs/modifiers/snap/edges.ts b/packages/@interactjs/modifiers/snap/edges.ts new file mode 100644 index 000000000..198683733 --- /dev/null +++ b/packages/@interactjs/modifiers/snap/edges.ts @@ -0,0 +1,66 @@ +/** + * @module modifiers/snapEdges + * + * @description + * This modifier allows snapping of the edges of targets during resize + * interactions. + * + * ```js + * interact(target).resizable({ + * snapEdges: { + * targets: [interact.snappers.grid({ x: 100, y: 50 })], + * }, + * }) + * + * interact(target).resizable({ + * snapEdges: { + * targets: [ + * interact.snappers.grid({ + * top: 50, + * left: 50, + * bottom: 100, + * right: 100, + * }), + * ], + * }, + * }) + * ``` + */ + +import clone from '@interactjs/utils/clone' +import extend from '@interactjs/utils/extend' + +import { makeModifier } from '../base' +import type { ModifierArg, ModifierModule } from '../types' + +import type { SnapOptions, SnapState } from './pointer' +import { snapSize } from './size' + +export type SnapEdgesOptions = Pick + +function start(arg: ModifierArg) { + const { edges } = arg + + if (!edges) { + return null + } + + arg.state.targetFields = arg.state.targetFields || [ + [edges.left ? 'left' : 'right', edges.top ? 'top' : 'bottom'], + ] + + return snapSize.start(arg) +} + +const snapEdges: ModifierModule> = { + start, + set: snapSize.set, + defaults: extend(clone(snapSize.defaults), { + targets: undefined, + range: undefined, + offset: { x: 0, y: 0 }, + } as const), +} + +export default makeModifier(snapEdges, 'snapEdges') +export { snapEdges } diff --git a/packages/@interactjs/modifiers/snap/pointer.spec.ts b/packages/@interactjs/modifiers/snap/pointer.spec.ts new file mode 100644 index 000000000..c0beb9b0a --- /dev/null +++ b/packages/@interactjs/modifiers/snap/pointer.spec.ts @@ -0,0 +1,117 @@ +import drag from '@interactjs/actions/drag/plugin' +import * as helpers from '@interactjs/core/tests/_helpers' +import type { Point } from '@interactjs/core/types' +import extend from '@interactjs/utils/extend' + +import modifiersBase from '../base' +import snap from '../snap/pointer' + +test('modifiers/snap', () => { + const rect = helpers.ltrbwh(0, 0, 100, 100, 100, 100) + const { interaction, interactable, coords, down, move, start, stop } = helpers.testEnv({ + plugins: [modifiersBase, drag], + rect, + }) + + coords.client = coords.page + + const origin = { x: 120, y: 120 } + let funcArgs!: { x: number; y: number; offset: number; index: number; unexpected: unknown[] } + + const target0 = Object.freeze({ x: 50, y: 100 }) + const targetFunc = (x, y, _interaction, offset, index, ...unexpected) => { + funcArgs = { x, y, offset, index, unexpected } + return target0 + } + const relativePoint = { x: 0, y: 0 } + + const options = { + offset: undefined as Point | undefined, + offsetWithOrigin: true, + targets: [target0, targetFunc], + range: Infinity, + relativePoints: [relativePoint], + } + + let lastEventModifiers!: any[] + interactable + .draggable({ + origin, + modifiers: [snap(options)], + }) + .on('dragmove dragstart dragend', (e) => { + lastEventModifiers = e.modifiers + }) + + down() + start({ name: 'drag' }) + extend(coords.page, { x: 50, y: 50 }) + move() + + // event.modifiers entry has expected props + expect(Object.keys(lastEventModifiers[0]).sort()).toEqual([ + 'delta', + 'distance', + 'inRange', + 'range', + 'target', + ]) + + // snaps to target and adds origin which will be subtracted by InteractEvent + expect(helpers.getProps(lastEventModifiers[0].target, ['x', 'y'])).toEqual({ + x: target0.x + origin.x, + y: target0.y + origin.y, + }) + + options.targets = [targetFunc] + down() + start({ name: 'drag' }) + move(true) + stop() + + expect(funcArgs).toEqual({ + x: coords.page.x - origin.x, + y: coords.page.y - origin.y, + offset: { + x: origin.x, + y: origin.y, + relativePoint, + index: 0, + }, + index: 0, + // x, y, interaction, offset, index are passed to target function; origin subtracted from x, y + unexpected: [], + }) + + options.offset = { x: 300, y: 300 } + options.offsetWithOrigin = false + + down() + start({ name: 'drag' }) + move(true) + + const { startOffset } = interaction.modification! + const relativeOffset = { + x: options.offset.x + startOffset.left, + y: options.offset.y + startOffset.top, + } + + // event.modifiers entry has source element of options.targets array, range, and offset + expect(helpers.getProps(lastEventModifiers[0].target, ['source', 'range', 'offset'])).toEqual({ + source: targetFunc, + range: Infinity, + offset: { ...relativeOffset, index: 0, relativePoint }, + }) + + // origin not added to target when !options.offsetWithOrigin + expect(helpers.getProps(lastEventModifiers[0].target, ['x', 'y'])).toEqual({ + x: target0.x + relativeOffset.x, + y: target0.y + relativeOffset.y, + }) + + // origin still subtracted from function target x, y args when !options.offsetWithOrigin + expect({ x: funcArgs.x, y: funcArgs.y }).toEqual({ + x: coords.page.x - origin.x - relativeOffset.x, + y: coords.page.y - origin.y - relativeOffset.y, + }) +}) diff --git a/packages/@interactjs/modifiers/snap/pointer.ts b/packages/@interactjs/modifiers/snap/pointer.ts new file mode 100644 index 000000000..a050ad0fe --- /dev/null +++ b/packages/@interactjs/modifiers/snap/pointer.ts @@ -0,0 +1,219 @@ +import type { Interaction, InteractionProxy } from '@interactjs/core/Interaction' +import type { ActionName, Point, RectResolvable, Element } from '@interactjs/core/types' +import extend from '@interactjs/utils/extend' +import getOriginXY from '@interactjs/utils/getOriginXY' +import hypot from '@interactjs/utils/hypot' +import is from '@interactjs/utils/is' +import { resolveRectLike, rectToXY } from '@interactjs/utils/rect' + +import { makeModifier } from '../base' +import type { ModifierArg, ModifierState } from '../types' + +export interface Offset { + x: number + y: number + index: number + relativePoint?: Point | null +} + +export interface SnapPosition { + x?: number + y?: number + range?: number + offset?: Offset + [index: string]: any +} + +export type SnapFunction = ( + x: number, + y: number, + interaction: InteractionProxy, + offset: Offset, + index: number, +) => SnapPosition +export type SnapTarget = SnapPosition | SnapFunction +export interface SnapOptions { + targets?: SnapTarget[] + // target range + range?: number + // self points for snapping. [0,0] = top left, [1,1] = bottom right + relativePoints?: Point[] + // startCoords = offset snapping from drag start page position + offset?: Point | RectResolvable<[Interaction]> | 'startCoords' + offsetWithOrigin?: boolean + origin?: RectResolvable<[Element]> | Point + endOnly?: boolean + enabled?: boolean +} + +export type SnapState = ModifierState< + SnapOptions, + { + offsets?: Offset[] + closest?: any + targetFields?: string[][] + } +> + +function start(arg: ModifierArg) { + const { interaction, interactable, element, rect, state, startOffset } = arg + const { options } = state + const origin = options.offsetWithOrigin ? getOrigin(arg) : { x: 0, y: 0 } + + let snapOffset: Point + + if (options.offset === 'startCoords') { + snapOffset = { + x: interaction.coords.start.page.x, + y: interaction.coords.start.page.y, + } + } else { + const offsetRect = resolveRectLike(options.offset as any, interactable, element, [interaction]) + + snapOffset = rectToXY(offsetRect) || { x: 0, y: 0 } + snapOffset.x += origin.x + snapOffset.y += origin.y + } + + const { relativePoints } = options + + state.offsets = + rect && relativePoints && relativePoints.length + ? relativePoints.map((relativePoint, index) => ({ + index, + relativePoint, + x: startOffset.left - rect.width * relativePoint.x + snapOffset.x, + y: startOffset.top - rect.height * relativePoint.y + snapOffset.y, + })) + : [ + { + index: 0, + relativePoint: null, + x: snapOffset.x, + y: snapOffset.y, + }, + ] +} + +function set(arg: ModifierArg) { + const { interaction, coords, state } = arg + const { options, offsets } = state + + const origin = getOriginXY(interaction.interactable!, interaction.element!, interaction.prepared.name) + const page = extend({}, coords) + const targets: SnapPosition[] = [] + + if (!options.offsetWithOrigin) { + page.x -= origin.x + page.y -= origin.y + } + + for (const offset of offsets!) { + const relativeX = page.x - offset.x + const relativeY = page.y - offset.y + + for (let index = 0, len = options.targets!.length; index < len; index++) { + const snapTarget = options.targets![index] + let target: SnapPosition + + if (is.func(snapTarget)) { + target = snapTarget(relativeX, relativeY, interaction._proxy, offset, index) + } else { + target = snapTarget + } + + if (!target) { + continue + } + + targets.push({ + x: (is.number(target.x) ? target.x : relativeX) + offset.x, + y: (is.number(target.y) ? target.y : relativeY) + offset.y, + + range: is.number(target.range) ? target.range : options.range, + source: snapTarget, + index, + offset, + }) + } + } + + const closest = { + target: null, + inRange: false, + distance: 0, + range: 0, + delta: { x: 0, y: 0 }, + } + + for (const target of targets) { + const range = target.range + const dx = target.x - page.x + const dy = target.y - page.y + const distance = hypot(dx, dy) + let inRange = distance <= range + + // Infinite targets count as being out of range + // compared to non infinite ones that are in range + if (range === Infinity && closest.inRange && closest.range !== Infinity) { + inRange = false + } + + if ( + !closest.target || + (inRange + ? // is the closest target in range? + closest.inRange && range !== Infinity + ? // the pointer is relatively deeper in this target + distance / range < closest.distance / closest.range + : // this target has Infinite range and the closest doesn't + (range === Infinity && closest.range !== Infinity) || + // OR this target is closer that the previous closest + distance < closest.distance + : // The other is not in range and the pointer is closer to this target + !closest.inRange && distance < closest.distance) + ) { + closest.target = target + closest.distance = distance + closest.range = range + closest.inRange = inRange + closest.delta.x = dx + closest.delta.y = dy + } + } + + if (closest.inRange) { + coords.x = closest.target.x + coords.y = closest.target.y + } + + state.closest = closest + return closest +} + +function getOrigin(arg: Partial>) { + const { element } = arg.interaction + const optionsOrigin = rectToXY(resolveRectLike(arg.state.options.origin as any, null, null, [element])) + const origin = optionsOrigin || getOriginXY(arg.interactable, element, arg.interaction.prepared.name) + + return origin +} + +const defaults: SnapOptions = { + range: Infinity, + targets: null, + offset: null, + offsetWithOrigin: true, + origin: null, + relativePoints: null, + endOnly: false, + enabled: false, +} +const snap = { + start, + set, + defaults, +} + +export default makeModifier(snap, 'snap') +export { snap } diff --git a/packages/@interactjs/modifiers/snap/size.spec.ts b/packages/@interactjs/modifiers/snap/size.spec.ts new file mode 100644 index 000000000..8879a8333 --- /dev/null +++ b/packages/@interactjs/modifiers/snap/size.spec.ts @@ -0,0 +1,36 @@ +import * as helpers from '@interactjs/core/tests/_helpers' + +import { snapSize } from '../snap/size' + +test('modifiers/snapSize', () => { + const { interaction, interactable } = helpers.testEnv() + interaction.interactable = interactable + interactable.getRect = () => ({ top: 0, left: 0, bottom: 100, right: 100 }) as any + interaction._interacting = true + + const target0 = Object.freeze({ x: 50, y: 100 }) + const options = { + targets: [{ ...target0 }], + range: Infinity, + } + const state = { + options, + delta: { x: 0, y: 0 }, + offset: [{ x: 0, y: 0 }], + } + const pageCoords = Object.freeze({ x: 10, y: 20 }) + const arg = { + interaction, + interactable: interaction.interactable, + edges: { top: true, left: true }, + state, + pageCoords, + coords: { ...pageCoords }, + } + + snapSize.start(arg as any) + snapSize.set(arg) + + // snapSize.set single target, zereo offset + expect(arg.coords).toEqual(target0) +}) diff --git a/packages/@interactjs/modifiers/snap/size.ts b/packages/@interactjs/modifiers/snap/size.ts new file mode 100644 index 000000000..df6df9b7c --- /dev/null +++ b/packages/@interactjs/modifiers/snap/size.ts @@ -0,0 +1,107 @@ +// This modifier allows snapping of the size of targets during resize +// interactions. + +import extend from '@interactjs/utils/extend' +import is from '@interactjs/utils/is' + +import { makeModifier } from '../base' +import type { ModifierArg } from '../types' + +import type { SnapOptions, SnapState } from './pointer' +import { snap } from './pointer' + +export type SnapSizeOptions = Pick + +function start(arg: ModifierArg) { + const { state, edges } = arg + const { options } = state + + if (!edges) { + return null + } + + arg.state = { + options: { + targets: null, + relativePoints: [ + { + x: edges.left ? 0 : 1, + y: edges.top ? 0 : 1, + }, + ], + offset: options.offset || 'self', + origin: { x: 0, y: 0 }, + range: options.range, + }, + } + + state.targetFields = state.targetFields || [ + ['width', 'height'], + ['x', 'y'], + ] + + snap.start(arg) + state.offsets = arg.state.offsets + + arg.state = state +} + +function set(arg) { + const { interaction, state, coords } = arg + const { options, offsets } = state + const relative = { + x: coords.x - offsets[0].x, + y: coords.y - offsets[0].y, + } + + state.options = extend({}, options) + state.options.targets = [] + + for (const snapTarget of options.targets || []) { + let target + + if (is.func(snapTarget)) { + target = snapTarget(relative.x, relative.y, interaction) + } else { + target = snapTarget + } + + if (!target) { + continue + } + + for (const [xField, yField] of state.targetFields) { + if (xField in target || yField in target) { + target.x = target[xField] + target.y = target[yField] + + break + } + } + + state.options.targets.push(target) + } + + const returnValue = snap.set(arg) + + state.options = options + + return returnValue +} + +const defaults: SnapSizeOptions = { + range: Infinity, + targets: null, + offset: null, + endOnly: false, + enabled: false, +} + +const snapSize = { + start, + set, + defaults, +} + +export default makeModifier(snapSize, 'snapSize') +export { snapSize } diff --git a/packages/@interactjs/modifiers/spring/spring.stub.ts b/packages/@interactjs/modifiers/spring/spring.stub.ts new file mode 100644 index 000000000..80c8a7126 --- /dev/null +++ b/packages/@interactjs/modifiers/spring/spring.stub.ts @@ -0,0 +1 @@ +export { default } from '../noop' diff --git a/packages/@interactjs/modifiers/spring/spring.ts b/packages/@interactjs/modifiers/spring/spring.ts new file mode 100644 index 000000000..80c8a7126 --- /dev/null +++ b/packages/@interactjs/modifiers/spring/spring.ts @@ -0,0 +1 @@ +export { default } from '../noop' diff --git a/packages/@interactjs/modifiers/transform/transform.stub.ts b/packages/@interactjs/modifiers/transform/transform.stub.ts new file mode 100644 index 000000000..80c8a7126 --- /dev/null +++ b/packages/@interactjs/modifiers/transform/transform.stub.ts @@ -0,0 +1 @@ +export { default } from '../noop' diff --git a/packages/@interactjs/modifiers/transform/transform.ts b/packages/@interactjs/modifiers/transform/transform.ts new file mode 100644 index 000000000..80c8a7126 --- /dev/null +++ b/packages/@interactjs/modifiers/transform/transform.ts @@ -0,0 +1 @@ +export { default } from '../noop' diff --git a/packages/@interactjs/modifiers/types.ts b/packages/@interactjs/modifiers/types.ts new file mode 100644 index 000000000..1df4f94da --- /dev/null +++ b/packages/@interactjs/modifiers/types.ts @@ -0,0 +1,67 @@ +import type { Interactable } from '@interactjs/core/Interactable' +import type { EventPhase } from '@interactjs/core/InteractEvent' +import type Interaction from '@interactjs/core/Interaction' +import type { EdgeOptions, FullRect, Point, Rect } from '@interactjs/core/types' + +export interface Modifier< + Defaults = any, + State extends ModifierState = any, + Name extends string = any, + Result = any, +> { + options: Defaults + methods: { + start?: (arg: ModifierArg) => void + set?: (arg: ModifierArg) => Result + beforeEnd?: (arg: ModifierArg) => Point | void + stop?: (arg: ModifierArg) => void + } + name?: Name + enable: () => Modifier + disable: () => Modifier +} + +export type ModifierState = { + options: Defaults + methods?: Modifier['methods'] + index?: number + name?: Name +} & StateProps + +export interface ModifierArg { + interaction: Interaction + interactable: Interactable + phase: EventPhase + rect: FullRect + edges: EdgeOptions + state: State + element: Element + pageCoords: Point + prevCoords: Point + prevRect?: FullRect + coords: Point + startOffset: Rect + preEnd?: boolean +} + +export interface ModifierModule< + Defaults extends { enabled?: boolean }, + State extends ModifierState, + Result = unknown, +> { + defaults?: Defaults + start?(arg: ModifierArg): void + set?(arg: ModifierArg): Result + beforeEnd?(arg: ModifierArg): Point | void + stop?(arg: ModifierArg): void +} + +export interface ModifierFunction< + Defaults extends { enabled?: boolean }, + State extends ModifierState, + Name extends string, +> { + (_options?: Partial): Modifier + _defaults: Defaults + _methods: ModifierModule +} diff --git a/packages/@interactjs/offset/offset.spec.ts b/packages/@interactjs/offset/offset.spec.ts new file mode 100644 index 000000000..1e1deb083 --- /dev/null +++ b/packages/@interactjs/offset/offset.spec.ts @@ -0,0 +1,25 @@ +import * as helpers from '@interactjs/core/tests/_helpers' + +import offset from './plugin' + +test('plugins/spring', () => { + const { interaction, event, coords, target } = helpers.testEnv({ plugins: [offset] }) + + const body = target as HTMLBodyElement + + interaction.pointerMove(event, event, body) + interaction.offsetBy({ x: 100, y: 100 }) + interaction.pointerMove(event, event, body) + + // coords are not updated when pointer is not down + expect(interaction.coords.cur.page).toEqual(coords.page) + + interaction.pointerUp(event, event, body, body) + interaction.stop() + interaction.pointerDown(event, event, body) + interaction.offsetBy({ x: 100, y: 50 }) + interaction.pointerMove(event, event, body) + + // coords are not updated when pointer is not down + expect(interaction.coords.cur.page).toEqual({ x: coords.page.x + 100, y: coords.page.y + 50 }) +}) diff --git a/packages/@interactjs/offset/package.json b/packages/@interactjs/offset/package.json new file mode 100644 index 000000000..a6f661851 --- /dev/null +++ b/packages/@interactjs/offset/package.json @@ -0,0 +1,27 @@ +{ + "name": "@interactjs/offset", + "version": "1.10.27", + "main": "index", + "module": "index", + "type": "module", + "repository": { + "type": "git", + "url": "https://github.com/taye/interact.js.git", + "directory": "packages/@interactjs/offset" + }, + "peerDependencies": { + "@interactjs/core": "1.10.27", + "@interactjs/utils": "1.10.27" + }, + "optionalDependencies": { + "@interactjs/interact": "1.10.27" + }, + "publishConfig": { + "access": "public" + }, + "sideEffects": [ + "**/index.js", + "**/index.prod.js" + ], + "license": "MIT" +} diff --git a/packages/@interactjs/offset/plugin.ts b/packages/@interactjs/offset/plugin.ts new file mode 100644 index 000000000..8ef378260 --- /dev/null +++ b/packages/@interactjs/offset/plugin.ts @@ -0,0 +1,113 @@ +import type Interaction from '@interactjs/core/Interaction' +import { _ProxyMethods } from '@interactjs/core/Interaction' +import type { Plugin } from '@interactjs/core/scope' +import type { Point } from '@interactjs/core/types' +import * as rectUtils from '@interactjs/utils/rect' + +declare module '@interactjs/core/Interaction' { + interface Interaction { + offsetBy?: typeof offsetBy + offset: { + total: Point + pending: Point + } + } + + enum _ProxyMethods { + offsetBy = '', + } +} + +;(_ProxyMethods as any).offsetBy = '' + +export function addTotal(interaction: Interaction) { + if (!interaction.pointerIsDown) { + return + } + + addToCoords(interaction.coords.cur, interaction.offset.total) + + interaction.offset.pending.x = 0 + interaction.offset.pending.y = 0 +} + +function beforeAction({ interaction }: { interaction: Interaction }) { + applyPending(interaction) +} + +function beforeEnd({ interaction }: { interaction: Interaction }): boolean | void { + const hadPending = applyPending(interaction) + + if (!hadPending) return + + interaction.move({ offset: true }) + interaction.end() + + return false +} + +function end({ interaction }: { interaction: Interaction }) { + interaction.offset.total.x = 0 + interaction.offset.total.y = 0 + interaction.offset.pending.x = 0 + interaction.offset.pending.y = 0 +} + +export function applyPending(interaction: Interaction) { + if (!hasPending(interaction)) { + return false + } + + const { pending } = interaction.offset + + addToCoords(interaction.coords.cur, pending) + addToCoords(interaction.coords.delta, pending) + rectUtils.addEdges(interaction.edges, interaction.rect, pending) + + pending.x = 0 + pending.y = 0 + + return true +} + +function offsetBy(this: Interaction, { x, y }: Point) { + this.offset.pending.x += x + this.offset.pending.y += y + + this.offset.total.x += x + this.offset.total.y += y +} + +function addToCoords({ page, client }, { x, y }: Point) { + page.x += x + page.y += y + client.x += x + client.y += y +} + +function hasPending(interaction: Interaction) { + return !!(interaction.offset.pending.x || interaction.offset.pending.y) +} + +const offset: Plugin = { + id: 'offset', + before: ['modifiers', 'pointer-events', 'actions', 'inertia'], + install(scope) { + scope.Interaction.prototype.offsetBy = offsetBy + }, + listeners: { + 'interactions:new': ({ interaction }) => { + interaction.offset = { + total: { x: 0, y: 0 }, + pending: { x: 0, y: 0 }, + } + }, + 'interactions:update-pointer': ({ interaction }) => addTotal(interaction), + 'interactions:before-action-start': beforeAction, + 'interactions:before-action-move': beforeAction, + 'interactions:before-action-end': beforeEnd, + 'interactions:stop': end, + }, +} + +export default offset diff --git a/packages/@interactjs/pointer-events/PointerEvent.spec.ts b/packages/@interactjs/pointer-events/PointerEvent.spec.ts new file mode 100644 index 000000000..3faeb09a1 --- /dev/null +++ b/packages/@interactjs/pointer-events/PointerEvent.spec.ts @@ -0,0 +1,94 @@ +import * as helpers from '@interactjs/core/tests/_helpers' +import * as pointerUtils from '@interactjs/utils/pointerUtils' + +import { PointerEvent } from './PointerEvent' + +test('PointerEvent constructor', () => { + const type = 'TEST_EVENT' + const pointerId = -100 + const testPointerProp = ['TEST_POINTER_PROP'] + const pointer = { + pointerId, + testPointerProp, + pointerType: 'TEST_POINTER_TYPE', + } as any + const testEventProp = ['TEST_EVENT_PROP'] + const event = { + testEventProp, + } as any + const { interaction } = helpers.testEnv() + const eventTarget = {} as Element + const pointerEvent = new PointerEvent(type, pointer, event, eventTarget, interaction as any, 0) as any + + // pointerEvent is extended form pointer + expect(pointerEvent.testPointerProp).toBe(testPointerProp) + // pointerEvent is extended form Event + expect(pointerEvent.testEventProp).toBe(testEventProp) + + // type is set correctly + expect(pointerEvent.type).toBe(type) + // pointerType is set correctly + expect(pointerEvent.pointerType).toBe(pointerUtils.getPointerType(pointer)) + // pointerId is set correctly + expect(pointerEvent.pointerId).toBe(pointerId) + // originalEvent is set correctly + expect(pointerEvent.originalEvent).toBe(event) + // interaction is set correctly + expect(pointerEvent.interaction).toBe(interaction._proxy) + // target is set correctly + expect(pointerEvent.target).toBe(eventTarget) + // currentTarget is null + expect(pointerEvent.currentTarget).toBeNull() +}) + +test('PointerEvent methods', () => { + const methodContexts = {} as any + const event: any = ['preventDefault', 'stopPropagation', 'stopImmediatePropagation'].reduce( + (acc, methodName) => { + acc[methodName] = function () { + methodContexts[methodName] = this + } + return acc + }, + helpers.newPointer(), + ) + const pointerEvent = new PointerEvent('TEST', {} as any, event, null, {} as any, 0) + + pointerEvent.preventDefault() + // PointerEvent.preventDefault() calls preventDefault of originalEvent + expect(methodContexts.preventDefault).toBe(event) + + // propagationStopped is false before call to stopPropagation + expect(pointerEvent.propagationStopped).toBe(false) + pointerEvent.stopPropagation() + // stopPropagation sets propagationStopped to true + expect(pointerEvent.propagationStopped).toBe(true) + // PointerEvent.stopPropagation() does not call stopPropagation of originalEvent + // immediatePropagationStopped is false before call to stopImmediatePropagation + expect(methodContexts.stopPropagation).toBeUndefined() + + expect(pointerEvent.immediatePropagationStopped).toBe(false) + pointerEvent.stopImmediatePropagation() + // PointerEvent.stopImmediatePropagation() does not call stopImmediatePropagation of originalEvent + expect(methodContexts.stopImmediatePropagation).toBeUndefined() + // stopImmediatePropagation sets immediatePropagationStopped to true + expect(pointerEvent.immediatePropagationStopped).toBe(true) + + const origin = { x: 20, y: 30 } + pointerEvent._subtractOrigin(origin) + + // subtractOrigin updates pageX correctly + expect(pointerEvent.pageX).toBe(event.pageX - origin.x) + // subtractOrigin updates pageY correctly + expect(pointerEvent.pageY).toBe(event.pageY - origin.y) + // subtractOrigin updates clientX correctly + expect(pointerEvent.clientX).toBe(event.clientX - origin.x) + // subtractOrigin updates clientY correctly + expect(pointerEvent.clientY).toBe(event.clientY - origin.y) + + pointerEvent._addOrigin(origin) + // addOrigin with the subtracted origin reverts to original coordinates + expect(['pageX', 'pageY', 'clientX', 'clientY'].every((prop) => pointerEvent[prop] === event[prop])).toBe( + true, + ) +}) diff --git a/packages/@interactjs/pointer-events/PointerEvent.ts b/packages/@interactjs/pointer-events/PointerEvent.ts new file mode 100644 index 000000000..c2452f067 --- /dev/null +++ b/packages/@interactjs/pointer-events/PointerEvent.ts @@ -0,0 +1,84 @@ +import { BaseEvent } from '@interactjs/core/BaseEvent' +import type Interaction from '@interactjs/core/Interaction' +import type { PointerEventType, PointerType, Point } from '@interactjs/core/types' +import * as pointerUtils from '@interactjs/utils/pointerUtils' + +export class PointerEvent extends BaseEvent { + declare type: T + declare originalEvent: PointerEventType + declare pointerId: number + declare pointerType: string + declare double: boolean + declare pageX: number + declare pageY: number + declare clientX: number + declare clientY: number + declare dt: number + declare eventable: any; + [key: string]: any + + constructor( + type: T, + pointer: PointerType | PointerEvent, + event: PointerEventType, + eventTarget: Node, + interaction: Interaction, + timeStamp: number, + ) { + super(interaction) + pointerUtils.pointerExtend(this, event) + + if (event !== pointer) { + pointerUtils.pointerExtend(this, pointer) + } + + this.timeStamp = timeStamp + this.originalEvent = event + this.type = type + this.pointerId = pointerUtils.getPointerId(pointer) + this.pointerType = pointerUtils.getPointerType(pointer) + this.target = eventTarget + this.currentTarget = null + + if (type === 'tap') { + const pointerIndex = interaction.getPointerIndex(pointer) + this.dt = this.timeStamp - interaction.pointers[pointerIndex].downTime + + const interval = this.timeStamp - interaction.tapTime + + this.double = + !!interaction.prevTap && + interaction.prevTap.type !== 'doubletap' && + interaction.prevTap.target === this.target && + interval < 500 + } else if (type === 'doubletap') { + this.dt = (pointer as PointerEvent<'tap'>).timeStamp - interaction.tapTime + this.double = true + } + } + + _subtractOrigin({ x: originX, y: originY }: Point) { + this.pageX -= originX + this.pageY -= originY + this.clientX -= originX + this.clientY -= originY + + return this + } + + _addOrigin({ x: originX, y: originY }: Point) { + this.pageX += originX + this.pageY += originY + this.clientX += originX + this.clientY += originY + + return this + } + + /** + * Prevent the default behaviour of the original Event + */ + preventDefault() { + this.originalEvent.preventDefault() + } +} diff --git a/packages/@interactjs/pointer-events/README.md b/packages/@interactjs/pointer-events/README.md new file mode 100644 index 000000000..549491910 --- /dev/null +++ b/packages/@interactjs/pointer-events/README.md @@ -0,0 +1,5 @@ +

+This package is an internal part of interactjs and is not meant +to be used independently as each update may introduce breaking changes +

diff --git a/packages/@interactjs/pointer-events/base.spec.ts b/packages/@interactjs/pointer-events/base.spec.ts new file mode 100644 index 000000000..ed9b2a7fa --- /dev/null +++ b/packages/@interactjs/pointer-events/base.spec.ts @@ -0,0 +1,201 @@ +import { Eventable } from '@interactjs/core/Eventable' +import type { Scope } from '@interactjs/core/scope' +import * as helpers from '@interactjs/core/tests/_helpers' +import type { PointerEventType, PointerType } from '@interactjs/core/types' + +import type { EventTargetList } from './base' +import pointerEvents from './base' +import interactableTargets from './interactableTargets' + +test('pointerEvents.types', () => { + expect(pointerEvents.types).toEqual({ + down: true, + move: true, + up: true, + cancel: true, + tap: true, + doubletap: true, + hold: true, + }) +}) + +test('pointerEvents.fire', () => { + const { scope, interaction, event, coords } = helpers.testEnv({ plugins: [pointerEvents] }) + + const eventable = new Eventable(pointerEvents.defaults) + const type = 'TEST' + const element = {} + const eventTarget = {} + const TEST_PROP = ['TEST_PROP'] + let firedEvent: any + const targets: EventTargetList = [ + { + eventable, + node: element as Node, + props: { + TEST_PROP, + }, + }, + ] + + eventable.on(type, (e) => { + firedEvent = e + }) + + pointerEvents.fire( + { + type, + eventTarget, + pointer: {}, + event: {}, + interaction: {}, + targets, + } as any, + scope, + ) + + // Fired event is an instance of pointerEvents.PointerEvent + expect(firedEvent instanceof pointerEvents.PointerEvent).toBe(true) + // Fired event type is correct + expect(firedEvent.type).toBe(type) + // Fired event currentTarget is correct + expect(firedEvent.currentTarget).toBe(element) + // Fired event target is correct + expect(firedEvent.target).toBe(eventTarget) + // Fired event has props from target.props + expect(firedEvent.TEST_PROP).toBe(TEST_PROP) + + scope.now = () => coords.timeStamp + + coords.timeStamp = 0 + interaction.pointerDown(event, event, scope.document) + coords.timeStamp = 500 + interaction.pointerUp(event, event, scope.document, scope.document) + + // interaction.tapTime is updated + expect(interaction.tapTime).toBe(500) + // interaction.prevTap is updated + expect(interaction.prevTap.type).toBe('tap') +}) + +test('pointerEvents.collectEventTargets', () => { + const { scope, interaction } = helpers.testEnv() + + const type = 'TEST' + const TEST_PROP = ['TEST_PROP'] + const target = { + node: {} as Node, + props: { TEST_PROP }, + eventable: new Eventable(pointerEvents.defaults), + } + let collectedTargets: EventTargetList + + function onCollect({ targets }: { targets?: EventTargetList }) { + targets.push(target) + + collectedTargets = targets + } + + scope.addListeners({ + 'pointerEvents:collect-targets': onCollect, + }) + + pointerEvents.collectEventTargets( + { + interaction, + pointer: {}, + event: {}, + eventTarget: {}, + type, + } as any, + scope, + ) + + expect(collectedTargets).toEqual([target]) +}) + +test('pointerEvents Interaction update-pointer signal', () => { + const scope: Scope = helpers.mockScope() + + scope.usePlugin(pointerEvents) + + const interaction = scope.interactions.new({}) + const initialHold = { duration: Infinity, timeout: null as number } + const event = {} as PointerEventType + + interaction.updatePointer(helpers.newPointer(0), event, null, false) + // set hold info for move on new pointer + expect(interaction.pointers.map((p) => p.hold)).toEqual([initialHold]) + + interaction.removePointer(helpers.newPointer(0), event) + + interaction.updatePointer(helpers.newPointer(0), event, null, true) + expect(interaction.pointers.map((p) => p.hold)).toEqual([initialHold]) + + interaction.updatePointer(helpers.newPointer(5), event, null, true) + expect(interaction.pointers.map((p) => p.hold)).toEqual([initialHold, initialHold]) +}) + +test('pointerEvents Interaction remove-pointer signal', () => { + const scope: Scope = helpers.mockScope() + + scope.usePlugin(pointerEvents) + + const interaction = scope.interactions.new({}) + + const ids = [0, 1, 2, 3] + const removals = [ + { id: 0, remain: [1, 2, 3], message: 'first of 4' }, + { id: 2, remain: [1, 3], message: 'middle of 3' }, + { id: 3, remain: [1], message: 'last of 2' }, + { id: 1, remain: [], message: 'final' }, + ] + + for (const id of ids) { + const index = interaction.updatePointer( + { pointerId: id } as PointerType, + {} as PointerEventType, + null, + true, + ) + // use the ids as the pointerInfo.hold value for this test + interaction.pointers[index].hold = id as any + } + + for (const removal of removals) { + interaction.removePointer({ pointerId: removal.id } as any, null) + + // `${removal.message} - remaining interaction.pointers[i].hold are correct` + expect(interaction.pointers.map((p) => p.hold as unknown as number)).toEqual(removal.remain) + } +}) + +test('pointerEvents down hold up tap', async () => { + const { interaction, event, interactable } = helpers.testEnv({ + plugins: [pointerEvents, interactableTargets], + }) + + const fired: PointerEvent[] = [] + + for (const type in pointerEvents.types) { + interactable.on(type, (e) => fired.push(e)) + } + + interaction.pointerDown(event, event, event.target) + interaction.pointerMove(event, event, event.target) + + // duplicate move event is not fired + expect(fired.map((e) => e.type)).toEqual(['down']) + + const holdTimer = interaction.pointers[0].hold + + // hold timeout is set + expect(holdTimer.timeout).toBeTruthy() + + await helpers.timeout(holdTimer.duration) + + interaction.pointerUp(event, event, event.target, event.target) + + // tap event is fired after down, hold and up events + expect(fired.map((e) => e.type)).toEqual(['down', 'hold', 'up', 'tap']) +}) diff --git a/packages/@interactjs/pointer-events/base.ts b/packages/@interactjs/pointer-events/base.ts new file mode 100644 index 000000000..20eaab7f8 --- /dev/null +++ b/packages/@interactjs/pointer-events/base.ts @@ -0,0 +1,367 @@ +import type { Eventable } from '@interactjs/core/Eventable' +import type { Interaction } from '@interactjs/core/Interaction' +import type { PerActionDefaults } from '@interactjs/core/options' +import type { Scope, SignalArgs, Plugin } from '@interactjs/core/scope' +import type { Point, PointerType, PointerEventType, Element } from '@interactjs/core/types' +import * as domUtils from '@interactjs/utils/domUtils' +import extend from '@interactjs/utils/extend' +import getOriginXY from '@interactjs/utils/getOriginXY' + +import { PointerEvent } from './PointerEvent' + +export type EventTargetList = Array<{ + node: Node + eventable: Eventable + props: { [key: string]: any } +}> + +export interface PointerEventOptions extends PerActionDefaults { + enabled?: undefined // not used + holdDuration?: number + ignoreFrom?: any + allowFrom?: any + origin?: Point | string | Element +} + +declare module '@interactjs/core/scope' { + interface Scope { + pointerEvents: typeof pointerEvents + } +} + +declare module '@interactjs/core/Interaction' { + interface Interaction { + prevTap?: PointerEvent + tapTime?: number + } +} + +declare module '@interactjs/core/PointerInfo' { + interface PointerInfo { + hold?: { + duration: number + timeout: any + } + } +} + +declare module '@interactjs/core/options' { + interface ActionDefaults { + pointerEvents: Options + } +} + +declare module '@interactjs/core/scope' { + interface SignalArgs { + 'pointerEvents:new': { pointerEvent: PointerEvent } + 'pointerEvents:fired': { + interaction: Interaction + pointer: PointerType | PointerEvent + event: PointerEventType | PointerEvent + eventTarget: Node + pointerEvent: PointerEvent + targets?: EventTargetList + type: string + } + 'pointerEvents:collect-targets': { + interaction: Interaction + pointer: PointerType | PointerEvent + event: PointerEventType | PointerEvent + eventTarget: Node + targets?: EventTargetList + type: string + path: Node[] + node: null + } + } +} + +const defaults: PointerEventOptions = { + holdDuration: 600, + ignoreFrom: null, + allowFrom: null, + origin: { x: 0, y: 0 }, +} + +const pointerEvents: Plugin = { + id: 'pointer-events/base', + before: ['inertia', 'modifiers', 'auto-start', 'actions'], + install, + listeners: { + 'interactions:new': addInteractionProps, + 'interactions:update-pointer': addHoldInfo, + 'interactions:move': moveAndClearHold, + 'interactions:down': (arg, scope) => { + downAndStartHold(arg, scope) + fire(arg, scope) + }, + 'interactions:up': (arg, scope) => { + clearHold(arg) + fire(arg, scope) + tapAfterUp(arg, scope) + }, + 'interactions:cancel': (arg, scope) => { + clearHold(arg) + fire(arg, scope) + }, + }, + PointerEvent, + fire, + collectEventTargets, + defaults, + types: { + down: true, + move: true, + up: true, + cancel: true, + tap: true, + doubletap: true, + hold: true, + } as { [type: string]: true }, +} + +function fire( + arg: { + pointer: PointerType | PointerEvent + event: PointerEventType | PointerEvent + eventTarget: Node + interaction: Interaction + type: T + targets?: EventTargetList + }, + scope: Scope, +) { + const { interaction, pointer, event, eventTarget, type, targets = collectEventTargets(arg, scope) } = arg + + const pointerEvent = new PointerEvent(type, pointer, event, eventTarget, interaction, scope.now()) + + scope.fire('pointerEvents:new', { pointerEvent }) + + const signalArg = { + interaction, + pointer, + event, + eventTarget, + targets, + type, + pointerEvent, + } + + for (let i = 0; i < targets.length; i++) { + const target = targets[i] + + for (const prop in target.props || {}) { + ;(pointerEvent as any)[prop] = target.props[prop] + } + + const origin = getOriginXY(target.eventable, target.node) + + pointerEvent._subtractOrigin(origin) + pointerEvent.eventable = target.eventable + pointerEvent.currentTarget = target.node + + target.eventable.fire(pointerEvent) + + pointerEvent._addOrigin(origin) + + if ( + pointerEvent.immediatePropagationStopped || + (pointerEvent.propagationStopped && + i + 1 < targets.length && + targets[i + 1].node !== pointerEvent.currentTarget) + ) { + break + } + } + + scope.fire('pointerEvents:fired', signalArg) + + if (type === 'tap') { + // if pointerEvent should make a double tap, create and fire a doubletap + // PointerEvent and use that as the prevTap + const prevTap = pointerEvent.double + ? fire( + { + interaction, + pointer, + event, + eventTarget, + type: 'doubletap', + }, + scope, + ) + : pointerEvent + + interaction.prevTap = prevTap + interaction.tapTime = prevTap.timeStamp + } + + return pointerEvent +} + +function collectEventTargets( + { + interaction, + pointer, + event, + eventTarget, + type, + }: { + interaction: Interaction + pointer: PointerType | PointerEvent + event: PointerEventType | PointerEvent + eventTarget: Node + type: T + }, + scope: Scope, +) { + const pointerIndex = interaction.getPointerIndex(pointer) + const pointerInfo = interaction.pointers[pointerIndex] + + // do not fire a tap event if the pointer was moved before being lifted + if ( + type === 'tap' && + (interaction.pointerWasMoved || + // or if the pointerup target is different to the pointerdown target + !(pointerInfo && pointerInfo.downTarget === eventTarget)) + ) { + return [] + } + + const path = domUtils.getPath(eventTarget as Element | Document) + const signalArg = { + interaction, + pointer, + event, + eventTarget, + type, + path, + targets: [] as EventTargetList, + node: null, + } + + for (const node of path) { + signalArg.node = node + + scope.fire('pointerEvents:collect-targets', signalArg) + } + + if (type === 'hold') { + signalArg.targets = signalArg.targets.filter( + (target) => + target.eventable.options.holdDuration === interaction.pointers[pointerIndex]?.hold?.duration, + ) + } + + return signalArg.targets +} + +function addInteractionProps({ interaction }) { + interaction.prevTap = null // the most recent tap event on this interaction + interaction.tapTime = 0 // time of the most recent tap event +} + +function addHoldInfo({ down, pointerInfo }: SignalArgs['interactions:update-pointer']) { + if (!down && pointerInfo.hold) { + return + } + + pointerInfo.hold = { duration: Infinity, timeout: null } +} + +function clearHold({ interaction, pointerIndex }) { + const hold = interaction.pointers[pointerIndex].hold + + if (hold && hold.timeout) { + clearTimeout(hold.timeout) + hold.timeout = null + } +} + +function moveAndClearHold(arg: SignalArgs['interactions:move'], scope: Scope) { + const { interaction, pointer, event, eventTarget, duplicate } = arg + + if (!duplicate && (!interaction.pointerIsDown || interaction.pointerWasMoved)) { + if (interaction.pointerIsDown) { + clearHold(arg) + } + + fire( + { + interaction, + pointer, + event, + eventTarget: eventTarget as Element, + type: 'move', + }, + scope, + ) + } +} + +function downAndStartHold( + { interaction, pointer, event, eventTarget, pointerIndex }: SignalArgs['interactions:down'], + scope: Scope, +) { + const timer = interaction.pointers[pointerIndex].hold! + const path = domUtils.getPath(eventTarget as Element | Document) + const signalArg = { + interaction, + pointer, + event, + eventTarget, + type: 'hold', + targets: [] as EventTargetList, + path, + node: null, + } + + for (const node of path) { + signalArg.node = node + + scope.fire('pointerEvents:collect-targets', signalArg) + } + + if (!signalArg.targets.length) return + + let minDuration = Infinity + + for (const target of signalArg.targets) { + const holdDuration = target.eventable.options.holdDuration + + if (holdDuration < minDuration) { + minDuration = holdDuration + } + } + + timer.duration = minDuration + timer.timeout = setTimeout(() => { + fire( + { + interaction, + eventTarget, + pointer, + event, + type: 'hold', + }, + scope, + ) + }, minDuration) +} + +function tapAfterUp( + { interaction, pointer, event, eventTarget }: SignalArgs['interactions:up'], + scope: Scope, +) { + if (!interaction.pointerWasMoved) { + fire({ interaction, eventTarget, pointer, event, type: 'tap' }, scope) + } +} + +function install(scope: Scope) { + scope.pointerEvents = pointerEvents + scope.defaults.actions.pointerEvents = pointerEvents.defaults + extend(scope.actions.phaselessTypes, pointerEvents.types) +} + +export default pointerEvents diff --git a/packages/@interactjs/pointer-events/holdRepeat.spec.ts b/packages/@interactjs/pointer-events/holdRepeat.spec.ts new file mode 100644 index 000000000..feb30b8f8 --- /dev/null +++ b/packages/@interactjs/pointer-events/holdRepeat.spec.ts @@ -0,0 +1,64 @@ +import { Eventable } from '@interactjs/core/Eventable' +import * as helpers from '@interactjs/core/tests/_helpers' + +import holdRepeat from './holdRepeat' + +test('holdRepeat count', () => { + const pointerEvent = { + type: 'hold', + count: 0, + } + + const { scope } = helpers.testEnv({ plugins: [holdRepeat] }) + + scope.fire('pointerEvents:new', { pointerEvent } as any) + // first hold count is 1 with count previously undefined + expect(pointerEvent.count).toBe(1) + + const count = 20 + pointerEvent.count = count + scope.fire('pointerEvents:new', { pointerEvent } as any) + // existing hold count is incremented + expect(pointerEvent.count).toBe(count + 1) +}) + +test('holdRepeat onFired', () => { + const { scope, interaction } = helpers.testEnv({ plugins: [holdRepeat] }) + + const pointerEvent = { + type: 'hold', + } + const eventTarget = {} + const eventable = new Eventable( + Object.assign({}, scope.pointerEvents.defaults, { + holdRepeatInterval: 0, + }), + ) + const signalArg = { + interaction, + pointerEvent, + eventTarget, + targets: [ + { + eventable, + }, + ], + } + + scope.fire('pointerEvents:fired', signalArg as any) + // interaction interval handle was not saved with 0 holdRepeatInterval + expect('holdIntervalHandle' in interaction).toBe(false) + + eventable.options.holdRepeatInterval = 10 + scope.fire('pointerEvents:fired', signalArg as any) + // interaction interval handle was saved with interval > 0 + expect('holdIntervalHandle' in interaction).toBe(true) + + clearInterval(interaction.holdIntervalHandle) + + pointerEvent.type = 'NOT_HOLD' + delete interaction.holdIntervalHandle + scope.fire('pointerEvents:fired', signalArg as any) + // interaction interval handle is not saved if pointerEvent.type is not "hold" + expect('holdIntervalHandle' in interaction).toBe(false) +}) diff --git a/packages/@interactjs/pointer-events/holdRepeat.ts b/packages/@interactjs/pointer-events/holdRepeat.ts new file mode 100644 index 000000000..2d43e6ff6 --- /dev/null +++ b/packages/@interactjs/pointer-events/holdRepeat.ts @@ -0,0 +1,95 @@ +import type Interaction from '@interactjs/core/Interaction' +import type { ListenerMap, Scope, SignalArgs, Plugin } from '@interactjs/core/scope' + +/* eslint-disable import/no-duplicates -- for typescript module augmentations */ +import './base' +import basePlugin from './base' +/* eslint-enable import/no-duplicates */ +import { type PointerEvent } from './PointerEvent' + +declare module '@interactjs/core/Interaction' { + interface Interaction { + holdIntervalHandle?: any + } +} + +declare module '@interactjs/pointer-events/PointerEvent' { + interface PointerEvent { + count?: number + } +} + +declare module '@interactjs/pointer-events/base' { + interface PointerEventOptions { + holdRepeatInterval?: number + } +} + +function install(scope: Scope) { + scope.usePlugin(basePlugin) + + const { pointerEvents } = scope + + // don't repeat by default + pointerEvents.defaults.holdRepeatInterval = 0 + pointerEvents.types.holdrepeat = scope.actions.phaselessTypes.holdrepeat = true +} + +function onNew({ pointerEvent }: { pointerEvent: PointerEvent }) { + if (pointerEvent.type !== 'hold') return + + pointerEvent.count = (pointerEvent.count || 0) + 1 +} + +function onFired( + { interaction, pointerEvent, eventTarget, targets }: SignalArgs['pointerEvents:fired'], + scope: Scope, +) { + if (pointerEvent.type !== 'hold' || !targets.length) return + + // get the repeat interval from the first eventable + const interval = targets[0].eventable.options.holdRepeatInterval + + // don't repeat if the interval is 0 or less + if (interval <= 0) return + + // set a timeout to fire the holdrepeat event + interaction.holdIntervalHandle = setTimeout(() => { + scope.pointerEvents.fire( + { + interaction, + eventTarget, + type: 'hold', + pointer: pointerEvent, + event: pointerEvent, + }, + scope, + ) + }, interval) +} + +function endHoldRepeat({ interaction }: { interaction: Interaction }) { + // set the interaction's holdStopTime property + // to stop further holdRepeat events + if (interaction.holdIntervalHandle) { + clearInterval(interaction.holdIntervalHandle) + interaction.holdIntervalHandle = null + } +} + +const holdRepeat: Plugin = { + id: 'pointer-events/holdRepeat', + install, + listeners: ['move', 'up', 'cancel', 'endall'].reduce( + (acc, enderTypes) => { + ;(acc as any)[`pointerEvents:${enderTypes}`] = endHoldRepeat + return acc + }, + { + 'pointerEvents:new': onNew, + 'pointerEvents:fired': onFired, + } as ListenerMap, + ), +} + +export default holdRepeat diff --git a/packages/@interactjs/pointer-events/interactableTargets.ts b/packages/@interactjs/pointer-events/interactableTargets.ts new file mode 100644 index 000000000..8f75b18b6 --- /dev/null +++ b/packages/@interactjs/pointer-events/interactableTargets.ts @@ -0,0 +1,77 @@ +import type { Interactable } from '@interactjs/core/Interactable' +import type { Scope, Plugin } from '@interactjs/core/scope' +import type { Element } from '@interactjs/core/types' +import extend from '@interactjs/utils/extend' + +import type { PointerEventOptions } from '@interactjs/pointer-events/base' + +declare module '@interactjs/core/Interactable' { + interface Interactable { + pointerEvents(options: Partial): this + /** @internal */ + __backCompatOption: (optionName: string, newValue: any) => any + } +} + +function install(scope: Scope) { + const { Interactable } = scope + + Interactable.prototype.pointerEvents = function ( + this: Interactable, + options: Partial, + ) { + extend(this.events.options, options) + + return this + } + + const __backCompatOption = Interactable.prototype._backCompatOption + + Interactable.prototype._backCompatOption = function (optionName, newValue) { + const ret = __backCompatOption.call(this, optionName, newValue) + + if (ret === this) { + this.events.options[optionName] = newValue + } + + return ret + } +} + +const plugin: Plugin = { + id: 'pointer-events/interactableTargets', + install, + listeners: { + 'pointerEvents:collect-targets': ({ targets, node, type, eventTarget }, scope) => { + scope.interactables.forEachMatch(node, (interactable: Interactable) => { + const eventable = interactable.events + const options = eventable.options + + if ( + eventable.types[type] && + eventable.types[type].length && + interactable.testIgnoreAllow(options, node, eventTarget) + ) { + targets.push({ + node, + eventable, + props: { interactable }, + }) + } + }) + }, + + 'interactable:new': ({ interactable }) => { + interactable.events.getRect = function (element: Element) { + return interactable.getRect(element) + } + }, + + 'interactable:set': ({ interactable, options }, scope) => { + extend(interactable.events.options, scope.pointerEvents.defaults) + extend(interactable.events.options, options.pointerEvents || {}) + }, + }, +} + +export default plugin diff --git a/packages/@interactjs/pointer-events/package.json b/packages/@interactjs/pointer-events/package.json new file mode 100644 index 000000000..60299a68b --- /dev/null +++ b/packages/@interactjs/pointer-events/package.json @@ -0,0 +1,27 @@ +{ + "name": "@interactjs/pointer-events", + "version": "1.10.27", + "main": "index", + "module": "index", + "type": "module", + "repository": { + "type": "git", + "url": "https://github.com/taye/interact.js.git", + "directory": "packages/@interactjs/pointer-events" + }, + "peerDependencies": { + "@interactjs/core": "1.10.27", + "@interactjs/utils": "1.10.27" + }, + "optionalDependencies": { + "@interactjs/interact": "1.10.27" + }, + "publishConfig": { + "access": "public" + }, + "sideEffects": [ + "**/index.js", + "**/index.prod.js" + ], + "license": "MIT" +} diff --git a/packages/@interactjs/pointer-events/plugin.ts b/packages/@interactjs/pointer-events/plugin.ts new file mode 100644 index 000000000..5e0ea0131 --- /dev/null +++ b/packages/@interactjs/pointer-events/plugin.ts @@ -0,0 +1,22 @@ +import type { Plugin } from '@interactjs/core/scope' + +/* eslint-disable import/no-duplicates -- for typescript module augmentations */ +import './base' +import './holdRepeat' +import './interactableTargets' + +import * as pointerEvents from './base' +import holdRepeat from './holdRepeat' +import interactableTargets from './interactableTargets' +/* eslint-enable import/no-duplicates */ + +const plugin: Plugin = { + id: 'pointer-events', + install(scope) { + scope.usePlugin(pointerEvents) + scope.usePlugin(holdRepeat) + scope.usePlugin(interactableTargets) + }, +} + +export default plugin diff --git a/packages/@interactjs/reflow/README.md b/packages/@interactjs/reflow/README.md new file mode 100644 index 000000000..549491910 --- /dev/null +++ b/packages/@interactjs/reflow/README.md @@ -0,0 +1,5 @@ +

+This package is an internal part of interactjs and is not meant +to be used independently as each update may introduce breaking changes +

diff --git a/packages/@interactjs/reflow/package.json b/packages/@interactjs/reflow/package.json new file mode 100644 index 000000000..5eea51d17 --- /dev/null +++ b/packages/@interactjs/reflow/package.json @@ -0,0 +1,27 @@ +{ + "name": "@interactjs/reflow", + "version": "1.10.27", + "main": "index", + "module": "index", + "type": "module", + "repository": { + "type": "git", + "url": "https://github.com/taye/interact.js.git", + "directory": "packages/@interactjs/reflow" + }, + "peerDependencies": { + "@interactjs/core": "1.10.27", + "@interactjs/utils": "1.10.27" + }, + "optionalDependencies": { + "@interactjs/interact": "1.10.27" + }, + "publishConfig": { + "access": "public" + }, + "sideEffects": [ + "**/index.js", + "**/index.prod.js" + ], + "license": "MIT" +} diff --git a/packages/@interactjs/reflow/plugin.ts b/packages/@interactjs/reflow/plugin.ts new file mode 100644 index 000000000..e0ffe8e60 --- /dev/null +++ b/packages/@interactjs/reflow/plugin.ts @@ -0,0 +1,186 @@ +import type { Interactable } from '@interactjs/core/Interactable' +import type { DoAnyPhaseArg, Interaction } from '@interactjs/core/Interaction' +import type { Scope, Plugin } from '@interactjs/core/scope' +import type { ActionName, ActionProps, Element } from '@interactjs/core/types' +import * as arr from '@interactjs/utils/arr' +import { copyAction } from '@interactjs/utils/misc' +import * as pointerUtils from '@interactjs/utils/pointerUtils' +import { tlbrToXywh } from '@interactjs/utils/rect' + +declare module '@interactjs/core/scope' { + interface SignalArgs { + 'interactions:before-action-reflow': Omit + 'interactions:action-reflow': DoAnyPhaseArg + 'interactions:after-action-reflow': DoAnyPhaseArg + } +} + +declare module '@interactjs/core/Interactable' { + interface Interactable { + /** + * ```js + * const interactable = interact(target) + * const drag = { name: drag, axis: 'x' } + * const resize = { name: resize, edges: { left: true, bottom: true } + * + * interactable.reflow(drag) + * interactable.reflow(resize) + * ``` + * + * Start an action sequence to re-apply modifiers, check drops, etc. + * + * @param { Object } action The action to begin + * @param { string } action.name The name of the action + * @returns { Promise } A promise that resolves to the `Interactable` when actions on all targets have ended + */ + reflow(action: ActionProps): ReturnType + } +} + +declare module '@interactjs/core/Interaction' { + interface Interaction { + _reflowPromise: Promise + _reflowResolve: (...args: unknown[]) => void + } +} + +declare module '@interactjs/core/InteractEvent' { + interface PhaseMap { + reflow?: true + } +} + +function install(scope: Scope) { + const { Interactable } = scope + + scope.actions.phases.reflow = true + + Interactable.prototype.reflow = function (action: ActionProps) { + return doReflow(this, action, scope) + } +} + +function doReflow( + interactable: Interactable, + action: ActionProps, + scope: Scope, +): Promise { + const elements = interactable.getAllElements() + + // tslint:disable-next-line variable-name + const Promise = (scope.window as any).Promise + const promises: Array> | null = Promise ? [] : null + + for (const element of elements) { + const rect = interactable.getRect(element as HTMLElement | SVGElement) + + if (!rect) { + break + } + + const runningInteraction = arr.find(scope.interactions.list, (interaction: Interaction) => { + return ( + interaction.interacting() && + interaction.interactable === interactable && + interaction.element === element && + interaction.prepared.name === action.name + ) + }) + let reflowPromise: Promise + + if (runningInteraction) { + runningInteraction.move() + + if (promises) { + reflowPromise = + runningInteraction._reflowPromise || + new Promise((resolve: any) => { + runningInteraction._reflowResolve = resolve + }) + } + } else { + const xywh = tlbrToXywh(rect) + const coords = { + page: { x: xywh.x, y: xywh.y }, + client: { x: xywh.x, y: xywh.y }, + timeStamp: scope.now(), + } + + const event = pointerUtils.coordsToEvent(coords) + reflowPromise = startReflow(scope, interactable, element, action, event) + } + + if (promises) { + promises.push(reflowPromise) + } + } + + return promises && Promise.all(promises).then(() => interactable) +} + +function startReflow( + scope: Scope, + interactable: Interactable, + element: Element, + action: ActionProps, + event: any, +) { + const interaction = scope.interactions.new({ pointerType: 'reflow' }) + const signalArg = { + interaction, + event, + pointer: event, + eventTarget: element, + phase: 'reflow', + } as const + + interaction.interactable = interactable + interaction.element = element + interaction.prevEvent = event + interaction.updatePointer(event, event, element, true) + pointerUtils.setZeroCoords(interaction.coords.delta) + + copyAction(interaction.prepared, action) + interaction._doPhase(signalArg) + + const { Promise } = scope.window as unknown as { Promise: PromiseConstructor } + const reflowPromise = Promise + ? new Promise((resolve) => { + interaction._reflowResolve = resolve + }) + : undefined + + interaction._reflowPromise = reflowPromise + interaction.start(action, interactable, element) + + if (interaction._interacting) { + interaction.move(signalArg) + interaction.end(event) + } else { + interaction.stop() + interaction._reflowResolve() + } + + interaction.removePointer(event, event) + + return reflowPromise +} + +const reflow: Plugin = { + id: 'reflow', + install, + listeners: { + // remove completed reflow interactions + 'interactions:stop': ({ interaction }, scope) => { + if (interaction.pointerType === 'reflow') { + if (interaction._reflowResolve) { + interaction._reflowResolve() + } + + arr.remove(scope.interactions.list, interaction) + } + }, + }, +} + +export default reflow diff --git a/packages/@interactjs/reflow/reflow.spec.ts b/packages/@interactjs/reflow/reflow.spec.ts new file mode 100644 index 000000000..5e8ba5c7b --- /dev/null +++ b/packages/@interactjs/reflow/reflow.spec.ts @@ -0,0 +1,146 @@ +import type { Interactable } from '@interactjs/core/Interactable' +import type { InteractEvent } from '@interactjs/core/InteractEvent' +import * as helpers from '@interactjs/core/tests/_helpers' +import type { ActionName, Point } from '@interactjs/core/types' +import PromisePolyfill from 'promise-polyfill' + +import reflow from './plugin' + +const testAction = { name: 'TEST' as ActionName } + +const Promise_ = Promise + +describe('reflow', () => { + test('sync', () => { + const rect = Object.freeze({ top: 100, left: 200, bottom: 300, right: 400 }) + + const { scope, interactable } = helpers.testEnv({ plugins: [reflow], rect }) + + Object.assign(scope.actions, { TEST: {}, names: ['TEST'] }) + + // reflow method is added to Interactable.prototype + expect(scope.Interactable.prototype.reflow instanceof Function).toBe(true) + + const fired: InteractEvent[] = [] + let beforeReflowDelta: Point + + interactable.fire = ((iEvent: any) => { + fired.push(iEvent) + }) as any + ;(interactable.options as any).TEST = { enabled: true } + interactable.rectChecker(() => ({ ...rect })) + + // modify move coords + scope.addListeners({ + 'interactions:before-action-move': ({ interaction }) => { + interaction.coords.cur.page = { + x: rect.left + 100, + y: rect.top - 50, + } + }, + 'interactions:before-action-reflow': ({ interaction }) => { + beforeReflowDelta = { ...interaction.coords.delta.page } + }, + }) + + interactable.reflow(testAction) + + const phases = ['reflow', 'start', 'move', 'end'] + expect(phases.map((_phase, index) => fired[index]?.type)).toEqual(phases.map((phase) => `TEST${phase}`)) + + for (const index in phases) { + const phase = phases[index] + // `event #${index} is ${phase}` + expect(fired[index].type).toBe(`TEST${phase}`) + } + + const interaction = fired[0]._interaction + + // uses element top left for event coords + expect(interaction.coords.start.page).toEqual({ + x: rect.left, + y: rect.top, + }) + + const reflowMove = fired[2] + + // interaction delta is zero before-action-reflow + expect(beforeReflowDelta!).toEqual({ x: 0, y: 0 }) + // move delta is correct with modified interaction coords + expect(reflowMove.delta).toEqual({ x: 100, y: -50 }) + // reflow pointer was lifted + expect(interaction.pointerIsDown).toBe(false) + // reflow pointer was removed from interaction + expect(interaction.pointers).toHaveLength(0) + // interaction is removed from list + expect(scope.interactions.list).not.toContain(interaction) + }) + + test('async', async () => { + const { scope } = helpers.testEnv({ plugins: [reflow] }) + + Object.assign(scope.actions, { TEST: {}, names: ['TEST'] }) + + let reflowEvent: any + let promise: Promise + + const interactable = scope.interactables.new(scope.document.documentElement) + const rect = Object.freeze({ top: 100, left: 200, bottom: 300, right: 400 }) + interactable.rectChecker(() => ({ ...rect })) + interactable.fire = ((iEvent: any) => { + reflowEvent = iEvent + }) as any + ;(interactable.options as any).TEST = { enabled: true } + + // test with Promise implementation + ;(scope.window as any).Promise = PromisePolyfill + + promise = interactable.reflow(testAction) + // method returns a Promise if available + expect(promise instanceof (scope.window as any).Promise).toBe(true) + // reflow may end synchronously + expect(reflowEvent.interaction.interacting()).toBe(false) + + // returned Promise resolves to interactable + expect(await promise).toBe(interactable) + + let stoppedFromTimeout: boolean + // block the end of the reflow interaction and stop it after a timeout + scope.addListeners({ + 'interactions:before-action-end': ({ interaction }) => { + setTimeout(() => { + interaction.stop() + stoppedFromTimeout = true + }, 0) + return false + }, + }) + + stoppedFromTimeout = false + promise = interactable.reflow(testAction) + + // interaction continues if end is blocked + expect(reflowEvent.interaction.interacting() && !stoppedFromTimeout).toBe(true) + await promise + // interaction is stopped after promise is resolved + expect(reflowEvent.interaction.interacting() && stoppedFromTimeout).toBe(false) + + // test without Promise implementation + stoppedFromTimeout = false + ;(scope.window as any).Promise = undefined + + promise = interactable.reflow(testAction) + // method returns null if no Proise is avilable + expect(promise).toBeNull() + // interaction continues if end is blocked without Promise + expect(reflowEvent.interaction.interacting() && !stoppedFromTimeout).toBe(true) + + await new Promise_((resolve) => + setTimeout(() => { + // interaction is stopped after timeout without Promised + expect(reflowEvent.interaction.interacting() || !stoppedFromTimeout).toBe(false) + resolve() + }, 0), + ) + }) +}) diff --git a/packages/@interactjs/snappers/all.ts b/packages/@interactjs/snappers/all.ts new file mode 100644 index 000000000..50f579d6f --- /dev/null +++ b/packages/@interactjs/snappers/all.ts @@ -0,0 +1,4 @@ +/* eslint-disable import/no-named-as-default, import/no-unresolved */ +export { default as edgeTarget } from './edgeTarget' +export { default as elements } from './elements' +export { default as grid } from './grid' diff --git a/packages/@interactjs/snappers/edgeTarget.stub.ts b/packages/@interactjs/snappers/edgeTarget.stub.ts new file mode 100644 index 000000000..ead516c97 --- /dev/null +++ b/packages/@interactjs/snappers/edgeTarget.stub.ts @@ -0,0 +1 @@ +export default () => {} diff --git a/packages/@interactjs/snappers/edgeTarget.ts b/packages/@interactjs/snappers/edgeTarget.ts new file mode 100644 index 000000000..ead516c97 --- /dev/null +++ b/packages/@interactjs/snappers/edgeTarget.ts @@ -0,0 +1 @@ +export default () => {} diff --git a/packages/@interactjs/snappers/elements.stub.ts b/packages/@interactjs/snappers/elements.stub.ts new file mode 100644 index 000000000..ead516c97 --- /dev/null +++ b/packages/@interactjs/snappers/elements.stub.ts @@ -0,0 +1 @@ +export default () => {} diff --git a/packages/@interactjs/snappers/elements.ts b/packages/@interactjs/snappers/elements.ts new file mode 100644 index 000000000..ead516c97 --- /dev/null +++ b/packages/@interactjs/snappers/elements.ts @@ -0,0 +1 @@ +export default () => {} diff --git a/packages/@interactjs/snappers/grid.ts b/packages/@interactjs/snappers/grid.ts new file mode 100644 index 000000000..14b785ca8 --- /dev/null +++ b/packages/@interactjs/snappers/grid.ts @@ -0,0 +1,72 @@ +import type { Rect, Point } from '@interactjs/core/types' +import type { SnapFunction, SnapTarget } from '@interactjs/modifiers/snap/pointer' + +export interface GridOptionsBase { + range?: number + limits?: Rect + offset?: Point +} +export interface GridOptionsXY extends GridOptionsBase { + x: number + y: number +} +export interface GridOptionsTopLeft extends GridOptionsBase { + top?: number + left?: number +} +export interface GridOptionsBottomRight extends GridOptionsBase { + bottom?: number + right?: number +} +export interface GridOptionsWidthHeight extends GridOptionsBase { + width?: number + height?: number +} + +export type GridOptions = GridOptionsXY | GridOptionsTopLeft | GridOptionsBottomRight | GridOptionsWidthHeight + +export default (grid: GridOptions) => { + const coordFields = ( + [ + ['x', 'y'], + ['left', 'top'], + ['right', 'bottom'], + ['width', 'height'], + ] as const + ).filter(([xField, yField]) => xField in grid || yField in grid) + + const gridFunc: SnapFunction & { + grid: typeof grid + coordFields: typeof coordFields + } = (x, y) => { + const { + range, + limits = { + left: -Infinity, + right: Infinity, + top: -Infinity, + bottom: Infinity, + }, + offset = { x: 0, y: 0 }, + } = grid + + const result: SnapTarget & { + grid: typeof grid + } = { range, grid, x: null as number, y: null as number } + + for (const [xField, yField] of coordFields) { + const gridx = Math.round((x - offset.x) / (grid as any)[xField]) + const gridy = Math.round((y - offset.y) / (grid as any)[yField]) + + result[xField] = Math.max(limits.left, Math.min(limits.right, gridx * (grid as any)[xField] + offset.x)) + result[yField] = Math.max(limits.top, Math.min(limits.bottom, gridy * (grid as any)[yField] + offset.y)) + } + + return result + } + + gridFunc.grid = grid + gridFunc.coordFields = coordFields + + return gridFunc +} diff --git a/packages/@interactjs/snappers/package.json b/packages/@interactjs/snappers/package.json new file mode 100644 index 000000000..10accea37 --- /dev/null +++ b/packages/@interactjs/snappers/package.json @@ -0,0 +1,26 @@ +{ + "name": "@interactjs/snappers", + "version": "1.10.27", + "main": "index", + "module": "index", + "type": "module", + "repository": { + "type": "git", + "url": "https://github.com/taye/interact.js.git", + "directory": "packages/@interactjs/snappers" + }, + "peerDependencies": { + "@interactjs/utils": "1.10.27" + }, + "optionalDependencies": { + "@interactjs/interact": "1.10.27" + }, + "publishConfig": { + "access": "public" + }, + "sideEffects": [ + "**/index.js", + "**/index.prod.js" + ], + "license": "MIT" +} diff --git a/packages/@interactjs/snappers/plugin.ts b/packages/@interactjs/snappers/plugin.ts new file mode 100644 index 000000000..f8208b43b --- /dev/null +++ b/packages/@interactjs/snappers/plugin.ts @@ -0,0 +1,23 @@ +import type { Plugin } from '@interactjs/core/scope' +import extend from '@interactjs/utils/extend' + +import * as allSnappers from './all' + +declare module '@interactjs/core/InteractStatic' { + export interface InteractStatic { + snappers: typeof allSnappers + createSnapGrid: typeof allSnappers.grid + } +} + +const snappersPlugin: Plugin = { + id: 'snappers', + install(scope) { + const { interactStatic: interact } = scope + + interact.snappers = extend(interact.snappers || {}, allSnappers) + interact.createSnapGrid = interact.snappers.grid + }, +} + +export default snappersPlugin diff --git a/packages/@interactjs/types/README.md b/packages/@interactjs/types/README.md new file mode 100644 index 000000000..549491910 --- /dev/null +++ b/packages/@interactjs/types/README.md @@ -0,0 +1,5 @@ +

+This package is an internal part of interactjs and is not meant +to be used independently as each update may introduce breaking changes +

diff --git a/packages/@interactjs/types/index.ts b/packages/@interactjs/types/index.ts new file mode 100644 index 000000000..39b3530f9 --- /dev/null +++ b/packages/@interactjs/types/index.ts @@ -0,0 +1,27 @@ +/* eslint-disable import/no-extraneous-dependencies */ +import type { InteractEvent as _InteractEvent, EventPhase } from '@interactjs/core/InteractEvent' +import type * as interaction from '@interactjs/core/Interaction' +import type { ActionName, ActionProps as _ActionProps } from '@interactjs/core/types' +// import module augmentations +import '@interactjs/interactjs' + +export * from '@interactjs/core/types' +export type { Plugin } from '@interactjs/core/scope' +export type { EventPhase } from '@interactjs/core/InteractEvent' +export type { Options } from '@interactjs/core/options' +export type { PointerEvent } from '@interactjs/pointer-events/PointerEvent' +export type { Interactable } from '@interactjs/core/Interactable' +export type { DragEvent } from '@interactjs/actions/drag/plugin' +export type { DropEvent } from '@interactjs/actions/drop/DropEvent' +export type { GestureEvent } from '@interactjs/actions/gesture/plugin' +export type { ResizeEvent } from '@interactjs/actions/resize/plugin' +export type { SnapFunction, SnapTarget } from '@interactjs/modifiers/snap/pointer' + +export type ActionProps = _ActionProps +export type Interaction = interaction.Interaction +export type InteractionProxy = interaction.InteractionProxy +export type PointerArgProps = interaction.PointerArgProps +export type InteractEvent = _InteractEvent< + T, + P +> diff --git a/packages/@interactjs/types/package.json b/packages/@interactjs/types/package.json new file mode 100644 index 000000000..d772bfdb8 --- /dev/null +++ b/packages/@interactjs/types/package.json @@ -0,0 +1,37 @@ +{ + "name": "@interactjs/types", + "version": "1.10.27", + "main": "index", + "module": "index", + "type": "module", + "repository": { + "type": "git", + "url": "https://github.com/taye/interact.js.git", + "directory": "packages/@interactjs/types" + }, + "typings": "typings.d.ts", + "devDependencies": { + "@interactjs/actions": "1.10.27", + "@interactjs/arrange": "1.10.27", + "@interactjs/auto-scroll": "1.10.27", + "@interactjs/auto-start": "1.10.27", + "@interactjs/core": "1.10.27", + "@interactjs/dev-tools": "1.10.27", + "@interactjs/inertia": "1.10.27", + "@interactjs/interact": "1.10.27", + "@interactjs/interactjs": "1.10.27", + "@interactjs/modifiers": "1.10.27", + "@interactjs/pointer-events": "1.10.27", + "@interactjs/reflow": "1.10.27", + "@interactjs/snappers": "1.10.27", + "@interactjs/utils": "1.10.27" + }, + "publishConfig": { + "access": "public" + }, + "sideEffects": [ + "**/index.js", + "**/index.prod.js" + ], + "license": "MIT" +} diff --git a/packages/@interactjs/types/types.spec.ts b/packages/@interactjs/types/types.spec.ts new file mode 100644 index 000000000..ff2628567 --- /dev/null +++ b/packages/@interactjs/types/types.spec.ts @@ -0,0 +1,36 @@ +/** @jest-environment node */ +import path from 'path' + +import * as execTypes from '@interactjs/_dev/scripts/execTypes' +import { mkdirp } from 'mkdirp' +import * as shell from 'shelljs' +import temp from 'temp' + +jest.setTimeout(15000) + +test('typings', async () => { + shell.config.fatal = true + + const tempDir = temp.track().mkdirSync('testProject') + const modulesDir = path.join(tempDir, 'node_modules') + const tempTypesDir = path.join(modulesDir, '@interactjs', 'types') + const interactDir = path.join(modulesDir, 'interactjs') + + await mkdirp(interactDir) + + // run .d.ts generation script with output to temp dir node_modules + await execTypes.combined(tempTypesDir) + + // copy .d.ts and package.json files of deps to temp dir + shell.cp(path.join('packages', 'interactjs', '{*.d.ts,package.json}'), interactDir) + shell.cp(path.join('packages', '@interactjs', 'types', '{*.d.ts,package.json}'), tempTypesDir) + shell.cp('-R', path.join(process.cwd(), 'test', 'fixtures', 'dependentTsProject', '*'), tempDir) + + expect(() => { + shell.exec(`${getBin('tsc')} -b`, { cwd: tempDir }) + }).not.toThrow() + shell.config.reset() +}) + +const nodeBins = path.join(process.cwd(), 'node_modules', '.bin') +const getBin = (name: string) => path.join(nodeBins, name) diff --git a/packages/@interactjs/utils/ElementState.stub.ts b/packages/@interactjs/utils/ElementState.stub.ts new file mode 100644 index 000000000..b1c6ea436 --- /dev/null +++ b/packages/@interactjs/utils/ElementState.stub.ts @@ -0,0 +1 @@ +export default {} diff --git a/packages/@interactjs/utils/ElementState.ts b/packages/@interactjs/utils/ElementState.ts new file mode 100644 index 000000000..b1c6ea436 --- /dev/null +++ b/packages/@interactjs/utils/ElementState.ts @@ -0,0 +1 @@ +export default {} diff --git a/packages/@interactjs/utils/README.md b/packages/@interactjs/utils/README.md new file mode 100644 index 000000000..549491910 --- /dev/null +++ b/packages/@interactjs/utils/README.md @@ -0,0 +1,5 @@ +

+This package is an internal part of interactjs and is not meant +to be used independently as each update may introduce breaking changes +

diff --git a/packages/@interactjs/utils/arr.ts b/packages/@interactjs/utils/arr.ts new file mode 100644 index 000000000..660a6276f --- /dev/null +++ b/packages/@interactjs/utils/arr.ts @@ -0,0 +1,27 @@ +type Filter = (element: T, index: number, array: T[]) => boolean + +export const contains = (array: T[], target: T) => array.indexOf(target) !== -1 + +export const remove = (array: T[], target: T) => array.splice(array.indexOf(target), 1) + +export const merge = (target: Array, source: U[]) => { + for (const item of source) { + target.push(item) + } + + return target +} + +export const from = (source: ArrayLike) => merge([] as T[], source as T[]) + +export const findIndex = (array: T[], func: Filter) => { + for (let i = 0; i < array.length; i++) { + if (func(array[i], i, array)) { + return i + } + } + + return -1 +} + +export const find = (array: T[], func: Filter): T | undefined => array[findIndex(array, func)] diff --git a/packages/@interactjs/utils/browser.ts b/packages/@interactjs/utils/browser.ts new file mode 100644 index 000000000..43af8fdb8 --- /dev/null +++ b/packages/@interactjs/utils/browser.ts @@ -0,0 +1,85 @@ +import domObjects from './domObjects' +import is from './is' + +const browser = { + init, + supportsTouch: null as boolean, + supportsPointerEvent: null as boolean, + isIOS7: null as boolean, + isIOS: null as boolean, + isIe9: null as boolean, + isOperaMobile: null as boolean, + prefixedMatchesSelector: null as 'matches', + pEventTypes: null as { + up: string + down: string + over: string + out: string + move: string + cancel: string + }, + wheelEvent: null as string, +} + +function init(window: any) { + const Element = domObjects.Element + const navigator: Partial = window.navigator || {} + + // Does the browser support touch input? + browser.supportsTouch = + 'ontouchstart' in window || + (is.func(window.DocumentTouch) && domObjects.document instanceof window.DocumentTouch) + + // Does the browser support PointerEvents + // https://github.com/taye/interact.js/issues/703#issuecomment-471570492 + browser.supportsPointerEvent = (navigator as any).pointerEnabled !== false && !!domObjects.PointerEvent + + browser.isIOS = /iP(hone|od|ad)/.test(navigator.platform) + + // scrolling doesn't change the result of getClientRects on iOS 7 + browser.isIOS7 = /iP(hone|od|ad)/.test(navigator.platform) && /OS 7[^\d]/.test(navigator.appVersion) + + browser.isIe9 = /MSIE 9/.test(navigator.userAgent) + + // Opera Mobile must be handled differently + browser.isOperaMobile = + navigator.appName === 'Opera' && browser.supportsTouch && /Presto/.test(navigator.userAgent) + + // prefix matchesSelector + browser.prefixedMatchesSelector = ( + 'matches' in Element.prototype + ? 'matches' + : 'webkitMatchesSelector' in Element.prototype + ? 'webkitMatchesSelector' + : 'mozMatchesSelector' in Element.prototype + ? 'mozMatchesSelector' + : 'oMatchesSelector' in Element.prototype + ? 'oMatchesSelector' + : 'msMatchesSelector' + ) as 'matches' + + browser.pEventTypes = browser.supportsPointerEvent + ? domObjects.PointerEvent === window.MSPointerEvent + ? { + up: 'MSPointerUp', + down: 'MSPointerDown', + over: 'mouseover', + out: 'mouseout', + move: 'MSPointerMove', + cancel: 'MSPointerCancel', + } + : { + up: 'pointerup', + down: 'pointerdown', + over: 'pointerover', + out: 'pointerout', + move: 'pointermove', + cancel: 'pointercancel', + } + : null + + // because Webkit and Opera still use 'mousewheel' event type + browser.wheelEvent = domObjects.document && 'onmousewheel' in domObjects.document ? 'mousewheel' : 'wheel' +} + +export default browser diff --git a/packages/@interactjs/utils/center.ts b/packages/@interactjs/utils/center.ts new file mode 100644 index 000000000..d0491922f --- /dev/null +++ b/packages/@interactjs/utils/center.ts @@ -0,0 +1,6 @@ +import type { Rect } from '@interactjs/core/types' + +export default (rect: Rect) => ({ + x: rect.left + (rect.right - rect.left) / 2, + y: rect.top + (rect.bottom - rect.top) / 2, +}) diff --git a/packages/@interactjs/utils/clone.ts b/packages/@interactjs/utils/clone.ts new file mode 100644 index 000000000..8c64cd0f0 --- /dev/null +++ b/packages/@interactjs/utils/clone.ts @@ -0,0 +1,21 @@ +import * as arr from './arr' +import is from './is' + +// tslint:disable-next-line ban-types +export default function clone(source: T): Partial { + const dest = {} as Partial + + for (const prop in source) { + const value = source[prop] + + if (is.plainObject(value)) { + dest[prop] = clone(value) as any + } else if (is.array(value)) { + dest[prop] = arr.from(value) as typeof value + } else { + dest[prop] = value + } + } + + return dest +} diff --git a/packages/@interactjs/utils/displace.stub.ts b/packages/@interactjs/utils/displace.stub.ts new file mode 100644 index 000000000..b1c6ea436 --- /dev/null +++ b/packages/@interactjs/utils/displace.stub.ts @@ -0,0 +1 @@ +export default {} diff --git a/packages/@interactjs/utils/displace.ts b/packages/@interactjs/utils/displace.ts new file mode 100644 index 000000000..b1c6ea436 --- /dev/null +++ b/packages/@interactjs/utils/displace.ts @@ -0,0 +1 @@ +export default {} diff --git a/packages/@interactjs/utils/domObjects.ts b/packages/@interactjs/utils/domObjects.ts new file mode 100644 index 000000000..e506d2cfc --- /dev/null +++ b/packages/@interactjs/utils/domObjects.ts @@ -0,0 +1,45 @@ +const domObjects: { + init: any + document: Document + DocumentFragment: typeof DocumentFragment + SVGElement: typeof SVGElement + SVGSVGElement: typeof SVGSVGElement + SVGElementInstance: any + Element: typeof Element + HTMLElement: typeof HTMLElement + Event: typeof Event + Touch: typeof Touch + PointerEvent: typeof PointerEvent +} = { + init, + document: null, + DocumentFragment: null, + SVGElement: null, + SVGSVGElement: null, + SVGElementInstance: null, + Element: null, + HTMLElement: null, + Event: null, + Touch: null, + PointerEvent: null, +} + +function blank() {} + +export default domObjects + +function init(window: Window) { + const win = window as any + + domObjects.document = win.document + domObjects.DocumentFragment = win.DocumentFragment || blank + domObjects.SVGElement = win.SVGElement || blank + domObjects.SVGSVGElement = win.SVGSVGElement || blank + domObjects.SVGElementInstance = win.SVGElementInstance || blank + domObjects.Element = win.Element || blank + domObjects.HTMLElement = win.HTMLElement || domObjects.Element + + domObjects.Event = win.Event + domObjects.Touch = win.Touch || blank + domObjects.PointerEvent = win.PointerEvent || win.MSPointerEvent +} diff --git a/packages/@interactjs/utils/domUtils.spec.ts b/packages/@interactjs/utils/domUtils.spec.ts new file mode 100644 index 000000000..90c5112fb --- /dev/null +++ b/packages/@interactjs/utils/domUtils.spec.ts @@ -0,0 +1,86 @@ +import domObjects from './domObjects' +import { indexOfDeepestElement } from './domUtils' + +interface MockNode { + name: string + lastChild: any + parentNode: MockNode | null + ownerDocument: MockNode | null + host?: MockNode +} + +test('utils/domUtils/indexOfDeepestElement', () => { + document.body.innerHTML = `
+
+
+
+
` + + domObjects.init(document) + + const ownerDocument: MockNode = { + name: 'Owner Document', + lastChild: null as any, + parentNode: null, + ownerDocument: null, + } + const html: MockNode = { + name: 'html', + lastChild: null as any, + ownerDocument, + parentNode: ownerDocument, + } + + const body: MockNode = { name: 'body', lastChild: null, ownerDocument, parentNode: html } + const wrapper: MockNode = { name: 'wrapper', ownerDocument, parentNode: body, lastChild: null } + const a: MockNode = { name: 'a', ownerDocument, parentNode: wrapper, lastChild: null } + const b1: MockNode = { name: 'b1', ownerDocument, parentNode: a, lastChild: null } + const b2: MockNode = { name: 'b2', ownerDocument, parentNode: a, lastChild: null } + const c1: MockNode = { name: 'c1', ownerDocument, parentNode: b1, lastChild: null } + const c2: MockNode = { name: 'c2', ownerDocument, parentNode: b1, lastChild: null } + const d1: MockNode = { name: 'd1', ownerDocument, parentNode: c1, lastChild: null } + const d1Comp: MockNode = { name: 'd1_comp', ownerDocument, parentNode: d1, lastChild: null } + const d2Shadow: MockNode = { + name: 'd2_shadow', + ownerDocument, + parentNode: null, + lastChild: null, + host: d1Comp, + } + + ownerDocument.lastChild = html + html.lastChild = body + body.lastChild = wrapper + a.lastChild = b2 + b1.lastChild = c2 + b2.lastChild = null + c1.lastChild = d1 + c2.lastChild = null + d1.lastChild = d1 + wrapper.lastChild = a + + const deepestShadow = [null, d2Shadow, c1, b1, a] as unknown as HTMLElement[] + expect(indexOfDeepestElement(deepestShadow)).toBe(deepestShadow.indexOf(d2Shadow as any)) + + const noShadow = [null, d1, c1, b1] as unknown as HTMLElement[] + + // only chooses elements that are passed in + expect(indexOfDeepestElement(noShadow)).toBe(noShadow.indexOf(d1 as any)) + + const siblings: NodeListOf = document.querySelectorAll('#topDiv > *') + + // last sibling is deepest with equal zIndex + expect(indexOfDeepestElement(siblings)).toBe(2) + + siblings[0].style.zIndex = '2' + siblings[1].style.zIndex = '2' + siblings[2].style.zIndex = '1' + + // works with shadow root + // sibling with higher z-index is selected + expect(indexOfDeepestElement(siblings)).toBe(1) + + const nodeWithoutParent: MockNode = { name: 'd1', ownerDocument, parentNode: null, lastChild: null } + const brokenElementCollection = [nodeWithoutParent, d1, c2] as unknown as HTMLElement[] + expect(indexOfDeepestElement(brokenElementCollection)).toBe(0) +}) diff --git a/packages/@interactjs/utils/domUtils.ts b/packages/@interactjs/utils/domUtils.ts new file mode 100644 index 000000000..97ef7d949 --- /dev/null +++ b/packages/@interactjs/utils/domUtils.ts @@ -0,0 +1,262 @@ +import type { Rect, Target, Element } from '@interactjs/core/types' + +import browser from './browser' +import domObjects from './domObjects' +import is from './is' +import * as win from './window' + +export function nodeContains(parent: Node, child: Node) { + if (parent.contains) { + return parent.contains(child as Node) + } + + while (child) { + if (child === parent) { + return true + } + + child = (child as Node).parentNode + } + + return false +} + +export function closest(element: Node, selector: string) { + while (is.element(element)) { + if (matchesSelector(element, selector)) { + return element + } + + element = parentNode(element) + } + + return null +} + +export function parentNode(node: Node | Document) { + let parent = node.parentNode + + if (is.docFrag(parent)) { + // skip past #shado-root fragments + // tslint:disable-next-line + while ((parent = (parent as any).host) && is.docFrag(parent)) { + continue + } + + return parent + } + + return parent +} + +export function matchesSelector(element: Element, selector: string) { + // remove /deep/ from selectors if shadowDOM polyfill is used + if (win.window !== win.realWindow) { + selector = selector.replace(/\/deep\//g, ' ') + } + + return element[browser.prefixedMatchesSelector](selector) +} + +const getParent = (el: Node | Document | ShadowRoot) => el.parentNode || (el as ShadowRoot).host + +// Test for the element that's "above" all other qualifiers +export function indexOfDeepestElement(elements: Element[] | NodeListOf) { + let deepestNodeParents: Node[] = [] + let deepestNodeIndex: number + + for (let i = 0; i < elements.length; i++) { + const currentNode = elements[i] + const deepestNode: Node = elements[deepestNodeIndex] + + // node may appear in elements array multiple times + if (!currentNode || i === deepestNodeIndex) { + continue + } + + if (!deepestNode) { + deepestNodeIndex = i + continue + } + + const currentNodeParent = getParent(currentNode) + const deepestNodeParent = getParent(deepestNode) + + // check if the deepest or current are document.documentElement/rootElement + // - if the current node is, do nothing and continue + if (currentNodeParent === currentNode.ownerDocument) { + continue + } + // - if deepest is, update with the current node and continue to next + else if (deepestNodeParent === currentNode.ownerDocument) { + deepestNodeIndex = i + continue + } + + // compare zIndex of siblings + if (currentNodeParent === deepestNodeParent) { + if (zIndexIsHigherThan(currentNode, deepestNode)) { + deepestNodeIndex = i + } + + continue + } + + // populate the ancestry array for the latest deepest node + deepestNodeParents = deepestNodeParents.length ? deepestNodeParents : getNodeParents(deepestNode) + + let ancestryStart: Node + + // if the deepest node is an HTMLElement and the current node is a non root svg element + if ( + deepestNode instanceof domObjects.HTMLElement && + currentNode instanceof domObjects.SVGElement && + !(currentNode instanceof domObjects.SVGSVGElement) + ) { + // TODO: is this check necessary? Was this for HTML elements embedded in SVG? + if (currentNode === deepestNodeParent) { + continue + } + + ancestryStart = currentNode.ownerSVGElement + } else { + ancestryStart = currentNode + } + + const currentNodeParents = getNodeParents(ancestryStart, deepestNode.ownerDocument) + let commonIndex = 0 + + // get (position of closest common ancestor) + 1 + while ( + currentNodeParents[commonIndex] && + currentNodeParents[commonIndex] === deepestNodeParents[commonIndex] + ) { + commonIndex++ + } + + const parents = [ + currentNodeParents[commonIndex - 1], + currentNodeParents[commonIndex], + deepestNodeParents[commonIndex], + ] + + if (parents[0]) { + let child = parents[0].lastChild + + while (child) { + if (child === parents[1]) { + deepestNodeIndex = i + deepestNodeParents = currentNodeParents + + break + } else if (child === parents[2]) { + break + } + + child = child.previousSibling + } + } + } + + return deepestNodeIndex +} + +function getNodeParents(node: Node, limit?: Node) { + const parents: Node[] = [] + let parent: Node = node + let parentParent: Node + + while ((parentParent = getParent(parent)) && parent !== limit && parentParent !== parent.ownerDocument) { + parents.unshift(parent) + parent = parentParent + } + + return parents +} + +function zIndexIsHigherThan(higherNode: Node, lowerNode: Node) { + const higherIndex = parseInt(win.getWindow(higherNode).getComputedStyle(higherNode).zIndex, 10) || 0 + const lowerIndex = parseInt(win.getWindow(lowerNode).getComputedStyle(lowerNode).zIndex, 10) || 0 + + return higherIndex >= lowerIndex +} + +export function matchesUpTo(element: Element, selector: string, limit: Node) { + while (is.element(element)) { + if (matchesSelector(element, selector)) { + return true + } + + element = parentNode(element) as Element + + if (element === limit) { + return matchesSelector(element, selector) + } + } + + return false +} + +export function getActualElement(element: Element) { + return (element as any).correspondingUseElement || element +} + +export function getScrollXY(relevantWindow?: Window) { + relevantWindow = relevantWindow || win.window + return { + x: relevantWindow.scrollX || relevantWindow.document.documentElement.scrollLeft, + y: relevantWindow.scrollY || relevantWindow.document.documentElement.scrollTop, + } +} + +export function getElementClientRect(element: Element): Required { + const clientRect = + element instanceof domObjects.SVGElement ? element.getBoundingClientRect() : element.getClientRects()[0] + + return ( + clientRect && { + left: clientRect.left, + right: clientRect.right, + top: clientRect.top, + bottom: clientRect.bottom, + width: clientRect.width || clientRect.right - clientRect.left, + height: clientRect.height || clientRect.bottom - clientRect.top, + } + ) +} + +export function getElementRect(element: Element) { + const clientRect = getElementClientRect(element) + + if (!browser.isIOS7 && clientRect) { + const scroll = getScrollXY(win.getWindow(element)) + + clientRect.left += scroll.x + clientRect.right += scroll.x + clientRect.top += scroll.y + clientRect.bottom += scroll.y + } + + return clientRect +} + +export function getPath(node: Node | Document) { + const path = [] + + while (node) { + path.push(node) + node = parentNode(node) + } + + return path +} + +export function trySelector(value: Target) { + if (!is.string(value)) { + return false + } + + // an exception will be raised if it is invalid + domObjects.document.querySelector(value) + return true +} diff --git a/packages/@interactjs/utils/exchange.stub.ts b/packages/@interactjs/utils/exchange.stub.ts new file mode 100644 index 000000000..b1c6ea436 --- /dev/null +++ b/packages/@interactjs/utils/exchange.stub.ts @@ -0,0 +1 @@ +export default {} diff --git a/packages/@interactjs/utils/exchange.ts b/packages/@interactjs/utils/exchange.ts new file mode 100644 index 000000000..b1c6ea436 --- /dev/null +++ b/packages/@interactjs/utils/exchange.ts @@ -0,0 +1 @@ +export default {} diff --git a/packages/@interactjs/utils/extend.ts b/packages/@interactjs/utils/extend.ts new file mode 100644 index 000000000..d513fbbdd --- /dev/null +++ b/packages/@interactjs/utils/extend.ts @@ -0,0 +1,9 @@ +export default function extend(dest: U & Partial, source: T): T & U { + for (const prop in source) { + ;(dest as unknown as T)[prop] = source[prop] + } + + const ret = dest as T & U + + return ret +} diff --git a/packages/@interactjs/utils/getOriginXY.ts b/packages/@interactjs/utils/getOriginXY.ts new file mode 100644 index 000000000..61bf8606d --- /dev/null +++ b/packages/@interactjs/utils/getOriginXY.ts @@ -0,0 +1,18 @@ +import type { PerActionDefaults } from '@interactjs/core/options' +import type { ActionName, HasGetRect } from '@interactjs/core/types' + +import { rectToXY, resolveRectLike } from './rect' + +export default function getOriginXY( + target: HasGetRect & { options: PerActionDefaults }, + element: Node, + actionName?: ActionName, +) { + const actionOptions = actionName && (target.options as any)[actionName] + const actionOrigin = actionOptions && actionOptions.origin + const origin = actionOrigin || target.options.origin + + const originRect = resolveRectLike(origin, target, element, [target && element]) + + return rectToXY(originRect) || { x: 0, y: 0 } +} diff --git a/packages/@interactjs/utils/hypot.ts b/packages/@interactjs/utils/hypot.ts new file mode 100644 index 000000000..422c74b3f --- /dev/null +++ b/packages/@interactjs/utils/hypot.ts @@ -0,0 +1 @@ +export default (x: number, y: number) => Math.sqrt(x * x + y * y) diff --git a/packages/@interactjs/utils/is.ts b/packages/@interactjs/utils/is.ts new file mode 100644 index 000000000..d235f62fc --- /dev/null +++ b/packages/@interactjs/utils/is.ts @@ -0,0 +1,47 @@ +import isWindow from './isWindow' +import * as win from './window' + +const window = (thing: any): thing is Window => thing === win.window || isWindow(thing) + +const docFrag = (thing: any): thing is DocumentFragment => object(thing) && thing.nodeType === 11 + +const object = (thing: any): thing is { [index: string]: any } => !!thing && typeof thing === 'object' + +const func = (thing: any): thing is (...args: any[]) => any => typeof thing === 'function' + +const number = (thing: any): thing is number => typeof thing === 'number' + +const bool = (thing: any): thing is boolean => typeof thing === 'boolean' + +const string = (thing: any): thing is string => typeof thing === 'string' + +const element = (thing: any): thing is HTMLElement | SVGElement => { + if (!thing || typeof thing !== 'object') { + return false + } + + const _window = win.getWindow(thing) || win.window + + return /object|function/.test(typeof Element) + ? thing instanceof Element || thing instanceof _window.Element + : thing.nodeType === 1 && typeof thing.nodeName === 'string' +} + +const plainObject: typeof object = (thing: any): thing is { [index: string]: any } => + object(thing) && !!thing.constructor && /function Object\b/.test(thing.constructor.toString()) + +const array = (thing: any): thing is T[] => + object(thing) && typeof thing.length !== 'undefined' && func(thing.splice) + +export default { + window, + docFrag, + object, + func, + number, + bool, + string, + element, + plainObject, + array, +} diff --git a/packages/@interactjs/utils/isNonNativeEvent.ts b/packages/@interactjs/utils/isNonNativeEvent.ts new file mode 100644 index 000000000..9310de72d --- /dev/null +++ b/packages/@interactjs/utils/isNonNativeEvent.ts @@ -0,0 +1,15 @@ +import type { Actions } from '@interactjs/core/types' + +export default function isNonNativeEvent(type: string, actions: Actions) { + if (actions.phaselessTypes[type]) { + return true + } + + for (const name in actions.map) { + if (type.indexOf(name) === 0 && type.substr(name.length) in actions.phases) { + return true + } + } + + return false +} diff --git a/packages/@interactjs/utils/isWindow.ts b/packages/@interactjs/utils/isWindow.ts new file mode 100644 index 000000000..ae6768dce --- /dev/null +++ b/packages/@interactjs/utils/isWindow.ts @@ -0,0 +1 @@ +export default (thing: any) => !!(thing && thing.Window) && thing instanceof thing.Window diff --git a/packages/@interactjs/utils/misc.ts b/packages/@interactjs/utils/misc.ts new file mode 100644 index 000000000..612ef567f --- /dev/null +++ b/packages/@interactjs/utils/misc.ts @@ -0,0 +1,26 @@ +import type { ActionName, ActionProps } from '@interactjs/core/types' + +import { window } from './window' + +export function warnOnce(this: T, method: (...args: any[]) => any, message: string) { + let warned = false + + return function (this: T) { + if (!warned) { + ;(window as any).console.warn(message) + warned = true + } + + return method.apply(this, arguments) + } +} + +export function copyAction(dest: ActionProps, src: ActionProps) { + dest.name = src.name + dest.axis = src.axis + dest.edges = src.edges + + return dest +} + +export const sign = (n: number) => (n >= 0 ? 1 : -1) diff --git a/packages/@interactjs/utils/normalizeListeners.spec.ts b/packages/@interactjs/utils/normalizeListeners.spec.ts new file mode 100644 index 000000000..e69cbd187 --- /dev/null +++ b/packages/@interactjs/utils/normalizeListeners.spec.ts @@ -0,0 +1,70 @@ +import normalizeListeners from './normalizeListeners' + +test('utils/normalizeListeners', () => { + const a = () => {} + const b = () => {} + const c = () => {} + + // single type, single listener function + expect(normalizeListeners('type1', a)).toEqual({ + type1: [a], + }) + + // multiple types, single listener function + expect(normalizeListeners('type1 type2', a)).toEqual({ + type1: [a], + type2: [a], + }) + + // array of types equivalent to space separated string + expect(normalizeListeners('type1 type2', a)).toEqual(normalizeListeners(['type1', 'type2'], a)) + + // single type, multiple listener functions + expect(normalizeListeners('type1', [a, b])).toEqual({ + type1: [a, b], + }) + + // single type prefix, object of { suffix: [fn, ...] } + expect(normalizeListeners('prefix', { _1: [a, b], _2: [b, c] })).toEqual({ + prefix_1: [a, b], + prefix_2: [b, c], + }) + + // multiple type prefixes, single length array of { suffix: [fn, ...] } + expect(normalizeListeners('prefix1 prefix2', [{ _1: [a, b], _2: [b, c] }])).toEqual({ + prefix1_1: [a, b], + prefix1_2: [b, c], + prefix2_1: [a, b], + prefix2_2: [b, c], + }) + + // object of { suffix: [fn, ...] } as type arg + expect(normalizeListeners({ _1: [a, b], _2: [b, c] })).toEqual({ + _1: [a, b], + _2: [b, c], + }) + + // object of { "suffix1 suffix2": [fn, ...], ... } as type arg + expect(normalizeListeners({ '_1 _2': [a, b], _3: [b, c] })).toEqual({ + _1: [a, b], + _2: [a, b], + _3: [b, c], + }) + + // single type prefix, object of { "suffix1 suffix2": [fn, ...], ... } + expect(normalizeListeners('prefix', { '_1 _2': [a, b], _3: [b, c] })).toEqual({ + prefix_1: [a, b], + prefix_2: [a, b], + prefix_3: [b, c], + }) + + // filter + expect(normalizeListeners('ignore', [{ _1: a, '': b }], (type) => !type.startsWith('ignore'))).toEqual({}) + expect( + normalizeListeners( + { ignore: { _1: a }, ig: { nore: b }, allow: { _x: c } }, + undefined, + (type) => !type.startsWith('ignore'), + ), + ).toEqual({ allow_x: [c] }) +}) diff --git a/packages/@interactjs/utils/normalizeListeners.ts b/packages/@interactjs/utils/normalizeListeners.ts new file mode 100644 index 000000000..25d242a85 --- /dev/null +++ b/packages/@interactjs/utils/normalizeListeners.ts @@ -0,0 +1,53 @@ +import type { EventTypes, Listener, ListenersArg } from '@interactjs/core/types' + +import is from './is' + +export interface NormalizedListeners { + [type: string]: Listener[] +} + +export default function normalize( + type: EventTypes, + listeners?: ListenersArg | ListenersArg[] | null, + filter = (_typeOrPrefix: string) => true, + result?: NormalizedListeners, +): NormalizedListeners { + result = result || {} + + if (is.string(type) && type.search(' ') !== -1) { + type = split(type) + } + + if (is.array(type)) { + type.forEach((t) => normalize(t, listeners, filter, result)) + return result + } + + // before: type = [{ drag: () => {} }], listeners = undefined + // after: type = '' , listeners = [{ drag: () => {} }] + if (is.object(type)) { + listeners = type + type = '' + } + + if (is.func(listeners) && filter(type)) { + result[type] = result[type] || [] + result[type].push(listeners) + } else if (is.array(listeners)) { + for (const l of listeners) { + normalize(type, l, filter, result) + } + } else if (is.object(listeners)) { + for (const prefix in listeners) { + const combinedTypes = split(prefix).map((p) => `${type}${p}`) + + normalize(combinedTypes, listeners[prefix], filter, result) + } + } + + return result as NormalizedListeners +} + +function split(type: string) { + return type.trim().split(/ +/) +} diff --git a/packages/@interactjs/utils/package.json b/packages/@interactjs/utils/package.json new file mode 100644 index 000000000..642751258 --- /dev/null +++ b/packages/@interactjs/utils/package.json @@ -0,0 +1,19 @@ +{ + "name": "@interactjs/utils", + "version": "1.10.27", + "type": "module", + "repository": { + "type": "git", + "url": "https://github.com/taye/interact.js.git", + "directory": "packages/@interactjs/utils" + }, + "peerDependencies": { + "@interactjs/feedback": "1.10.27", + "@interactjs/symbol-tree": "1.10.27" + }, + "publishConfig": { + "access": "public" + }, + "sideEffects": false, + "license": "MIT" +} diff --git a/packages/@interactjs/utils/pointerExtend.ts b/packages/@interactjs/utils/pointerExtend.ts new file mode 100644 index 000000000..ae2401426 --- /dev/null +++ b/packages/@interactjs/utils/pointerExtend.ts @@ -0,0 +1,25 @@ +const VENDOR_PREFIXES = ['webkit', 'moz'] + +export default function pointerExtend(dest: Partial }>, source: T) { + dest.__set ||= {} as any + + for (const prop in source) { + // skip deprecated prefixed properties + if (VENDOR_PREFIXES.some((prefix) => prop.indexOf(prefix) === 0)) continue + + if (typeof dest[prop] !== 'function' && prop !== '__set') { + Object.defineProperty(dest, prop, { + get() { + if (prop in dest.__set) return dest.__set[prop] + + return (dest.__set[prop] = source[prop] as any) + }, + set(value: any) { + dest.__set[prop] = value + }, + configurable: true, + }) + } + } + return dest +} diff --git a/packages/@interactjs/utils/pointerUtils.ts b/packages/@interactjs/utils/pointerUtils.ts new file mode 100644 index 000000000..3858f6826 --- /dev/null +++ b/packages/@interactjs/utils/pointerUtils.ts @@ -0,0 +1,289 @@ +import type { InteractEvent } from '@interactjs/core/InteractEvent' +import type { CoordsSetMember, PointerType, Point, PointerEventType, Element } from '@interactjs/core/types' + +import browser from './browser' +import dom from './domObjects' +import * as domUtils from './domUtils' +import hypot from './hypot' +import is from './is' +import pointerExtend from './pointerExtend' + +export function copyCoords(dest: CoordsSetMember, src: CoordsSetMember) { + dest.page = dest.page || ({} as any) + dest.page.x = src.page.x + dest.page.y = src.page.y + + dest.client = dest.client || ({} as any) + dest.client.x = src.client.x + dest.client.y = src.client.y + + dest.timeStamp = src.timeStamp +} + +export function setCoordDeltas(targetObj: CoordsSetMember, prev: CoordsSetMember, cur: CoordsSetMember) { + targetObj.page.x = cur.page.x - prev.page.x + targetObj.page.y = cur.page.y - prev.page.y + targetObj.client.x = cur.client.x - prev.client.x + targetObj.client.y = cur.client.y - prev.client.y + targetObj.timeStamp = cur.timeStamp - prev.timeStamp +} + +export function setCoordVelocity(targetObj: CoordsSetMember, delta: CoordsSetMember) { + const dt = Math.max(delta.timeStamp / 1000, 0.001) + + targetObj.page.x = delta.page.x / dt + targetObj.page.y = delta.page.y / dt + targetObj.client.x = delta.client.x / dt + targetObj.client.y = delta.client.y / dt + targetObj.timeStamp = dt +} + +export function setZeroCoords(targetObj: CoordsSetMember) { + targetObj.page.x = 0 + targetObj.page.y = 0 + targetObj.client.x = 0 + targetObj.client.y = 0 +} + +export function isNativePointer(pointer: any) { + return pointer instanceof dom.Event || pointer instanceof dom.Touch +} + +// Get specified X/Y coords for mouse or event.touches[0] +export function getXY(type: string, pointer: PointerType | InteractEvent, xy: Point) { + xy = xy || ({} as Point) + type = type || 'page' + + xy.x = pointer[(type + 'X') as 'pageX'] + xy.y = pointer[(type + 'Y') as 'pageY'] + + return xy +} + +export function getPageXY(pointer: PointerType | InteractEvent, page?: Point) { + page = page || { x: 0, y: 0 } + + // Opera Mobile handles the viewport and scrolling oddly + if (browser.isOperaMobile && isNativePointer(pointer)) { + getXY('screen', pointer, page) + + page.x += window.scrollX + page.y += window.scrollY + } else { + getXY('page', pointer, page) + } + + return page +} + +export function getClientXY(pointer: PointerType, client: Point) { + client = client || ({} as any) + + if (browser.isOperaMobile && isNativePointer(pointer)) { + // Opera Mobile handles the viewport and scrolling oddly + getXY('screen', pointer, client) + } else { + getXY('client', pointer, client) + } + + return client +} + +export function getPointerId(pointer: { pointerId?: number; identifier?: number; type?: string }) { + return is.number(pointer.pointerId) ? pointer.pointerId! : pointer.identifier! +} + +export function setCoords(dest: CoordsSetMember, pointers: any[], timeStamp: number) { + const pointer = pointers.length > 1 ? pointerAverage(pointers) : pointers[0] + + getPageXY(pointer, dest.page) + getClientXY(pointer, dest.client) + + dest.timeStamp = timeStamp +} + +export function getTouchPair(event: TouchEvent | PointerType[]) { + const touches: PointerType[] = [] + + // array of touches is supplied + if (is.array(event)) { + touches[0] = event[0] + touches[1] = event[1] + } + // an event + else { + if (event.type === 'touchend') { + if (event.touches.length === 1) { + touches[0] = event.touches[0] + touches[1] = event.changedTouches[0] + } else if (event.touches.length === 0) { + touches[0] = event.changedTouches[0] + touches[1] = event.changedTouches[1] + } + } else { + touches[0] = event.touches[0] + touches[1] = event.touches[1] + } + } + + return touches +} + +export function pointerAverage(pointers: PointerType[]) { + const average = { + pageX: 0, + pageY: 0, + clientX: 0, + clientY: 0, + screenX: 0, + screenY: 0, + } + + type CoordKeys = keyof typeof average + + for (const pointer of pointers) { + for (const prop in average) { + average[prop as CoordKeys] += pointer[prop as CoordKeys] + } + } + for (const prop in average) { + average[prop as CoordKeys] /= pointers.length + } + + return average +} + +export function touchBBox(event: PointerType[]) { + if (!event.length) { + return null + } + + const touches = getTouchPair(event) + const minX = Math.min(touches[0].pageX, touches[1].pageX) + const minY = Math.min(touches[0].pageY, touches[1].pageY) + const maxX = Math.max(touches[0].pageX, touches[1].pageX) + const maxY = Math.max(touches[0].pageY, touches[1].pageY) + + return { + x: minX, + y: minY, + left: minX, + top: minY, + right: maxX, + bottom: maxY, + width: maxX - minX, + height: maxY - minY, + } +} + +export function touchDistance(event: PointerType[] | TouchEvent, deltaSource: string) { + const sourceX = (deltaSource + 'X') as 'pageX' + const sourceY = (deltaSource + 'Y') as 'pageY' + const touches = getTouchPair(event) + + const dx = touches[0][sourceX] - touches[1][sourceX] + const dy = touches[0][sourceY] - touches[1][sourceY] + + return hypot(dx, dy) +} + +export function touchAngle(event: PointerType[] | TouchEvent, deltaSource: string) { + const sourceX = (deltaSource + 'X') as 'pageX' + const sourceY = (deltaSource + 'Y') as 'pageY' + const touches = getTouchPair(event) + const dx = touches[1][sourceX] - touches[0][sourceX] + const dy = touches[1][sourceY] - touches[0][sourceY] + const angle = (180 * Math.atan2(dy, dx)) / Math.PI + + return angle +} + +export function getPointerType(pointer: { pointerType?: string; identifier?: number; type?: string }) { + return is.string(pointer.pointerType) + ? pointer.pointerType + : is.number(pointer.pointerType) + ? [undefined, undefined, 'touch', 'pen', 'mouse'][pointer.pointerType]! + : // if the PointerEvent API isn't available, then the "pointer" must + // be either a MouseEvent, TouchEvent, or Touch object + /touch/.test(pointer.type || '') || pointer instanceof dom.Touch + ? 'touch' + : 'mouse' +} + +// [ event.target, event.currentTarget ] +export function getEventTargets(event: Event) { + const path = is.func(event.composedPath) + ? (event.composedPath() as Element[]) + : (event as unknown as { path: Element[] }).path + + return [ + domUtils.getActualElement(path ? path[0] : (event.target as Element)), + domUtils.getActualElement(event.currentTarget as Element), + ] +} + +export function newCoords(): CoordsSetMember { + return { + page: { x: 0, y: 0 }, + client: { x: 0, y: 0 }, + timeStamp: 0, + } +} + +export function coordsToEvent(coords: MockCoords) { + const event = { + coords, + get page() { + return this.coords.page + }, + get client() { + return this.coords.client + }, + get timeStamp() { + return this.coords.timeStamp + }, + get pageX() { + return this.coords.page.x + }, + get pageY() { + return this.coords.page.y + }, + get clientX() { + return this.coords.client.x + }, + get clientY() { + return this.coords.client.y + }, + get pointerId() { + return this.coords.pointerId + }, + get target() { + return this.coords.target + }, + get type() { + return this.coords.type + }, + get pointerType() { + return this.coords.pointerType + }, + get buttons() { + return this.coords.buttons + }, + preventDefault() {}, + } + + return event as typeof event & PointerType & PointerEventType +} + +export interface MockCoords { + page: Point + client: Point + timeStamp?: number + pointerId?: any + target?: any + type?: string + pointerType?: string + buttons?: number +} + +export { pointerExtend } diff --git a/packages/@interactjs/utils/raf.ts b/packages/@interactjs/utils/raf.ts new file mode 100644 index 000000000..0a4ab70e2 --- /dev/null +++ b/packages/@interactjs/utils/raf.ts @@ -0,0 +1,44 @@ +let lastTime = 0 +let request: typeof requestAnimationFrame +let cancel: typeof cancelAnimationFrame + +function init(global: Window | typeof globalThis) { + request = global.requestAnimationFrame + cancel = global.cancelAnimationFrame + + if (!request) { + const vendors = ['ms', 'moz', 'webkit', 'o'] + + for (const vendor of vendors) { + request = global[`${vendor}RequestAnimationFrame` as 'requestAnimationFrame'] + cancel = + global[`${vendor}CancelAnimationFrame` as 'cancelAnimationFrame'] || + global[`${vendor}CancelRequestAnimationFrame` as 'cancelAnimationFrame'] + } + } + + request = request && request.bind(global) + cancel = cancel && cancel.bind(global) + + if (!request) { + request = (callback) => { + const currTime = Date.now() + const timeToCall = Math.max(0, 16 - (currTime - lastTime)) + const token = global.setTimeout(() => { + // eslint-disable-next-line n/no-callback-literal + callback(currTime + timeToCall) + }, timeToCall) + + lastTime = currTime + timeToCall + return token as any + } + + cancel = (token) => clearTimeout(token) + } +} + +export default { + request: (callback: FrameRequestCallback) => request(callback), + cancel: (token: number) => cancel(token), + init, +} diff --git a/packages/@interactjs/utils/rect.ts b/packages/@interactjs/utils/rect.ts new file mode 100644 index 000000000..4d66436dc --- /dev/null +++ b/packages/@interactjs/utils/rect.ts @@ -0,0 +1,106 @@ +import type { + HasGetRect, + RectResolvable, + Rect, + Element, + Point, + FullRect, + EdgeOptions, +} from '@interactjs/core/types' + +import { closest, getElementRect, parentNode } from './domUtils' +import extend from './extend' +import is from './is' + +export function getStringOptionResult(value: any, target: HasGetRect, element: Node) { + if (value === 'parent') { + return parentNode(element) + } + + if (value === 'self') { + return target.getRect(element as Element) + } + + return closest(element, value) +} + +export function resolveRectLike( + value: RectResolvable, + target?: HasGetRect, + element?: Node, + functionArgs?: T, +) { + let returnValue: any = value + if (is.string(returnValue)) { + returnValue = getStringOptionResult(returnValue, target, element) + } else if (is.func(returnValue)) { + returnValue = returnValue(...functionArgs) + } + + if (is.element(returnValue)) { + returnValue = getElementRect(returnValue) + } + + return returnValue as Rect +} + +export function toFullRect(rect: Rect): FullRect { + const { top, left, bottom, right } = rect + const width = rect.width ?? rect.right - rect.left + const height = rect.height ?? rect.bottom - rect.top + + return { top, left, bottom, right, width, height } +} + +export function rectToXY(rect: Rect | Point) { + return ( + rect && { + x: 'x' in rect ? rect.x : rect.left, + y: 'y' in rect ? rect.y : rect.top, + } + ) +} + +export function xywhToTlbr>(rect: T) { + if (rect && !('left' in rect && 'top' in rect)) { + rect = extend({}, rect) + + rect.left = rect.x || 0 + rect.top = rect.y || 0 + rect.right = rect.right || rect.left + rect.width + rect.bottom = rect.bottom || rect.top + rect.height + } + + return rect as Rect & T +} + +export function tlbrToXywh(rect: Rect & Partial) { + if (rect && !('x' in rect && 'y' in rect)) { + rect = extend({}, rect) + + rect.x = rect.left || 0 + rect.y = rect.top || 0 + rect.width = rect.width || (rect.right || 0) - rect.x + rect.height = rect.height || (rect.bottom || 0) - rect.y + } + + return rect as FullRect & Point +} + +export function addEdges(edges: EdgeOptions, rect: Rect, delta: Point) { + if (edges.left) { + rect.left += delta.x + } + if (edges.right) { + rect.right += delta.x + } + if (edges.top) { + rect.top += delta.y + } + if (edges.bottom) { + rect.bottom += delta.y + } + + rect.width = rect.right - rect.left + rect.height = rect.bottom - rect.top +} diff --git a/packages/@interactjs/utils/shallowEqual.ts b/packages/@interactjs/utils/shallowEqual.ts new file mode 100644 index 000000000..7cf0edcb9 --- /dev/null +++ b/packages/@interactjs/utils/shallowEqual.ts @@ -0,0 +1,23 @@ +export default function shallowEqual(left: any, right: any) { + if (left === right) { + return true + } + + if (!left || !right) { + return false + } + + const leftKeys = Object.keys(left) + + if (leftKeys.length !== Object.keys(right).length) { + return false + } + + for (const key of leftKeys) { + if (left[key] !== right[key]) { + return false + } + } + + return true +} diff --git a/packages/@interactjs/utils/window.ts b/packages/@interactjs/utils/window.ts new file mode 100644 index 000000000..08c0c6a9a --- /dev/null +++ b/packages/@interactjs/utils/window.ts @@ -0,0 +1,37 @@ +import isWindow from './isWindow' + +export let realWindow = undefined as Window + +let win = undefined as Window +export { win as window } + +export function init(window: Window & { wrap?: (...args: any[]) => any }) { + // get wrapped window if using Shadow DOM polyfill + + realWindow = window + + // create a TextNode + const el = window.document.createTextNode('') + + // check if it's wrapped by a polyfill + if (el.ownerDocument !== window.document && typeof window.wrap === 'function' && window.wrap(el) === el) { + // use wrapped window + window = window.wrap(window) + } + + win = window +} + +if (typeof window !== 'undefined' && !!window) { + init(window) +} + +export function getWindow(node: any) { + if (isWindow(node)) { + return node + } + + const rootNode = node.ownerDocument || node + + return rootNode.defaultView || win.window +} diff --git a/packages/interactjs/.npmignore b/packages/interactjs/.npmignore new file mode 100644 index 000000000..ee83689f9 --- /dev/null +++ b/packages/interactjs/.npmignore @@ -0,0 +1,6 @@ +*.ts +!*.d.ts +*.spec.ts +*.spec.js +dist/docs +guide diff --git a/packages/interactjs/LICENSE b/packages/interactjs/LICENSE new file mode 100644 index 000000000..e4854f77d --- /dev/null +++ b/packages/interactjs/LICENSE @@ -0,0 +1,23 @@ +Copyright (c) 2012-present Taye Adeyemi + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the Software +without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to +whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall +be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/interactjs/README.md b/packages/interactjs/README.md new file mode 100644 index 000000000..8b9b17063 --- /dev/null +++ b/packages/interactjs/README.md @@ -0,0 +1,112 @@ +interact.js + +

+ JavaScript drag and drop, resizing and multi-touch gestures with inertia and snapping for modern browsers (and also IE9+). +

+ +
+Gitter +jsDelivr +Build Status + +
+
+ +Features include: + + - **inertia** and **snapping** + - **multi-touch**, simultaneous interactions + - cross browser and device, supporting the **desktop and mobile** versions of + Chrome, Firefox and Opera as well as **Internet Explorer 9+** + - interaction with [**SVG**](http://interactjs.io/#use_in_svg_files) elements + - being **standalone and customizable** + - **not modifying the DOM** except to change the cursor (but you can disable + that) + +Installation +------------ + +* [npm](https://www.npmjs.org/): `npm install interactjs` +* [jsDelivr CDN](https://cdn.jsdelivr.net/npm/interactjs/): `` +* [unpkg CDN](https://unpkg.com/interactjs/): `` +* [Rails 5.1+](https://rubyonrails.org/): + 1. `yarn add interactjs` + 2. `//= require interactjs/interact` +* [Webjars SBT/Play 2](https://www.webjars.org/): `libraryDependencies ++= Seq("org.webjars.npm" % "interactjs" % version)` + +### Typescript definitions + +The project is written in Typescript and the npm package includes the type +definitions, but if you need the typings alone, you can install them with: + +``` +npm install --save-dev @interactjs/types +``` + +Documentation +------------- + +http://interactjs.io/docs + +Example +------- + +```javascript +var pixelSize = 16; + +interact('.rainbow-pixel-canvas') + .origin('self') + .draggable({ + modifiers: [ + interact.modifiers.snap({ + // snap to the corners of a grid + targets: [ + interact.snappers.grid({ x: pixelSize, y: pixelSize }), + ], + }) + ], + listeners: { + // draw colored squares on move + move: function (event) { + var context = event.target.getContext('2d'), + // calculate the angle of the drag direction + dragAngle = 180 * Math.atan2(event.dx, event.dy) / Math.PI; + + // set color based on drag angle and speed + context.fillStyle = 'hsl(' + dragAngle + ', 86%, ' + + (30 + Math.min(event.speed / 1000, 1) * 50) + '%)'; + + // draw squares + context.fillRect(event.pageX - pixelSize / 2, event.pageY - pixelSize / 2, + pixelSize, pixelSize); + } + } + }) + // clear the canvas on doubletap + .on('doubletap', function (event) { + var context = event.target.getContext('2d'); + + context.clearRect(0, 0, context.canvas.width, context.canvas.height); + }); + + function resizeCanvases () { + [].forEach.call(document.querySelectorAll('.rainbow-pixel-canvas'), function (canvas) { + canvas.width = document.body.clientWidth; + canvas.height = window.innerHeight * 0.7; + }); + } + + // interact.js can also add DOM event listeners + interact(document).on('DOMContentLoaded', resizeCanvases); + interact(window).on('resize', resizeCanvases); +``` + +See the above code in action at https://codepen.io/taye/pen/tCKAm + +License +------- + +interact.js is released under the [MIT License](http://taye.mit-license.org). + +[ijs-twitter]: https://twitter.com/interactjs +[upcoming-changes]: https://github.com/taye/interact.js/blob/main/CHANGELOG.md#upcoming-changes diff --git a/packages/interactjs/bower.json b/packages/interactjs/bower.json new file mode 100644 index 000000000..0d73a7b86 --- /dev/null +++ b/packages/interactjs/bower.json @@ -0,0 +1,42 @@ +{ + "name": "interactjs", + "main": "index.js", + "license": "SEE LICENSE AT https://interactjs.io/license", + "description": "Drag and drop, resizing and multi-touch gestures with inertia and snapping for modern browsers (and also IE9+)", + "homepage": "http://interactjs.io", + "authors": [ + { + "name": "Taye Adeyemi", + "email": "dev@taye.me", + "url": "http://taye.me" + } + ], + "keywords": [ + "interact.js", + "draggable", + "droppable", + "drag", + "drop", + "drag and drop", + "resize", + "touch", + "multi-touch", + "gesture", + "snap", + "inertia", + "grid", + "autoscroll", + "SVG" + ], + "moduleType": [ + "amd", + "globals", + "node" + ], + "ignore": [ + "/*", + "!src", + "!dist", + "!LICENSE" + ] +} diff --git a/packages/interactjs/index.d.ts b/packages/interactjs/index.d.ts new file mode 100644 index 000000000..ff69e7901 --- /dev/null +++ b/packages/interactjs/index.d.ts @@ -0,0 +1,3 @@ +import '@interactjs/types' +import interact from '@interactjs/interactjs/index' +export default interact diff --git a/packages/interactjs/index.ts b/packages/interactjs/index.ts new file mode 100644 index 000000000..68d8fa73e --- /dev/null +++ b/packages/interactjs/index.ts @@ -0,0 +1,12 @@ +// eslint-disable-next-line import/no-extraneous-dependencies +import interact from '@interactjs/interactjs' + +export default interact + +if (typeof module === 'object' && !!module) { + try { + module.exports = interact + } catch {} +} + +;(interact as any).default = interact diff --git a/packages/interactjs/package.json b/packages/interactjs/package.json new file mode 100644 index 000000000..3cd21f27e --- /dev/null +++ b/packages/interactjs/package.json @@ -0,0 +1,48 @@ +{ + "name": "interactjs", + "version": "1.10.27", + "main": "dist/interact.min.js", + "typings": "index.d.ts", + "description": "Drag and drop, resizing and multi-touch gestures with inertia and snapping for modern browsers (and also IE9+)", + "homepage": "https://interactjs.io", + "authors": [ + { + "name": "Taye Adeyemi", + "email": "dev@taye.me", + "url": "https://taye.me" + } + ], + "repository": { + "type": "git", + "url": "https://github.com/taye/interact.js.git" + }, + "keywords": [ + "interact.js", + "draggable", + "droppable", + "drag", + "drop", + "drag and drop", + "resize", + "touch", + "multi-touch", + "gesture", + "snap", + "inertia", + "grid", + "autoscroll", + "SVG", + "interact" + ], + "scripts": { + "test": "cd ../; npm test" + }, + "dependencies": { + "@interactjs/types": "1.10.27" + }, + "sideEffects": [ + "**/index.js", + "**/index.prod.js" + ], + "license": "MIT" +} diff --git a/scripts/.eslintrc.cjs b/scripts/.eslintrc.cjs new file mode 100644 index 000000000..3630b3f0e --- /dev/null +++ b/scripts/.eslintrc.cjs @@ -0,0 +1,5 @@ +module.exports = { + extends: '../.eslintrc.cjs', + parserOptions: { sourceType: 'script', ecmaVersion: 2018 }, + rules: { 'import/no-extraneous-dependencies': 'off' }, +} diff --git a/scripts/addPluginIndexes.js b/scripts/addPluginIndexes.js new file mode 100644 index 000000000..b1ba73e98 --- /dev/null +++ b/scripts/addPluginIndexes.js @@ -0,0 +1,33 @@ +const fs = require('fs').promises +const path = require('path') + +const { mkdirp } = require('mkdirp') + +module.exports = (plugins) => { + return Promise.all( + plugins.map(async (modulePath) => { + const [scopePath] = modulePath.split('/') + const packagePath = path.join('packages', '@interactjs', scopePath) + const pluginPath = path.join('packages', '@interactjs', modulePath) + const dest = path.join(packagePath, path.dirname(path.relative(packagePath, pluginPath)), 'index.ts') + const destDir = path.dirname(dest) + + const pluginSpecifier = pluginPath.replace(/^packages./, '') + + await mkdirp(destDir) + + await fs.writeFile( + dest, + [ + '/* eslint-disable no-console, eol-last, import/no-duplicates, import/no-extraneous-dependencies, import/order */', + `import '${pluginSpecifier}'`, + "import interact from '@interactjs/interact/index'", + `import plugin from '${pluginSpecifier}'`, + 'interact.use(plugin)', + ].join('\n'), + ) + + console.log(`wrote ${dest}`) + }), + ) +} diff --git a/scripts/babel/absolute-imports.js b/scripts/babel/absolute-imports.js new file mode 100644 index 000000000..e22e32aaa --- /dev/null +++ b/scripts/babel/absolute-imports.js @@ -0,0 +1,45 @@ +const path = require('path') + +const resolveSync = require('resolve').sync + +const { getModuleDirectories, shouldIgnoreImport, getRelativeToRoot } = require('../utils') + +module.exports = function transformImportsToAbsolute() { + const fixImportSource = ({ node: { source } }, { opts, filename }) => { + if (!source || (opts.ignore && opts.ignore(filename, source.value))) return + + const { moduleDirectory = getModuleDirectories() } = opts + + if (shouldIgnoreImport(source.value)) return + + const { extension = '', prefix } = opts + const basedir = path.dirname(filename) + + let resolvedImport = '' + + resolvedImport = resolveSync(source.value, { + extensions: ['.ts', '.tsx', '.js'], + basedir, + moduleDirectory, + }) + + try { + const unrootedImport = getRelativeToRoot(resolvedImport, moduleDirectory, prefix).result + + source.value = extension === null ? unrootedImport : unrootedImport.replace(/\.[jt]sx?$/, extension) + } catch (error) { + source.value = resolveSync(source.value, { + basedir, + moduleDirectory, + }) + } + } + + return { + name: '@interactjs/_dev:absolute-imports', + visitor: { + ImportDeclaration: fixImportSource, + ExportNamedDeclaration: fixImportSource, + }, + } +} diff --git a/scripts/babel/inline-env-vars.js b/scripts/babel/inline-env-vars.js new file mode 100644 index 000000000..d21557f0f --- /dev/null +++ b/scripts/babel/inline-env-vars.js @@ -0,0 +1,22 @@ +module.exports = function transformInlineEnvironmentVariables({ types: t }) { + return { + name: '@interactjs/_dev:inline-env-vars', + visitor: { + // eslint-disable-next-line no-shadow + MemberExpression(path, { opts: { include, exclude, env } = {} }) { + if (path.get('object').matchesPattern('process.env')) { + const key = path.toComputedKey() + if ( + t.isStringLiteral(key) && + (!include || include.indexOf(key.value) !== -1) && + (!exclude || exclude.indexOf(key.value) === -1) + ) { + const name = key.value + const value = env && name in env ? env[name] : process.env[name] + path.replaceWith(t.valueToNode(value)) + } + } + }, + }, + } +} diff --git a/scripts/babel/relative-imports.js b/scripts/babel/relative-imports.js new file mode 100644 index 000000000..d2e45079e --- /dev/null +++ b/scripts/babel/relative-imports.js @@ -0,0 +1,50 @@ +const path = require('path') + +const resolveSync = require('resolve').sync + +const { getModuleDirectories, shouldIgnoreImport, getRelativeToRoot } = require('../utils') + +module.exports = function transformImportsToRelative() { + const fixImportSource = ({ node: { source } }, { opts, filename }) => { + if (!source || (opts.ignore && opts.ignore(filename))) return + + const { moduleDirectory = getModuleDirectories() } = opts + + if (shouldIgnoreImport(source.value, filename, moduleDirectory)) return + + const { extension = '.js' } = opts + + const basedir = path.dirname(getRelativeToRoot(filename, moduleDirectory).result) + let resolvedImport = '' + + for (const root of moduleDirectory) { + try { + resolvedImport = resolveSync(source.value, { + extensions: ['.ts', '.tsx'], + basedir: path.join(root, basedir), + moduleDirectory, + }) + break + } catch {} + } + + if (!resolvedImport) { + throw new Error(`Couldn't find module "${source.value}" from "${filename}"`) + } + + const relativeImport = path.relative(basedir, getRelativeToRoot(resolvedImport, moduleDirectory).result) + + const importWithDir = /^[./\\]/.test(relativeImport) ? relativeImport : `${path.sep}${relativeImport}` + + source.value = importWithDir.replace(/^\//, `.${path.sep}`).replace(/\.tsx?$/, extension) + } + + return { + name: '@interactjs/_dev:relative-imports', + visitor: { + ImportDeclaration: fixImportSource, + ExportNamedDeclaration: fixImportSource, + ExportAllDeclaration: fixImportSource, + }, + } +} diff --git a/scripts/babel/vue-sfc.js b/scripts/babel/vue-sfc.js new file mode 100644 index 000000000..f25fab03b --- /dev/null +++ b/scripts/babel/vue-sfc.js @@ -0,0 +1,61 @@ +const { parse, compileScript, compileStyle } = require('@vue/compiler-sfc') +const hash = require('hash-sum') + +module.exports = function transformVueSfc() { + return { + name: '@interactjs/_dev:vue-sfc', + parserOverride(source, options, babelParse) { + const { sourceFileName, filename = sourceFileName } = options + + if (!filename?.endsWith('.vue')) return + + const { code, map } = compileSfc(source, { filename, isProd: true }) + const newFilename = filename + '.ts' + + return babelParse(code, { + ...options, + inputSourceMap: map, + sourceFileName: newFilename, + filename: newFilename, + }) + }, + } +} + +function compileSfc(source, { filename, isProd = true }) { + const id = hash([filename, source].join('\0')) + const { descriptor: sfc, errors: parseErrors } = parse(source, { + filename, + sourceMap: !isProd, + }) + + if (parseErrors.length) throw parseErrors + + const script = compileScript(sfc, { id, inlineTemplate: true, isProd }) + const styles = sfc.styles.map((style) => + compileStyle({ + source: style.content, + filename, + id, + scoped: style.attrs.scoped, + isProd, + }), + ) + + return { + code: `${script.content}\n;${getStyleStatement(styles)}`, + map: script.map, + } +} + +function getStyleStatement(styles) { + if (!styles.length) return '' + + const css = styles.map((style) => style.code).join('\n') + // TODO: minify CSS + const html = `` + + return ['document.head.insertAdjacentHTML(', '"beforeEnd",', JSON.stringify(html), ')'].join('') +} + +module.exports.compileSfc = compileSfc diff --git a/scripts/bin/_check_deps.js b/scripts/bin/_check_deps.js new file mode 100644 index 000000000..9c3dc0102 --- /dev/null +++ b/scripts/bin/_check_deps.js @@ -0,0 +1,27 @@ +const fs = require('fs/promises') + +const { getPackageJsons, errorExit } = require('../utils') + +async function checkDeps () { + const packageJsons = await getPackageJsons() + const pkgNames = new Set(packageJsons.map(([, pkg]) => pkg.name)) + + Promise.all( + packageJsons.map(async ([p, pkg]) => { + for (const depField of ['dependencies', 'peerDependencies', 'devDependencies']) { + const missingDeps = Object.keys(pkg[depField] || {}).filter( + (depName) => depName.startsWith('@interactjs/') && !pkgNames.has(depName), + ) + + for (const depName of missingDeps) { + delete pkg[depField][depName] + console.warn(`tidying ${pkg.name} ${depField} โœ• ${depName}`) + } + } + + await fs.writeFile(p, JSON.stringify(pkg, null, 2) + '\n') + }), + ) +} + +checkDeps().catch(errorExit) diff --git a/scripts/bin/add_plugin_indexes.js b/scripts/bin/add_plugin_indexes.js new file mode 100644 index 000000000..db9f6e2c9 --- /dev/null +++ b/scripts/bin/add_plugin_indexes.js @@ -0,0 +1,28 @@ +const { isPro } = require('../utils') + +require('../addPluginIndexes')([ + 'actions/plugin', + 'actions/drag/plugin', + 'actions/drop/plugin', + 'actions/resize/plugin', + 'actions/gesture/plugin', + 'auto-scroll/plugin', + 'auto-start/plugin', + 'dev-tools/plugin', + 'inertia/plugin', + 'modifiers/plugin', + 'pointer-events/plugin', + 'reflow/plugin', + 'snappers/plugin', + ...(isPro + ? [ + 'react/plugin', + 'vue/plugin', + 'multi-target/plugin', + 'feedback/plugin', + 'clone/plugin', + 'arrange/plugin', + 'iframes/plugin', + ] + : []), +]) diff --git a/scripts/bin/bundle.js b/scripts/bin/bundle.js new file mode 100644 index 000000000..c2e1bc693 --- /dev/null +++ b/scripts/bin/bundle.js @@ -0,0 +1,21 @@ +const path = require('path') + +const bundler = require('../bundler') +const headers = require('../headers') +const { errorExit } = require('../utils') + +const [, , entry = 'packages/interactjs', name = 'interact'] = process.argv +const entryPkgDir = path.join(process.cwd(), entry) + +const options = { + headers, + entry: path.join(entryPkgDir, 'index.ts'), + destDir: path.join(entryPkgDir, 'dist'), + name, +} + +process.stdout.write('Bundling...') + +bundler(options) + .then(async (code) => console.log(' done.')) + .catch(errorExit) diff --git a/scripts/bin/clean.js b/scripts/bin/clean.js new file mode 100644 index 000000000..a1ee74fa4 --- /dev/null +++ b/scripts/bin/clean.js @@ -0,0 +1,30 @@ +const fs = require('fs') +const path = require('path') + +const shell = require('shelljs') + +const { getBuiltJsFiles } = require('../utils') + +console.log('removing typescript generated files.') +shell.exec('tsc -b types.tsconfig.json --clean') + +Promise.all([getBuiltJsFiles(), import('del').then((m) => m.deleteAsync)]).then(async ([filenames, del]) => { + console.log(`removing ${filenames.length} generated files and directories.`) + + await Promise.all( + filenames.map((filename) => { + return del(filename) + }), + ) + + // remove empty directories + const directories = [...new Set(filenames.map(path.dirname))].sort().reverse() + + for (const dir of directories) { + const files = await fs.promises.readdir(dir) + + if (!files.length) { + await del(dir) + } + } +}) diff --git a/scripts/bin/lint.js b/scripts/bin/lint.js new file mode 100644 index 000000000..fa6d50315 --- /dev/null +++ b/scripts/bin/lint.js @@ -0,0 +1,69 @@ +const { existsSync, promises: fs } = require('fs') + +const { ESLint } = require('eslint') +const { glob } = require('glob') +const prettier = require('prettier') +const yargs = require('yargs') + +const { lintSourcesGlob, lintIgnoreGlobs, errorExit } = require('../utils') + +const { fix, _: fileArgs } = yargs.boolean('fix').argv +const jsExt = /\.js$/ +const dtsExt = /\.d\.ts$/ + +main().catch(errorExit) + +async function main() { + const sources = fileArgs.length ? fileArgs : await getSources() + + console.log(`Linting ${sources.length} 'file${sources.length === 1 ? '' : 's'}...`) + + if (fix) { + await Promise.all(sources.map(formatWithPrettier)) + } + + const eslint = new ESLint({ + fix, + useEslintrc: true, + }) + const results = await eslint.lintFiles(sources) + const formatter = await eslint.loadFormatter('stylish') + + if (fix) { + await ESLint.outputFixes(results) + } + + console.log(formatter.format(results)) + + const hasUnfixedError = results.some((r) => r.errorCount > (fix ? r.fixableErrorCount : 0)) + + if (hasUnfixedError) { + throw new Error('unfixed errors remain') + } +} + +async function formatWithPrettier(filepath) { + const [source, config] = await Promise.all([ + fs.readFile(filepath).then((buffer) => buffer.toString()), + prettier.resolveConfig(filepath), + ]) + const output = await prettier.format(source, { ...config, filepath }) + + if (source !== output) await fs.writeFile(filepath, output) +} + +async function getSources() { + const sources = await glob(lintSourcesGlob, { + ignore: lintIgnoreGlobs, + silent: true, + }) + + return sources.filter((source) => !isGenerated(source)) +} + +function isGenerated(source) { + return ( + (dtsExt.test(source) && existsSync(source.replace(dtsExt, '.ts'))) || + (jsExt.test(source) && existsSync(source.replace(jsExt, '.ts'))) + ) +} diff --git a/scripts/bin/release.js b/scripts/bin/release.js new file mode 100644 index 000000000..4fb51a590 --- /dev/null +++ b/scripts/bin/release.js @@ -0,0 +1,168 @@ +const fs = require('fs').promises +const path = require('path') + +const shell = require('shelljs') + +const { getPackages, isPro, registryUrl, errorExit } = require('../utils') + +const cwd = process.cwd() + +process.env.PATH = `${cwd}/bin:${cwd}/node_modules/.bin:${process.env.PATH}` + +shell.config.verbose = true +shell.config.fatal = true + +ensureCleanIndex() + +const { gitTag } = checkVersion() +let packages + +main().catch(errorExit) + +async function main(ps) { + configGitUser() + gitDetatch() + + clean() + + packages = await getPackages() + + await runBuild() + + await commit() + await pushAndPublish() +} + +function configGitUser() { + shell.exec('git config user.name "CI"') + shell.exec('git config user.email "<>"') +} + +function ensureCleanIndex() { + // make sure the repo is clean + try { + shell.exec('git diff-index -G . HEAD --stat --exit-code') + } catch { + throw new Error('working directory must be clean') + } +} + +function checkVersion() { + const getVersion = require('../getVersion') + const version = require('semver').clean(getVersion()) + + if (!version) { + throw new Error('failed to parse version') + } + + return { + version, + gitTag: 'v' + version, + } +} + +function gitDetatch() { + shell.exec('git checkout --detach') +} + +function clean() { + shell.exec('_clean') +} + +async function runBuild() { + // copy README to interactjs package + await Promise.all( + packages + .filter((p) => p.endsWith('interactjs')) + .map((p) => fs.copyFile(`${cwd}/README.md`, `${p}/README.md`)), + ) + + // copy license file and npmignore to all packages + const licenseFilename = isPro ? 'LICENSE.md' : 'LICENSE' + await Promise.all( + packages.map(async (pkg) => { + await fs.copyFile(licenseFilename, path.join(pkg, licenseFilename)) + await fs.copyFile('.npmignore', path.join(pkg, '.npmignore')) + }), + ) + + if (isPro) await fs.rm(path.resolve('LICENSE')) + + // clean up scope deps + shell.exec('npx _check_deps') + + if (!isPro) { + // bundle interactjs + shell.exec('npm run build:bundle') + // ensure that the output is valid ES5 syntax + shell.exec('acorn --silent --ecma5 packages/interactjs/dist/*.js') + + // generate docs + shell.exec('npm run build:docs') + } + + // create @interactjs/**/use/* modules + shell.exec('npx _add_plugin_indexes') + + // generate types + shell.exec('npx _types') + + // generate esnext .js modules + shell.exec('rollup -c esnext.rollup.config.cjs') + // ensure that the output is valid ES2018 syntax + shell.exec('acorn --silent --module --ecma2018 packages/**/*.js') + + // set publishConfig + await editPackageJsons((pkg) => { + pkg.publishConfig = isPro ? { access: 'restricted', registry: registryUrl } : { access: 'public' } + }) +} + +function commit() { + // commit and add new version tag + shell.exec('git add --all .') + shell.exec('git add --force packages') + if (!isPro) shell.exec('git add --force dist/api') + shell.exec('git reset **/node_modules') + shell.exec(`git commit --no-verify -m ${gitTag}`) +} + +async function pushAndPublish() { + const { NPM_TAG } = process.env + + try { + shell.exec(`git push --no-verify origin HEAD:refs/tags/${gitTag}`) + } catch { + throw new Error(`failed to push git tag ${gitTag} to origin`) + } + + const gitHead = shell.exec('git rev-parse --short HEAD').trim() + await editPackageJsons((pkg) => { + pkg.gitHead = gitHead + }) + + const { deleteAsync } = await import('del') + if (isPro) await deleteAsync('packages/**/*.map') + + const npmPublishCommand = 'npm publish' + (NPM_TAG ? ` --tag ${NPM_TAG}` : '') + const packagesToPublish = isPro ? packages.filter((p) => /@interactjs\//.test(p)) : packages + + for (const pkg of packagesToPublish) { + shell.exec(npmPublishCommand, { cwd: path.resolve(pkg) }) + } + + shell.exec('git checkout $(git ls-files "**package.json")') +} + +async function editPackageJsons(func) { + await Promise.all( + ['.', ...packages].map(async (packageDir) => { + const file = path.resolve(packageDir, 'package.json') + const pkg = JSON.parse((await fs.readFile(file)).toString()) + + func(pkg) + + await fs.writeFile(file, `${JSON.stringify(pkg, null, 2)}\n`) + }), + ) +} diff --git a/scripts/bin/types.js b/scripts/bin/types.js new file mode 100644 index 000000000..b20a457a1 --- /dev/null +++ b/scripts/bin/types.js @@ -0,0 +1,18 @@ +const path = require('path') + +const shell = require('shelljs') + +const execTypes = require('../execTypes') +const { errorExit } = require('../utils') + +shell.config.verbose = true +shell.config.fatal = true + +const typesDir = '@interactjs/types' + +;(async () => { + const modulesDir = path.resolve('packages') + + execTypes.modular(modulesDir) + await execTypes.combined(path.join(modulesDir, typesDir)) +})().catch(errorExit) diff --git a/scripts/bin/version.js b/scripts/bin/version.js new file mode 100644 index 000000000..902336f04 --- /dev/null +++ b/scripts/bin/version.js @@ -0,0 +1,63 @@ +const fs = require('fs') +const path = require('path') + +const { glob } = require('glob') +const semver = require('semver') + +const getVersion = require('../getVersion') + +let [, , cwd, versionChange, prereleaseId] = process.argv + +if (cwd === undefined || !cwd.startsWith('/')) { + ;[cwd, versionChange, prereleaseId] = [process.cwd(), cwd, versionChange] +} + +const depFields = ['dependencies', 'peerDependencies', 'devDependencies', 'optionalDependencies'] + +let currentVersion + +const previousVersion = getVersion(cwd) + +if (versionChange) { + if (/^(major|minor|patch|premajor|preminor|prepatch|prerelease)$/.test(versionChange)) { + currentVersion = semver.inc(previousVersion, versionChange, prereleaseId) + } else { + currentVersion = semver.clean(versionChange) + + if (currentVersion === null) { + throw Error(`Invalid version change "${previousVersion}" -> "${versionChange}"`) + } + } + + const versionTable = [] + + for (const file of [ + 'package.json', + ...glob.sync('packages/{@interactjs/*,interactjs}/package.json', { cwd }), + ]) { + const pkg = require(path.resolve(file)) + + versionTable.push({ package: pkg.name, old: pkg.version, new: currentVersion }) + + pkg.version = currentVersion + + for (const deps of depFields.map((f) => pkg[f]).filter(Boolean)) { + for (const name of Object.keys(deps).filter((n) => /@?interactjs\//.test(n))) { + if (deps[name] === previousVersion) { + deps[name] = currentVersion + } else { + console.warn(`${file}: not updating "${name}" from "${deps[name]}"`) + } + } + } + + fs.writeFileSync(file, `${JSON.stringify(pkg, null, 2)}\n`) + } + + console.table(versionTable) +} +// if this was run with no arguments, get the current version +else { + currentVersion = previousVersion + console.log(currentVersion) +} diff --git a/scripts/execTypes.js b/scripts/execTypes.js new file mode 100644 index 000000000..6f9e4aefb --- /dev/null +++ b/scripts/execTypes.js @@ -0,0 +1,25 @@ +const fs = require('fs') +const path = require('path') + +const shell = require('shelljs') + +module.exports = { + modular(modulesDir) { + shell.exec(`npx tsc -p types.tsconfig.json --outDir ${modulesDir}/@interactjs`) + }, + async combined(outDir) { + const outFile = path.join(outDir, 'index.d.ts') + + // await del(path.join(typesOutDir, outBasename)) + shell.exec(`npx tsc -p types.tsconfig.json --rootDir packages --outFile ${outFile}`) + + const namespaceDeclaration = ` +import * as Interact from '@interactjs/types/index' + +export as namespace Interact +export = Interact +`.trimStart() + + await fs.promises.writeFile(path.join(outDir, 'typings.d.ts'), namespaceDeclaration) + }, +} diff --git a/scripts/getVersion.js b/scripts/getVersion.js new file mode 100644 index 000000000..ef2e2f94c --- /dev/null +++ b/scripts/getVersion.js @@ -0,0 +1,7 @@ +const path = require('path') + +module.exports = (cwd = process.cwd()) => { + const rootPkg = require(path.resolve(cwd, 'package.json')) + + return rootPkg.version +} diff --git a/scripts/headers.js b/scripts/headers.js new file mode 100644 index 000000000..7ed9484c7 --- /dev/null +++ b/scripts/headers.js @@ -0,0 +1,23 @@ +const version = require('../scripts/getVersion')() + +module.exports = + process.env.INTERACTJS_TIER === 'pro' + ? { + raw: `/** + * interact.js ${version} + * + * Copyright (c) 2012-present Taye Adeyemi + * https://interactjs.io/license + */\n`, + min: `/* interact.js ${version} | https://interactjs.io/license */\n`, + } + : { + raw: `/** + * interact.js ${version} + * + * Copyright (c) 2012-present Taye Adeyemi + * Released under the MIT License. + * https://raw.github.com/taye/interact.js/main/LICENSE + */\n`, + min: `/* interact.js ${version} | https://raw.github.com/taye/interact.js/main/LICENSE */\n`, + } diff --git a/scripts/utils.js b/scripts/utils.js new file mode 100644 index 000000000..38500eab9 --- /dev/null +++ b/scripts/utils.js @@ -0,0 +1,226 @@ +const fs = require('fs') +const path = require('path') + +const { glob } = require('glob') +const resolveSync = require('resolve').sync + +const sourcesGlob = 'packages/{,@}interactjs/**/**/*{.ts,.tsx,.vue}' +const lintSourcesGlob = `{${sourcesGlob},{scripts,examples,docs}/**/*.{js,cjs,md},bin/**/*}` +const commonIgnoreGlobs = ['**/node_modules/**', '**/*_*', '**/*.d.ts', '**/dist/**', 'examples/js/**'] +const lintIgnoreGlobs = [...commonIgnoreGlobs] +const sourcesIgnoreGlobs = [...commonIgnoreGlobs, '**/*.spec.ts'] +const builtFilesGlob = + '{{**/dist/**,packages/{,@}interactjs/**/**/*.js{,.map}},packages/@interactjs/**/index.ts}' +const builtFilesIgnoreGlobs = [ + '**/node_modules/**', + 'packages/@interactjs/{dev-tools/babel-plugin-prod.js,{types,interact,interactjs,rebound}/index.ts}', +] + +const getSources = ({ cwd = process.cwd(), ...options } = {}) => + glob(sourcesGlob, { + cwd, + ignore: sourcesIgnoreGlobs, + strict: false, + nodir: true, + absolute: true, + ...options, + }) + +const getBuiltJsFiles = ({ cwd = process.cwd() } = {}) => + glob(builtFilesGlob, { + cwd, + ignore: builtFilesIgnoreGlobs, + strict: false, + nodir: true, + }) + +function getEsnextBabelOptions(presetEnvOptions) { + return { + babelrc: false, + configFile: false, + sourceMaps: true, + presets: [ + [ + require.resolve('@babel/preset-env'), + { + shippedProposals: true, + ...presetEnvOptions, + }, + ], + [ + require.resolve('@babel/preset-typescript'), + { isTSX: false, onlyRemoveTypeImports: true, allExtensions: true, allowDeclareFields: true }, + ], + ], + plugins: [ + require.resolve('./babel/vue-sfc'), + require.resolve('@babel/plugin-proposal-optional-catch-binding'), + require.resolve('@babel/plugin-proposal-optional-chaining'), + require.resolve('@babel/plugin-transform-nullish-coalescing-operator'), + require.resolve('@babel/plugin-transform-logical-assignment-operators'), + ], + assumptions: { + iterableIsArray: true, + noDocumentAll: true, + noNewArrows: true, + setPublicClassFields: true, + }, + } +} + +function getDevPackageDir() { + return path.join(__dirname, '..') +} + +function getModuleName(tsName) { + return tsName.replace(/\.[jt]sx?$/, '') +} + +function getModuleDirectories() { + return [path.join(__dirname, '..', 'packages'), path.join(process.cwd(), 'node_modules')] +} + +async function getPackages(options) { + const packageJsonPaths = await glob('packages/{@interactjs/*,interactjs}/package.json', { + ignore: commonIgnoreGlobs, + ...options, + }) + const packageDirs = packageJsonPaths.map(path.dirname) + + return [...new Set(packageDirs)].sort() +} + +async function getPackageJsons(packages = getPackages()) { + return Promise.all( + (await packages).map(async (p) => { + const jsonPath = path.resolve(p, 'package.json') + const pkg = JSON.parse((await fs.promises.readFile(jsonPath)).toString()) + return [jsonPath, pkg] + }), + ) +} + +function shouldIgnoreImport(sourceValue) { + return !/^(\.{1-2}|(@interactjs))[\\/]/.test(sourceValue) +} + +const isPro = process.env.INTERACTJS_TIER === 'pro' +const registryUrl = isPro ? 'https://registry.interactjs.io' : undefined + +function extendBabelOptions( + { ignore = [], plugins = [], presets = [], ...others }, + base = getEsnextBabelOptions(), +) { + return { + ...base, + ...others, + ignore: [...(base.ignore || []), ...ignore], + presets: [...(base.presets || []), ...presets], + plugins: [...(base.plugins || []), ...plugins], + } +} + +function getPackageDir(filename) { + let packageDir = filename + + while (!fs.existsSync(path.join(packageDir, 'package.json'))) { + packageDir = path.dirname(packageDir) + + if (packageDir === path.sep) { + throw new Error(`Couldn't find a package for ${filename}`) + } + } + + return packageDir +} + +function getRelativeToRoot(filename, moduleDirectory, prefix = '/') { + filename = path.normalize(filename) + + const ret = withBestRoot((root) => { + const valid = filename.startsWith(root) + const result = valid && path.join(prefix, path.relative(root, filename)) + const priority = valid && -result.length + + return { valid, result, priority } + }, moduleDirectory) + + if (!ret.result) { + throw new Error(`Couldn't find module ${filename} in ${moduleDirectory.join(' or')}.`) + } + + return ret +} + +/** + * use the result of `func` most shallow valid root + */ +function withBestRoot(func, moduleDirectory) { + const roots = moduleDirectory.map(path.normalize) + + return ( + roots.reduce((best, root) => { + const { result, valid, priority } = func(root) + + if (!valid) { + return best + } + + if (!best || priority > best.priority) { + return { result, priority, root } + } + + return best + }, null) || {} + ) +} + +function resolveImport(specifier, basedir, moduleDirectory) { + if (specifier.startsWith('.')) { + specifier = path.join(basedir, specifier) + } + + return resolveSync(specifier, { + extensions: ['.ts', '.tsx'], + moduleDirectory, + }) +} + +function getShims() { + try { + return require('../scripts/shims') + } catch { + return [] + } +} + +function errorExit(error) { + console.error(error) + process.exit(1) +} + +module.exports = { + getSources, + sourcesGlob, + lintSourcesGlob, + commonIgnoreGlobs, + sourcesIgnoreGlobs, + lintIgnoreGlobs, + getBuiltJsFiles, + getEsnextBabelOptions, + extendBabelOptions, + getDevPackageDir, + getPackages, + getPackageJsons, + getModuleName, + getModuleDirectories, + getPackageDir, + getRelativeToRoot, + withBestRoot, + resolveImport, + isPro, + registryUrl, + shouldIgnoreImport, + getShims, + errorExit, +} diff --git a/shims.d.ts b/shims.d.ts new file mode 100644 index 000000000..455519673 --- /dev/null +++ b/shims.d.ts @@ -0,0 +1,5 @@ +declare module '*.vue' { + import type { DefineComponent } from 'vue' + const component: ReturnType> + export default component +} diff --git a/test/.eslintrc.cjs b/test/.eslintrc.cjs new file mode 100644 index 000000000..f90ee3e70 --- /dev/null +++ b/test/.eslintrc.cjs @@ -0,0 +1,5 @@ +module.exports = { + extends: '../.eslintrc.cjs', + env: { browser: true }, + rules: { 'no-console': 2, strict: [2, 'never'] }, +} diff --git a/test/data.js b/test/data.js deleted file mode 100644 index 4d8751d5c..000000000 --- a/test/data.js +++ /dev/null @@ -1,90 +0,0 @@ -if (window.PointerEvent) { - var types = { - down: 'pointerdown', - move: 'pointermove', - up: 'pointerup' - }; -} -else { - var types = { - down: 'touchstart', - move: 'touchmove', - up: 'touchend' - }; -} - -window.data = { - downMove2Up: [ - { x: 120, y: 55, type: 'mousedown', pointerId: 1 }, - { x: 0, y: -200, type: 'mousemove', pointerId: 1 }, - { x: 80, y: -100, type: 'mousemove', pointerId: 1 }, - { x: 80, y: -100, type: 'mouseup', pointerId: 1 } - ], - touch2Move2End2: [ - { - type: types.down, - pointerId: 1, - x: -23, - y: -78, - touches: [ - { x: -23, y: -78, identifier: 1 } - ], - changed: [] - }, - { - type: types.down, - pointerId: 2, - x: -100, - y: -100, - touches: [ - { x: -23, y: -78, identifier: 1 }, - { x: 100, y: 100, identifier: 2 } - ], - changed: [] - }, - { - type: types.move, - pointerId: 1, - x: -50, - y: -50, - touches: [ - { x: 50, y: -50, identifier: 1 }, - { x: 100, y: 100, identifier: 2 } - ], - changed: [] - }, - { - type: types.move, - pointerId: 2, - x: 50, - y: -50, - touches: [ - { x: 50, y: -50, identifier: 1 }, - { x: -50, y: 50, identifier: 2 } - ], - changed: [] - }, - { - type: types.up, - pointerId: 1, - x: -50, - y: 50, - touches: [ - { x: -50, y: 50, identifier: 2 } - ], - changed: [ - { x: 50, y: -50, identifier: 1 } - ] - }, - { - type: types.up, - pointerId: 2, - x: -50, - y: 50, - touches: [], - changed: [ - { x: -50, y: 50, identifier: 2 } - ] - } - ] -}; diff --git a/test/fixtures/babelPluginProject/index.js b/test/fixtures/babelPluginProject/index.js new file mode 100644 index 000000000..95367e3bb --- /dev/null +++ b/test/fixtures/babelPluginProject/index.js @@ -0,0 +1,2 @@ +// eslint-disable-next-line import/no-extraneous-dependencies +export * from '@interactjs/a' diff --git a/test/fixtures/babelPluginProject/node_modules/@interactjs/a/a.js b/test/fixtures/babelPluginProject/node_modules/@interactjs/a/a.js new file mode 100644 index 000000000..bad14e27b --- /dev/null +++ b/test/fixtures/babelPluginProject/node_modules/@interactjs/a/a.js @@ -0,0 +1,2 @@ +export const a = {} +export * from './b' diff --git a/test/fixtures/babelPluginProject/node_modules/@interactjs/a/b/b.js b/test/fixtures/babelPluginProject/node_modules/@interactjs/a/b/b.js new file mode 100644 index 000000000..d9e512a20 --- /dev/null +++ b/test/fixtures/babelPluginProject/node_modules/@interactjs/a/b/b.js @@ -0,0 +1 @@ +export const b = {} diff --git a/test/fixtures/babelPluginProject/node_modules/@interactjs/a/b/index.js b/test/fixtures/babelPluginProject/node_modules/@interactjs/a/b/index.js new file mode 100644 index 000000000..8a77536e3 --- /dev/null +++ b/test/fixtures/babelPluginProject/node_modules/@interactjs/a/b/index.js @@ -0,0 +1 @@ +export { b } from './b' diff --git a/test/fixtures/babelPluginProject/node_modules/@interactjs/a/package-main-file.js b/test/fixtures/babelPluginProject/node_modules/@interactjs/a/package-main-file.js new file mode 100644 index 000000000..21c83d478 --- /dev/null +++ b/test/fixtures/babelPluginProject/node_modules/@interactjs/a/package-main-file.js @@ -0,0 +1 @@ +export * from './a' diff --git a/test/fixtures/babelPluginProject/node_modules/@interactjs/a/package.json b/test/fixtures/babelPluginProject/node_modules/@interactjs/a/package.json new file mode 100644 index 000000000..31294ad4d --- /dev/null +++ b/test/fixtures/babelPluginProject/node_modules/@interactjs/a/package.json @@ -0,0 +1,3 @@ +{ + "main": "package-main-file.js" +} diff --git a/test/fixtures/dependentTsProject/index.ts b/test/fixtures/dependentTsProject/index.ts new file mode 100644 index 000000000..3447fa6ef --- /dev/null +++ b/test/fixtures/dependentTsProject/index.ts @@ -0,0 +1,246 @@ +/* eslint-disable import/no-extraneous-dependencies, import/no-unresolved */ +import interact from 'interactjs' + +// Interactables +interact(document.body) +interact(document) +interact(window) + +interact('.drag-and-resize') + .draggable({ + inertia: true, + modifiers: [ + interact.modifiers.snap({ + targets: [ + { x: 100, y: 200 }, + (x: number, y: number) => ({ x: x % 20, y }), + interact.snappers.grid({ x: 20, y: 0 }), + ], + offset: 'startCoords', + relativePoints: [{ x: 0, y: 1 }], + endOnly: true, + }), + interact.modifiers.snapSize({ + targets: [ + { x: 100, y: 200 }, + (x: number, y: number) => ({ x: x % 20, y }), + interact.snappers.grid({ width: 100, height: 500 }), + ], + endOnly: true, + }), + interact.modifiers.restrictRect({ + restriction: 'parent', + endOnly: true, + }), + interact.modifiers.restrict({ + restriction: _ => ({ top: 0, left: 0, bottom: 1, right: 1 }), + }), + interact.modifiers.restrict({ + restriction: _ => document.body, + }), + interact.modifiers.restrictSize({ + min: document.body, + max: 'parent', + }), + interact.modifiers.restrictEdges({ + inner: document.body, + outer: 'parent', + }), + ], + }) + .resizable({ + inertia: true, + }) + +// Selector context +const myList = document.querySelector('#my-list') as HTMLElement | SVGElement + +interact('li', { + context: myList, +}).draggable({ + /* ... */ +}) + +// Action options +const target = 'li' +interact(target) + .draggable({ + max: 1, + maxPerElement: 2, + manualStart: true, + modifiers: [], + inertia: { + /* ... */ + }, + autoScroll: { + /* ... */ + }, + + lockAxis: 'x' || 'y' || 'start', + startAxis: 'x' || 'y', + }) + .resizable({ + max: 1, + maxPerElement: 2, + manualStart: true, + modifiers: [], + inertia: { + /* ... */ + }, + autoScroll: { + /* ... */ + }, + margin: 50, + + square: true || false, + axis: 'x' || 'y', + }) + .gesturable({ + max: 1, + maxPerElement: 2, + manualStart: true, + modifiers: [], + }) + +// autoscroll +const element = 'li' +interact(element) + .draggable({ + autoScroll: true, + }) + .resizable({ + autoScroll: { + container: document.body, + margin: 50, + distance: 5, + interval: 10, + }, + }) + +// axis +interact(target) + .draggable({ + startAxis: 'x', + lockAxis: 'y', + }) + .draggable({ + startAxis: 'xy', + lockAxis: 'x', + }) + +interact(target).resizable({ + axis: 'x', +}) + +const handleEl = 'li' +interact(target).resizable({ + edges: { + top: true, // Use pointer coords to check for resize. + left: false, // Disable resizing from left edge. + bottom: '.resize-s', // Resize if pointer target matches selector + right: handleEl, // Resize if pointer target is the given Element + }, +}) + +// resize invert +interact(target).resizable({ + edges: { bottom: true, right: true }, + invert: 'reposition', +}) + +// resize square +interact(target).resizable({ + squareResize: true, +}) + +// dropzone accept +interact(target).dropzone({ + accept: '.drag0, .drag1', +}) + +// dropzone overlap +interact(target).dropzone({ + overlap: 0.25, +}) + +// dropzone checker +interact(target).dropzone({ + checker ( + _dragEvent: Interact.Element, // related dragmove or dragend + _event: Event, // Touch, Pointer or Mouse Event + dropped: boolean, // bool default checker result + _dropzone: Interact.Interactable, // dropzone Interactable + dropElement: Interact.Element, // dropzone elemnt + _draggable: Interact.Interactable, // draggable Interactable + _draggableElement: Interact.Element, + ) { + // draggable element + // only allow drops into empty dropzone elements + return dropped && !dropElement.hasChildNodes() + }, +}) + +interact.dynamicDrop() +interact.dynamicDrop(false) + +// Events +function listener (event: Interact.InteractEvent) { + const { type, pageX, pageY } = event + alert({ type, pageX, pageY }) +} + +interact(target) + .on('dragstart', listener) + .on('dragmove dragend', listener) + .on(['resizemove', 'resizeend'], listener) + .on({ + gesturestart: listener, + gestureend: listener, + }) + +interact.on('resize', (event: Interact.ResizeEvent) => { + const { rect, deltaRect } = event + alert(JSON.stringify({ rect, deltaRect })) +}) + +interact(target).resizable({ + listeners: [{ start: listener, move: listener }], +}) + +interact(target).draggable({ + listeners: { start: listener, end: listener }, +}) + +interact(target).draggable({ + onstart: listener, + onmove: listener, + onend: listener, +}) + +interact.on(['dragmove', 'resizestart'], listener) + +// devTools options +interact(target).devTools({ + ignore: { boxSizing: true, touchAction: true }, +}) + +const dropTarget = 'div' +// Drop Events +interact(dropTarget) + .dropzone({ + ondrop (event) { + alert(event.relatedTarget.id + ' was dropped into ' + event.target.id) + }, + }) + .on('dropactivate', event => { + event.target.classList.add('drop-activated') + }) + +interact(target).on('up', _event => {}) + +// fast click +interact('a[href]').on('tap', event => { + window.location.href = event.currentTarget.href + + event.preventDefault() +}) diff --git a/test/fixtures/dependentTsProject/tsconfig.json b/test/fixtures/dependentTsProject/tsconfig.json new file mode 100644 index 000000000..df1bd31b5 --- /dev/null +++ b/test/fixtures/dependentTsProject/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "esnext", + "module": "commonjs", + "lib": ["dom", "esnext"], + "strict": true, + "skipLibCheck": false, + "esModuleInterop": true, + "noEmit": true + } +} diff --git a/test/index.html b/test/index.html deleted file mode 100644 index 1fb0795cf..000000000 --- a/test/index.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - - interact.js tests - - - - - - - - - - - - -
- - - - - - - - - diff --git a/test/test.js b/test/test.js deleted file mode 100644 index 50d86ffa0..000000000 --- a/test/test.js +++ /dev/null @@ -1,452 +0,0 @@ -var expect = chai.expect, - should = chai.should(), - debug = interact.debug(), - PointerEvent = window.PointerEvent || window.MSPointerEvent; - -function blank () {} - -function mockEvent (options, target, currentTarget) { - 'use strict'; - - options.target = options.target || target; - options.currentTarget = options.currentTarget || currentTarget; - - return { - target: options.target, - currentTarget: options.current || options.target, - type: options.type, - pageX: options.x, - pageY: options.y, - clientX: options.x - (options.scrollX|0), - clientY: options.y - (options.scrollY|0), - touches: options.touches && options.touches.map(mockEvent), - changedTouches: options.changed && options.changed.map(mockEvent), - pointerId: options.pointerId || 0, - identifier: options.identifier || 0, - - preventDefault: blank, - stopPropagation: blank, - stopImmediatePropagation: blank - }; -} - -describe('interact', function () { - 'use strict'; - - describe('when called as a function', function () { - var validSelector = 'svg .draggable, body button'; - - it('should return an Interactable when given an Element', function () { - var bod = interact(document.body); - - expect(bod).to.be.an.instanceof(debug.Interactable); - - bod.element().should.equal(document.body); - }); - - it('should return an Interactable when given a valid CSS selector string', function () { - var interactable = interact(validSelector); - - expect(interactable).to.be.an.instanceof(debug.Interactable); - }); - - it('should throw an error when given a string that is not a valid selector', function () { - var error = 0; - - try { - interact('<< invalid selector >>'); - } - catch (e) { - error = e; - } - - error.should.be.instanceof(DOMException); - }); - - it('should return the same value from a given parameter unless returned Interactable is unset', function () { var iBody = interact(document.body), - iSelector = interact(validSelector); - - interact(document.body).should.equal(iBody); - interact(validSelector).should.equal(iSelector); - - iBody.unset(); - iSelector.unset(); - - interact(document.body).should.not.equal(iBody); - interact(validSelector).should.not.equal(iSelector); - }); - }); - - describe('gobal options', function () { - - }); -}); - -describe('Interactable', function () { - 'use strict'; - - var defaults = debug.defaultOptions, - iable = interact(document.createElement('div')), - simpleOptions = { - draggable : 'draggable', - dropzone : 'dropzone', - resizable : 'resizable', - squareResize : 'squareResize', - gesturable : 'gesturable', - styleCursor : 'styleCursor', - origin : 'origin', - deltaSource : 'deltaSource' - }, - enableOptions = [ - 'snap', - 'autoScroll', - 'restrict', - 'inertia' - ]; - - describe('options', function () { - it('should return the default setting if they were never previously set', function () { - var option, i; - - for (option in simpleOptions) { - iable[option]().should.equal(defaults[simpleOptions[option]]); - } - - for (i = 0; option = enableOptions[i], i < enableOptions.length; i++) { - if (iable[option]()) { - iable[option]().should.equal(defaults[option]); - } - else { - iable[option]().should.equal(defaults[option + 'Enabled']); - } - } - - iable.rectChecker().should.equal(debug.Interactable.prototype.getRect); - expect(iable.actionChecker()).to.equal(null); - }); - }); - - describe('#element', function () { - it('should return the element if this is not a selector Interactable', function () { - var p = document.createElement('p'); - - interact(p).element().should.equal(p); - }); - }); - - describe('#actionChecker', function () { - var div = document.createElement('div'), - iDiv = interact(div) - .draggable(true) - .resizable(true) - .gesturable(true), - i, - action, - actions = ['drag', 'resizexy', 'resizex', 'resizey', 'gesture'], - returnActionI = function () { return actions[i]; }; - - it('should set set the function used to determine actions on pointer down events', function () { - iDiv.actionChecker(returnActionI); - - for (i = 0; action = actions[i], i < actions.length; i++) { - debug.pointerDown.call(div, mockEvent({ - target: div, - pointerId: 1 - })); - - if (PointerEvent && action === 'gesture') { - debug.pointerDown.call(div, mockEvent({ - target: div, - pointerId: 2 - })); - } - - interact.debug().prepared.should.equal(action); - - interact.stop(); - } - }); - }); -}); - -describe('Events', function () { - 'use strict'; - - interact.stop(); - - var dragElement = document.createElement('div'), - draggable = interact(dragElement), - events = [], - mockEvents = data.downMove2Up.map(function (e) { - return mockEvent(e, dragElement); - }), - pushEvent = function (event) { - events.push(event); - }; - - describe('drag sequence', function () { - draggable.draggable({ - onstart: pushEvent, - onmove: pushEvent, - onend: pushEvent - }).actionChecker(function () { - return 'drag'; - }); - - debug.pointerDown(mockEvents[0]); - debug.pointerMove(mockEvents[1]); - debug.pointerMove(mockEvents[2]); - debug.pointerUp (mockEvents[3]); - - it('should be triggered by mousedown -> mousemove -> mouseup sequence', function () { - events.length.should.equal(4); - - events[0].type.should.equal('dragstart'); - events[1].type.should.equal('dragmove'); - events[2].type.should.equal('dragmove'); - events[3].type.should.equal('dragend'); - }); - - it('should have the same coordinates as the original events', function () { - var event, mock, i; - - for (i = 0; event = events[i], mock = mockEvents[i], i < events.length; i++) { - event.pageX.should.equal(mock.pageX); - event.pageY.should.equal(mock.pageY); - event.clientX.should.equal(mock.clientX); - event.clientY.should.equal(mock.clientY); - } - }); - - it('should have the same x0/y0 and clientX0/Y0 as the start event of the sequence', function () { - var startEvent = mockEvents[0], - event, i; - - for (i = 0; event = events[i], i < events.length; i++) { - event.x0.should.equal(startEvent.pageX); - event.y0.should.equal(startEvent.pageY); - event.clientX0.should.equal(startEvent.clientX); - event.clientY0.should.equal(startEvent.clientY); - } - }); - - it('should keep the same target', function () { - var event, i; - - for (i = 0; event = events[i], i < events.length; i++) { - event.target.should.equal(dragElement); - } - }); - - describe('dragstart', function () { - var downEvent = mockEvents[0], - startEvent = events[0]; - - it('should have dy/dx of 0', function () { - startEvent.dx.should.equal(0); - startEvent.dy.should.equal(0); - }); - }); - - describe('dragmove', function () { - - it('should have dy/dx of this event\'s coordinates - the previous event\'s', function () { - events[1].dx.should.equal(events[1].pageX - events[0].pageX); - events[1].dy.should.equal(events[1].pageY - events[0].pageY); - - events[2].dx.should.equal(events[2].pageX - events[1].pageX); - events[2].dy.should.equal(events[2].pageY - events[1].pageY); - }); - }); - - describe('dragend', function () { - it('should have dy/dx of the end event\'s coordinates - the start event\'s', function () { - events[3].dx.should.equal(events[2].pageX - events[0].pageX); - events[3].dy.should.equal(events[2].pageY - events[0].pageY); - }); - }); - }); - - describe('Gesture sequence', function () { - interact.stop(); - - var pushEvent = function (event) { - gestureEvents.push(event); - }, - element = document.createElement('button'), - iElement = interact(element).gesturable({ - onstart: pushEvent, - onmove: pushEvent, - onend: pushEvent - }).actionChecker(function () { return 'gesture'; }), - mockEvents = data.touch2Move2End2.map(function (e) { - return mockEvent(e, element); - }), - gestureEvents = [], - eventMap = [1, 2, 3, 4]; - - // The pointers must be recorded here since event listeners - // don't call the related functions. The recorded pointermove events - // are used to calculate gesture angle, scale, etc. - - debug.pointerDown(mockEvents[0]); - debug.pointerDown(mockEvents[1]); - - debug[PointerEvent? 'recordPointers': 'recordTouches'](mockEvents[2]); - debug.pointerMove(mockEvents[2]); - - debug[PointerEvent? 'recordPointers': 'recordTouches'](mockEvents[3]); - debug.pointerMove(mockEvents[3]); - - debug.pointerUp(mockEvents[4]); - debug.pointerUp(mockEvents[5]); - - it('should be started by 2 touches starting and moving and end when there are fewer than two active touches', function () { - gestureEvents.length.should.equal(4); - - gestureEvents[0].type.should.equal('gesturestart'); - gestureEvents[1].type.should.equal('gesturemove'); - gestureEvents[2].type.should.equal('gesturemove'); - gestureEvents[3].type.should.equal('gestureend'); - }); - - describe('touches', function () { - - it('should be the original list of touches in the correspoinding touch event', function () { - - if (PointerEvent) { - gestureEvents[0].touches.should.eql([mockEvents[2], mockEvents[1]]); - gestureEvents[1].touches.should.eql([mockEvents[2], mockEvents[1]]); - gestureEvents[2].touches.should.eql([mockEvents[2], mockEvents[3]]); - gestureEvents[3].touches.should.eql([mockEvents[2], mockEvents[3]]); - } - else { - for (var i = 0, gEvent, mEvent; - mEvent = mockEvents[eventMap[i]], gEvent = gestureEvents[i], i < gestureEvents.length; - i++) { - - gEvent.touches.should.eql(mEvent.touches); - } - } - }); - }); - - describe('coordinates', function () { - it('should be the averages of touches', function () { - for (var i = 0, gEvent, mEvent; - mEvent = mockEvents[eventMap[i]], gEvent = gestureEvents[i], i < gestureEvents.length; - i++) { - - var average = PointerEvent? mEvent: interact.getTouchAverage(mEvent), - coords = ['pageX', 'pageY', 'clientX', 'clientY']; - - coords.forEach(function (coord) { - gEvent[coord].should.equal(average[coord]); - }); - } - }); - }); - - describe('angle', function () { - it('should be the angle of the line joining the first two touches of the correspoinding touch event', function () { - if (PointerEvent) { - gestureEvents[0].angle.should.eql(interact.getTouchAngle([mockEvents[2], mockEvents[1]])); - gestureEvents[1].angle.should.eql(interact.getTouchAngle([mockEvents[2], mockEvents[1]])); - gestureEvents[2].angle.should.eql(interact.getTouchAngle([mockEvents[2], mockEvents[3]])); - gestureEvents[3].angle.should.eql(interact.getTouchAngle([mockEvents[2], mockEvents[3]])); - } - else { - for (var i = 0, gEvent, mEvent; - mEvent = mockEvents[eventMap[i]], gEvent = gestureEvents[i], i < gestureEvents.length; - i++) { - - gEvent.angle.should.equal(interact.getTouchAngle(mEvent)); - } - } - }); - }); - - describe('da', function () { - describe('in a gesturestart', function () { - it('should be 0', function () { - gestureEvents[0].da.should.equal(0); - }); - }); - - describe('in a gesturemove', function () { - it('should be (thisEvent.angle - previousEvent.angle)', function () { - gestureEvents[1].da.should.equal(gestureEvents[1].angle - gestureEvents[0].angle); - gestureEvents[2].da.should.equal(gestureEvents[2].angle - gestureEvents[1].angle); - }); - }); - - describe('in a gestureend', function () { - it('should be (lastMoveEvent.angle - startEvent.angle)', function () { - gestureEvents[3].da.should.equal(gestureEvents[2].angle - gestureEvents[0].angle); - }); - }); - }); - - describe('distance', function () { - it('should be the distance between the first two touches of the correspoinding touch event', function () { - if (PointerEvent) { - gestureEvents[0].distance.should.eql(interact.getTouchDistance([mockEvents[2], mockEvents[1]])); - gestureEvents[1].distance.should.eql(interact.getTouchDistance([mockEvents[2], mockEvents[1]])); - gestureEvents[2].distance.should.eql(interact.getTouchDistance([mockEvents[2], mockEvents[3]])); - gestureEvents[3].distance.should.eql(interact.getTouchDistance([mockEvents[2], mockEvents[3]])); - } - else { - for (var i = 0, gEvent, mEvent; - mEvent = mockEvents[eventMap[i]], gEvent = gestureEvents[i], i < gestureEvents.length; - i++) { - - gEvent.distance.should.equal(interact.getTouchDistance(mEvent)); - gEvent.distance.should.equal(interact.getTouchDistance(mEvent)); - } - } - }); - }); - - describe('scale', function () { - describe('in a gesturestart', function () { - it('should be 1', function () { - gestureEvents[0].scale.should.equal(1); - }); - }); - - describe('in a gesturemove', function () { - it('should be the (thisevent.distance / startevent.distance)', function () { - gestureEvents[1].scale.should.equal(gestureEvents[1].distance / gestureEvents[0].distance); - gestureEvents[2].scale.should.equal(gestureEvents[2].distance / gestureEvents[0].distance); - }); - }); - - describe('in a gestureend', function () { - it('should be the scale of the last gesturemove event', function () { - gestureEvents[3].scale.should.equal(gestureEvents[2].scale); - }); - }); - }); - - describe('ds', function () { - describe('in a gesturestart', function () { - it('should be 0', function () { - gestureEvents[0].ds.should.equal(0); - }); - }); - - describe('in a gesturemove', function () { - it('should be the difference between this event\'s scale and that of the previous event', function () { - gestureEvents[1].ds.should.equal(gestureEvents[1].scale - gestureEvents[0].scale); - gestureEvents[2].ds.should.equal(gestureEvents[2].scale - gestureEvents[1].scale); - }); - }); - - describe('in a gestureend', function () { - it('should be (lastMoveEvent.scale - startEvent.scale)', function () { - gestureEvents[3].ds.should.equal(gestureEvents[2].scale - gestureEvents[0].scale); - }); - }); - }); - }); -}); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 000000000..1bc1edc4e --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,41 @@ +{ + "include": [ + "packages", + "shims.d.ts", + "scripts", + "jsdoc" + ], + "exclude": [ + "test/fixtures", + "_angular", + "**/dist" + ], + "compilerOptions": { + "target": "esnext", + "module": "esnext", + "moduleResolution": "node", + "esModuleInterop": true, + + "allowJs": false, + "skipLibCheck": true, + "noEmit": true, + + "strict": false, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "jsx": "preserve", + "experimentalDecorators": true, + + "lib": [ + "esnext", + "dom" + ], + + "baseUrl": ".", + "paths": { + "@interactjs/_dev/*": ["./*"] + } + } +} diff --git a/typedoc.config.cjs b/typedoc.config.cjs new file mode 100644 index 000000000..2258bb9bb --- /dev/null +++ b/typedoc.config.cjs @@ -0,0 +1,42 @@ +const { isPro } = require('./scripts/utils') + +/** @type {import('typedoc').TypeDocOptions} */ +const config = { + name: '@interactjs', + entryPoints: [ + 'actions', + 'auto-scroll', + 'auto-start', + 'core', + 'dev-tools', + 'inertia', + 'modifiers', + 'pointer-events', + 'reflow', + 'snappers', + ].map((pkg) => `./packages/@interactjs/${pkg}/*.ts`), + exclude: ['**/*.{spec,stub}.ts{,x}', '**/_*'], + excludeReferences: true, + excludeInternal: true, + excludeExternals: true, + excludePrivate: true, + excludeNotDocumented: true, + excludeNotDocumentedKinds: [ + 'Module', + 'Variable', + 'Function', + 'Constructor', + 'Method', + 'IndexSignature', + 'ConstructorSignature', + 'Reference', + ], + basePath: './packages/@interactjs', + titleLink: '/', + readme: 'none', + disableSources: isPro, + plugin: ['typedoc-plugin-markdown'], + out: './dist/api', +} + +module.exports = config diff --git a/types.tsconfig.json b/types.tsconfig.json new file mode 100644 index 000000000..5f6ff85fb --- /dev/null +++ b/types.tsconfig.json @@ -0,0 +1,19 @@ +{ + "include": ["packages/@interactjs", "shims.d.ts"], + "exclude": ["**/*.spec.ts", "packages/@interactjs/vue", "packages/@interactjs/react"], + "compilerOptions": { + "target": "esnext", + "module": "esnext", + "lib": ["esnext", "dom"], + "moduleResolution": "node", + "esModuleInterop": true, + + "declaration": true, + "declarationMap": false, + "emitDeclarationOnly": true, + "stripInternal": true, + "skipLibCheck": true, + + "strict": false + } +} diff --git a/vijest.config.js b/vijest.config.js new file mode 100644 index 000000000..be14502e9 --- /dev/null +++ b/vijest.config.js @@ -0,0 +1,8 @@ +module.exports = { + launch: { + executablePath: 'chromium', + // devtools: true, + // slowMo: 1000, + }, + shareBrowserContext: true, +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 000000000..5d98076e8 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,45 @@ +import path from 'path' + +import vue from '@vitejs/plugin-vue' +import serveIndex from 'serve-index' +import type { Plugin } from 'vite' +import { defineConfig } from 'vite' + +const examplesDir = path.resolve(__dirname, 'examples') + +export default defineConfig({ + root: examplesDir, + resolve: { + alias: { + '@interactjs/': path.resolve(__dirname, 'packages/@interactjs'), + interactjs: path.resolve(__dirname, 'packages/interactjs'), + }, + }, + define: { + ...getDefinedEnv(), + }, + plugins: [vue(), dirListing()], + optimizeDeps: { + include: ['react'], + }, + server: { + port: 8081, + }, +}) + +function getDefinedEnv () { + const entries = Object.entries(process.env) + .filter(([key]) => /^(NODE_ENV|npm_package_version|INTERACTJS_.*)$/.test(key)) + .map(([key, value]) => [`process.env.${key}`, JSON.stringify(value)]) + + return Object.fromEntries(entries) +} + +function dirListing (): Plugin { + return { + name: 'dir-listing', + configureServer (server) { + server.middlewares.use(serveIndex(examplesDir, { icons: true }) as any) + }, + } +} diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 000000000..78d6e6674 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,7813 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@aashutoshrathi/word-wrap@^1.2.3": + version "1.2.6" + resolved "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" + integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== + +"@adobe/css-tools@^4.0.1": + version "4.3.1" + resolved "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.1.tgz#abfccb8ca78075a2b6187345c26243c1a0842f28" + integrity sha512-/62yikz7NLScCGAAST5SHdnjaDJQBDq0M2muyRTpf2VQhw6StBg2ALiu73zSJQ4fMVLA+0uBhBHAle7Wg+2kSg== + +"@ampproject/remapping@^2.2.0": + version "2.2.1" + resolved "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" + integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.13", "@babel/code-frame@^7.23.5": + version "7.23.5" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz#9009b69a8c602293476ad598ff53e4562e15c244" + integrity sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA== + dependencies: + "@babel/highlight" "^7.23.4" + chalk "^2.4.2" + +"@babel/compat-data@^7.22.6", "@babel/compat-data@^7.22.9", "@babel/compat-data@^7.23.3", "@babel/compat-data@^7.23.5": + version "7.23.5" + resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz#ffb878728bb6bdcb6f4510aa51b1be9afb8cfd98" + integrity sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw== + +"@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.18.2", "@babel/core@^7.7.2", "@babel/core@^7.8.0": + version "7.23.5" + resolved "https://registry.npmjs.org/@babel/core/-/core-7.23.5.tgz#6e23f2acbcb77ad283c5ed141f824fd9f70101c7" + integrity sha512-Cwc2XjUrG4ilcfOw4wBAK+enbdgwAcAJCfGUItPBKR7Mjw4aEfAFYrLxeRp4jWgtNIKn3n2AlBOfwwafl+42/g== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.23.5" + "@babel/generator" "^7.23.5" + "@babel/helper-compilation-targets" "^7.22.15" + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helpers" "^7.23.5" + "@babel/parser" "^7.23.5" + "@babel/template" "^7.22.15" + "@babel/traverse" "^7.23.5" + "@babel/types" "^7.23.5" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + +"@babel/generator@^7.23.5", "@babel/generator@^7.7.2": + version "7.23.5" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.23.5.tgz#17d0a1ea6b62f351d281350a5f80b87a810c4755" + integrity sha512-BPssCHrBD+0YrxviOa3QzpqwhNIXKEtOa2jQrm4FlmkC2apYgRnQcmPWiGZDlGxiNtltnUFolMe8497Esry+jA== + dependencies: + "@babel/types" "^7.23.5" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + +"@babel/helper-annotate-as-pure@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882" + integrity sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-builder-binary-assignment-operator-visitor@^7.22.15": + version "7.22.15" + resolved "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz#5426b109cf3ad47b91120f8328d8ab1be8b0b956" + integrity sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw== + dependencies: + "@babel/types" "^7.22.15" + +"@babel/helper-compilation-targets@^7.22.15", "@babel/helper-compilation-targets@^7.22.6": + version "7.22.15" + resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz#0698fc44551a26cf29f18d4662d5bf545a6cfc52" + integrity sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw== + dependencies: + "@babel/compat-data" "^7.22.9" + "@babel/helper-validator-option" "^7.22.15" + browserslist "^4.21.9" + lru-cache "^5.1.1" + semver "^6.3.1" + +"@babel/helper-create-class-features-plugin@^7.22.15": + version "7.23.5" + resolved "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.5.tgz#2a8792357008ae9ce8c0f2b78b9f646ac96b314b" + integrity sha512-QELlRWxSpgdwdJzSJn4WAhKC+hvw/AtHbbrIoncKHkhKKR/luAlKkgBDcri1EzWAo8f8VvYVryEHN4tax/V67A== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-member-expression-to-functions" "^7.23.0" + "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.20" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + semver "^6.3.1" + +"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.22.15", "@babel/helper-create-regexp-features-plugin@^7.22.5": + version "7.22.15" + resolved "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz#5ee90093914ea09639b01c711db0d6775e558be1" + integrity sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + regexpu-core "^5.3.1" + semver "^6.3.1" + +"@babel/helper-define-polyfill-provider@^0.4.3": + version "0.4.3" + resolved "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.3.tgz#a71c10f7146d809f4a256c373f462d9bba8cf6ba" + integrity sha512-WBrLmuPP47n7PNwsZ57pqam6G/RGo1vw/87b0Blc53tZNGZ4x7YvZ6HgQe2vo1W/FR20OgjeZuGXzudPiXHFug== + dependencies: + "@babel/helper-compilation-targets" "^7.22.6" + "@babel/helper-plugin-utils" "^7.22.5" + debug "^4.1.1" + lodash.debounce "^4.0.8" + resolve "^1.14.2" + +"@babel/helper-environment-visitor@^7.22.20": + version "7.22.20" + resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" + integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== + +"@babel/helper-function-name@^7.22.5", "@babel/helper-function-name@^7.23.0": + version "7.23.0" + resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" + integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== + dependencies: + "@babel/template" "^7.22.15" + "@babel/types" "^7.23.0" + +"@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-member-expression-to-functions@^7.22.15", "@babel/helper-member-expression-to-functions@^7.23.0": + version "7.23.0" + resolved "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz#9263e88cc5e41d39ec18c9a3e0eced59a3e7d366" + integrity sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA== + dependencies: + "@babel/types" "^7.23.0" + +"@babel/helper-module-imports@^7.18.6", "@babel/helper-module-imports@^7.22.15", "@babel/helper-module-imports@^7.22.5": + version "7.22.15" + resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz#16146307acdc40cc00c3b2c647713076464bdbf0" + integrity sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w== + dependencies: + "@babel/types" "^7.22.15" + +"@babel/helper-module-transforms@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz#d7d12c3c5d30af5b3c0fcab2a6d5217773e2d0f1" + integrity sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-module-imports" "^7.22.15" + "@babel/helper-simple-access" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/helper-validator-identifier" "^7.22.20" + +"@babel/helper-optimise-call-expression@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz#f21531a9ccbff644fdd156b4077c16ff0c3f609e" + integrity sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295" + integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== + +"@babel/helper-remap-async-to-generator@^7.22.20": + version "7.22.20" + resolved "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz#7b68e1cb4fa964d2996fd063723fb48eca8498e0" + integrity sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-wrap-function" "^7.22.20" + +"@babel/helper-replace-supers@^7.22.20": + version "7.22.20" + resolved "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz#e37d367123ca98fe455a9887734ed2e16eb7a793" + integrity sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-member-expression-to-functions" "^7.22.15" + "@babel/helper-optimise-call-expression" "^7.22.5" + +"@babel/helper-simple-access@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" + integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-skip-transparent-expression-wrappers@^7.20.0", "@babel/helper-skip-transparent-expression-wrappers@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz#007f15240b5751c537c40e77abb4e89eeaaa8847" + integrity sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" + integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-string-parser@^7.23.4": + version "7.23.4" + resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83" + integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ== + +"@babel/helper-validator-identifier@^7.22.20": + version "7.22.20" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== + +"@babel/helper-validator-option@^7.22.15", "@babel/helper-validator-option@^7.23.5": + version "7.23.5" + resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307" + integrity sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw== + +"@babel/helper-wrap-function@^7.22.20": + version "7.22.20" + resolved "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz#15352b0b9bfb10fc9c76f79f6342c00e3411a569" + integrity sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw== + dependencies: + "@babel/helper-function-name" "^7.22.5" + "@babel/template" "^7.22.15" + "@babel/types" "^7.22.19" + +"@babel/helpers@^7.23.5": + version "7.23.5" + resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.5.tgz#52f522840df8f1a848d06ea6a79b79eefa72401e" + integrity sha512-oO7us8FzTEsG3U6ag9MfdF1iA/7Z6dz+MtFhifZk8C8o453rGJFFWUP1t+ULM9TUIAzC9uxXEiXjOiVMyd7QPg== + dependencies: + "@babel/template" "^7.22.15" + "@babel/traverse" "^7.23.5" + "@babel/types" "^7.23.5" + +"@babel/highlight@^7.23.4": + version "7.23.4" + resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz#edaadf4d8232e1a961432db785091207ead0621b" + integrity sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A== + dependencies: + "@babel/helper-validator-identifier" "^7.22.20" + chalk "^2.4.2" + js-tokens "^4.0.0" + +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.22.15", "@babel/parser@^7.23.5": + version "7.23.5" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.23.5.tgz#37dee97c4752af148e1d38c34b856b2507660563" + integrity sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ== + +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.23.3.tgz#5cd1c87ba9380d0afb78469292c954fee5d2411a" + integrity sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.23.3.tgz#f6652bb16b94f8f9c20c50941e16e9756898dc5d" + integrity sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/plugin-transform-optional-chaining" "^7.23.3" + +"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.23.3.tgz#20c60d4639d18f7da8602548512e9d3a4c8d7098" + integrity sha512-XaJak1qcityzrX0/IU5nKHb34VaibwP3saKqG6a/tppelgllOH13LUann4ZCIBcVOeE6H18K4Vx9QKkVww3z/w== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-proposal-export-default-from@^7.17.12": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.23.3.tgz#6f511a676c540ccc8d17a8553dbba9230b0ddac0" + integrity sha512-Q23MpLZfSGZL1kU7fWqV262q65svLSCIP5kZ/JCW/rKTCm/FrLjpvEd2kfUYMVeHh4QhV/xzyoRAHWrAZJrE3Q== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-export-default-from" "^7.23.3" + +"@babel/plugin-proposal-optional-catch-binding@^7.16.7": + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz#f9400d0e6a3ea93ba9ef70b09e72dd6da638a2cb" + integrity sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + +"@babel/plugin-proposal-optional-chaining@^7.17.12": + version "7.21.0" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz#886f5c8978deb7d30f678b2e24346b287234d3ea" + integrity sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + +"@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2": + version "7.21.0-placeholder-for-preset-env.2" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz#7844f9289546efa9febac2de4cfe358a050bd703" + integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w== + +"@babel/plugin-syntax-async-generators@^7.8.4": + version "7.8.4" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-bigint@^7.8.3": + version "7.8.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" + integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-class-properties@^7.12.13", "@babel/plugin-syntax-class-properties@^7.8.3": + version "7.12.13" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" + integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-syntax-class-static-block@^7.14.5": + version "7.14.5" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" + integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-dynamic-import@^7.8.3": + version "7.8.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" + integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-export-default-from@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.23.3.tgz#7e6d4bf595d5724230200fb2b7401d4734b15335" + integrity sha512-KeENO5ck1IeZ/l2lFZNy+mpobV3D2Zy5C1YFnWm+YuY5mQiAWc4yAp13dqgguwsBsFVLh4LPCEqCa5qW13N+hw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-syntax-export-namespace-from@^7.8.3": + version "7.8.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" + integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-syntax-import-assertions@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.23.3.tgz#9c05a7f592982aff1a2768260ad84bcd3f0c77fc" + integrity sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-syntax-import-attributes@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.23.3.tgz#992aee922cf04512461d7dae3ff6951b90a2dc06" + integrity sha512-pawnE0P9g10xgoP7yKr6CK63K2FMsTE+FZidZO/1PwRdzmAPVs+HS1mAURUsgaoxammTJvULUdIkEK0gOcU2tA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-syntax-import-meta@^7.10.4", "@babel/plugin-syntax-import-meta@^7.8.3": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" + integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-json-strings@^7.8.3": + version "7.8.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-jsx@^7.22.5", "@babel/plugin-syntax-jsx@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz#8f2e4f8a9b5f9aa16067e142c1ac9cd9f810f473" + integrity sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" + integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": + version "7.8.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-numeric-separator@^7.10.4", "@babel/plugin-syntax-numeric-separator@^7.8.3": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-object-rest-spread@^7.8.3": + version "7.8.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-catch-binding@^7.8.3": + version "7.8.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-chaining@^7.8.3": + version "7.8.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-private-property-in-object@^7.14.5": + version "7.14.5" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" + integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-top-level-await@^7.14.5", "@babel/plugin-syntax-top-level-await@^7.8.3": + version "7.14.5" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" + integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-typescript@^7.23.3", "@babel/plugin-syntax-typescript@^7.7.2": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz#24f460c85dbbc983cd2b9c4994178bcc01df958f" + integrity sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-syntax-unicode-sets-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz#d49a3b3e6b52e5be6740022317580234a6a47357" + integrity sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-arrow-functions@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.23.3.tgz#94c6dcfd731af90f27a79509f9ab7fb2120fc38b" + integrity sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-async-generator-functions@^7.23.4": + version "7.23.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.4.tgz#93ac8e3531f347fba519b4703f9ff2a75c6ae27a" + integrity sha512-efdkfPhHYTtn0G6n2ddrESE91fgXxjlqLsnUtPWnJs4a4mZIbUaK7ffqKIIUKXSHwcDvaCVX6GXkaJJFqtX7jw== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-remap-async-to-generator" "^7.22.20" + "@babel/plugin-syntax-async-generators" "^7.8.4" + +"@babel/plugin-transform-async-to-generator@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.23.3.tgz#d1f513c7a8a506d43f47df2bf25f9254b0b051fa" + integrity sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw== + dependencies: + "@babel/helper-module-imports" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-remap-async-to-generator" "^7.22.20" + +"@babel/plugin-transform-block-scoped-functions@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.23.3.tgz#fe1177d715fb569663095e04f3598525d98e8c77" + integrity sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-block-scoping@^7.23.4": + version "7.23.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.4.tgz#b2d38589531c6c80fbe25e6b58e763622d2d3cf5" + integrity sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-class-properties@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.23.3.tgz#35c377db11ca92a785a718b6aa4e3ed1eb65dc48" + integrity sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-class-static-block@^7.23.4": + version "7.23.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.23.4.tgz#2a202c8787a8964dd11dfcedf994d36bfc844ab5" + integrity sha512-nsWu/1M+ggti1SOALj3hfx5FXzAY06fwPJsUZD4/A5e1bWi46VUIWtD+kOX6/IdhXGsXBWllLFDSnqSCdUNydQ== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + +"@babel/plugin-transform-classes@^7.23.5": + version "7.23.5" + resolved "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.5.tgz#e7a75f815e0c534cc4c9a39c56636c84fc0d64f2" + integrity sha512-jvOTR4nicqYC9yzOHIhXG5emiFEOpappSJAl73SDSEDcybD+Puuze8Tnpb9p9qEyYup24tq891gkaygIFvWDqg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-compilation-targets" "^7.22.15" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.20" + "@babel/helper-split-export-declaration" "^7.22.6" + globals "^11.1.0" + +"@babel/plugin-transform-computed-properties@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.23.3.tgz#652e69561fcc9d2b50ba4f7ac7f60dcf65e86474" + integrity sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/template" "^7.22.15" + +"@babel/plugin-transform-destructuring@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.3.tgz#8c9ee68228b12ae3dff986e56ed1ba4f3c446311" + integrity sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-dotall-regex@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.23.3.tgz#3f7af6054882ede89c378d0cf889b854a993da50" + integrity sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-duplicate-keys@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.23.3.tgz#664706ca0a5dfe8d066537f99032fc1dc8b720ce" + integrity sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-dynamic-import@^7.23.4": + version "7.23.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.23.4.tgz#c7629e7254011ac3630d47d7f34ddd40ca535143" + integrity sha512-V6jIbLhdJK86MaLh4Jpghi8ho5fGzt3imHOBu/x0jlBaPYqDoWz4RDXjmMOfnh+JWNaQleEAByZLV0QzBT4YQQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + +"@babel/plugin-transform-exponentiation-operator@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.23.3.tgz#ea0d978f6b9232ba4722f3dbecdd18f450babd18" + integrity sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ== + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-export-namespace-from@^7.23.4": + version "7.23.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.23.4.tgz#084c7b25e9a5c8271e987a08cf85807b80283191" + integrity sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + +"@babel/plugin-transform-for-of@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.23.3.tgz#afe115ff0fbce735e02868d41489093c63e15559" + integrity sha512-X8jSm8X1CMwxmK878qsUGJRmbysKNbdpTv/O1/v0LuY/ZkZrng5WYiekYSdg9m09OTmDDUWeEDsTE+17WYbAZw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-function-name@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.23.3.tgz#8f424fcd862bf84cb9a1a6b42bc2f47ed630f8dc" + integrity sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw== + dependencies: + "@babel/helper-compilation-targets" "^7.22.15" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-json-strings@^7.23.4": + version "7.23.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.23.4.tgz#a871d9b6bd171976efad2e43e694c961ffa3714d" + integrity sha512-81nTOqM1dMwZ/aRXQ59zVubN9wHGqk6UtqRK+/q+ciXmRy8fSolhGVvG09HHRGo4l6fr/c4ZhXUQH0uFW7PZbg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-json-strings" "^7.8.3" + +"@babel/plugin-transform-literals@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.23.3.tgz#8214665f00506ead73de157eba233e7381f3beb4" + integrity sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-logical-assignment-operators@^7.23.4": + version "7.23.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.23.4.tgz#e599f82c51d55fac725f62ce55d3a0886279ecb5" + integrity sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + +"@babel/plugin-transform-member-expression-literals@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.23.3.tgz#e37b3f0502289f477ac0e776b05a833d853cabcc" + integrity sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-modules-amd@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.3.tgz#e19b55436a1416829df0a1afc495deedfae17f7d" + integrity sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw== + dependencies: + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-modules-commonjs@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz#661ae831b9577e52be57dd8356b734f9700b53b4" + integrity sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA== + dependencies: + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-simple-access" "^7.22.5" + +"@babel/plugin-transform-modules-systemjs@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.3.tgz#fa7e62248931cb15b9404f8052581c302dd9de81" + integrity sha512-ZxyKGTkF9xT9YJuKQRo19ewf3pXpopuYQd8cDXqNzc3mUNbOME0RKMoZxviQk74hwzfQsEe66dE92MaZbdHKNQ== + dependencies: + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.20" + +"@babel/plugin-transform-modules-umd@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.23.3.tgz#5d4395fccd071dfefe6585a4411aa7d6b7d769e9" + integrity sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg== + dependencies: + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-named-capturing-groups-regex@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz#67fe18ee8ce02d57c855185e27e3dc959b2e991f" + integrity sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-new-target@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.23.3.tgz#5491bb78ed6ac87e990957cea367eab781c4d980" + integrity sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-nullish-coalescing-operator@^7.23.4": + version "7.23.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.23.4.tgz#45556aad123fc6e52189ea749e33ce090637346e" + integrity sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + +"@babel/plugin-transform-numeric-separator@^7.23.4": + version "7.23.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.23.4.tgz#03d08e3691e405804ecdd19dd278a40cca531f29" + integrity sha512-mps6auzgwjRrwKEZA05cOwuDc9FAzoyFS4ZsG/8F43bTLf/TgkJg7QXOrPO1JO599iA3qgK9MXdMGOEC8O1h6Q== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + +"@babel/plugin-transform-object-rest-spread@^7.23.4": + version "7.23.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.23.4.tgz#2b9c2d26bf62710460bdc0d1730d4f1048361b83" + integrity sha512-9x9K1YyeQVw0iOXJlIzwm8ltobIIv7j2iLyP2jIhEbqPRQ7ScNgwQufU2I0Gq11VjyG4gI4yMXt2VFags+1N3g== + dependencies: + "@babel/compat-data" "^7.23.3" + "@babel/helper-compilation-targets" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.23.3" + +"@babel/plugin-transform-object-super@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.23.3.tgz#81fdb636dcb306dd2e4e8fd80db5b2362ed2ebcd" + integrity sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.20" + +"@babel/plugin-transform-optional-catch-binding@^7.23.4": + version "7.23.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.23.4.tgz#318066de6dacce7d92fa244ae475aa8d91778017" + integrity sha512-XIq8t0rJPHf6Wvmbn9nFxU6ao4c7WhghTR5WyV8SrJfUFzyxhCm4nhC+iAp3HFhbAKLfYpgzhJ6t4XCtVwqO5A== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + +"@babel/plugin-transform-optional-chaining@^7.23.3", "@babel/plugin-transform-optional-chaining@^7.23.4": + version "7.23.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.4.tgz#6acf61203bdfc4de9d4e52e64490aeb3e52bd017" + integrity sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + +"@babel/plugin-transform-parameters@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.23.3.tgz#83ef5d1baf4b1072fa6e54b2b0999a7b2527e2af" + integrity sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-private-methods@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.23.3.tgz#b2d7a3c97e278bfe59137a978d53b2c2e038c0e4" + integrity sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-private-property-in-object@^7.23.4": + version "7.23.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.23.4.tgz#3ec711d05d6608fd173d9b8de39872d8dbf68bf5" + integrity sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + +"@babel/plugin-transform-property-literals@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.23.3.tgz#54518f14ac4755d22b92162e4a852d308a560875" + integrity sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-regenerator@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.23.3.tgz#141afd4a2057298602069fce7f2dc5173e6c561c" + integrity sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + regenerator-transform "^0.15.2" + +"@babel/plugin-transform-reserved-words@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.23.3.tgz#4130dcee12bd3dd5705c587947eb715da12efac8" + integrity sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-runtime@^7.18.2": + version "7.23.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.4.tgz#5132b388580002fc5cb7c84eccfb968acdc231cb" + integrity sha512-ITwqpb6V4btwUG0YJR82o2QvmWrLgDnx/p2A3CTPYGaRgULkDiC0DRA2C4jlRB9uXGUEfaSS/IGHfVW+ohzYDw== + dependencies: + "@babel/helper-module-imports" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + babel-plugin-polyfill-corejs2 "^0.4.6" + babel-plugin-polyfill-corejs3 "^0.8.5" + babel-plugin-polyfill-regenerator "^0.5.3" + semver "^6.3.1" + +"@babel/plugin-transform-shorthand-properties@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.23.3.tgz#97d82a39b0e0c24f8a981568a8ed851745f59210" + integrity sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-spread@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.23.3.tgz#41d17aacb12bde55168403c6f2d6bdca563d362c" + integrity sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + +"@babel/plugin-transform-sticky-regex@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.23.3.tgz#dec45588ab4a723cb579c609b294a3d1bd22ff04" + integrity sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-template-literals@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.23.3.tgz#5f0f028eb14e50b5d0f76be57f90045757539d07" + integrity sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-typeof-symbol@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.23.3.tgz#9dfab97acc87495c0c449014eb9c547d8966bca4" + integrity sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-typescript@^7.23.3": + version "7.23.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.23.4.tgz#da12914d17b3c4b307f32c5fd91fbfdf17d56f86" + integrity sha512-39hCCOl+YUAyMOu6B9SmUTiHUU0t/CxJNUmY3qRdJujbqi+lrQcL11ysYUsAvFWPBdhihrv1z0oRG84Yr3dODQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-typescript" "^7.23.3" + +"@babel/plugin-transform-unicode-escapes@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.23.3.tgz#1f66d16cab01fab98d784867d24f70c1ca65b925" + integrity sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-unicode-property-regex@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.23.3.tgz#19e234129e5ffa7205010feec0d94c251083d7ad" + integrity sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-unicode-regex@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.23.3.tgz#26897708d8f42654ca4ce1b73e96140fbad879dc" + integrity sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-unicode-sets-regex@^7.23.3": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.23.3.tgz#4fb6f0a719c2c5859d11f6b55a050cc987f3799e" + integrity sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/preset-env@^7.18.2": + version "7.23.5" + resolved "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.5.tgz#350a3aedfa9f119ad045b068886457e895ba0ca1" + integrity sha512-0d/uxVD6tFGWXGDSfyMD1p2otoaKmu6+GD+NfAx0tMaH+dxORnp7T9TaVQ6mKyya7iBtCIVxHjWT7MuzzM9z+A== + dependencies: + "@babel/compat-data" "^7.23.5" + "@babel/helper-compilation-targets" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-validator-option" "^7.23.5" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.23.3" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.23.3" + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly" "^7.23.3" + "@babel/plugin-proposal-private-property-in-object" "7.21.0-placeholder-for-preset-env.2" + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-class-properties" "^7.12.13" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-syntax-import-assertions" "^7.23.3" + "@babel/plugin-syntax-import-attributes" "^7.23.3" + "@babel/plugin-syntax-import-meta" "^7.10.4" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + "@babel/plugin-syntax-top-level-await" "^7.14.5" + "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6" + "@babel/plugin-transform-arrow-functions" "^7.23.3" + "@babel/plugin-transform-async-generator-functions" "^7.23.4" + "@babel/plugin-transform-async-to-generator" "^7.23.3" + "@babel/plugin-transform-block-scoped-functions" "^7.23.3" + "@babel/plugin-transform-block-scoping" "^7.23.4" + "@babel/plugin-transform-class-properties" "^7.23.3" + "@babel/plugin-transform-class-static-block" "^7.23.4" + "@babel/plugin-transform-classes" "^7.23.5" + "@babel/plugin-transform-computed-properties" "^7.23.3" + "@babel/plugin-transform-destructuring" "^7.23.3" + "@babel/plugin-transform-dotall-regex" "^7.23.3" + "@babel/plugin-transform-duplicate-keys" "^7.23.3" + "@babel/plugin-transform-dynamic-import" "^7.23.4" + "@babel/plugin-transform-exponentiation-operator" "^7.23.3" + "@babel/plugin-transform-export-namespace-from" "^7.23.4" + "@babel/plugin-transform-for-of" "^7.23.3" + "@babel/plugin-transform-function-name" "^7.23.3" + "@babel/plugin-transform-json-strings" "^7.23.4" + "@babel/plugin-transform-literals" "^7.23.3" + "@babel/plugin-transform-logical-assignment-operators" "^7.23.4" + "@babel/plugin-transform-member-expression-literals" "^7.23.3" + "@babel/plugin-transform-modules-amd" "^7.23.3" + "@babel/plugin-transform-modules-commonjs" "^7.23.3" + "@babel/plugin-transform-modules-systemjs" "^7.23.3" + "@babel/plugin-transform-modules-umd" "^7.23.3" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.22.5" + "@babel/plugin-transform-new-target" "^7.23.3" + "@babel/plugin-transform-nullish-coalescing-operator" "^7.23.4" + "@babel/plugin-transform-numeric-separator" "^7.23.4" + "@babel/plugin-transform-object-rest-spread" "^7.23.4" + "@babel/plugin-transform-object-super" "^7.23.3" + "@babel/plugin-transform-optional-catch-binding" "^7.23.4" + "@babel/plugin-transform-optional-chaining" "^7.23.4" + "@babel/plugin-transform-parameters" "^7.23.3" + "@babel/plugin-transform-private-methods" "^7.23.3" + "@babel/plugin-transform-private-property-in-object" "^7.23.4" + "@babel/plugin-transform-property-literals" "^7.23.3" + "@babel/plugin-transform-regenerator" "^7.23.3" + "@babel/plugin-transform-reserved-words" "^7.23.3" + "@babel/plugin-transform-shorthand-properties" "^7.23.3" + "@babel/plugin-transform-spread" "^7.23.3" + "@babel/plugin-transform-sticky-regex" "^7.23.3" + "@babel/plugin-transform-template-literals" "^7.23.3" + "@babel/plugin-transform-typeof-symbol" "^7.23.3" + "@babel/plugin-transform-unicode-escapes" "^7.23.3" + "@babel/plugin-transform-unicode-property-regex" "^7.23.3" + "@babel/plugin-transform-unicode-regex" "^7.23.3" + "@babel/plugin-transform-unicode-sets-regex" "^7.23.3" + "@babel/preset-modules" "0.1.6-no-external-plugins" + babel-plugin-polyfill-corejs2 "^0.4.6" + babel-plugin-polyfill-corejs3 "^0.8.5" + babel-plugin-polyfill-regenerator "^0.5.3" + core-js-compat "^3.31.0" + semver "^6.3.1" + +"@babel/preset-modules@0.1.6-no-external-plugins": + version "0.1.6-no-external-plugins" + resolved "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz#ccb88a2c49c817236861fee7826080573b8a923a" + integrity sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/types" "^7.4.4" + esutils "^2.0.2" + +"@babel/preset-typescript@^7.17.12": + version "7.23.3" + resolved "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.23.3.tgz#14534b34ed5b6d435aa05f1ae1c5e7adcc01d913" + integrity sha512-17oIGVlqz6CchO9RFYn5U6ZpWRZIngayYCtrPRSgANSwC2V1Jb+iP74nVxzzXJte8b8BYxrL1yY96xfhTBrNNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-validator-option" "^7.22.15" + "@babel/plugin-syntax-jsx" "^7.23.3" + "@babel/plugin-transform-modules-commonjs" "^7.23.3" + "@babel/plugin-transform-typescript" "^7.23.3" + +"@babel/regjsgen@^0.8.0": + version "0.8.0" + resolved "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" + integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== + +"@babel/runtime@^7.12.5", "@babel/runtime@^7.18.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": + version "7.23.5" + resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.5.tgz#11edb98f8aeec529b82b211028177679144242db" + integrity sha512-NdUTHcPe4C99WxPub+K9l9tK5/lV4UXIoaHSYgzco9BCyjKAAwzdBI+wWtYqHt7LJdbo74ZjRPJgzVweq1sz0w== + dependencies: + regenerator-runtime "^0.14.0" + +"@babel/template@^7.22.15", "@babel/template@^7.22.5", "@babel/template@^7.3.3": + version "7.22.15" + resolved "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" + integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== + dependencies: + "@babel/code-frame" "^7.22.13" + "@babel/parser" "^7.22.15" + "@babel/types" "^7.22.15" + +"@babel/traverse@^7.22.5", "@babel/traverse@^7.23.5", "@babel/traverse@^7.7.2": + version "7.23.5" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.5.tgz#f546bf9aba9ef2b042c0e00d245990c15508e7ec" + integrity sha512-czx7Xy5a6sapWWRx61m1Ke1Ra4vczu1mCTtJam5zRTBOonfdJ+S/B6HYmGYu3fJtr8GGET3si6IhgWVBhJ/m8w== + dependencies: + "@babel/code-frame" "^7.23.5" + "@babel/generator" "^7.23.5" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.23.5" + "@babel/types" "^7.23.5" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.22.19", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.5", "@babel/types@^7.3.3", "@babel/types@^7.4.4": + version "7.23.5" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.23.5.tgz#48d730a00c95109fa4393352705954d74fb5b602" + integrity sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w== + dependencies: + "@babel/helper-string-parser" "^7.23.4" + "@babel/helper-validator-identifier" "^7.22.20" + to-fast-properties "^2.0.0" + +"@bcoe/v8-coverage@^0.2.3": + version "0.2.3" + resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" + integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== + +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + dependencies: + "@jridgewell/trace-mapping" "0.3.9" + +"@csstools/css-parser-algorithms@^2.3.2": + version "2.3.2" + resolved "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.3.2.tgz#1e0d581dbf4518cb3e939c3b863cb7180c8cedad" + integrity sha512-sLYGdAdEY2x7TSw9FtmdaTrh2wFtRJO5VMbBrA8tEqEod7GEggFmxTSK9XqExib3yMuYNcvcTdCZIP6ukdjAIA== + +"@csstools/css-tokenizer@^2.2.1": + version "2.2.1" + resolved "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.2.1.tgz#9dc431c9a5f61087af626e41ac2a79cce7bb253d" + integrity sha512-Zmsf2f/CaEPWEVgw29odOj+WEVoiJy9s9NOv5GgNY9mZ1CZ7394By6wONrONrTsnNDv6F9hR02nvFihrGVGHBg== + +"@csstools/media-query-list-parser@^2.1.5": + version "2.1.5" + resolved "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.5.tgz#94bc8b3c3fd7112a40b7bf0b483e91eba0654a0f" + integrity sha512-IxVBdYzR8pYe89JiyXQuYk4aVVoCPhMJkz6ElRwlVysjwURTsTk/bmY/z4FfeRE+CRBMlykPwXEVUg8lThv7AQ== + +"@csstools/selector-specificity@^3.0.0": + version "3.0.0" + resolved "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-3.0.0.tgz#798622546b63847e82389e473fd67f2707d82247" + integrity sha512-hBI9tfBtuPIi885ZsZ32IMEU/5nlZH/KOVYJCOh7gyMxaVLGmLedYqFN6Ui1LXkI8JlC8IsuC0rF0btcRZKd5g== + +"@esbuild/android-arm64@0.19.9": + version "0.19.9" + resolved "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.9.tgz#683794bdc3d27222d3eced7b74cad15979548031" + integrity sha512-q4cR+6ZD0938R19MyEW3jEsMzbb/1rulLXiNAJQADD/XYp7pT+rOS5JGxvpRW8dFDEfjW4wLgC/3FXIw4zYglQ== + +"@esbuild/android-arm@0.19.9": + version "0.19.9" + resolved "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.9.tgz#21a4de41f07b2af47401c601d64dfdefd056c595" + integrity sha512-jkYjjq7SdsWuNI6b5quymW0oC83NN5FdRPuCbs9HZ02mfVdAP8B8eeqLSYU3gb6OJEaY5CQabtTFbqBf26H3GA== + +"@esbuild/android-x64@0.19.9": + version "0.19.9" + resolved "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.9.tgz#e2d7674bc025ddc8699f0cc76cb97823bb63c252" + integrity sha512-KOqoPntWAH6ZxDwx1D6mRntIgZh9KodzgNOy5Ebt9ghzffOk9X2c1sPwtM9P+0eXbefnDhqYfkh5PLP5ULtWFA== + +"@esbuild/darwin-arm64@0.19.9": + version "0.19.9" + resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.9.tgz#ae7a582289cc5c0bac15d4b9020a90cb7288f1e9" + integrity sha512-KBJ9S0AFyLVx2E5D8W0vExqRW01WqRtczUZ8NRu+Pi+87opZn5tL4Y0xT0mA4FtHctd0ZgwNoN639fUUGlNIWw== + +"@esbuild/darwin-x64@0.19.9": + version "0.19.9" + resolved "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.9.tgz#8a216c66dcf51addeeb843d8cfaeff712821d12b" + integrity sha512-vE0VotmNTQaTdX0Q9dOHmMTao6ObjyPm58CHZr1UK7qpNleQyxlFlNCaHsHx6Uqv86VgPmR4o2wdNq3dP1qyDQ== + +"@esbuild/freebsd-arm64@0.19.9": + version "0.19.9" + resolved "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.9.tgz#63d4f603e421252c3cd836b18d01545be7c6c440" + integrity sha512-uFQyd/o1IjiEk3rUHSwUKkqZwqdvuD8GevWF065eqgYfexcVkxh+IJgwTaGZVu59XczZGcN/YMh9uF1fWD8j1g== + +"@esbuild/freebsd-x64@0.19.9": + version "0.19.9" + resolved "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.9.tgz#a3db52595be65360eae4de1d1fa3c1afd942e1e4" + integrity sha512-WMLgWAtkdTbTu1AWacY7uoj/YtHthgqrqhf1OaEWnZb7PQgpt8eaA/F3LkV0E6K/Lc0cUr/uaVP/49iE4M4asA== + +"@esbuild/linux-arm64@0.19.9": + version "0.19.9" + resolved "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.9.tgz#4ae5811ce9f8d7df5eb9edd9765ea9401a534f13" + integrity sha512-PiPblfe1BjK7WDAKR1Cr9O7VVPqVNpwFcPWgfn4xu0eMemzRp442hXyzF/fSwgrufI66FpHOEJk0yYdPInsmyQ== + +"@esbuild/linux-arm@0.19.9": + version "0.19.9" + resolved "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.9.tgz#9807e92cfd335f46326394805ad488e646e506f2" + integrity sha512-C/ChPohUYoyUaqn1h17m/6yt6OB14hbXvT8EgM1ZWaiiTYz7nWZR0SYmMnB5BzQA4GXl3BgBO1l8MYqL/He3qw== + +"@esbuild/linux-ia32@0.19.9": + version "0.19.9" + resolved "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.9.tgz#18892c10f3106652b16f9da88a0362dc95ed46c7" + integrity sha512-f37i/0zE0MjDxijkPSQw1CO/7C27Eojqb+r3BbHVxMLkj8GCa78TrBZzvPyA/FNLUMzP3eyHCVkAopkKVja+6Q== + +"@esbuild/linux-loong64@0.19.9": + version "0.19.9" + resolved "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.9.tgz#dc2ebf9a125db0a1bba18c2bbfd4fbdcbcaf61c2" + integrity sha512-t6mN147pUIf3t6wUt3FeumoOTPfmv9Cc6DQlsVBpB7eCpLOqQDyWBP1ymXn1lDw4fNUSb/gBcKAmvTP49oIkaA== + +"@esbuild/linux-mips64el@0.19.9": + version "0.19.9" + resolved "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.9.tgz#4c2f7c5d901015e3faf1563c4a89a50776cb07fd" + integrity sha512-jg9fujJTNTQBuDXdmAg1eeJUL4Jds7BklOTkkH80ZgQIoCTdQrDaHYgbFZyeTq8zbY+axgptncko3v9p5hLZtw== + +"@esbuild/linux-ppc64@0.19.9": + version "0.19.9" + resolved "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.9.tgz#8385332713b4e7812869622163784a5633f76fc4" + integrity sha512-tkV0xUX0pUUgY4ha7z5BbDS85uI7ABw3V1d0RNTii7E9lbmV8Z37Pup2tsLV46SQWzjOeyDi1Q7Wx2+QM8WaCQ== + +"@esbuild/linux-riscv64@0.19.9": + version "0.19.9" + resolved "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.9.tgz#23f1db24fa761be311874f32036c06249aa20cba" + integrity sha512-DfLp8dj91cufgPZDXr9p3FoR++m3ZJ6uIXsXrIvJdOjXVREtXuQCjfMfvmc3LScAVmLjcfloyVtpn43D56JFHg== + +"@esbuild/linux-s390x@0.19.9": + version "0.19.9" + resolved "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.9.tgz#2dffe497726b897c9f0109e774006e25b33b4fd0" + integrity sha512-zHbglfEdC88KMgCWpOl/zc6dDYJvWGLiUtmPRsr1OgCViu3z5GncvNVdf+6/56O2Ca8jUU+t1BW261V6kp8qdw== + +"@esbuild/linux-x64@0.19.9": + version "0.19.9" + resolved "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.9.tgz#ceb1d62cd830724ff5b218e5d3172a8bad59420e" + integrity sha512-JUjpystGFFmNrEHQnIVG8hKwvA2DN5o7RqiO1CVX8EN/F/gkCjkUMgVn6hzScpwnJtl2mPR6I9XV1oW8k9O+0A== + +"@esbuild/netbsd-x64@0.19.9": + version "0.19.9" + resolved "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.9.tgz#0cbca65e9ef4d3fc41502d3e055e6f49479a8f18" + integrity sha512-GThgZPAwOBOsheA2RUlW5UeroRfESwMq/guy8uEe3wJlAOjpOXuSevLRd70NZ37ZrpO6RHGHgEHvPg1h3S1Jug== + +"@esbuild/openbsd-x64@0.19.9": + version "0.19.9" + resolved "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.9.tgz#1f57adfbee09c743292c6758a3642e875bcad1cf" + integrity sha512-Ki6PlzppaFVbLnD8PtlVQfsYw4S9n3eQl87cqgeIw+O3sRr9IghpfSKY62mggdt1yCSZ8QWvTZ9jo9fjDSg9uw== + +"@esbuild/sunos-x64@0.19.9": + version "0.19.9" + resolved "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.9.tgz#116be6adbd2c7479edeeb5f6ea0441002ab4cb9c" + integrity sha512-MLHj7k9hWh4y1ddkBpvRj2b9NCBhfgBt3VpWbHQnXRedVun/hC7sIyTGDGTfsGuXo4ebik2+3ShjcPbhtFwWDw== + +"@esbuild/win32-arm64@0.19.9": + version "0.19.9" + resolved "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.9.tgz#2be22131ab18af4693fd737b161d1ef34de8ca9d" + integrity sha512-GQoa6OrQ8G08guMFgeXPH7yE/8Dt0IfOGWJSfSH4uafwdC7rWwrfE6P9N8AtPGIjUzdo2+7bN8Xo3qC578olhg== + +"@esbuild/win32-ia32@0.19.9": + version "0.19.9" + resolved "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.9.tgz#e10ead5a55789b167b4225d2469324538768af7c" + integrity sha512-UOozV7Ntykvr5tSOlGCrqU3NBr3d8JqPes0QWN2WOXfvkWVGRajC+Ym0/Wj88fUgecUCLDdJPDF0Nna2UK3Qtg== + +"@esbuild/win32-x64@0.19.9": + version "0.19.9" + resolved "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.9.tgz#b2da6219b603e3fa371a78f53f5361260d0c5585" + integrity sha512-oxoQgglOP7RH6iasDrhY+R/3cHrfwIDvRlT4CGChflq6twk8iENeVvMJjmvBb94Ik1Z+93iGO27err7w6l54GQ== + +"@eslint-community/eslint-utils@^4.1.2", "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": + version "4.4.0" + resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.5.1", "@eslint-community/regexpp@^4.6.0", "@eslint-community/regexpp@^4.6.1": + version "4.10.0" + resolved "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz#548f6de556857c8bb73bbee70c35dc82a2e74d63" + integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA== + +"@eslint/eslintrc@^2.1.4": + version "2.1.4" + resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" + integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.6.0" + globals "^13.19.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@8.55.0": + version "8.55.0" + resolved "https://registry.npmjs.org/@eslint/js/-/js-8.55.0.tgz#b721d52060f369aa259cf97392403cb9ce892ec6" + integrity sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA== + +"@humanwhocodes/config-array@^0.11.13": + version "0.11.13" + resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz#075dc9684f40a531d9b26b0822153c1e832ee297" + integrity sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ== + dependencies: + "@humanwhocodes/object-schema" "^2.0.1" + debug "^4.1.1" + minimatch "^3.0.5" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/object-schema@^2.0.1": + version "2.0.1" + resolved "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz#e5211452df060fa8522b55c7b3c0c4d1981cb044" + integrity sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw== + +"@istanbuljs/load-nyc-config@^1.0.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" + integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== + 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" + +"@istanbuljs/schema@^0.1.2": + version "0.1.3" + resolved "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== + +"@jest/console@^27.5.1": + version "27.5.1" + resolved "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz#260fe7239602fe5130a94f1aa386eff54b014bba" + integrity sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg== + dependencies: + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^27.5.1" + jest-util "^27.5.1" + slash "^3.0.0" + +"@jest/core@^27.5.1": + version "27.5.1" + resolved "https://registry.npmjs.org/@jest/core/-/core-27.5.1.tgz#267ac5f704e09dc52de2922cbf3af9edcd64b626" + integrity sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ== + dependencies: + "@jest/console" "^27.5.1" + "@jest/reporters" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + emittery "^0.8.1" + exit "^0.1.2" + graceful-fs "^4.2.9" + jest-changed-files "^27.5.1" + jest-config "^27.5.1" + jest-haste-map "^27.5.1" + jest-message-util "^27.5.1" + jest-regex-util "^27.5.1" + jest-resolve "^27.5.1" + jest-resolve-dependencies "^27.5.1" + jest-runner "^27.5.1" + jest-runtime "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + jest-validate "^27.5.1" + jest-watcher "^27.5.1" + micromatch "^4.0.4" + rimraf "^3.0.0" + slash "^3.0.0" + strip-ansi "^6.0.0" + +"@jest/environment@^27.5.1": + version "27.5.1" + resolved "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz#d7425820511fe7158abbecc010140c3fd3be9c74" + integrity sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA== + dependencies: + "@jest/fake-timers" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + jest-mock "^27.5.1" + +"@jest/expect-utils@^29.7.0": + version "29.7.0" + resolved "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" + integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA== + dependencies: + jest-get-type "^29.6.3" + +"@jest/fake-timers@^27.5.1": + version "27.5.1" + resolved "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz#76979745ce0579c8a94a4678af7a748eda8ada74" + integrity sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ== + dependencies: + "@jest/types" "^27.5.1" + "@sinonjs/fake-timers" "^8.0.1" + "@types/node" "*" + jest-message-util "^27.5.1" + jest-mock "^27.5.1" + jest-util "^27.5.1" + +"@jest/globals@^27.5.1": + version "27.5.1" + resolved "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz#7ac06ce57ab966566c7963431cef458434601b2b" + integrity sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q== + dependencies: + "@jest/environment" "^27.5.1" + "@jest/types" "^27.5.1" + expect "^27.5.1" + +"@jest/reporters@^27.5.1": + version "27.5.1" + resolved "https://registry.npmjs.org/@jest/reporters/-/reporters-27.5.1.tgz#ceda7be96170b03c923c37987b64015812ffec04" + integrity sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@jest/console" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" + collect-v8-coverage "^1.0.0" + exit "^0.1.2" + glob "^7.1.2" + graceful-fs "^4.2.9" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-instrument "^5.1.0" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.1.3" + jest-haste-map "^27.5.1" + jest-resolve "^27.5.1" + jest-util "^27.5.1" + jest-worker "^27.5.1" + slash "^3.0.0" + source-map "^0.6.0" + string-length "^4.0.1" + terminal-link "^2.0.0" + v8-to-istanbul "^8.1.0" + +"@jest/schemas@^29.6.3": + version "29.6.3" + resolved "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" + integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== + dependencies: + "@sinclair/typebox" "^0.27.8" + +"@jest/source-map@^27.5.1": + version "27.5.1" + resolved "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz#6608391e465add4205eae073b55e7f279e04e8cf" + integrity sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg== + dependencies: + callsites "^3.0.0" + graceful-fs "^4.2.9" + source-map "^0.6.0" + +"@jest/test-result@^27.5.1": + version "27.5.1" + resolved "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz#56a6585fa80f7cdab72b8c5fc2e871d03832f5bb" + integrity sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag== + dependencies: + "@jest/console" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + +"@jest/test-sequencer@^27.5.1": + version "27.5.1" + resolved "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz#4057e0e9cea4439e544c6353c6affe58d095745b" + integrity sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ== + dependencies: + "@jest/test-result" "^27.5.1" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-runtime "^27.5.1" + +"@jest/transform@^27.5.1": + version "27.5.1" + resolved "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz#6c3501dcc00c4c08915f292a600ece5ecfe1f409" + integrity sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw== + dependencies: + "@babel/core" "^7.1.0" + "@jest/types" "^27.5.1" + babel-plugin-istanbul "^6.1.1" + chalk "^4.0.0" + convert-source-map "^1.4.0" + fast-json-stable-stringify "^2.0.0" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-regex-util "^27.5.1" + jest-util "^27.5.1" + micromatch "^4.0.4" + pirates "^4.0.4" + slash "^3.0.0" + source-map "^0.6.1" + write-file-atomic "^3.0.0" + +"@jest/types@^27.5.1": + version "27.5.1" + resolved "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz#3c79ec4a8ba61c170bf937bcf9e98a9df175ec80" + integrity sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^16.0.0" + chalk "^4.0.0" + +"@jest/types@^29.6.3": + version "29.6.3" + resolved "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" + integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== + dependencies: + "@jest/schemas" "^29.6.3" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + +"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": + version "0.3.3" + resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" + integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": + version "3.1.1" + resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== + +"@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + +"@jridgewell/source-map@^0.3.3": + version "0.3.5" + resolved "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz#a3bb4d5c6825aab0d281268f47f6ad5853431e91" + integrity sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.4.15": + version "1.4.15" + resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.20" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz#72e45707cf240fa6b081d0366f8265b0cd10197f" + integrity sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@microsoft/tsdoc-config@0.16.2": + version "0.16.2" + resolved "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.16.2.tgz#b786bb4ead00d54f53839a458ce626c8548d3adf" + integrity sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw== + dependencies: + "@microsoft/tsdoc" "0.14.2" + ajv "~6.12.6" + jju "~1.4.0" + resolve "~1.19.0" + +"@microsoft/tsdoc@0.14.2": + version "0.14.2" + resolved "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz#c3ec604a0b54b9a9b87e9735dfc59e1a5da6a5fb" + integrity sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug== + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": + version "1.2.8" + resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@one-ini/wasm@0.1.1": + version "0.1.1" + resolved "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz#6013659736c9dbfccc96e8a9c2b3de317df39323" + integrity sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw== + +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + +"@rollup/plugin-babel@^6.0.4": + version "6.0.4" + resolved "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-6.0.4.tgz#bd698e351fa9aa9619fcae780aea2a603d98e4c4" + integrity sha512-YF7Y52kFdFT/xVSuVdjkV5ZdX/3YtmX0QulG+x0taQOtJdHYzVU61aSSkAgVJ7NOv6qPkIYiJSgSWWN/DM5sGw== + dependencies: + "@babel/helper-module-imports" "^7.18.6" + "@rollup/pluginutils" "^5.0.1" + +"@rollup/plugin-commonjs@^25.0.7": + version "25.0.7" + resolved "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.7.tgz#145cec7589ad952171aeb6a585bbeabd0fd3b4cf" + integrity sha512-nEvcR+LRjEjsaSsc4x3XZfCCvZIaSMenZu/OiwOKGN2UhQpAYI7ru7czFvyWbErlpoGjnSX3D5Ch5FcMA3kRWQ== + dependencies: + "@rollup/pluginutils" "^5.0.1" + commondir "^1.0.1" + estree-walker "^2.0.2" + glob "^8.0.3" + is-reference "1.2.1" + magic-string "^0.30.3" + +"@rollup/plugin-node-resolve@^15.2.3": + version "15.2.3" + resolved "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz#e5e0b059bd85ca57489492f295ce88c2d4b0daf9" + integrity sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ== + dependencies: + "@rollup/pluginutils" "^5.0.1" + "@types/resolve" "1.20.2" + deepmerge "^4.2.2" + is-builtin-module "^3.2.1" + is-module "^1.0.0" + resolve "^1.22.1" + +"@rollup/plugin-replace@^5.0.5": + version "5.0.5" + resolved "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-5.0.5.tgz#33d5653dce6d03cb24ef98bef7f6d25b57faefdf" + integrity sha512-rYO4fOi8lMaTg/z5Jb+hKnrHHVn8j2lwkqwyS4kTRhKyWOLf2wST2sWXr4WzWiTcoHTp2sTjqUbqIj2E39slKQ== + dependencies: + "@rollup/pluginutils" "^5.0.1" + magic-string "^0.30.3" + +"@rollup/plugin-terser@^0.4.4": + version "0.4.4" + resolved "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-0.4.4.tgz#15dffdb3f73f121aa4fbb37e7ca6be9aeea91962" + integrity sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A== + dependencies: + serialize-javascript "^6.0.1" + smob "^1.0.0" + terser "^5.17.4" + +"@rollup/pluginutils@^5.0.1": + version "5.1.0" + resolved "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz#7e53eddc8c7f483a4ad0b94afb1f7f5fd3c771e0" + integrity sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g== + dependencies: + "@types/estree" "^1.0.0" + estree-walker "^2.0.2" + picomatch "^2.3.1" + +"@rollup/rollup-android-arm-eabi@4.7.0": + version "4.7.0" + resolved "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.7.0.tgz#c144935afdf83e3da0ddea4d903360f99f69c79a" + integrity sha512-rGku10pL1StFlFvXX5pEv88KdGW6DHUghsxyP/aRYb9eH+74jTGJ3U0S/rtlsQ4yYq1Hcc7AMkoJOb1xu29Fxw== + +"@rollup/rollup-android-arm64@4.7.0": + version "4.7.0" + resolved "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.7.0.tgz#4e05031399a9c795612c9694827ec4ba55771bec" + integrity sha512-/EBw0cuJ/KVHiU2qyVYUhogXz7W2vXxBzeE9xtVIMC+RyitlY2vvaoysMUqASpkUtoNIHlnKTu/l7mXOPgnKOA== + +"@rollup/rollup-darwin-arm64@4.7.0": + version "4.7.0" + resolved "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.7.0.tgz#196018fa70b525a893a949fe8e1eeb797d9a7227" + integrity sha512-4VXG1bgvClJdbEYYjQ85RkOtwN8sqI3uCxH0HC5w9fKdqzRzgG39K7GAehATGS8jghA7zNoS5CjSKkDEqWmNZg== + +"@rollup/rollup-darwin-x64@4.7.0": + version "4.7.0" + resolved "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.7.0.tgz#484937c6b987bebaeccdae774977ad4bf7bcd940" + integrity sha512-/ImhO+T/RWJ96hUbxiCn2yWI0/MeQZV/aeukQQfhxiSXuZJfyqtdHPUPrc84jxCfXTxbJLmg4q+GBETeb61aNw== + +"@rollup/rollup-linux-arm-gnueabihf@4.7.0": + version "4.7.0" + resolved "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.7.0.tgz#c495ba7aa13427aaeb4ea0864ab9c2a3d5aa9c09" + integrity sha512-zhye8POvTyUXlKbfPBVqoHy3t43gIgffY+7qBFqFxNqVtltQLtWeHNAbrMnXiLIfYmxcoL/feuLDote2tx+Qbg== + +"@rollup/rollup-linux-arm64-gnu@4.7.0": + version "4.7.0" + resolved "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.7.0.tgz#0ef4ef25e83f610b56d94a5a1fffa27220d5224d" + integrity sha512-RAdr3OJnUum6Vs83cQmKjxdTg31zJnLLTkjhcFt0auxM6jw00GD6IPFF42uasYPr/wGC6TRm7FsQiJyk0qIEfg== + +"@rollup/rollup-linux-arm64-musl@4.7.0": + version "4.7.0" + resolved "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.7.0.tgz#d75add714d898cee4e4a5baeb2e30641e483b0e3" + integrity sha512-nhWwYsiJwZGq7SyR3afS3EekEOsEAlrNMpPC4ZDKn5ooYSEjDLe9W/xGvoIV8/F/+HNIY6jY8lIdXjjxfxopXw== + +"@rollup/rollup-linux-riscv64-gnu@4.7.0": + version "4.7.0" + resolved "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.7.0.tgz#b7ed4894b44a47f4145cce77175fe578d1379863" + integrity sha512-rlfy5RnQG1aop1BL/gjdH42M2geMUyVQqd52GJVirqYc787A/XVvl3kQ5NG/43KXgOgE9HXgCaEH05kzQ+hLoA== + +"@rollup/rollup-linux-x64-gnu@4.7.0": + version "4.7.0" + resolved "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.7.0.tgz#bccd53f20de2c1b1e7024898dc5b69375a5abe4e" + integrity sha512-cCkoGlGWfBobdDtiiypxf79q6k3/iRVGu1HVLbD92gWV5WZbmuWJCgRM4x2N6i7ljGn1cGytPn9ZAfS8UwF6vg== + +"@rollup/rollup-linux-x64-musl@4.7.0": + version "4.7.0" + resolved "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.7.0.tgz#554315d4f252f9f324da587fbf5048aaaf1da12e" + integrity sha512-R2oBf2p/Arc1m+tWmiWbpHBjEcJnHVnv6bsypu4tcKdrYTpDfl1UT9qTyfkIL1iiii5D4WHxUHCg5X0pzqmxFg== + +"@rollup/rollup-win32-arm64-msvc@4.7.0": + version "4.7.0" + resolved "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.7.0.tgz#95902325d07919e25dff32be9428acbc1b889101" + integrity sha512-CPtgaQL1aaPc80m8SCVEoxFGHxKYIt3zQYC3AccL/SqqiWXblo3pgToHuBwR8eCP2Toa+X1WmTR/QKFMykws7g== + +"@rollup/rollup-win32-ia32-msvc@4.7.0": + version "4.7.0" + resolved "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.7.0.tgz#f9ca12cc03ebca191ff6b832785c5a5e1974cf55" + integrity sha512-pmioUlttNh9GXF5x2CzNa7Z8kmRTyhEzzAC+2WOOapjewMbl+3tGuAnxbwc5JyG8Jsz2+hf/QD/n5VjimOZ63g== + +"@rollup/rollup-win32-x64-msvc@4.7.0": + version "4.7.0" + resolved "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.7.0.tgz#1ad18d12c21d09a12d88c904647f1ea64123c32e" + integrity sha512-SeZzC2QhhdBQUm3U0c8+c/P6UlRyBcLL2Xp5KX7z46WXZxzR8RJSIWL9wSUeBTgxog5LTPJuPj0WOT9lvrtP7Q== + +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== + +"@sinonjs/commons@^1.7.0": + version "1.8.6" + resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz#80c516a4dc264c2a69115e7578d62581ff455ed9" + integrity sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ== + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^8.0.1": + version "8.1.0" + resolved "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz#3fdc2b6cb58935b21bfb8d1625eb1300484316e7" + integrity sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg== + dependencies: + "@sinonjs/commons" "^1.7.0" + +"@testing-library/dom@^9.3.3": + version "9.3.3" + resolved "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.3.tgz#108c23a5b0ef51121c26ae92eb3179416b0434f5" + integrity sha512-fB0R+fa3AUqbLHWyxXa2kGVtf1Fe1ZZFr0Zp6AIbIAzXb2mKbEXl+PCQNUOaq5lbTab5tfctfXRNsWXxa2f7Aw== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/runtime" "^7.12.5" + "@types/aria-query" "^5.0.1" + aria-query "5.1.3" + chalk "^4.1.0" + dom-accessibility-api "^0.5.9" + lz-string "^1.5.0" + pretty-format "^27.0.2" + +"@testing-library/jest-dom@^5.14.1": + version "5.17.0" + resolved "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz#5e97c8f9a15ccf4656da00fecab505728de81e0c" + integrity sha512-ynmNeT7asXyH3aSVv4vvX4Rb+0qjOhdNHnO/3vuZNqPmhDpV/+rCSGwQ7bLcmU2cJ4dvoheIO85LQj0IbJHEtg== + dependencies: + "@adobe/css-tools" "^4.0.1" + "@babel/runtime" "^7.9.2" + "@types/testing-library__jest-dom" "^5.9.1" + aria-query "^5.0.0" + chalk "^3.0.0" + css.escape "^1.5.1" + dom-accessibility-api "^0.5.6" + lodash "^4.17.15" + redent "^3.0.0" + +"@testing-library/user-event@^14.5.1": + version "14.5.1" + resolved "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.5.1.tgz#27337d72046d5236b32fd977edee3f74c71d332f" + integrity sha512-UCcUKrUYGj7ClomOo2SpNVvx4/fkd/2BbIHDCle8A0ax+P3bU7yJwDBDrS6ZwdTMARWTGODX1hEsCcO+7beJjg== + +"@tootallnate/once@1": + version "1.1.2" + resolved "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" + integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== + +"@tsconfig/node10@^1.0.7": + version "1.0.9" + resolved "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" + integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA== + +"@tsconfig/node12@^1.0.7": + version "1.0.11" + resolved "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + +"@tsconfig/node14@^1.0.0": + version "1.0.3" + resolved "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + +"@tsconfig/node16@^1.0.2": + version "1.0.4" + resolved "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" + integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== + +"@types/aria-query@^5.0.1": + version "5.0.4" + resolved "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz#1a31c3d378850d2778dabb6374d036dcba4ba708" + integrity sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw== + +"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": + version "7.20.5" + resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" + integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== + dependencies: + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + +"@types/babel__generator@*": + version "7.6.7" + resolved "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.7.tgz#a7aebf15c7bc0eb9abd638bdb5c0b8700399c9d0" + integrity sha512-6Sfsq+EaaLrw4RmdFWE9Onp63TOUue71AWb4Gpa6JxzgTYtimbM086WnYTy2U67AofR++QKCo08ZP6pwx8YFHQ== + dependencies: + "@babel/types" "^7.0.0" + +"@types/babel__template@*": + version "7.4.4" + resolved "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f" + integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": + version "7.20.4" + resolved "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.4.tgz#ec2c06fed6549df8bc0eb4615b683749a4a92e1b" + integrity sha512-mSM/iKUk5fDDrEV/e83qY+Cr3I1+Q3qqTuEn++HAWYjEa1+NxZr6CNrcJGf2ZTnq4HoFGC3zaTPZTobCzCFukA== + dependencies: + "@babel/types" "^7.20.7" + +"@types/estree@*", "@types/estree@^1.0.0": + version "1.0.5" + resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" + integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== + +"@types/glob@~7.2.0": + version "7.2.0" + resolved "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb" + integrity sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA== + dependencies: + "@types/minimatch" "*" + "@types/node" "*" + +"@types/graceful-fs@^4.1.2": + version "4.1.9" + resolved "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz#2a06bc0f68a20ab37b3e36aa238be6abdf49e8b4" + integrity sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ== + dependencies: + "@types/node" "*" + +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": + version "2.0.6" + resolved "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" + integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== + +"@types/istanbul-lib-report@*": + version "3.0.3" + resolved "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf" + integrity sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^3.0.0": + version "3.0.4" + resolved "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54" + integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ== + dependencies: + "@types/istanbul-lib-report" "*" + +"@types/jest@*": + version "29.5.10" + resolved "https://registry.npmjs.org/@types/jest/-/jest-29.5.10.tgz#a10fc5bab9e426081c12b2ef73d24d4f0c9b7f50" + integrity sha512-tE4yxKEphEyxj9s4inideLHktW/x6DwesIwWZ9NN1FKf9zbJYsnhBoA9vrHA/IuIOKwPa5PcFBNV4lpMIOEzyQ== + dependencies: + expect "^29.0.0" + pretty-format "^29.0.0" + +"@types/jest@27": + version "27.5.2" + resolved "https://registry.npmjs.org/@types/jest/-/jest-27.5.2.tgz#ec49d29d926500ffb9fd22b84262e862049c026c" + integrity sha512-mpT8LJJ4CMeeahobofYWIjFo0xonRS/HfxnVEPMPFSQdGUt1uHCnoPT7Zhb+sjDU2wz0oKV0OLUR0WzrHNgfeA== + dependencies: + jest-matcher-utils "^27.0.0" + pretty-format "^27.0.0" + +"@types/json-schema@^7.0.12", "@types/json-schema@^7.0.9": + version "7.0.15" + resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== + +"@types/mdast@^3.0.0": + version "3.0.15" + resolved "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz#49c524a263f30ffa28b71ae282f813ed000ab9f5" + integrity sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ== + dependencies: + "@types/unist" "^2" + +"@types/minimatch@*": + version "5.1.2" + resolved "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca" + integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA== + +"@types/node@*": + version "20.10.0" + resolved "https://registry.npmjs.org/@types/node/-/node-20.10.0.tgz#16ddf9c0a72b832ec4fcce35b8249cf149214617" + integrity sha512-D0WfRmU9TQ8I9PFx9Yc+EBHw+vSpIub4IDvQivcp26PtPrdMGAq5SDcpXEo/epqa/DXotVpekHiLNTg3iaKXBQ== + dependencies: + undici-types "~5.26.4" + +"@types/node@^17.0.42": + version "17.0.45" + resolved "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz#2c0fafd78705e7a18b7906b5201a522719dc5190" + integrity sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw== + +"@types/prettier@^2.1.5": + version "2.7.3" + resolved "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz#3e51a17e291d01d17d3fc61422015a933af7a08f" + integrity sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA== + +"@types/prop-types@*": + version "15.7.11" + resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz#2596fb352ee96a1379c657734d4b913a613ad563" + integrity sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng== + +"@types/react@^18.2.43": + version "18.2.43" + resolved "https://registry.npmjs.org/@types/react/-/react-18.2.43.tgz#c58e5abe241e6f71f60ce30e2a9aceb9d3a2a374" + integrity sha512-nvOV01ZdBdd/KW6FahSbcNplt2jCJfyWdTos61RYHV+FVv5L/g9AOX1bmbVcWcLFL8+KHQfh1zVIQrud6ihyQA== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + +"@types/resolve@1.20.2": + version "1.20.2" + resolved "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz#97d26e00cd4a0423b4af620abecf3e6f442b7975" + integrity sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q== + +"@types/scheduler@*": + version "0.16.8" + resolved "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz#ce5ace04cfeabe7ef87c0091e50752e36707deff" + integrity sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A== + +"@types/semver@^7.3.12", "@types/semver@^7.5.0": + version "7.5.6" + resolved "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz#c65b2bfce1bec346582c07724e3f8c1017a20339" + integrity sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A== + +"@types/shelljs@^0.8.11": + version "0.8.15" + resolved "https://registry.npmjs.org/@types/shelljs/-/shelljs-0.8.15.tgz#22c6ab9dfe05cec57d8e6cb1a95ea173aee9fcac" + integrity sha512-vzmnCHl6hViPu9GNLQJ+DZFd6BQI2DBTUeOvYHqkWQLMfKAAQYMb/xAmZkTogZI/vqXHCWkqDRymDI5p0QTi5Q== + dependencies: + "@types/glob" "~7.2.0" + "@types/node" "*" + +"@types/stack-utils@^2.0.0": + version "2.0.3" + resolved "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" + integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== + +"@types/testing-library__jest-dom@^5.9.1": + version "5.14.9" + resolved "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.9.tgz#0fb1e6a0278d87b6737db55af5967570b67cb466" + integrity sha512-FSYhIjFlfOpGSRyVoMBMuS3ws5ehFQODymf3vlI7U1K8c7PHwWwFY7VREfmsuzHSOnoKs/9/Y983ayOs7eRzqw== + dependencies: + "@types/jest" "*" + +"@types/unist@^2", "@types/unist@^2.0.2": + version "2.0.10" + resolved "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz#04ffa7f406ab628f7f7e97ca23e290cd8ab15efc" + integrity sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA== + +"@types/yargs-parser@*": + version "21.0.3" + resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" + integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== + +"@types/yargs@^16.0.0": + version "16.0.9" + resolved "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz#ba506215e45f7707e6cbcaf386981155b7ab956e" + integrity sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA== + dependencies: + "@types/yargs-parser" "*" + +"@types/yargs@^17.0.8": + version "17.0.32" + resolved "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz#030774723a2f7faafebf645f4e5a48371dca6229" + integrity sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog== + dependencies: + "@types/yargs-parser" "*" + +"@types/yauzl@^2.9.1": + version "2.10.3" + resolved "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz#e9b2808b4f109504a03cda958259876f61017999" + integrity sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q== + dependencies: + "@types/node" "*" + +"@typescript-eslint/eslint-plugin@^6.13.2": + version "6.13.2" + resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.13.2.tgz#2e03506c5362a65e43cb132c37c9ce2d3cb51470" + integrity sha512-3+9OGAWHhk4O1LlcwLBONbdXsAhLjyCFogJY/cWy2lxdVJ2JrcTF2pTGMaLl2AE7U1l31n8Py4a8bx5DLf/0dQ== + dependencies: + "@eslint-community/regexpp" "^4.5.1" + "@typescript-eslint/scope-manager" "6.13.2" + "@typescript-eslint/type-utils" "6.13.2" + "@typescript-eslint/utils" "6.13.2" + "@typescript-eslint/visitor-keys" "6.13.2" + debug "^4.3.4" + graphemer "^1.4.0" + ignore "^5.2.4" + natural-compare "^1.4.0" + semver "^7.5.4" + ts-api-utils "^1.0.1" + +"@typescript-eslint/parser@^6.13.2": + version "6.13.2" + resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.13.2.tgz#390b79cc9a57a5f904d197a201cc4b6bc4f9afb9" + integrity sha512-MUkcC+7Wt/QOGeVlM8aGGJZy1XV5YKjTpq9jK6r6/iLsGXhBVaGP5N0UYvFsu9BFlSpwY9kMretzdBH01rkRXg== + dependencies: + "@typescript-eslint/scope-manager" "6.13.2" + "@typescript-eslint/types" "6.13.2" + "@typescript-eslint/typescript-estree" "6.13.2" + "@typescript-eslint/visitor-keys" "6.13.2" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@5.62.0": + version "5.62.0" + resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c" + integrity sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w== + dependencies: + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" + +"@typescript-eslint/scope-manager@6.13.2": + version "6.13.2" + resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.2.tgz#5fa4e4adace028dafac212c770640b94e7b61052" + integrity sha512-CXQA0xo7z6x13FeDYCgBkjWzNqzBn8RXaE3QVQVIUm74fWJLkJkaHmHdKStrxQllGh6Q4eUGyNpMe0b1hMkXFA== + dependencies: + "@typescript-eslint/types" "6.13.2" + "@typescript-eslint/visitor-keys" "6.13.2" + +"@typescript-eslint/type-utils@6.13.2": + version "6.13.2" + resolved "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.13.2.tgz#ebec2da14a6bb7122e0fd31eea72a382c39c6102" + integrity sha512-Qr6ssS1GFongzH2qfnWKkAQmMUyZSyOr0W54nZNU1MDfo+U4Mv3XveeLZzadc/yq8iYhQZHYT+eoXJqnACM1tw== + dependencies: + "@typescript-eslint/typescript-estree" "6.13.2" + "@typescript-eslint/utils" "6.13.2" + debug "^4.3.4" + ts-api-utils "^1.0.1" + +"@typescript-eslint/types@5.62.0": + version "5.62.0" + resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" + integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== + +"@typescript-eslint/types@6.13.2": + version "6.13.2" + resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.2.tgz#c044aac24c2f6cefb8e921e397acad5417dd0ae6" + integrity sha512-7sxbQ+EMRubQc3wTfTsycgYpSujyVbI1xw+3UMRUcrhSy+pN09y/lWzeKDbvhoqcRbHdc+APLs/PWYi/cisLPg== + +"@typescript-eslint/typescript-estree@5.62.0": + version "5.62.0" + resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b" + integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA== + dependencies: + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/typescript-estree@6.13.2": + version "6.13.2" + resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.2.tgz#ae556ee154c1acf025b48d37c3ef95a1d55da258" + integrity sha512-SuD8YLQv6WHnOEtKv8D6HZUzOub855cfPnPMKvdM/Bh1plv1f7Q/0iFUDLKKlxHcEstQnaUU4QZskgQq74t+3w== + dependencies: + "@typescript-eslint/types" "6.13.2" + "@typescript-eslint/visitor-keys" "6.13.2" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.5.4" + ts-api-utils "^1.0.1" + +"@typescript-eslint/utils@6.13.2": + version "6.13.2" + resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.13.2.tgz#8eb89e53adc6d703a879b131e528807245486f89" + integrity sha512-b9Ptq4eAZUym4idijCRzl61oPCwwREcfDI8xGk751Vhzig5fFZR9CyzDz4Sp/nxSLBYxUPyh4QdIDqWykFhNmQ== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + "@types/json-schema" "^7.0.12" + "@types/semver" "^7.5.0" + "@typescript-eslint/scope-manager" "6.13.2" + "@typescript-eslint/types" "6.13.2" + "@typescript-eslint/typescript-estree" "6.13.2" + semver "^7.5.4" + +"@typescript-eslint/utils@^5.10.0": + version "5.62.0" + resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86" + integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@types/json-schema" "^7.0.9" + "@types/semver" "^7.3.12" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" + eslint-scope "^5.1.1" + semver "^7.3.7" + +"@typescript-eslint/visitor-keys@5.62.0": + version "5.62.0" + resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e" + integrity sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw== + dependencies: + "@typescript-eslint/types" "5.62.0" + eslint-visitor-keys "^3.3.0" + +"@typescript-eslint/visitor-keys@6.13.2": + version "6.13.2" + resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.2.tgz#e0a4a80cf842bb08e6127b903284166ac4a5594c" + integrity sha512-OGznFs0eAQXJsp+xSd6k/O1UbFi/K/L7WjqeRoFE7vadjAF9y0uppXhYNQNEqygjou782maGClOoZwPqF0Drlw== + dependencies: + "@typescript-eslint/types" "6.13.2" + eslint-visitor-keys "^3.4.1" + +"@ungap/structured-clone@^1.2.0": + version "1.2.0" + resolved "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" + integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== + +"@vitejs/plugin-vue@^4.5.2": + version "4.5.2" + resolved "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.5.2.tgz#1212d81bc83680e14448fefe55abd9fe1ed49ed1" + integrity sha512-UGR3DlzLi/SaVBPX0cnSyE37vqxU3O6chn8l0HJNzQzDia6/Au2A4xKv+iIJW8w2daf80G7TYHhi1pAUjdZ0bQ== + +"@vue/babel-helper-vue-transform-on@^1.1.5": + version "1.1.5" + resolved "https://registry.npmjs.org/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.1.5.tgz#a976486b21e108e545524fe41ffe3fc9bbc28c7f" + integrity sha512-SgUymFpMoAyWeYWLAY+MkCK3QEROsiUnfaw5zxOVD/M64KQs8D/4oK6Q5omVA2hnvEOE0SCkH2TZxs/jnnUj7w== + +"@vue/babel-plugin-jsx@^1.1.5": + version "1.1.5" + resolved "https://registry.npmjs.org/@vue/babel-plugin-jsx/-/babel-plugin-jsx-1.1.5.tgz#5088bae7dbb83531d94df3742ff650c12fd54973" + integrity sha512-nKs1/Bg9U1n3qSWnsHhCVQtAzI6aQXqua8j/bZrau8ywT1ilXQbK4FwEJGmU8fV7tcpuFvWmmN7TMmV1OBma1g== + dependencies: + "@babel/helper-module-imports" "^7.22.5" + "@babel/plugin-syntax-jsx" "^7.22.5" + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.5" + "@babel/types" "^7.22.5" + "@vue/babel-helper-vue-transform-on" "^1.1.5" + camelcase "^6.3.0" + html-tags "^3.3.1" + svg-tags "^1.0.0" + +"@vue/compiler-core@3.3.11": + version "3.3.11" + resolved "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.11.tgz#9fa26f8c81b9b34365f94ce1ed4d0e6e6f94a2ac" + integrity sha512-h97/TGWBilnLuRaj58sxNrsUU66fwdRKLOLQ9N/5iNDfp+DZhYH9Obhe0bXxhedl8fjAgpRANpiZfbgWyruQ0w== + dependencies: + "@babel/parser" "^7.23.5" + "@vue/shared" "3.3.11" + estree-walker "^2.0.2" + source-map-js "^1.0.2" + +"@vue/compiler-dom@3.3.11": + version "3.3.11" + resolved "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.11.tgz#36a76ea3a296d41bad133a6912cb0a847d969e4f" + integrity sha512-zoAiUIqSKqAJ81WhfPXYmFGwDRuO+loqLxvXmfUdR5fOitPoUiIeFI9cTTyv9MU5O1+ZZglJVTusWzy+wfk5hw== + dependencies: + "@vue/compiler-core" "3.3.11" + "@vue/shared" "3.3.11" + +"@vue/compiler-sfc@3.3.11", "@vue/compiler-sfc@^3.3.11": + version "3.3.11" + resolved "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.3.11.tgz#acfae240c875d067e0e2c9a4e2d910074408c73b" + integrity sha512-U4iqPlHO0KQeK1mrsxCN0vZzw43/lL8POxgpzcJweopmqtoYy9nljJzWDIQS3EfjiYhfdtdk9Gtgz7MRXnz3GA== + dependencies: + "@babel/parser" "^7.23.5" + "@vue/compiler-core" "3.3.11" + "@vue/compiler-dom" "3.3.11" + "@vue/compiler-ssr" "3.3.11" + "@vue/reactivity-transform" "3.3.11" + "@vue/shared" "3.3.11" + estree-walker "^2.0.2" + magic-string "^0.30.5" + postcss "^8.4.32" + source-map-js "^1.0.2" + +"@vue/compiler-ssr@3.3.11": + version "3.3.11" + resolved "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.3.11.tgz#598942a73b64f2bd3f95908b104a7fbb55fc41a2" + integrity sha512-Zd66ZwMvndxRTgVPdo+muV4Rv9n9DwQ4SSgWWKWkPFebHQfVYRrVjeygmmDmPewsHyznCNvJ2P2d6iOOhdv8Qg== + dependencies: + "@vue/compiler-dom" "3.3.11" + "@vue/shared" "3.3.11" + +"@vue/reactivity-transform@3.3.11": + version "3.3.11" + resolved "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.3.11.tgz#2bd486f4eff60c8724309925618891e722fcfadc" + integrity sha512-fPGjH0wqJo68A0wQ1k158utDq/cRyZNlFoxGwNScE28aUFOKFEnCBsvyD8jHn+0kd0UKVpuGuaZEQ6r9FJRqCg== + dependencies: + "@babel/parser" "^7.23.5" + "@vue/compiler-core" "3.3.11" + "@vue/shared" "3.3.11" + estree-walker "^2.0.2" + magic-string "^0.30.5" + +"@vue/reactivity@3.3.11", "@vue/reactivity@^3.3.11": + version "3.3.11" + resolved "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.11.tgz#91f8e6c9ac60a595a5278c836b197628fd947a0d" + integrity sha512-D5tcw091f0nuu+hXq5XANofD0OXnBmaRqMYl5B3fCR+mX+cXJIGNw/VNawBqkjLNWETrFW0i+xH9NvDbTPVh7g== + dependencies: + "@vue/shared" "3.3.11" + +"@vue/runtime-core@3.3.11": + version "3.3.11" + resolved "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.3.11.tgz#63defba57bc54c1dac68a95b56c2633b1419193d" + integrity sha512-g9ztHGwEbS5RyWaOpXuyIVFTschclnwhqEbdy5AwGhYOgc7m/q3NFwr50MirZwTTzX55JY8pSkeib9BX04NIpw== + dependencies: + "@vue/reactivity" "3.3.11" + "@vue/shared" "3.3.11" + +"@vue/runtime-dom@3.3.11", "@vue/runtime-dom@^3.3.11": + version "3.3.11" + resolved "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.3.11.tgz#1146d8d280b0fec4d2e18c4a4c8f8121d0cecc09" + integrity sha512-OlhtV1PVpbgk+I2zl+Y5rQtDNcCDs12rsRg71XwaA2/Rbllw6mBLMi57VOn8G0AjOJ4Mdb4k56V37+g8ukShpQ== + dependencies: + "@vue/runtime-core" "3.3.11" + "@vue/shared" "3.3.11" + csstype "^3.1.2" + +"@vue/server-renderer@3.3.11": + version "3.3.11" + resolved "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.3.11.tgz#409aed8031a125791e2143552975ecd1958ad601" + integrity sha512-AIWk0VwwxCAm4wqtJyxBylRTXSy1wCLOKbWxHaHiu14wjsNYtiRCSgVuqEPVuDpErOlRdNnuRgipQfXRLjLN5A== + dependencies: + "@vue/compiler-ssr" "3.3.11" + "@vue/shared" "3.3.11" + +"@vue/shared@3.3.11": + version "3.3.11" + resolved "https://registry.npmjs.org/@vue/shared/-/shared-3.3.11.tgz#f6a038e15237edefcc90dbfe7edb806dd355c7bd" + integrity sha512-u2G8ZQ9IhMWTMXaWqZycnK4UthG1fA238CD+DP4Dm4WJi5hdUKKLg0RMRaRpDPNMdkTwIDkp7WtD0Rd9BH9fLw== + +"@vue/test-utils@^2.4.2": + version "2.4.3" + resolved "https://registry.npmjs.org/@vue/test-utils/-/test-utils-2.4.3.tgz#d86786d809f918c77fb9e1fafaa2438af37c17d3" + integrity sha512-F4K7mF+ad++VlTrxMJVRnenKSJmO6fkQt2wpRDiKDesQMkfpniGWsqEi/JevxGBo2qEkwwjvTUAoiGJLNx++CA== + dependencies: + js-beautify "^1.14.9" + vue-component-type-helpers "^1.8.21" + +abab@^2.0.3, abab@^2.0.5: + version "2.0.6" + resolved "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" + integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== + +abbrev@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz#cf59829b8b4f03f89dda2771cb7f3653828c89bf" + integrity sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ== + +accepts@~1.3.4: + version "1.3.8" + resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + +acorn-globals@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" + integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg== + dependencies: + acorn "^7.1.1" + acorn-walk "^7.1.1" + +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn-walk@^7.1.1: + version "7.2.0" + resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" + integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== + +acorn-walk@^8.1.1: + version "8.3.0" + resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.0.tgz#2097665af50fd0cf7a2dfccd2b9368964e66540f" + integrity sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA== + +acorn@^7.1.1: + version "7.4.1" + resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== + +acorn@^8.2.4, acorn@^8.4.1, acorn@^8.8.2, acorn@^8.9.0: + version "8.11.2" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz#ca0d78b51895be5390a5903c5b3bdcdaf78ae40b" + integrity sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w== + +agent-base@6: + version "6.0.2" + resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + +aggregate-error@^4.0.0: + version "4.0.1" + resolved "https://registry.npmjs.org/aggregate-error/-/aggregate-error-4.0.1.tgz#25091fe1573b9e0be892aeda15c7c66a545f758e" + integrity sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w== + dependencies: + clean-stack "^4.0.0" + indent-string "^5.0.0" + +ajv@^6.12.4, ajv@~6.12.6: + version "6.12.6" + resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ajv@^8.0.1: + version "8.12.0" + resolved "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" + integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + +ansi-escapes@^4.2.1: + version "4.3.2" + resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== + dependencies: + type-fest "^0.21.3" + +ansi-escapes@^6.2.0: + version "6.2.0" + resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.0.tgz#8a13ce75286f417f1963487d86ba9f90dccf9947" + integrity sha512-kzRaCqXnpzWs+3z5ABPQiVke+iq0KXkHo8xiWV4RPTi5Yli0l97BEQuhXV1s7+aSU/fu1kUuxgS4MsQ0fRuygw== + dependencies: + type-fest "^3.0.0" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-regex@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" + integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== + +ansi-sequence-parser@^1.1.0: + version "1.1.1" + resolved "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz#e0aa1cdcbc8f8bb0b5bca625aac41f5f056973cf" + integrity sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg== + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^5.0.0: + version "5.2.0" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + +ansi-styles@^6.0.0, ansi-styles@^6.2.1: + version "6.2.1" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + +anymatch@^3.0.3: + version "3.1.3" + resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +aria-query@5.1.3: + version "5.1.3" + resolved "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz#19db27cd101152773631396f7a95a3b58c22c35e" + integrity sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ== + dependencies: + deep-equal "^2.0.5" + +aria-query@^5.0.0: + version "5.3.0" + resolved "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz#650c569e41ad90b51b3d7df5e5eed1c7549c103e" + integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A== + dependencies: + dequal "^2.0.3" + +array-buffer-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz#fabe8bc193fea865f317fe7807085ee0dee5aead" + integrity sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A== + dependencies: + call-bind "^1.0.2" + is-array-buffer "^3.0.1" + +array-includes@^3.1.6, array-includes@^3.1.7: + version "3.1.7" + resolved "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz#8cd2e01b26f7a3086cbc87271593fe921c62abda" + integrity sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" + is-string "^1.0.7" + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + +array.prototype.findlastindex@^1.2.3: + version "1.2.3" + resolved "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz#b37598438f97b579166940814e2c0493a4f50207" + integrity sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + get-intrinsic "^1.2.1" + +array.prototype.flat@^1.3.1, array.prototype.flat@^1.3.2: + version "1.3.2" + resolved "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz#1476217df8cff17d72ee8f3ba06738db5b387d18" + integrity sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + +array.prototype.flatmap@^1.3.1, array.prototype.flatmap@^1.3.2: + version "1.3.2" + resolved "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz#c9a7c6831db8e719d6ce639190146c24bbd3e527" + integrity sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + +array.prototype.tosorted@^1.1.1: + version "1.1.2" + resolved "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.2.tgz#620eff7442503d66c799d95503f82b475745cefd" + integrity sha512-HuQCHOlk1Weat5jzStICBCd83NxiIMwqDg/dHEsoefabn/hJRj5pVdWcPUSpRrwhwxZOsQassMpgN/xRYFBMIg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + get-intrinsic "^1.2.1" + +arraybuffer.prototype.slice@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz#98bd561953e3e74bb34938e77647179dfe6e9f12" + integrity sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw== + dependencies: + array-buffer-byte-length "^1.0.0" + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" + is-array-buffer "^3.0.2" + is-shared-array-buffer "^1.0.2" + +astral-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== + +async@^1.5.2: + version "1.5.2" + resolved "https://registry.npmjs.org/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" + integrity sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w== + +asynciterator.prototype@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz#8c5df0514936cdd133604dfcc9d3fb93f09b2b62" + integrity sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg== + dependencies: + has-symbols "^1.0.3" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +available-typed-arrays@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" + integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== + +babel-helper-vue-jsx-merge-props@^2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-2.0.3.tgz#22aebd3b33902328e513293a8e4992b384f9f1b6" + integrity sha512-gsLiKK7Qrb7zYJNgiXKpXblxbV5ffSwR0f5whkPAaBAR4fhi6bwRZxX9wBlIc5M/v8CCkXUbXZL4N/nSE97cqg== + +babel-jest@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz#a1bf8d61928edfefd21da27eb86a695bfd691444" + integrity sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg== + dependencies: + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/babel__core" "^7.1.14" + babel-plugin-istanbul "^6.1.1" + babel-preset-jest "^27.5.1" + chalk "^4.0.0" + graceful-fs "^4.2.9" + slash "^3.0.0" + +babel-plugin-istanbul@^6.1.1: + version "6.1.1" + resolved "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" + integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@istanbuljs/load-nyc-config" "^1.0.0" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-instrument "^5.0.4" + test-exclude "^6.0.0" + +babel-plugin-jest-hoist@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz#9be98ecf28c331eb9f5df9c72d6f89deb8181c2e" + integrity sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ== + dependencies: + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" + "@types/babel__core" "^7.0.0" + "@types/babel__traverse" "^7.0.6" + +babel-plugin-polyfill-corejs2@^0.4.6: + version "0.4.6" + resolved "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.6.tgz#b2df0251d8e99f229a8e60fc4efa9a68b41c8313" + integrity sha512-jhHiWVZIlnPbEUKSSNb9YoWcQGdlTLq7z1GHL4AjFxaoOUMuuEVJ+Y4pAaQUGOGk93YsVCKPbqbfw3m0SM6H8Q== + dependencies: + "@babel/compat-data" "^7.22.6" + "@babel/helper-define-polyfill-provider" "^0.4.3" + semver "^6.3.1" + +babel-plugin-polyfill-corejs3@^0.8.5: + version "0.8.6" + resolved "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.6.tgz#25c2d20002da91fe328ff89095c85a391d6856cf" + integrity sha512-leDIc4l4tUgU7str5BWLS2h8q2N4Nf6lGZP6UrNDxdtfF2g69eJ5L0H7S8A5Ln/arfFAfHor5InAdZuIOwZdgQ== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.4.3" + core-js-compat "^3.33.1" + +babel-plugin-polyfill-regenerator@^0.5.3: + version "0.5.3" + resolved "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.3.tgz#d4c49e4b44614607c13fb769bcd85c72bb26a4a5" + integrity sha512-8sHeDOmXC8csczMrYEOf0UTNa4yE2SxV5JGeT/LP1n0OYVDUUFPxG9vdk2AlDlIit4t+Kf0xCtpgXPBwnn/9pw== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.4.3" + +babel-plugin-syntax-jsx@^6.18.0: + version "6.18.0" + resolved "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946" + integrity sha512-qrPaCSo9c8RHNRHIotaufGbuOBN8rtdC4QrrFFc43vyWCCz7Kl7GL1PGaXtMGQZUXrkCjNEgxDfmAuAabr/rlw== + +babel-preset-current-node-syntax@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" + integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ== + dependencies: + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-bigint" "^7.8.3" + "@babel/plugin-syntax-class-properties" "^7.8.3" + "@babel/plugin-syntax-import-meta" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.8.3" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-top-level-await" "^7.8.3" + +babel-preset-jest@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz#91f10f58034cb7989cb4f962b69fa6eef6a6bc81" + integrity sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag== + dependencies: + babel-plugin-jest-hoist "^27.5.1" + babel-preset-current-node-syntax "^1.0.0" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +balanced-match@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz#dc70f920d78db8b858535795867bf48f820633d9" + integrity sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA== + +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +batch@0.6.1: + version "0.6.1" + resolved "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" + integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw== + +bl@^4.0.3: + version "4.1.0" + resolved "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + +boolbase@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +browser-process-hrtime@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" + integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== + +browserslist@^4.21.9, browserslist@^4.22.1: + version "4.22.1" + resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz#ba91958d1a59b87dab6fed8dfbcb3da5e2e9c619" + integrity sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ== + dependencies: + caniuse-lite "^1.0.30001541" + electron-to-chromium "^1.4.535" + node-releases "^2.0.13" + update-browserslist-db "^1.0.13" + +bser@2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" + integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== + dependencies: + node-int64 "^0.4.0" + +buffer-crc32@~0.2.3: + version "0.2.13" + resolved "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +buffer@^5.2.1, buffer@^5.5.0: + version "5.7.1" + resolved "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +builtin-modules@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" + integrity sha512-wxXCdllwGhI2kCC0MnvTGYTMvnVZTvqgypkiTI8Pa5tcz2i6VqsqwYGgqwXji+4RgCzms6EajE4IxiUH6HH8nQ== + +builtin-modules@^3.3.0: + version "3.3.0" + resolved "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" + integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw== + +builtins@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz#87f6db9ab0458be728564fa81d876d8d74552fa9" + integrity sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ== + dependencies: + semver "^7.0.0" + +bulma@^0.9.4: + version "0.9.4" + resolved "https://registry.npmjs.org/bulma/-/bulma-0.9.4.tgz#0ca8aeb1847a34264768dba26a064c8be72674a1" + integrity sha512-86FlT5+1GrsgKbPLRRY7cGDg8fsJiP/jzTqXXVqiUZZ2aZT8uemEOHlU1CDU+TxklPEZ11HZNNWclRBBecP4CQ== + +call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.4, call-bind@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513" + integrity sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ== + dependencies: + function-bind "^1.1.2" + get-intrinsic "^1.2.1" + set-function-length "^1.1.1" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camelcase@^5.0.0, camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +camelcase@^6.2.0, camelcase@^6.3.0: + version "6.3.0" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +caniuse-lite@^1.0.30001541: + version "1.0.30001565" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001565.tgz#a528b253c8a2d95d2b415e11d8b9942acc100c4f" + integrity sha512-xrE//a3O7TP0vaJ8ikzkD2c2NgcVUvsEe2IvFTntV4Yd1Z9FVzh+gW+enX96L0psrbaFMcVcH2l90xNuGDWc8w== + +chalk@5.3.0: + version "5.3.0" + resolved "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385" + integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== + +chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" + integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chalk@^4.0.0, chalk@^4.1.0: + version "4.1.2" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +char-regex@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" + integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== + +character-entities-legacy@^1.0.0: + version "1.1.4" + resolved "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz#94bc1845dce70a5bb9d2ecc748725661293d8fc1" + integrity sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA== + +character-entities@^1.0.0: + version "1.2.4" + resolved "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz#e12c3939b7eaf4e5b15e7ad4c5e28e1d48c5b16b" + integrity sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw== + +character-reference-invalid@^1.0.0: + version "1.1.4" + resolved "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz#083329cda0eae272ab3dbbf37e9a382c13af1560" + integrity sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg== + +chownr@^1.1.1: + version "1.1.4" + resolved "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + +ci-info@^3.2.0: + version "3.9.0" + resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" + integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== + +cjs-module-lexer@^1.0.0: + version "1.2.3" + resolved "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz#6c370ab19f8a3394e318fe682686ec0ac684d107" + integrity sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ== + +clean-stack@^4.0.0: + version "4.2.0" + resolved "https://registry.npmjs.org/clean-stack/-/clean-stack-4.2.0.tgz#c464e4cde4ac789f4e0735c5d75beb49d7b30b31" + integrity sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg== + dependencies: + escape-string-regexp "5.0.0" + +cli-cursor@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz#3cecfe3734bf4fe02a8361cbdc0f6fe28c6a57ea" + integrity sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg== + dependencies: + restore-cursor "^4.0.0" + +cli-truncate@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz#6cc28a2924fee9e25ce91e973db56c7066e6172a" + integrity sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA== + dependencies: + slice-ansi "^5.0.0" + string-width "^7.0.0" + +cliui@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" + integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^6.2.0" + +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + +clone@^2.1.2: + version "2.1.2" + resolved "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" + integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w== + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.npmjs.org/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== + +collect-v8-coverage@^1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz#c0b29bcd33bcd0779a1344c2136051e6afd3d9e9" + integrity sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q== + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +colord@^2.9.3: + version "2.9.3" + resolved "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43" + integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw== + +colorette@^2.0.20: + version "2.0.20" + resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@11.1.0: + version "11.1.0" + resolved "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz#62fdce76006a68e5c1ab3314dc92e800eb83d906" + integrity sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ== + +commander@^10.0.0: + version "10.0.1" + resolved "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" + integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== + +commander@^2.20.0: + version "2.20.3" + resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +config-chain@^1.1.13: + version "1.1.13" + resolved "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4" + integrity sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ== + dependencies: + ini "^1.3.4" + proto-list "~1.2.1" + +connect@^3: + version "3.7.0" + resolved "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8" + integrity sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ== + dependencies: + debug "2.6.9" + finalhandler "1.1.2" + parseurl "~1.3.3" + utils-merge "1.0.1" + +convert-source-map@^1.4.0, convert-source-map@^1.6.0: + version "1.9.0" + resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" + integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== + +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + +core-js-compat@^3.31.0, core-js-compat@^3.33.1: + version "3.33.3" + resolved "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.33.3.tgz#ec678b772c5a2d8a7c60a91c3a81869aa704ae01" + integrity sha512-cNzGqFsh3Ot+529GIXacjTJ7kegdt5fPXxCBVS1G0iaZpuo/tBz399ymceLJveQhFFZ8qThHiP3fzuoQjKN2ow== + dependencies: + browserslist "^4.22.1" + +cosmiconfig@^9.0.0: + version "9.0.0" + resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz#34c3fc58287b915f3ae905ab6dc3de258b55ad9d" + integrity sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg== + dependencies: + env-paths "^2.2.1" + import-fresh "^3.3.0" + js-yaml "^4.1.0" + parse-json "^5.2.0" + +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + +cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +css-functions-list@^3.2.1: + version "3.2.1" + resolved "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.2.1.tgz#2eb205d8ce9f9ce74c5c1d7490b66b77c45ce3ea" + integrity sha512-Nj5YcaGgBtuUmn1D7oHqPW0c9iui7xsTsj5lIX8ZgevdfhmjFfKB3r8moHJtNJnctnYXJyYX5I1pp90HM4TPgQ== + +css-tree@^2.3.1: + version "2.3.1" + resolved "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz#10264ce1e5442e8572fc82fbe490644ff54b5c20" + integrity sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw== + dependencies: + mdn-data "2.0.30" + source-map-js "^1.0.1" + +css.escape@^1.5.1: + version "1.5.1" + resolved "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb" + integrity sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg== + +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + +cssom@^0.4.4: + version "0.4.4" + resolved "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" + integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== + +cssom@~0.3.6: + version "0.3.8" + resolved "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" + integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== + +cssstyle@^2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" + integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== + dependencies: + cssom "~0.3.6" + +csstype@^3.0.2, csstype@^3.1.2: + version "3.1.2" + resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b" + integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ== + +data-urls@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" + integrity sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ== + dependencies: + abab "^2.0.3" + whatwg-mimetype "^2.3.0" + whatwg-url "^8.0.0" + +debug@2.6.9: + version "2.6.9" + resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@4, debug@4.3.4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: + version "4.3.4" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +debug@4.3.1: + version "4.3.1" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" + integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== + dependencies: + ms "2.1.2" + +debug@^3.2.7: + version "3.2.7" + resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + +decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== + +decimal.js@^10.2.1: + version "10.4.3" + resolved "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" + integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== + +decode-uri-component@^0.2.2: + version "0.2.2" + resolved "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" + integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== + +dedent@^0.7.0: + version "0.7.0" + resolved "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" + integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== + +deep-equal@^2.0.5: + version "2.2.3" + resolved "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz#af89dafb23a396c7da3e862abc0be27cf51d56e1" + integrity sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA== + dependencies: + array-buffer-byte-length "^1.0.0" + call-bind "^1.0.5" + es-get-iterator "^1.1.3" + get-intrinsic "^1.2.2" + is-arguments "^1.1.1" + is-array-buffer "^3.0.2" + is-date-object "^1.0.5" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + isarray "^2.0.5" + object-is "^1.1.5" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.5.1" + side-channel "^1.0.4" + which-boxed-primitive "^1.0.2" + which-collection "^1.0.1" + which-typed-array "^1.1.13" + +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +deepmerge@^4.2.2: + version "4.3.1" + resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== + +define-data-property@^1.0.1, define-data-property@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3" + integrity sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ== + dependencies: + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + +define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0, define-properties@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== + dependencies: + define-data-property "^1.0.1" + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + +del@^7.1.0: + version "7.1.0" + resolved "https://registry.npmjs.org/del/-/del-7.1.0.tgz#0de0044d556b649ff05387f1fa7c885e155fd1b6" + integrity sha512-v2KyNk7efxhlyHpjEvfyxaAihKKK0nWCuf6ZtqZcFFpQRG0bJ12Qsr0RpvsICMjAAZ8DOVCxrlqpxISlMHC4Kg== + dependencies: + globby "^13.1.2" + graceful-fs "^4.2.10" + is-glob "^4.0.3" + is-path-cwd "^3.0.0" + is-path-inside "^4.0.0" + p-map "^5.5.0" + rimraf "^3.0.2" + slash "^4.0.0" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== + +dequal@^2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" + integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== + +detect-newline@^3.0.0: + version "3.1.0" + resolved "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" + integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== + +devtools-protocol@0.0.901419: + version "0.0.901419" + resolved "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.901419.tgz#79b5459c48fe7e1c5563c02bd72f8fec3e0cebcd" + integrity sha512-4INMPwNm9XRpBukhNbF7OB6fNTTCaI8pzy/fXg0xQzAy5h3zL1P8xT3QazgKqBrb/hAYwIBizqDBZ7GtJE74QQ== + +diff-sequences@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327" + integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== + +diff-sequences@^29.6.3: + version "29.6.3" + resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" + integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== + +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== + dependencies: + esutils "^2.0.2" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +dom-accessibility-api@^0.5.6, dom-accessibility-api@^0.5.9: + version "0.5.16" + resolved "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453" + integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg== + +domexception@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" + integrity sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg== + dependencies: + webidl-conversions "^5.0.0" + +editorconfig@^1.0.3: + version "1.0.4" + resolved "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz#040c9a8e9a6c5288388b87c2db07028aa89f53a3" + integrity sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q== + dependencies: + "@one-ini/wasm" "0.1.1" + commander "^10.0.0" + minimatch "9.0.1" + semver "^7.5.3" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + +electron-to-chromium@^1.4.535: + version "1.4.601" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.601.tgz#cac69868548aee89961ffe63ff5a7716f0685b75" + integrity sha512-SpwUMDWe9tQu8JX5QCO1+p/hChAi9AE9UpoC3rcHVc+gdCGlbT3SGb5I1klgb952HRIyvt9wZhSz9bNBYz9swA== + +emittery@^0.8.1: + version "0.8.1" + resolved "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860" + integrity sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg== + +emoji-regex@^10.3.0: + version "10.3.0" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz#76998b9268409eb3dae3de989254d456e70cfe23" + integrity sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== + +end-of-stream@^1.1.0, end-of-stream@^1.4.1: + version "1.4.4" + resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +enhanced-resolve@^5.12.0: + version "5.15.0" + resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35" + integrity sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +env-paths@^2.2.1: + version "2.2.1" + resolved "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" + integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== + +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +es-abstract@^1.22.1: + version "1.22.3" + resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz#48e79f5573198de6dee3589195727f4f74bc4f32" + integrity sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA== + dependencies: + array-buffer-byte-length "^1.0.0" + arraybuffer.prototype.slice "^1.0.2" + available-typed-arrays "^1.0.5" + call-bind "^1.0.5" + es-set-tostringtag "^2.0.1" + es-to-primitive "^1.2.1" + function.prototype.name "^1.1.6" + get-intrinsic "^1.2.2" + get-symbol-description "^1.0.0" + globalthis "^1.0.3" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + internal-slot "^1.0.5" + is-array-buffer "^3.0.2" + is-callable "^1.2.7" + is-negative-zero "^2.0.2" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + is-string "^1.0.7" + is-typed-array "^1.1.12" + is-weakref "^1.0.2" + object-inspect "^1.13.1" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.5.1" + safe-array-concat "^1.0.1" + safe-regex-test "^1.0.0" + string.prototype.trim "^1.2.8" + string.prototype.trimend "^1.0.7" + string.prototype.trimstart "^1.0.7" + typed-array-buffer "^1.0.0" + typed-array-byte-length "^1.0.0" + typed-array-byte-offset "^1.0.0" + typed-array-length "^1.0.4" + unbox-primitive "^1.0.2" + which-typed-array "^1.1.13" + +es-get-iterator@^1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz#3ef87523c5d464d41084b2c3c9c214f1199763d6" + integrity sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + has-symbols "^1.0.3" + is-arguments "^1.1.1" + is-map "^2.0.2" + is-set "^2.0.2" + is-string "^1.0.7" + isarray "^2.0.5" + stop-iteration-iterator "^1.0.0" + +es-iterator-helpers@^1.0.12: + version "1.0.15" + resolved "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.15.tgz#bd81d275ac766431d19305923707c3efd9f1ae40" + integrity sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g== + dependencies: + asynciterator.prototype "^1.0.0" + call-bind "^1.0.2" + define-properties "^1.2.1" + es-abstract "^1.22.1" + es-set-tostringtag "^2.0.1" + function-bind "^1.1.1" + get-intrinsic "^1.2.1" + globalthis "^1.0.3" + has-property-descriptors "^1.0.0" + has-proto "^1.0.1" + has-symbols "^1.0.3" + internal-slot "^1.0.5" + iterator.prototype "^1.1.2" + safe-array-concat "^1.0.1" + +es-set-tostringtag@^2.0.1: + version "2.0.2" + resolved "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz#11f7cc9f63376930a5f20be4915834f4bc74f9c9" + integrity sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q== + dependencies: + get-intrinsic "^1.2.2" + has-tostringtag "^1.0.0" + hasown "^2.0.0" + +es-shim-unscopables@^1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz#1f6942e71ecc7835ed1c8a83006d8771a63a3763" + integrity sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw== + dependencies: + hasown "^2.0.0" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +esbuild@^0.19.3: + version "0.19.9" + resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.19.9.tgz#423a8f35153beb22c0b695da1cd1e6c0c8cdd490" + integrity sha512-U9CHtKSy+EpPsEBa+/A2gMs/h3ylBC0H0KSqIg7tpztHerLi6nrrcoUJAkNCEPumx8yJ+Byic4BVwHgRbN0TBg== + optionalDependencies: + "@esbuild/android-arm" "0.19.9" + "@esbuild/android-arm64" "0.19.9" + "@esbuild/android-x64" "0.19.9" + "@esbuild/darwin-arm64" "0.19.9" + "@esbuild/darwin-x64" "0.19.9" + "@esbuild/freebsd-arm64" "0.19.9" + "@esbuild/freebsd-x64" "0.19.9" + "@esbuild/linux-arm" "0.19.9" + "@esbuild/linux-arm64" "0.19.9" + "@esbuild/linux-ia32" "0.19.9" + "@esbuild/linux-loong64" "0.19.9" + "@esbuild/linux-mips64el" "0.19.9" + "@esbuild/linux-ppc64" "0.19.9" + "@esbuild/linux-riscv64" "0.19.9" + "@esbuild/linux-s390x" "0.19.9" + "@esbuild/linux-x64" "0.19.9" + "@esbuild/netbsd-x64" "0.19.9" + "@esbuild/openbsd-x64" "0.19.9" + "@esbuild/sunos-x64" "0.19.9" + "@esbuild/win32-arm64" "0.19.9" + "@esbuild/win32-ia32" "0.19.9" + "@esbuild/win32-x64" "0.19.9" + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + +escape-string-regexp@5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8" + integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw== + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + +escape-string-regexp@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +escodegen@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz#ba93bbb7a43986d29d6041f99f5262da773e2e17" + integrity sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w== + dependencies: + esprima "^4.0.1" + estraverse "^5.2.0" + esutils "^2.0.2" + optionalDependencies: + source-map "~0.6.1" + +eslint-compat-utils@^0.1.2: + version "0.1.2" + resolved "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.1.2.tgz#f45e3b5ced4c746c127cf724fb074cd4e730d653" + integrity sha512-Jia4JDldWnFNIru1Ehx1H5s9/yxiRHY/TimCuUc0jNexew3cF1gI6CYZil1ociakfWO3rRqFjl1mskBblB3RYg== + +eslint-config-prettier@^9.0.0: + version "9.1.0" + resolved "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz#31af3d94578645966c082fcb71a5846d3c94867f" + integrity sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw== + +eslint-config-standard@^17.0.0: + version "17.1.0" + resolved "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz#40ffb8595d47a6b242e07cbfd49dc211ed128975" + integrity sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q== + +eslint-import-resolver-node@^0.3.9: + version "0.3.9" + resolved "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac" + integrity sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g== + dependencies: + debug "^3.2.7" + is-core-module "^2.13.0" + resolve "^1.22.4" + +eslint-import-resolver-typescript@^3.6.1: + version "3.6.1" + resolved "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.6.1.tgz#7b983680edd3f1c5bce1a5829ae0bc2d57fe9efa" + integrity sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg== + dependencies: + debug "^4.3.4" + enhanced-resolve "^5.12.0" + eslint-module-utils "^2.7.4" + fast-glob "^3.3.1" + get-tsconfig "^4.5.0" + is-core-module "^2.11.0" + is-glob "^4.0.3" + +eslint-module-utils@^2.7.4, eslint-module-utils@^2.8.0: + version "2.8.0" + resolved "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz#e439fee65fc33f6bba630ff621efc38ec0375c49" + integrity sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw== + dependencies: + debug "^3.2.7" + +eslint-plugin-es-x@^7.1.0: + version "7.5.0" + resolved "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.5.0.tgz#d08d9cd155383e35156c48f736eb06561d07ba92" + integrity sha512-ODswlDSO0HJDzXU0XvgZ3lF3lS3XAZEossh15Q2UHjwrJggWeBoKqqEsLTZLXl+dh5eOAozG0zRcYtuE35oTuQ== + dependencies: + "@eslint-community/eslint-utils" "^4.1.2" + "@eslint-community/regexpp" "^4.6.0" + eslint-compat-utils "^0.1.2" + +eslint-plugin-import@^2.26.0: + version "2.29.0" + resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.0.tgz#8133232e4329ee344f2f612885ac3073b0b7e155" + integrity sha512-QPOO5NO6Odv5lpoTkddtutccQjysJuFxoPS7fAHO+9m9udNHvTCPSAMW9zGAYj8lAIdr40I8yPCdUYrncXtrwg== + dependencies: + array-includes "^3.1.7" + array.prototype.findlastindex "^1.2.3" + array.prototype.flat "^1.3.2" + array.prototype.flatmap "^1.3.2" + debug "^3.2.7" + doctrine "^2.1.0" + eslint-import-resolver-node "^0.3.9" + eslint-module-utils "^2.8.0" + hasown "^2.0.0" + is-core-module "^2.13.1" + is-glob "^4.0.3" + minimatch "^3.1.2" + object.fromentries "^2.0.7" + object.groupby "^1.0.1" + object.values "^1.1.7" + semver "^6.3.1" + tsconfig-paths "^3.14.2" + +eslint-plugin-jest@^27.6.0: + version "27.6.0" + resolved "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.6.0.tgz#e5c0cf735b3c8cad0ef9db5b565b2fc99f5e55ed" + integrity sha512-MTlusnnDMChbElsszJvrwD1dN3x6nZl//s4JD23BxB6MgR66TZlL064su24xEIS3VACfAoHV1vgyMgPw8nkdng== + dependencies: + "@typescript-eslint/utils" "^5.10.0" + +eslint-plugin-markdown@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/eslint-plugin-markdown/-/eslint-plugin-markdown-3.0.1.tgz#fc6765bdb5f82a75e2438d7fac619602f2abc38c" + integrity sha512-8rqoc148DWdGdmYF6WSQFT3uQ6PO7zXYgeBpHAOAakX/zpq+NvFYbDA/H7PYzHajwtmaOzAwfxyl++x0g1/N9A== + dependencies: + mdast-util-from-markdown "^0.8.5" + +eslint-plugin-n@^16.3.1: + version "16.3.1" + resolved "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-16.3.1.tgz#6cd377d1349fed10854b6535392e91fb4123193b" + integrity sha512-w46eDIkxQ2FaTHcey7G40eD+FhTXOdKudDXPUO2n9WNcslze/i/HT2qJ3GXjHngYSGDISIgPNhwGtgoix4zeOw== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + builtins "^5.0.1" + eslint-plugin-es-x "^7.1.0" + get-tsconfig "^4.7.0" + ignore "^5.2.4" + is-builtin-module "^3.2.1" + is-core-module "^2.12.1" + minimatch "^3.1.2" + resolve "^1.22.2" + semver "^7.5.3" + +eslint-plugin-promise@^6.0.0: + version "6.1.1" + resolved "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz#269a3e2772f62875661220631bd4dafcb4083816" + integrity sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig== + +eslint-plugin-react@^7.30.0: + version "7.33.2" + resolved "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.33.2.tgz#69ee09443ffc583927eafe86ffebb470ee737608" + integrity sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw== + dependencies: + array-includes "^3.1.6" + array.prototype.flatmap "^1.3.1" + array.prototype.tosorted "^1.1.1" + doctrine "^2.1.0" + es-iterator-helpers "^1.0.12" + estraverse "^5.3.0" + jsx-ast-utils "^2.4.1 || ^3.0.0" + minimatch "^3.1.2" + object.entries "^1.1.6" + object.fromentries "^2.0.6" + object.hasown "^1.1.2" + object.values "^1.1.6" + prop-types "^15.8.1" + resolve "^2.0.0-next.4" + semver "^6.3.1" + string.prototype.matchall "^4.0.8" + +eslint-plugin-require-path-exists@^1.1.9: + version "1.1.9" + resolved "https://registry.npmjs.org/eslint-plugin-require-path-exists/-/eslint-plugin-require-path-exists-1.1.9.tgz#ba79c4cc6fe2782c49c4ad396ae4a47d90b54231" + integrity sha512-moZRfrPr4GFyT/W8PHzjzC7D4Hnj7Us+GYj0fbVKQoPvP4xIF8VG702L1jzyhqE8eIYkcs8p1CoqSfjk9WkxBg== + dependencies: + builtin-modules "^1.1.1" + fs-plus "^3.0.0" + resolve "^1.1.7" + +eslint-plugin-tsdoc@^0.2.17: + version "0.2.17" + resolved "https://registry.npmjs.org/eslint-plugin-tsdoc/-/eslint-plugin-tsdoc-0.2.17.tgz#27789495bbd8778abbf92db1707fec2ed3dfe281" + integrity sha512-xRmVi7Zx44lOBuYqG8vzTXuL6IdGOeF9nHX17bjJ8+VE6fsxpdGem0/SBTmAwgYMKYB1WBkqRJVQ+n8GK041pA== + dependencies: + "@microsoft/tsdoc" "0.14.2" + "@microsoft/tsdoc-config" "0.16.2" + +eslint-plugin-vue@^9.1.1: + version "9.19.2" + resolved "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.19.2.tgz#7ab83a001a1ac8bccae013c5b9cb5d2c644fb376" + integrity sha512-CPDqTOG2K4Ni2o4J5wixkLVNwgctKXFu6oBpVJlpNq7f38lh9I80pRTouZSJ2MAebPJlINU/KTFSXyQfBUlymA== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + natural-compare "^1.4.0" + nth-check "^2.1.1" + postcss-selector-parser "^6.0.13" + semver "^7.5.4" + vue-eslint-parser "^9.3.1" + xml-name-validator "^4.0.0" + +eslint-scope@^5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +eslint-scope@^7.1.1, eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint@^8.17.0: + version "8.55.0" + resolved "https://registry.npmjs.org/eslint/-/eslint-8.55.0.tgz#078cb7b847d66f2c254ea1794fa395bf8e7e03f8" + integrity sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.4" + "@eslint/js" "8.55.0" + "@humanwhocodes/config-array" "^0.11.13" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + "@ungap/structured-clone" "^1.2.0" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" + esquery "^1.4.2" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + graphemer "^1.4.0" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + strip-ansi "^6.0.1" + text-table "^0.2.0" + +espree@^9.3.1, espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== + dependencies: + acorn "^8.9.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" + +esprima@^4.0.0, esprima@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esquery@^1.4.0, esquery@^1.4.2: + version "1.5.0" + resolved "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" + integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0: + version "5.3.0" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +estree-walker@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" + integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +eventemitter3@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" + integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== + +execa@8.0.1: + version "8.0.1" + resolved "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz#51f6a5943b580f963c3ca9c6321796db8cc39b8c" + integrity sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^8.0.1" + human-signals "^5.0.0" + is-stream "^3.0.0" + merge-stream "^2.0.0" + npm-run-path "^5.1.0" + onetime "^6.0.0" + signal-exit "^4.1.0" + strip-final-newline "^3.0.0" + +execa@^5.0.0: + version "5.1.1" + resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + +exit@^0.1.2: + version "0.1.2" + resolved "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" + integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== + +expect@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz#83ce59f1e5bdf5f9d2b94b61d2050db48f3fef74" + integrity sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw== + dependencies: + "@jest/types" "^27.5.1" + jest-get-type "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + +expect@^29.0.0: + version "29.7.0" + resolved "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" + integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw== + dependencies: + "@jest/expect-utils" "^29.7.0" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + +extract-zip@2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" + integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== + dependencies: + debug "^4.1.1" + get-stream "^5.1.0" + yauzl "^2.10.0" + optionalDependencies: + "@types/yauzl" "^2.9.1" + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-glob@^3.2.9, fast-glob@^3.3.0, fast-glob@^3.3.1, fast-glob@^3.3.2: + version "3.3.2" + resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fastest-levenshtein@^1.0.16: + version "1.0.16" + resolved "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" + integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== + +fastq@^1.6.0: + version "1.15.0" + resolved "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" + integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== + dependencies: + reusify "^1.0.4" + +fb-watchman@^2.0.0: + version "2.0.2" + resolved "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c" + integrity sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA== + dependencies: + bser "2.1.1" + +fd-slicer@~1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" + integrity sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g== + dependencies: + pend "~1.2.0" + +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + +file-entry-cache@^7.0.2: + version "7.0.2" + resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-7.0.2.tgz#2d61bb70ba89b9548e3035b7c9173fe91deafff0" + integrity sha512-TfW7/1iI4Cy7Y8L6iqNdZQVvdXn0f8B4QcIXmkIbtTIe/Okm/nSlHb4IwGzRVOd3WfSieCgvf5cMzEfySAIl0g== + dependencies: + flat-cache "^3.2.0" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +filter-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b" + integrity sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ== + +finalhandler@1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + 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" + +find-cache-dir@^3: + version "3.3.2" + resolved "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" + integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig== + dependencies: + commondir "^1.0.1" + make-dir "^3.0.2" + pkg-dir "^4.1.0" + +find-up@^4.0.0, find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat-cache@^3.0.4, flat-cache@^3.2.0: + version "3.2.0" + resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" + integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== + dependencies: + flatted "^3.2.9" + keyv "^4.5.3" + rimraf "^3.0.2" + +flatted@^3.2.9: + version "3.2.9" + resolved "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf" + integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== + +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + +foreground-child@^3.1.0: + version "3.1.1" + resolved "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz#1d173e776d75d2772fed08efe4a0de1ea1b12d0d" + integrity sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + +form-data@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" + integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + +fs-extra@^11.2.0: + version "11.2.0" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b" + integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-plus@^3.0.0: + version "3.1.1" + resolved "https://registry.npmjs.org/fs-plus/-/fs-plus-3.1.1.tgz#02c085ba0a013084cff2f3e89b17c60c1d9b4ab5" + integrity sha512-Se2PJdOWXqos1qVTkvqqjb0CSnfBnwwD+pq+z4ksT+e97mEShod/hrNg0TRCCsXPbJzcIq+NuzQhigunMWMJUA== + dependencies: + async "^1.5.2" + mkdirp "^0.5.1" + rimraf "^2.5.2" + underscore-plus "1.x" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@^2.3.2, fsevents@~2.3.2, fsevents@~2.3.3: + version "2.3.3" + resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.1, function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +function.prototype.name@^1.1.5, function.prototype.name@^1.1.6: + version "1.1.6" + resolved "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" + integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + functions-have-names "^1.2.3" + +functions-have-names@^1.2.3: + version "1.2.3" + resolved "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +get-caller-file@^2.0.1, get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-east-asian-width@^1.0.0: + version "1.2.0" + resolved "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz#5e6ebd9baee6fb8b7b6bd505221065f0cd91f64e" + integrity sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA== + +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: + version "1.2.2" + resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz#281b7622971123e1ef4b3c90fd7539306da93f3b" + integrity sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA== + dependencies: + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + +get-package-type@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" + integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== + +get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + +get-stream@^8.0.1: + version "8.0.1" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz#def9dfd71742cd7754a7761ed43749a27d02eca2" + integrity sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA== + +get-symbol-description@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" + integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + +get-tsconfig@^4.5.0, get-tsconfig@^4.7.0: + version "4.7.2" + resolved "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.2.tgz#0dcd6fb330391d46332f4c6c1bf89a6514c2ddce" + integrity sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A== + dependencies: + resolve-pkg-maps "^1.0.0" + +glob-parent@^5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob@^10.3.10, glob@^10.3.3: + version "10.3.10" + resolved "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz#0351ebb809fd187fe421ab96af83d3a70715df4b" + integrity sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g== + dependencies: + foreground-child "^3.1.0" + jackspeak "^2.3.5" + minimatch "^9.0.1" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-scurry "^1.10.1" + +glob@^7, glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: + version "7.2.3" + resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^8.0.3: + version "8.1.0" + resolved "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" + integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^5.0.1" + once "^1.3.0" + +global-modules@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" + integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== + dependencies: + global-prefix "^3.0.0" + +global-prefix@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97" + integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg== + dependencies: + ini "^1.3.5" + kind-of "^6.0.2" + which "^1.3.1" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globals@^13.19.0: + version "13.23.0" + resolved "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz#ef31673c926a0976e1f61dab4dca57e0c0a8af02" + integrity sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA== + dependencies: + type-fest "^0.20.2" + +globalthis@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" + integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== + dependencies: + define-properties "^1.1.3" + +globby@^11.1.0: + version "11.1.0" + resolved "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + +globby@^13.1.2: + version "13.2.2" + resolved "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz#63b90b1bf68619c2135475cbd4e71e66aa090592" + integrity sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w== + dependencies: + dir-glob "^3.0.1" + fast-glob "^3.3.0" + ignore "^5.2.4" + merge2 "^1.4.1" + slash "^4.0.0" + +globjoin@^0.1.4: + version "0.1.4" + resolved "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz#2f4494ac8919e3767c5cbb691e9f463324285d43" + integrity sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg== + +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + +graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.10, graceful-fs@^4.2.4, graceful-fs@^4.2.9: + version "4.2.11" + resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + +handlebars@^4.7.7: + version "4.7.8" + resolved "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz#41c42c18b1be2365439188c77c6afae71c0cd9e9" + integrity sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ== + dependencies: + minimist "^1.2.5" + neo-async "^2.6.2" + source-map "^0.6.1" + wordwrap "^1.0.0" + optionalDependencies: + uglify-js "^3.1.4" + +has-bigints@^1.0.1, has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-property-descriptors@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz#52ba30b6c5ec87fd89fa574bc1c39125c6f65340" + integrity sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg== + dependencies: + get-intrinsic "^1.2.2" + +has-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" + integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== + +has-symbols@^1.0.2, has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-tostringtag@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" + integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== + dependencies: + has-symbols "^1.0.2" + +hash-sum@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/hash-sum/-/hash-sum-2.0.0.tgz#81d01bb5de8ea4a214ad5d6ead1b523460b0b45a" + integrity sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg== + +hasown@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c" + integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA== + dependencies: + function-bind "^1.1.2" + +html-encoding-sniffer@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" + integrity sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ== + dependencies: + whatwg-encoding "^1.0.5" + +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + +html-tags@^3.3.1: + version "3.3.1" + resolved "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz#a04026a18c882e4bba8a01a3d39cfe465d40b5ce" + integrity sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ== + +http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" + integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + +http-proxy-agent@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" + integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== + dependencies: + "@tootallnate/once" "1" + agent-base "6" + debug "4" + +https-proxy-agent@5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" + integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== + dependencies: + agent-base "6" + debug "4" + +https-proxy-agent@^5.0.0: + version "5.0.1" + resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== + dependencies: + agent-base "6" + debug "4" + +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + +human-signals@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28" + integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ== + +husky@8.0.3: + version "8.0.3" + resolved "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz#4936d7212e46d1dea28fef29bb3a108872cd9184" + integrity sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg== + +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +ieee754@^1.1.13: + version "1.2.1" + resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +ignore@^5.2.0, ignore@^5.2.4, ignore@^5.3.0: + version "5.3.0" + resolved "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz#67418ae40d34d6999c95ff56016759c718c82f78" + integrity sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg== + +import-fresh@^3.2.1, import-fresh@^3.3.0: + version "3.3.0" + resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +import-local@^3.0.2: + version "3.1.0" + resolved "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" + integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + +indent-string@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz#4fd2980fccaf8622d14c64d694f4cf33c81951a5" + integrity sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@^2.0.3, inherits@^2.0.4: + version "2.0.4" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== + +ini@^1.3.4, ini@^1.3.5: + version "1.3.8" + resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + +internal-slot@^1.0.4, internal-slot@^1.0.5: + version "1.0.6" + resolved "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz#37e756098c4911c5e912b8edbf71ed3aa116f930" + integrity sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg== + dependencies: + get-intrinsic "^1.2.2" + hasown "^2.0.0" + side-channel "^1.0.4" + +interpret@^1.0.0: + version "1.4.0" + resolved "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" + integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== + +is-alphabetical@^1.0.0: + version "1.0.4" + resolved "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz#9e7d6b94916be22153745d184c298cbf986a686d" + integrity sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg== + +is-alphanumerical@^1.0.0: + version "1.0.4" + resolved "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz#7eb9a2431f855f6b1ef1a78e326df515696c4dbf" + integrity sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A== + dependencies: + is-alphabetical "^1.0.0" + is-decimal "^1.0.0" + +is-arguments@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe" + integrity sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.0" + is-typed-array "^1.1.10" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== + +is-async-function@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz#8e4418efd3e5d3a6ebb0164c05ef5afb69aa9646" + integrity sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA== + dependencies: + has-tostringtag "^1.0.0" + +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" + +is-boolean-object@^1.1.0: + version "1.1.2" + resolved "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-builtin-module@^3.2.1: + version "3.2.1" + resolved "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz#f03271717d8654cfcaf07ab0463faa3571581169" + integrity sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A== + dependencies: + builtin-modules "^3.3.0" + +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: + version "1.2.7" + resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + +is-core-module@^2.1.0, is-core-module@^2.11.0, is-core-module@^2.12.1, is-core-module@^2.13.0, is-core-module@^2.13.1: + version "2.13.1" + resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" + integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== + dependencies: + hasown "^2.0.0" + +is-date-object@^1.0.1, is-date-object@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" + +is-decimal@^1.0.0: + version "1.0.4" + resolved "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz#65a3a5958a1c5b63a706e1b333d7cd9f630d3fa5" + integrity sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-finalizationregistry@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz#c8749b65f17c133313e661b1289b95ad3dbd62e6" + integrity sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw== + dependencies: + call-bind "^1.0.2" + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-fullwidth-code-point@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz#fae3167c729e7463f8461ce512b080a49268aa88" + integrity sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ== + +is-fullwidth-code-point@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz#9609efced7c2f97da7b60145ef481c787c7ba704" + integrity sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA== + dependencies: + get-east-asian-width "^1.0.0" + +is-generator-fn@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" + integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== + +is-generator-function@^1.0.10: + version "1.0.10" + resolved "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" + integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== + dependencies: + has-tostringtag "^1.0.0" + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-hexadecimal@^1.0.0: + version "1.0.4" + resolved "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" + integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw== + +is-map@^2.0.1, is-map@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127" + integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg== + +is-module@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" + integrity sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g== + +is-negative-zero@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" + integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== + +is-number-object@^1.0.4: + version "1.0.7" + resolved "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" + integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== + dependencies: + has-tostringtag "^1.0.0" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-path-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-3.0.0.tgz#889b41e55c8588b1eb2a96a61d05740a674521c7" + integrity sha512-kyiNFFLU0Ampr6SDZitD/DwUo4Zs1nSdnygUBqsu3LooL00Qvb5j+UnvApUn/TTj1J3OuE6BTdQ5rudKmU2ZaA== + +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + +is-path-inside@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-4.0.0.tgz#805aeb62c47c1b12fc3fd13bfb3ed1e7430071db" + integrity sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA== + +is-plain-object@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" + integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== + +is-potential-custom-element-name@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" + integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== + +is-reference@1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7" + integrity sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ== + dependencies: + "@types/estree" "*" + +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-set@^2.0.1, is-set@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz#90755fa4c2562dc1c5d4024760d6119b94ca18ec" + integrity sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g== + +is-shared-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" + integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== + dependencies: + call-bind "^1.0.2" + +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +is-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" + integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== + +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + +is-typed-array@^1.1.10, is-typed-array@^1.1.12, is-typed-array@^1.1.9: + version "1.1.12" + resolved "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz#d0bab5686ef4a76f7a73097b95470ab199c57d4a" + integrity sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg== + dependencies: + which-typed-array "^1.1.11" + +is-typedarray@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== + +is-weakmap@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2" + integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA== + +is-weakref@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" + +is-weakset@^2.0.1: + version "2.0.2" + resolved "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz#4569d67a747a1ce5a994dfd4ef6dcea76e7c0a1d" + integrity sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: + version "3.2.2" + resolved "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" + integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== + +istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: + version "5.2.1" + resolved "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" + integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== + dependencies: + "@babel/core" "^7.12.3" + "@babel/parser" "^7.14.7" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.2.0" + semver "^6.3.0" + +istanbul-lib-report@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d" + integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^4.0.0" + supports-color "^7.1.0" + +istanbul-lib-source-maps@^4.0.0: + version "4.0.1" + resolved "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" + integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== + dependencies: + debug "^4.1.1" + istanbul-lib-coverage "^3.0.0" + source-map "^0.6.1" + +istanbul-reports@^3.1.3: + version "3.1.6" + resolved "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz#2544bcab4768154281a2f0870471902704ccaa1a" + integrity sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + +iterator.prototype@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz#5e29c8924f01916cb9335f1ff80619dcff22b0c0" + integrity sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w== + dependencies: + define-properties "^1.2.1" + get-intrinsic "^1.2.1" + has-symbols "^1.0.3" + reflect.getprototypeof "^1.0.4" + set-function-name "^2.0.1" + +jackspeak@2.1.1, jackspeak@^2.3.5: + version "2.1.1" + resolved "https://registry.npmjs.org/jackspeak/-/jackspeak-2.1.1.tgz#2a42db4cfbb7e55433c28b6f75d8b796af9669cd" + integrity sha512-juf9stUEwUaILepraGOWIJTLwg48bUnBmRqd2ln2Os1sW987zeoj/hzhbvRB95oMuS2ZTpjULmdwHNX4rzZIZw== + dependencies: + cliui "^8.0.1" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + +jest-changed-files@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz#a348aed00ec9bf671cc58a66fcbe7c3dfd6a68f5" + integrity sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw== + dependencies: + "@jest/types" "^27.5.1" + execa "^5.0.0" + throat "^6.0.1" + +jest-circus@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz#37a5a4459b7bf4406e53d637b49d22c65d125ecc" + integrity sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw== + dependencies: + "@jest/environment" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + dedent "^0.7.0" + expect "^27.5.1" + is-generator-fn "^2.0.0" + jest-each "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + jest-runtime "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + pretty-format "^27.5.1" + slash "^3.0.0" + stack-utils "^2.0.3" + throat "^6.0.1" + +jest-cli@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz#278794a6e6458ea8029547e6c6cbf673bd30b145" + integrity sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw== + dependencies: + "@jest/core" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + import-local "^3.0.2" + jest-config "^27.5.1" + jest-util "^27.5.1" + jest-validate "^27.5.1" + prompts "^2.0.1" + yargs "^16.2.0" + +jest-config@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz#5c387de33dca3f99ad6357ddeccd91bf3a0e4a41" + integrity sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA== + dependencies: + "@babel/core" "^7.8.0" + "@jest/test-sequencer" "^27.5.1" + "@jest/types" "^27.5.1" + babel-jest "^27.5.1" + chalk "^4.0.0" + ci-info "^3.2.0" + deepmerge "^4.2.2" + glob "^7.1.1" + graceful-fs "^4.2.9" + jest-circus "^27.5.1" + jest-environment-jsdom "^27.5.1" + jest-environment-node "^27.5.1" + jest-get-type "^27.5.1" + jest-jasmine2 "^27.5.1" + jest-regex-util "^27.5.1" + jest-resolve "^27.5.1" + jest-runner "^27.5.1" + jest-util "^27.5.1" + jest-validate "^27.5.1" + micromatch "^4.0.4" + parse-json "^5.2.0" + pretty-format "^27.5.1" + slash "^3.0.0" + strip-json-comments "^3.1.1" + +jest-diff@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def" + integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw== + dependencies: + chalk "^4.0.0" + diff-sequences "^27.5.1" + jest-get-type "^27.5.1" + pretty-format "^27.5.1" + +jest-diff@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" + integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw== + dependencies: + chalk "^4.0.0" + diff-sequences "^29.6.3" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + +jest-docblock@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz#14092f364a42c6108d42c33c8cf30e058e25f6c0" + integrity sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ== + dependencies: + detect-newline "^3.0.0" + +jest-each@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz#5bc87016f45ed9507fed6e4702a5b468a5b2c44e" + integrity sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ== + dependencies: + "@jest/types" "^27.5.1" + chalk "^4.0.0" + jest-get-type "^27.5.1" + jest-util "^27.5.1" + pretty-format "^27.5.1" + +jest-environment-jsdom@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz#ea9ccd1fc610209655a77898f86b2b559516a546" + integrity sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw== + dependencies: + "@jest/environment" "^27.5.1" + "@jest/fake-timers" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + jest-mock "^27.5.1" + jest-util "^27.5.1" + jsdom "^16.6.0" + +jest-environment-node@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz#dedc2cfe52fab6b8f5714b4808aefa85357a365e" + integrity sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw== + dependencies: + "@jest/environment" "^27.5.1" + "@jest/fake-timers" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + jest-mock "^27.5.1" + jest-util "^27.5.1" + +jest-get-type@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1" + integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw== + +jest-get-type@^29.6.3: + version "29.6.3" + resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" + integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== + +jest-haste-map@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz#9fd8bd7e7b4fa502d9c6164c5640512b4e811e7f" + integrity sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng== + dependencies: + "@jest/types" "^27.5.1" + "@types/graceful-fs" "^4.1.2" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.9" + jest-regex-util "^27.5.1" + jest-serializer "^27.5.1" + jest-util "^27.5.1" + jest-worker "^27.5.1" + micromatch "^4.0.4" + walker "^1.0.7" + optionalDependencies: + fsevents "^2.3.2" + +jest-jasmine2@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz#a037b0034ef49a9f3d71c4375a796f3b230d1ac4" + integrity sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ== + dependencies: + "@jest/environment" "^27.5.1" + "@jest/source-map" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + expect "^27.5.1" + is-generator-fn "^2.0.0" + jest-each "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + jest-runtime "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + pretty-format "^27.5.1" + throat "^6.0.1" + +jest-leak-detector@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz#6ec9d54c3579dd6e3e66d70e3498adf80fde3fb8" + integrity sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ== + dependencies: + jest-get-type "^27.5.1" + pretty-format "^27.5.1" + +jest-matcher-utils@^27.0.0, jest-matcher-utils@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab" + integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw== + dependencies: + chalk "^4.0.0" + jest-diff "^27.5.1" + jest-get-type "^27.5.1" + pretty-format "^27.5.1" + +jest-matcher-utils@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" + integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g== + dependencies: + chalk "^4.0.0" + jest-diff "^29.7.0" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + +jest-message-util@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz#bdda72806da10d9ed6425e12afff38cd1458b6cf" + integrity sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^27.5.1" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^27.5.1" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-message-util@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" + integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^29.6.3" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^29.7.0" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-mock@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz#19948336d49ef4d9c52021d34ac7b5f36ff967d6" + integrity sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og== + dependencies: + "@jest/types" "^27.5.1" + "@types/node" "*" + +jest-pnp-resolver@^1.2.2: + version "1.2.3" + resolved "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" + integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== + +jest-regex-util@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz#4da143f7e9fd1e542d4aa69617b38e4a78365b95" + integrity sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg== + +jest-resolve-dependencies@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz#d811ecc8305e731cc86dd79741ee98fed06f1da8" + integrity sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg== + dependencies: + "@jest/types" "^27.5.1" + jest-regex-util "^27.5.1" + jest-snapshot "^27.5.1" + +jest-resolve@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz#a2f1c5a0796ec18fe9eb1536ac3814c23617b384" + integrity sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw== + dependencies: + "@jest/types" "^27.5.1" + chalk "^4.0.0" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-pnp-resolver "^1.2.2" + jest-util "^27.5.1" + jest-validate "^27.5.1" + resolve "^1.20.0" + resolve.exports "^1.1.0" + slash "^3.0.0" + +jest-runner@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz#071b27c1fa30d90540805c5645a0ec167c7b62e5" + integrity sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ== + dependencies: + "@jest/console" "^27.5.1" + "@jest/environment" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" + emittery "^0.8.1" + graceful-fs "^4.2.9" + jest-docblock "^27.5.1" + jest-environment-jsdom "^27.5.1" + jest-environment-node "^27.5.1" + jest-haste-map "^27.5.1" + jest-leak-detector "^27.5.1" + jest-message-util "^27.5.1" + jest-resolve "^27.5.1" + jest-runtime "^27.5.1" + jest-util "^27.5.1" + jest-worker "^27.5.1" + source-map-support "^0.5.6" + throat "^6.0.1" + +jest-runtime@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz#4896003d7a334f7e8e4a53ba93fb9bcd3db0a1af" + integrity sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A== + dependencies: + "@jest/environment" "^27.5.1" + "@jest/fake-timers" "^27.5.1" + "@jest/globals" "^27.5.1" + "@jest/source-map" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + chalk "^4.0.0" + cjs-module-lexer "^1.0.0" + collect-v8-coverage "^1.0.0" + execa "^5.0.0" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-message-util "^27.5.1" + jest-mock "^27.5.1" + jest-regex-util "^27.5.1" + jest-resolve "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + slash "^3.0.0" + strip-bom "^4.0.0" + +jest-serializer@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz#81438410a30ea66fd57ff730835123dea1fb1f64" + integrity sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w== + dependencies: + "@types/node" "*" + graceful-fs "^4.2.9" + +jest-snapshot@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz#b668d50d23d38054a51b42c4039cab59ae6eb6a1" + integrity sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA== + dependencies: + "@babel/core" "^7.7.2" + "@babel/generator" "^7.7.2" + "@babel/plugin-syntax-typescript" "^7.7.2" + "@babel/traverse" "^7.7.2" + "@babel/types" "^7.0.0" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/babel__traverse" "^7.0.4" + "@types/prettier" "^2.1.5" + babel-preset-current-node-syntax "^1.0.0" + chalk "^4.0.0" + expect "^27.5.1" + graceful-fs "^4.2.9" + jest-diff "^27.5.1" + jest-get-type "^27.5.1" + jest-haste-map "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + jest-util "^27.5.1" + natural-compare "^1.4.0" + pretty-format "^27.5.1" + semver "^7.3.2" + +jest-util@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz#3ba9771e8e31a0b85da48fe0b0891fb86c01c2f9" + integrity sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw== + dependencies: + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + +jest-util@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" + integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== + dependencies: + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + +jest-validate@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz#9197d54dc0bdb52260b8db40b46ae668e04df067" + integrity sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ== + dependencies: + "@jest/types" "^27.5.1" + camelcase "^6.2.0" + chalk "^4.0.0" + jest-get-type "^27.5.1" + leven "^3.1.0" + pretty-format "^27.5.1" + +jest-watcher@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz#71bd85fb9bde3a2c2ec4dc353437971c43c642a2" + integrity sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw== + dependencies: + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + jest-util "^27.5.1" + string-length "^4.0.1" + +jest-worker@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jest@27: + version "27.5.1" + resolved "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz#dadf33ba70a779be7a6fc33015843b51494f63fc" + integrity sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ== + dependencies: + "@jest/core" "^27.5.1" + import-local "^3.0.2" + jest-cli "^27.5.1" + +jju@~1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz#a3abe2718af241a2b2904f84a625970f389ae32a" + integrity sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA== + +js-beautify@^1.14.9: + version "1.14.11" + resolved "https://registry.npmjs.org/js-beautify/-/js-beautify-1.14.11.tgz#57b17e009549ac845bdc58eddf8e1862e311314e" + integrity sha512-rPogWqAfoYh1Ryqqh2agUpVfbxAhbjuN1SmU86dskQUKouRiggUTCO4+2ym9UPXllc2WAp0J+T5qxn7Um3lCdw== + dependencies: + config-chain "^1.1.13" + editorconfig "^1.0.3" + glob "^10.3.3" + nopt "^7.2.0" + +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +jsdom@^16.6.0: + version "16.7.0" + resolved "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710" + integrity sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw== + dependencies: + abab "^2.0.5" + acorn "^8.2.4" + acorn-globals "^6.0.0" + cssom "^0.4.4" + cssstyle "^2.3.0" + data-urls "^2.0.0" + decimal.js "^10.2.1" + domexception "^2.0.1" + escodegen "^2.0.0" + form-data "^3.0.0" + html-encoding-sniffer "^2.0.1" + http-proxy-agent "^4.0.1" + 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 "^2.0.0" + webidl-conversions "^6.1.0" + whatwg-encoding "^1.0.5" + whatwg-mimetype "^2.3.0" + whatwg-url "^8.5.0" + ws "^7.4.6" + xml-name-validator "^3.0.0" + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +json5@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" + integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== + dependencies: + minimist "^1.2.0" + +json5@^2.2.3: + version "2.2.3" + resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + +jsonc-parser@^3.2.0: + version "3.2.0" + resolved "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76" + integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w== + +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +"jsx-ast-utils@^2.4.1 || ^3.0.0": + version "3.3.5" + resolved "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz#4766bd05a8e2a11af222becd19e15575e52a853a" + integrity sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ== + dependencies: + array-includes "^3.1.6" + array.prototype.flat "^1.3.1" + object.assign "^4.1.4" + object.values "^1.1.6" + +keyv@^4.5.3: + version "4.5.4" + resolved "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + +kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +kleur@^3.0.3: + version "3.0.3" + resolved "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" + integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== + +known-css-properties@^0.29.0: + version "0.29.0" + resolved "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.29.0.tgz#e8ba024fb03886f23cb882e806929f32d814158f" + integrity sha512-Ne7wqW7/9Cz54PDt4I3tcV+hAyat8ypyOGzYRJQfdxnnjeWsTxt1cy8pjvvKeI5kfXuyvULyeeAvwvvtAX3ayQ== + +leven@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +lilconfig@3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-3.0.0.tgz#f8067feb033b5b74dab4602a5f5029420be749bc" + integrity sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g== + +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== + +lint-staged@^15.2.0: + version "15.2.0" + resolved "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.0.tgz#3111534ca58096a3c8f70b044b6e7fe21b36f859" + integrity sha512-TFZzUEV00f+2YLaVPWBWGAMq7So6yQx+GG8YRMDeOEIf95Zn5RyiLMsEiX4KTNl9vq/w+NqRJkLA1kPIo15ufQ== + dependencies: + chalk "5.3.0" + commander "11.1.0" + debug "4.3.4" + execa "8.0.1" + lilconfig "3.0.0" + listr2 "8.0.0" + micromatch "4.0.5" + pidtree "0.6.0" + string-argv "0.3.2" + yaml "2.3.4" + +listr2@8.0.0: + version "8.0.0" + resolved "https://registry.npmjs.org/listr2/-/listr2-8.0.0.tgz#aa7c230995f8ce378585f7c96c0c6d1cefa4700d" + integrity sha512-u8cusxAcyqAiQ2RhYvV7kRKNLgUvtObIbhOX2NCXqvp1UU32xIg5CT22ykS2TPKJXZWJwtK3IKLiqAGlGNE+Zg== + dependencies: + cli-truncate "^4.0.0" + colorette "^2.0.20" + eventemitter3 "^5.0.1" + log-update "^6.0.0" + rfdc "^1.3.0" + wrap-ansi "^9.0.0" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lodash.truncate@^4.4.2: + version "4.4.2" + resolved "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" + integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw== + +lodash@^4.17.15, lodash@^4.17.21, lodash@^4.7.0: + version "4.17.21" + resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +log-update@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/log-update/-/log-update-6.0.0.tgz#0ddeb7ac6ad658c944c1de902993fce7c33f5e59" + integrity sha512-niTvB4gqvtof056rRIrTZvjNYE4rCUzO6X/X+kYjd7WFxXeJ0NwEFnRxX6ehkvv3jTwrXnNdtAak5XYZuIyPFw== + dependencies: + ansi-escapes "^6.2.0" + cli-cursor "^4.0.0" + slice-ansi "^7.0.0" + strip-ansi "^7.1.0" + wrap-ansi "^9.0.0" + +loose-envify@^1.1.0, loose-envify@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +"lru-cache@^9.1.1 || ^10.0.0": + version "10.1.0" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz#2098d41c2dc56500e6c88584aa656c84de7d0484" + integrity sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag== + +lunr@^2.3.9: + version "2.3.9" + resolved "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1" + integrity sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow== + +lz-string@^1.5.0: + version "1.5.0" + resolved "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941" + integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ== + +magic-string@^0.30.3, magic-string@^0.30.5: + version "0.30.5" + resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz#1994d980bd1c8835dc6e78db7cbd4ae4f24746f9" + integrity sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA== + dependencies: + "@jridgewell/sourcemap-codec" "^1.4.15" + +make-dir@^3.0.2: + version "3.1.0" + resolved "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + +make-dir@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" + integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== + dependencies: + semver "^7.5.3" + +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + +makeerror@1.0.12: + version "1.0.12" + resolved "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" + integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== + dependencies: + tmpl "1.0.5" + +marked@^4.3.0: + version "4.3.0" + resolved "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz#796362821b019f734054582038b116481b456cf3" + integrity sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A== + +mathml-tag-names@^2.1.3: + version "2.1.3" + resolved "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3" + integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg== + +mdast-util-from-markdown@^0.8.5: + version "0.8.5" + resolved "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.5.tgz#d1ef2ca42bc377ecb0463a987910dae89bd9a28c" + integrity sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ== + dependencies: + "@types/mdast" "^3.0.0" + mdast-util-to-string "^2.0.0" + micromark "~2.11.0" + parse-entities "^2.0.0" + unist-util-stringify-position "^2.0.0" + +mdast-util-to-string@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz#b8cfe6a713e1091cb5b728fc48885a4767f8b97b" + integrity sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w== + +mdn-data@2.0.30: + version "2.0.30" + resolved "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz#ce4df6f80af6cfbe218ecd5c552ba13c4dfa08cc" + integrity sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA== + +meow@^12.1.1: + version "12.1.1" + resolved "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz#e558dddbab12477b69b2e9a2728c327f191bace6" + integrity sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw== + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromark@~2.11.0: + version "2.11.4" + resolved "https://registry.npmjs.org/micromark/-/micromark-2.11.4.tgz#d13436138eea826383e822449c9a5c50ee44665a" + integrity sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA== + dependencies: + debug "^4.0.0" + parse-entities "^2.0.0" + +micromatch@4.0.5, micromatch@^4.0.4, micromatch@^4.0.5: + version "4.0.5" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +mimic-fn@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" + integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== + +min-indent@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" + integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== + +minimatch@9.0.1: + version "9.0.1" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz#8a555f541cf976c622daf078bb28f29fb927c253" + integrity sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^5.0.1: + version "5.1.6" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^9.0.1, minimatch@^9.0.3: + version "9.0.3" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" + integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== + dependencies: + brace-expansion "^2.0.1" + +minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0": + version "7.0.4" + resolved "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz#dbce03740f50a4786ba994c1fb908844d27b038c" + integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ== + +mkdirp@^0.5.1: + version "0.5.6" + resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + +mkdirp@^1, mkdirp@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +mkdirp@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz#e44e4c5607fb279c168241713cc6e0fea9adcb50" + integrity sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@^2.1.1: + version "2.1.3" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +nanoid@^3.3.7: + version "3.3.7" + resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" + integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +node-fetch@2.6.1: + version "2.6.1" + resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== + +node-int64@^0.4.0: + version "0.4.0" + resolved "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== + +node-releases@^2.0.13: + version "2.0.14" + resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" + integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== + +nopt@^7.2.0: + version "7.2.0" + resolved "https://registry.npmjs.org/nopt/-/nopt-7.2.0.tgz#067378c68116f602f552876194fd11f1292503d7" + integrity sha512-CVDtwCdhYIvnAzFoJ6NJ6dX3oga9/HyciQDnG1vQDjSLMeKLJ4A93ZqYKDrgYSr1FBY5/hMYC+2VCi24pgpkGA== + dependencies: + abbrev "^2.0.0" + +normalize-path@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +npm-run-path@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz#bc62f7f3f6952d9894bd08944ba011a6ee7b7e00" + integrity sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q== + dependencies: + path-key "^4.0.0" + +nth-check@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" + integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== + dependencies: + boolbase "^1.0.0" + +nwsapi@^2.2.0: + version "2.2.7" + resolved "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz#738e0707d3128cb750dddcfe90e4610482df0f30" + integrity sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ== + +object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-inspect@^1.13.1, object-inspect@^1.9.0: + version "1.13.1" + resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" + integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== + +object-is@^1.1.5: + version "1.1.5" + resolved "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" + integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.1.4: + version "4.1.4" + resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" + integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + has-symbols "^1.0.3" + object-keys "^1.1.1" + +object.entries@^1.1.6: + version "1.1.7" + resolved "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz#2b47760e2a2e3a752f39dd874655c61a7f03c131" + integrity sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + +object.fromentries@^2.0.6, object.fromentries@^2.0.7: + version "2.0.7" + resolved "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz#71e95f441e9a0ea6baf682ecaaf37fa2a8d7e616" + integrity sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + +object.groupby@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.1.tgz#d41d9f3c8d6c778d9cbac86b4ee9f5af103152ee" + integrity sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" + +object.hasown@^1.1.2: + version "1.1.3" + resolved "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.3.tgz#6a5f2897bb4d3668b8e79364f98ccf971bda55ae" + integrity sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA== + dependencies: + define-properties "^1.2.0" + es-abstract "^1.22.1" + +object.values@^1.1.6, object.values@^1.1.7: + version "1.1.7" + resolved "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz#617ed13272e7e1071b43973aa1655d9291b8442a" + integrity sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww== + dependencies: + ee-first "1.1.1" + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +onetime@^5.1.0, onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +onetime@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4" + integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== + dependencies: + mimic-fn "^4.0.0" + +optionator@^0.9.3: + version "0.9.3" + resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" + integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== + dependencies: + "@aashutoshrathi/word-wrap" "^1.2.3" + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +p-map@^5.5.0: + version "5.5.0" + resolved "https://registry.npmjs.org/p-map/-/p-map-5.5.0.tgz#054ca8ca778dfa4cf3f8db6638ccb5b937266715" + integrity sha512-VFqfGDHlx87K66yZrNdI4YGtD70IRyd+zSvgks6mzHPRNkoKy+9EKP4SFC77/vTTQYmRmti7dvqC+m5jBrBAcg== + dependencies: + aggregate-error "^4.0.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-entities@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz#53c6eb5b9314a1f4ec99fa0fdf7ce01ecda0cbe8" + integrity sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ== + dependencies: + character-entities "^1.0.0" + character-entities-legacy "^1.0.0" + character-reference-invalid "^1.0.0" + is-alphanumerical "^1.0.0" + is-decimal "^1.0.0" + is-hexadecimal "^1.0.0" + +parse-json@^5.2.0: + version "5.2.0" + resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + +parse5@6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + +parseurl@~1.3.2, parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +path-browserify@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" + integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-key@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18" + integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== + +path-parse@^1.0.6, path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-scurry@^1.10.1: + version "1.10.1" + resolved "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz#9ba6bf5aa8500fe9fd67df4f0d9483b2b0bfc698" + integrity sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ== + dependencies: + lru-cache "^9.1.1 || ^10.0.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pidtree@0.6.0: + version "0.6.0" + resolved "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz#90ad7b6d42d5841e69e0a2419ef38f8883aa057c" + integrity sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g== + +pirates@^4.0.4: + version "4.0.6" + resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" + integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== + +pkg-dir@4.2.0, pkg-dir@^4.1.0, pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +postcss-media-query-parser@^0.2.3: + version "0.2.3" + resolved "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz#27b39c6f4d94f81b1a73b8f76351c609e5cef244" + integrity sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig== + +postcss-resolve-nested-selector@^0.1.1: + version "0.1.1" + resolved "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz#29ccbc7c37dedfac304e9fff0bf1596b3f6a0e4e" + integrity sha512-HvExULSwLqHLgUy1rl3ANIqCsvMS0WHss2UOsXhXnQaZ9VCc2oBvIpXrl00IUFT5ZDITME0o6oiXeiHr2SAIfw== + +postcss-safe-parser@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.0.tgz#6273d4e5149e286db5a45bc6cf6eafcad464014a" + integrity sha512-ovehqRNVCpuFzbXoTb4qLtyzK3xn3t/CUBxOs8LsnQjQrShaB4lKiHoVqY8ANaC0hBMHq5QVWk77rwGklFUDrg== + +postcss-selector-parser@^6.0.13: + version "6.0.13" + resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz#d05d8d76b1e8e173257ef9d60b706a8e5e99bf1b" + integrity sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + +postcss-sorting@^8.0.2: + version "8.0.2" + resolved "https://registry.npmjs.org/postcss-sorting/-/postcss-sorting-8.0.2.tgz#6393385ece272baf74bee9820fb1b58098e4eeca" + integrity sha512-M9dkSrmU00t/jK7rF6BZSZauA5MAaBW4i5EnJXspMwt4iqTh/L9j6fgMnbElEOfyRyfLfVbIHj/R52zHzAPe1Q== + +postcss-value-parser@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" + integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== + +postcss@^8.4.21, postcss@^8.4.32: + version "8.4.32" + resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz#1dac6ac51ab19adb21b8b34fd2d93a86440ef6c9" + integrity sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw== + dependencies: + nanoid "^3.3.7" + picocolors "^1.0.0" + source-map-js "^1.0.2" + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +prettier@^3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/prettier/-/prettier-3.1.1.tgz#6ba9f23165d690b6cbdaa88cb0807278f7019848" + integrity sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw== + +pretty-format@^27.0.0, pretty-format@^27.0.2, pretty-format@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" + integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== + dependencies: + ansi-regex "^5.0.1" + ansi-styles "^5.0.0" + react-is "^17.0.1" + +pretty-format@^29.0.0, pretty-format@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" + integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== + dependencies: + "@jest/schemas" "^29.6.3" + ansi-styles "^5.0.0" + react-is "^18.0.0" + +progress@2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/progress/-/progress-2.0.1.tgz#c9242169342b1c29d275889c95734621b1952e31" + integrity sha512-OE+a6vzqazc+K6LxJrX5UPyKFvGnL5CYmq2jFGNIBWHpc4QyE49/YOumcrpQFJpfejmvRtbJzgO1zPmMCqlbBg== + +promise-polyfill@^8.2.3: + version "8.3.0" + resolved "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.3.0.tgz#9284810268138d103807b11f4e23d5e945a4db63" + integrity sha512-H5oELycFml5yto/atYqmjyigJoAo3+OXwolYiH7OfQuYlAqhxNvTfiNMbV9hsC6Yp83yE5r2KTVmtrG6R9i6Pg== + +prompts@^2.0.1: + version "2.4.2" + resolved "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" + integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== + dependencies: + kleur "^3.0.3" + sisteransi "^1.0.5" + +prop-types@^15.8.1: + version "15.8.1" + resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + +proto-list@~1.2.1: + version "1.2.4" + resolved "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" + integrity sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA== + +proxy-from-env@1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + +psl@^1.1.33: + version "1.9.0" + resolved "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" + integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode@^2.1.0, punycode@^2.1.1: + version "2.3.1" + resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +puppeteer-to-istanbul@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/puppeteer-to-istanbul/-/puppeteer-to-istanbul-1.4.0.tgz#451dced6f42652448f55cf0bc780b35512c8d1b0" + integrity sha512-dzW8u/PMqMZppvoXCFod8IkCTI2JL0yP2YUBbaALnX+iJJ6gqjk77fIoK9MqnMqRZAcoa81GLFfZExakWg/Q4Q== + dependencies: + clone "^2.1.2" + mkdirp "^1.0.4" + v8-to-istanbul "^1.2.1" + yargs "^15.3.1" + +puppeteer@^10: + version "10.4.0" + resolved "https://registry.npmjs.org/puppeteer/-/puppeteer-10.4.0.tgz#a6465ff97fda0576c4ac29601406f67e6fea3dc7" + integrity sha512-2cP8mBoqnu5gzAVpbZ0fRaobBWZM8GEUF4I1F6WbgHrKV/rz7SX8PG2wMymZgD0wo0UBlg2FBPNxlF/xlqW6+w== + dependencies: + debug "4.3.1" + devtools-protocol "0.0.901419" + extract-zip "2.0.1" + https-proxy-agent "5.0.0" + node-fetch "2.6.1" + pkg-dir "4.2.0" + progress "2.0.1" + proxy-from-env "1.1.0" + rimraf "3.0.2" + tar-fs "2.0.0" + unbzip2-stream "1.3.3" + ws "7.4.6" + +query-string@*, query-string@7.1.3: + version "7.1.3" + resolved "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz#a1cf90e994abb113a325804a972d98276fe02328" + integrity sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg== + dependencies: + decode-uri-component "^0.2.2" + filter-obj "^1.1.0" + split-on-first "^1.0.0" + strict-uri-encode "^2.0.0" + +querystringify@^2.1.1: + version "2.2.0" + resolved "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +react-dom@^18.1.0: + version "18.2.0" + resolved "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" + integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== + dependencies: + loose-envify "^1.1.0" + scheduler "^0.23.0" + +react-is@^16.13.1: + version "16.13.1" + resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + +react-is@^17.0.1: + version "17.0.2" + resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" + integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== + +react-is@^18.0.0: + version "18.2.0" + resolved "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" + integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== + +react@^18.1.0: + version "18.2.0" + resolved "https://registry.npmjs.org/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" + integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== + dependencies: + loose-envify "^1.1.0" + +readable-stream@^3.1.1, readable-stream@^3.4.0: + version "3.6.2" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +rebound@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/rebound/-/rebound-0.1.0.tgz#0638c61a93666bb515a58a03e1cfb34021e88b72" + integrity sha512-pKCVuoHvyKPr0tWS+CTCbJcUgifaH3K0bcZlHZpUS7GdNK/m78QgndlZH6+JpvXzuTwbUKZCiHgix0sce433uw== + +rechoir@^0.6.2: + version "0.6.2" + resolved "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" + integrity sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw== + dependencies: + resolve "^1.1.6" + +redent@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" + integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg== + dependencies: + indent-string "^4.0.0" + strip-indent "^3.0.0" + +reflect.getprototypeof@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz#aaccbf41aca3821b87bb71d9dcbc7ad0ba50a3f3" + integrity sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" + globalthis "^1.0.3" + which-builtin-type "^1.1.3" + +regenerate-unicode-properties@^10.1.0: + version "10.1.1" + resolved "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz#6b0e05489d9076b04c436f318d9b067bba459480" + integrity sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q== + dependencies: + regenerate "^1.4.2" + +regenerate@^1.4.2: + version "1.4.2" + resolved "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" + integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== + +regenerator-runtime@^0.14.0: + version "0.14.0" + resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45" + integrity sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA== + +regenerator-transform@^0.15.2: + version "0.15.2" + resolved "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz#5bbae58b522098ebdf09bca2f83838929001c7a4" + integrity sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg== + dependencies: + "@babel/runtime" "^7.8.4" + +regexp.prototype.flags@^1.5.0, regexp.prototype.flags@^1.5.1: + version "1.5.1" + resolved "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz#90ce989138db209f81492edd734183ce99f9677e" + integrity sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + set-function-name "^2.0.0" + +regexpu-core@^5.3.1: + version "5.3.2" + resolved "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz#11a2b06884f3527aec3e93dbbf4a3b958a95546b" + integrity sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ== + dependencies: + "@babel/regjsgen" "^0.8.0" + regenerate "^1.4.2" + regenerate-unicode-properties "^10.1.0" + regjsparser "^0.9.1" + unicode-match-property-ecmascript "^2.0.0" + unicode-match-property-value-ecmascript "^2.1.0" + +regjsparser@^0.9.1: + version "0.9.1" + resolved "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz#272d05aa10c7c1f67095b1ff0addae8442fc5709" + integrity sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ== + dependencies: + jsesc "~0.5.0" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve-pkg-maps@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" + integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== + +resolve.exports@^1.1.0: + version "1.1.1" + resolved "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.1.tgz#05cfd5b3edf641571fd46fa608b610dda9ead999" + integrity sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ== + +resolve@^1.1.6, resolve@^1.1.7, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.22.0, resolve@^1.22.1, resolve@^1.22.2, resolve@^1.22.4: + version "1.22.8" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +resolve@^2.0.0-next.4: + version "2.0.0-next.5" + resolved "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz#6b0ec3107e671e52b68cd068ef327173b90dc03c" + integrity sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +resolve@~1.19.0: + version "1.19.0" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz#1af5bf630409734a067cae29318aac7fa29a267c" + integrity sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg== + dependencies: + is-core-module "^2.1.0" + path-parse "^1.0.6" + +restore-cursor@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz#519560a4318975096def6e609d44100edaa4ccb9" + integrity sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rfdc@^1.3.0: + version "1.3.0" + resolved "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" + integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== + +rimraf@3.0.2, rimraf@^3.0.0, rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +rimraf@^2.5.2: + version "2.7.1" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +rimraf@~2.6.2: + version "2.6.3" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + +rollup@4.7.0, rollup@^4.2.0: + version "4.7.0" + resolved "https://registry.npmjs.org/rollup/-/rollup-4.7.0.tgz#04173cb398cd7643b9f71846dfbbda0e94fd0b1f" + integrity sha512-7Kw0dUP4BWH78zaZCqF1rPyQ8D5DSU6URG45v1dqS/faNsx9WXyess00uTOZxKr7oR/4TOjO1CPudT8L1UsEgw== + optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.7.0" + "@rollup/rollup-android-arm64" "4.7.0" + "@rollup/rollup-darwin-arm64" "4.7.0" + "@rollup/rollup-darwin-x64" "4.7.0" + "@rollup/rollup-linux-arm-gnueabihf" "4.7.0" + "@rollup/rollup-linux-arm64-gnu" "4.7.0" + "@rollup/rollup-linux-arm64-musl" "4.7.0" + "@rollup/rollup-linux-riscv64-gnu" "4.7.0" + "@rollup/rollup-linux-x64-gnu" "4.7.0" + "@rollup/rollup-linux-x64-musl" "4.7.0" + "@rollup/rollup-win32-arm64-msvc" "4.7.0" + "@rollup/rollup-win32-ia32-msvc" "4.7.0" + "@rollup/rollup-win32-x64-msvc" "4.7.0" + fsevents "~2.3.2" + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +safe-array-concat@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz#91686a63ce3adbea14d61b14c99572a8ff84754c" + integrity sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + has-symbols "^1.0.3" + isarray "^2.0.5" + +safe-buffer@^5.1.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-regex-test@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" + integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + is-regex "^1.1.4" + +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +saxes@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" + integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== + dependencies: + xmlchars "^2.2.0" + +scheduler@^0.23.0: + version "0.23.0" + resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" + integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== + dependencies: + loose-envify "^1.1.0" + +semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: + version "6.3.1" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.0.0, semver@^7.3.2, semver@^7.3.6, semver@^7.3.7, semver@^7.5.3, semver@^7.5.4: + version "7.5.4" + resolved "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + +serialize-javascript@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz#b206efb27c3da0b0ab6b52f48d170b7996458e5c" + integrity sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w== + dependencies: + randombytes "^2.1.0" + +serve-index@^1.9.1: + version "1.9.1" + resolved "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" + integrity sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw== + dependencies: + accepts "~1.3.4" + batch "0.6.1" + debug "2.6.9" + escape-html "~1.0.3" + http-errors "~1.6.2" + mime-types "~2.1.17" + parseurl "~1.3.2" + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== + +set-function-length@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz#4bc39fafb0307224a33e106a7d35ca1218d659ed" + integrity sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ== + dependencies: + define-data-property "^1.1.1" + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + +set-function-name@^2.0.0, set-function-name@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz#12ce38b7954310b9f61faa12701620a0c882793a" + integrity sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA== + dependencies: + define-data-property "^1.0.1" + functions-have-names "^1.2.3" + has-property-descriptors "^1.0.0" + +setprototypeof@1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +shelljs@^0.8.5: + version "0.8.5" + resolved "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c" + integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow== + dependencies: + glob "^7.0.0" + interpret "^1.0.0" + rechoir "^0.6.2" + +shiki@^0.14.1: + version "0.14.5" + resolved "https://registry.npmjs.org/shiki/-/shiki-0.14.5.tgz#375dd214e57eccb04f0daf35a32aa615861deb93" + integrity sha512-1gCAYOcmCFONmErGTrS1fjzJLA7MGZmKzrBNX7apqSwhyITJg2O102uFzXUeBxNnEkDA9vHIKLyeKq0V083vIw== + dependencies: + ansi-sequence-parser "^1.1.0" + jsonc-parser "^3.2.0" + vscode-oniguruma "^1.7.0" + vscode-textmate "^8.0.0" + +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + +signal-exit@^3.0.2, signal-exit@^3.0.3: + version "3.0.7" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +signal-exit@^4.0.1, signal-exit@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + +sisteransi@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +slash@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" + integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== + +slice-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + +slice-ansi@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz#b73063c57aa96f9cd881654b15294d95d285c42a" + integrity sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ== + dependencies: + ansi-styles "^6.0.0" + is-fullwidth-code-point "^4.0.0" + +slice-ansi@^7.0.0: + version "7.1.0" + resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz#cd6b4655e298a8d1bdeb04250a433094b347b9a9" + integrity sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg== + dependencies: + ansi-styles "^6.2.1" + is-fullwidth-code-point "^5.0.0" + +smob@^1.0.0: + version "1.4.1" + resolved "https://registry.npmjs.org/smob/-/smob-1.4.1.tgz#66270e7df6a7527664816c5b577a23f17ba6f5b5" + integrity sha512-9LK+E7Hv5R9u4g4C3p+jjLstaLe11MDsL21UpYaCNmapvMkYhqCV4A/f/3gyH8QjMyh6l68q9xC85vihY9ahMQ== + +source-map-js@^1.0.1, source-map-js@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + +source-map-support@^0.5.6, source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +source-map@^0.7.3: + version "0.7.4" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" + integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== + +split-on-first@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" + integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw== + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + +stack-utils@^2.0.3: + version "2.0.6" + resolved "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" + integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== + dependencies: + escape-string-regexp "^2.0.0" + +"statuses@>= 1.4.0 < 2", statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== + +stop-iteration-iterator@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz#6a60be0b4ee757d1ed5254858ec66b10c49285e4" + integrity sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ== + dependencies: + internal-slot "^1.0.4" + +strict-uri-encode@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" + integrity sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ== + +string-argv@0.3.2: + version "0.3.2" + resolved "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz#2b6d0ef24b656274d957d54e0a4bbf6153dc02b6" + integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q== + +string-length@^4.0.1: + version "4.0.2" + resolved "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" + integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== + dependencies: + char-regex "^1.0.2" + strip-ansi "^6.0.0" + +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/string-width/-/string-width-7.0.0.tgz#14aa1b7aaa126d5b64fa79d3c894da8a9650ba06" + integrity sha512-GPQHj7row82Hjo9hKZieKcHIhaAIKOJvFSIZXuCU9OASVZrMNUaZuz++SPVrBjnLsnk4k+z9f2EIypgxf2vNFw== + dependencies: + emoji-regex "^10.3.0" + get-east-asian-width "^1.0.0" + strip-ansi "^7.1.0" + +string.prototype.matchall@^4.0.8: + version "4.0.10" + resolved "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz#a1553eb532221d4180c51581d6072cd65d1ee100" + integrity sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" + has-symbols "^1.0.3" + internal-slot "^1.0.5" + regexp.prototype.flags "^1.5.0" + set-function-name "^2.0.0" + side-channel "^1.0.4" + +string.prototype.trim@^1.2.8: + version "1.2.8" + resolved "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz#f9ac6f8af4bd55ddfa8895e6aea92a96395393bd" + integrity sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + +string.prototype.trimend@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz#1bb3afc5008661d73e2dc015cd4853732d6c471e" + integrity sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + +string.prototype.trimstart@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz#d4cdb44b83a4737ffbac2d406e405d43d0184298" + integrity sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^7.1.0: + version "7.1.0" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== + +strip-bom@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" + integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +strip-final-newline@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" + integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== + +strip-indent@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" + integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== + dependencies: + min-indent "^1.0.0" + +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +stylelint-config-css-modules@^4.2.0: + version "4.3.0" + resolved "https://registry.npmjs.org/stylelint-config-css-modules/-/stylelint-config-css-modules-4.3.0.tgz#aa2a861f9cf30c31676013db5412c15048ca97c3" + integrity sha512-KvIvhzzjpcjHKkGSPkQCueoZJHrb6sZ6GCtrQb/J45HQTBVwJAeNYXaSZZK6ZQOC7NxJ4v5kLxpQLDiCK6zzgw== + optionalDependencies: + stylelint-scss "^5.0.0 || ^6.0.0" + +stylelint-config-html@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/stylelint-config-html/-/stylelint-config-html-1.1.0.tgz#999db19aea713b7ff6dde92ada76e4c1bd812b66" + integrity sha512-IZv4IVESjKLumUGi+HWeb7skgO6/g4VMuAYrJdlqQFndgbj6WJAXPhaysvBiXefX79upBdQVumgYcdd17gCpjQ== + +stylelint-config-recess-order@^4.2.0: + version "4.4.0" + resolved "https://registry.npmjs.org/stylelint-config-recess-order/-/stylelint-config-recess-order-4.4.0.tgz#1c57dbad7c62647fb4699157100b97e80cc97b00" + integrity sha512-Q99kvZyIM/aoPEV4dRDkzD3fZLzH0LXi+pawCf1r700uUeF/PHQ5PZXjwFUuGrWhOzd1N+cuVm+OUGsY2fRN5A== + dependencies: + stylelint-order "6.x" + +stylelint-config-recommended@^14.0.0: + version "14.0.0" + resolved "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-14.0.0.tgz#b395c7014838d2aaca1755eebd914d0bb5274994" + integrity sha512-jSkx290CglS8StmrLp2TxAppIajzIBZKYm3IxT89Kg6fGlxbPiTiyH9PS5YUuVAFwaJLl1ikiXX0QWjI0jmgZQ== + +stylelint-config-standard@^35.0.0: + version "35.0.0" + resolved "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-35.0.0.tgz#f4574670affb72b6c99d2b5ca5ad010a11ee8d19" + integrity sha512-JyQrNZk2BZwVKFauGGxW2U6RuhIfQ4XoHHo+rBzMHcAkLnwI/knpszwXjzxiMgSfcxbZBckM7Vq4LHoANTR85g== + dependencies: + stylelint-config-recommended "^14.0.0" + +stylelint-order@6.x: + version "6.0.3" + resolved "https://registry.npmjs.org/stylelint-order/-/stylelint-order-6.0.3.tgz#160b78650bd90463241b992581efee7159baefc2" + integrity sha512-1j1lOb4EU/6w49qZeT2SQVJXm0Ht+Qnq9GMfUa3pMwoyojIWfuA+JUDmoR97Bht1RLn4ei0xtLGy87M7d29B1w== + dependencies: + postcss "^8.4.21" + postcss-sorting "^8.0.2" + +"stylelint-scss@^5.0.0 || ^6.0.0": + version "5.3.1" + resolved "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-5.3.1.tgz#7f0f5f06d0a2a3c515aa71d3a8de3548045e03e1" + integrity sha512-5I9ZDIm77BZrjOccma5WyW2nJEKjXDd4Ca8Kk+oBapSO4pewSlno3n+OyimcyVJJujQZkBN2D+xuMkIamSc6hA== + dependencies: + known-css-properties "^0.29.0" + postcss-media-query-parser "^0.2.3" + postcss-resolve-nested-selector "^0.1.1" + postcss-selector-parser "^6.0.13" + postcss-value-parser "^4.2.0" + +stylelint@^16.0.1: + version "16.0.1" + resolved "https://registry.npmjs.org/stylelint/-/stylelint-16.0.1.tgz#613ad8841f78e10bef48bf953d759e0f73d3e153" + integrity sha512-nPO7f7JBxX0gPwdQs1fBQL+b0PabHykxSQ9HtpobbtV7pe2lb/nmlqxKepPUwHJTi9P9iu+Hkwt1mdmOnkkTVw== + dependencies: + "@csstools/css-parser-algorithms" "^2.3.2" + "@csstools/css-tokenizer" "^2.2.1" + "@csstools/media-query-list-parser" "^2.1.5" + "@csstools/selector-specificity" "^3.0.0" + balanced-match "^2.0.0" + colord "^2.9.3" + cosmiconfig "^9.0.0" + css-functions-list "^3.2.1" + css-tree "^2.3.1" + debug "^4.3.4" + fast-glob "^3.3.2" + fastest-levenshtein "^1.0.16" + file-entry-cache "^7.0.2" + global-modules "^2.0.0" + globby "^11.1.0" + globjoin "^0.1.4" + html-tags "^3.3.1" + ignore "^5.3.0" + imurmurhash "^0.1.4" + is-plain-object "^5.0.0" + known-css-properties "^0.29.0" + mathml-tag-names "^2.1.3" + meow "^12.1.1" + micromatch "^4.0.5" + normalize-path "^3.0.0" + picocolors "^1.0.0" + postcss "^8.4.32" + postcss-resolve-nested-selector "^0.1.1" + postcss-safe-parser "^7.0.0" + postcss-selector-parser "^6.0.13" + postcss-value-parser "^4.2.0" + resolve-from "^5.0.0" + string-width "^4.2.3" + strip-ansi "^7.1.0" + supports-hyperlinks "^3.0.0" + svg-tags "^1.0.0" + table "^6.8.1" + write-file-atomic "^5.0.1" + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.0.0, supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-hyperlinks@^2.0.0: + version "2.3.0" + resolved "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz#3943544347c1ff90b15effb03fc14ae45ec10624" + integrity sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA== + dependencies: + has-flag "^4.0.0" + supports-color "^7.0.0" + +supports-hyperlinks@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.0.0.tgz#c711352a5c89070779b4dad54c05a2f14b15c94b" + integrity sha512-QBDPHyPQDRTy9ku4URNGY5Lah8PAaXs6tAAwp55sL5WCsSW7GIfdf6W5ixfziW+t7wh3GVvHyHHyQ1ESsoRvaA== + dependencies: + has-flag "^4.0.0" + supports-color "^7.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +svg-tags@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764" + integrity sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA== + +symbol-tree@^3.2.4: + version "3.2.4" + resolved "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" + integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== + +table@^6.8.1: + version "6.8.1" + resolved "https://registry.npmjs.org/table/-/table-6.8.1.tgz#ea2b71359fe03b017a5fbc296204471158080bdf" + integrity sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA== + dependencies: + ajv "^8.0.1" + lodash.truncate "^4.4.2" + slice-ansi "^4.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + +tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + +tar-fs@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.0.tgz#677700fc0c8b337a78bee3623fdc235f21d7afad" + integrity sha512-vaY0obB6Om/fso8a8vakQBzwholQ7v5+uy+tF3Ozvxv1KNezmVQAiWtcNmMHFSFPqL3dJA8ha6gdtFbfX9mcxA== + dependencies: + chownr "^1.1.1" + mkdirp "^0.5.1" + pump "^3.0.0" + tar-stream "^2.0.0" + +tar-stream@^2.0.0: + version "2.2.0" + resolved "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" + integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== + dependencies: + bl "^4.0.3" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + +temp@^0.9.4: + version "0.9.4" + resolved "https://registry.npmjs.org/temp/-/temp-0.9.4.tgz#cd20a8580cb63635d0e4e9d4bd989d44286e7620" + integrity sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA== + dependencies: + mkdirp "^0.5.1" + rimraf "~2.6.2" + +terminal-link@^2.0.0: + version "2.1.1" + resolved "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" + integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== + dependencies: + ansi-escapes "^4.2.1" + supports-hyperlinks "^2.0.0" + +terser@^5.17.4: + version "5.24.0" + resolved "https://registry.npmjs.org/terser/-/terser-5.24.0.tgz#4ae50302977bca4831ccc7b4fef63a3c04228364" + integrity sha512-ZpGR4Hy3+wBEzVEnHvstMvqpD/nABNelQn/z2r0fjVWGQsN3bpOLzQlqDxmb4CDZnXq5lpjnQ+mHQLAOpfM5iw== + dependencies: + "@jridgewell/source-map" "^0.3.3" + acorn "^8.8.2" + commander "^2.20.0" + source-map-support "~0.5.20" + +test-exclude@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" + integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== + dependencies: + "@istanbuljs/schema" "^0.1.2" + glob "^7.1.4" + minimatch "^3.0.4" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +throat@^6.0.1: + version "6.0.2" + resolved "https://registry.npmjs.org/throat/-/throat-6.0.2.tgz#51a3fbb5e11ae72e2cf74861ed5c8020f89f29fe" + integrity sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ== + +through@^2.3.8: + version "2.3.8" + resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== + +tmpl@1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" + integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +tough-cookie@^4.0.0: + version "4.1.3" + resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf" + integrity sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw== + dependencies: + psl "^1.1.33" + punycode "^2.1.1" + universalify "^0.2.0" + url-parse "^1.5.3" + +tr46@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240" + integrity sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw== + dependencies: + punycode "^2.1.1" + +ts-api-utils@^1.0.1: + version "1.0.3" + resolved "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz#f12c1c781d04427313dbac808f453f050e54a331" + integrity sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg== + +ts-node@^10.9.2: + version "10.9.2" + resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" + integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== + dependencies: + "@cspotcode/source-map-support" "^0.8.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.1" + yn "3.1.1" + +tsconfig-paths@^3.14.2: + version "3.14.2" + resolved "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz#6e32f1f79412decd261f92d633a9dc1cfa99f088" + integrity sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g== + dependencies: + "@types/json5" "^0.0.29" + json5 "^1.0.2" + minimist "^1.2.6" + strip-bom "^3.0.0" + +tslib@^1.8.1: + version "1.14.1" + resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tsutils@^3.21.0: + version "3.21.0" + resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" + integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== + dependencies: + tslib "^1.8.1" + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-detect@4.0.8: + version "4.0.8" + resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== + +type-fest@^3.0.0: + version "3.13.1" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz#bb744c1f0678bea7543a2d1ec24e83e68e8c8706" + integrity sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g== + +typed-array-buffer@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz#18de3e7ed7974b0a729d3feecb94338d1472cd60" + integrity sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + is-typed-array "^1.1.10" + +typed-array-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz#d787a24a995711611fb2b87a4052799517b230d0" + integrity sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + +typed-array-byte-offset@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz#cbbe89b51fdef9cd6aaf07ad4707340abbc4ea0b" + integrity sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + +typed-array-length@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" + integrity sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + is-typed-array "^1.1.9" + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + +typedoc-plugin-markdown@^3.17.1: + version "3.17.1" + resolved "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.17.1.tgz#c33f42363c185adf842f4699166015f7fe0ed02b" + integrity sha512-QzdU3fj0Kzw2XSdoL15ExLASt2WPqD7FbLeaqwT70+XjKyTshBnUlQA5nNREO1C2P8Uen0CDjsBLMsCQ+zd0lw== + dependencies: + handlebars "^4.7.7" + +typedoc@^0.25.4: + version "0.25.4" + resolved "https://registry.npmjs.org/typedoc/-/typedoc-0.25.4.tgz#5c2c0677881f504e41985f29d9aef0dbdb6f1e6f" + integrity sha512-Du9ImmpBCw54bX275yJrxPVnjdIyJO/84co0/L9mwe0R3G4FSR6rQ09AlXVRvZEGMUg09+z/usc8mgygQ1aidA== + dependencies: + lunr "^2.3.9" + marked "^4.3.0" + minimatch "^9.0.3" + shiki "^0.14.1" + +typescript@^5.3.3: + version "5.3.3" + resolved "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz#b3ce6ba258e72e6305ba66f5c9b452aaee3ffe37" + integrity sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw== + +uglify-js@^3.1.4: + version "3.17.4" + resolved "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz#61678cf5fa3f5b7eb789bb345df29afb8257c22c" + integrity sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g== + +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== + dependencies: + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" + which-boxed-primitive "^1.0.2" + +unbzip2-stream@1.3.3: + version "1.3.3" + resolved "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.3.3.tgz#d156d205e670d8d8c393e1c02ebd506422873f6a" + integrity sha512-fUlAF7U9Ah1Q6EieQ4x4zLNejrRvDWUYmxXUpN3uziFYCHapjWFaCAnreY9bGgxzaMCFAPPpYNng57CypwJVhg== + dependencies: + buffer "^5.2.1" + through "^2.3.8" + +underscore-plus@1.x: + version "1.7.0" + resolved "https://registry.npmjs.org/underscore-plus/-/underscore-plus-1.7.0.tgz#107f1900c520ac1fefe4edec6580a7ff08a99d0f" + integrity sha512-A3BEzkeicFLnr+U/Q3EyWwJAQPbA19mtZZ4h+lLq3ttm9kn8WC4R3YpuJZEXmWdLjYP47Zc8aLZm9kwdv+zzvA== + dependencies: + underscore "^1.9.1" + +underscore@^1.9.1: + version "1.13.6" + resolved "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz#04786a1f589dc6c09f761fc5f45b89e935136441" + integrity sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A== + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +unicode-canonical-property-names-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" + integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== + +unicode-match-property-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" + integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== + dependencies: + unicode-canonical-property-names-ecmascript "^2.0.0" + unicode-property-aliases-ecmascript "^2.0.0" + +unicode-match-property-value-ecmascript@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz#cb5fffdcd16a05124f5a4b0bf7c3770208acbbe0" + integrity sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA== + +unicode-property-aliases-ecmascript@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd" + integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== + +unist-util-stringify-position@^2.0.0: + version "2.0.3" + resolved "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz#cce3bfa1cdf85ba7375d1d5b17bdc4cada9bd9da" + integrity sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g== + dependencies: + "@types/unist" "^2.0.2" + +universalify@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" + integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== + +universalify@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" + integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== + +unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + +update-browserslist-db@^1.0.13: + version "1.0.13" + resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4" + integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +url-parse@^1.5.3: + version "1.5.10" + resolved "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + +util-deprecate@^1.0.1, util-deprecate@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== + +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + +v8-to-istanbul@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-1.2.1.tgz#8f63a94b7f91243f5dcc6a495540b5653beb1279" + integrity sha512-NglPycIwSQeSJj7VJ6L8vTsPKC9MG5Lcx4n3SvYqNHzklbMI4dGcLJnkLPEPJ3uB8UyTdWviMhM0Ptq+xD5UFQ== + +v8-to-istanbul@^8.1.0: + version "8.1.1" + resolved "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz#77b752fd3975e31bbcef938f85e9bd1c7a8d60ed" + integrity sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.1" + convert-source-map "^1.6.0" + source-map "^0.7.3" + +vijest@^0.0.2: + version "0.0.2" + resolved "https://registry.npmjs.org/vijest/-/vijest-0.0.2.tgz#198856eb394e8a7512b8c58bd41e3823314bd160" + integrity sha512-7mXnTTQNd4ArRPs5/LdnEdLUs8Pv6fS6gk3pujK5318QcueVPle54+6Y87uMhxin+yYQuIXaielWJMXSOshM0Q== + dependencies: + "@testing-library/jest-dom" "^5.14.1" + connect "^3" + find-cache-dir "^3" + glob "^7" + mkdirp "^1" + puppeteer "^10" + puppeteer-to-istanbul "^1.4.0" + query-string "*" + ws "^8.2.2" + +vite@^5.0.7: + version "5.0.7" + resolved "https://registry.npmjs.org/vite/-/vite-5.0.7.tgz#ad081d735f6769f76b556818500bdafb72c3fe93" + integrity sha512-B4T4rJCDPihrQo2B+h1MbeGL/k/GMAHzhQ8S0LjQ142s6/+l3hHTT095ORvsshj4QCkoWu3Xtmob5mazvakaOw== + dependencies: + esbuild "^0.19.3" + postcss "^8.4.32" + rollup "^4.2.0" + optionalDependencies: + fsevents "~2.3.3" + +vscode-oniguruma@^1.7.0: + version "1.7.0" + resolved "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz#439bfad8fe71abd7798338d1cd3dc53a8beea94b" + integrity sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA== + +vscode-textmate@^8.0.0: + version "8.0.0" + resolved "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz#2c7a3b1163ef0441097e0b5d6389cd5504b59e5d" + integrity sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg== + +vue-component-type-helpers@^1.8.21: + version "1.8.24" + resolved "https://registry.npmjs.org/vue-component-type-helpers/-/vue-component-type-helpers-1.8.24.tgz#702d4bc4421d42d5fcbe0412e5f316110871f585" + integrity sha512-lqWs/7fdRXoSBAlbouHBX+LNuaY6gI9xWW34m/ZIz9zVPYHEyw0b2/zaCBwlKx0NtKTeF/6pOpvrxVkh7nhIYg== + +vue-eslint-parser@^9.3.1: + version "9.3.2" + resolved "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.3.2.tgz#6f9638e55703f1c77875a19026347548d93fd499" + integrity sha512-q7tWyCVaV9f8iQyIA5Mkj/S6AoJ9KBN8IeUSf3XEmBrOtxOZnfTg5s4KClbZBCK3GtnT/+RyCLZyDHuZwTuBjg== + dependencies: + debug "^4.3.4" + eslint-scope "^7.1.1" + eslint-visitor-keys "^3.3.0" + espree "^9.3.1" + esquery "^1.4.0" + lodash "^4.17.21" + semver "^7.3.6" + +vue@^3.3.11: + version "3.3.11" + resolved "https://registry.npmjs.org/vue/-/vue-3.3.11.tgz#898d97025f73cdb5fc4e3ae3fd07a54615232140" + integrity sha512-d4oBctG92CRO1cQfVBZp6WJAs0n8AK4Xf5fNjQCBeKCvMI1efGQ5E3Alt1slFJS9fZuPcFoiAiqFvQlv1X7t/w== + dependencies: + "@vue/compiler-dom" "3.3.11" + "@vue/compiler-sfc" "3.3.11" + "@vue/runtime-dom" "3.3.11" + "@vue/server-renderer" "3.3.11" + "@vue/shared" "3.3.11" + +w3c-hr-time@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" + integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== + dependencies: + browser-process-hrtime "^1.0.0" + +w3c-xmlserializer@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a" + integrity sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA== + dependencies: + xml-name-validator "^3.0.0" + +walker@^1.0.7: + version "1.0.8" + resolved "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" + integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== + dependencies: + makeerror "1.0.12" + +webidl-conversions@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" + integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== + +webidl-conversions@^6.1.0: + version "6.1.0" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" + integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== + +whatwg-encoding@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" + integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== + dependencies: + iconv-lite "0.4.24" + +whatwg-mimetype@^2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" + integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== + +whatwg-url@^8.0.0, whatwg-url@^8.5.0: + version "8.7.0" + resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77" + integrity sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg== + dependencies: + lodash "^4.7.0" + tr46 "^2.1.0" + webidl-conversions "^6.1.0" + +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + +which-builtin-type@^1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.3.tgz#b1b8443707cc58b6e9bf98d32110ff0c2cbd029b" + integrity sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw== + dependencies: + function.prototype.name "^1.1.5" + has-tostringtag "^1.0.0" + is-async-function "^2.0.0" + is-date-object "^1.0.5" + is-finalizationregistry "^1.0.2" + is-generator-function "^1.0.10" + is-regex "^1.1.4" + is-weakref "^1.0.2" + isarray "^2.0.5" + which-boxed-primitive "^1.0.2" + which-collection "^1.0.1" + which-typed-array "^1.1.9" + +which-collection@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz#70eab71ebbbd2aefaf32f917082fc62cdcb70906" + integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A== + dependencies: + is-map "^2.0.1" + is-set "^2.0.1" + is-weakmap "^2.0.1" + is-weakset "^2.0.1" + +which-module@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409" + integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== + +which-typed-array@^1.1.11, which-typed-array@^1.1.13, which-typed-array@^1.1.9: + version "1.1.13" + resolved "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz#870cd5be06ddb616f504e7b039c4c24898184d36" + integrity sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.4" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + +which@^1.3.1: + version "1.3.1" + resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wordwrap@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== + +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^9.0.0: + version "9.0.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz#1a3dc8b70d85eeb8398ddfb1e4a02cd186e58b3e" + integrity sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q== + dependencies: + ansi-styles "^6.2.1" + string-width "^7.0.0" + strip-ansi "^7.1.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +write-file-atomic@^3.0.0: + version "3.0.3" + resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" + integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== + dependencies: + imurmurhash "^0.1.4" + is-typedarray "^1.0.0" + signal-exit "^3.0.2" + typedarray-to-buffer "^3.1.5" + +write-file-atomic@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz#68df4717c55c6fa4281a7860b4c2ba0a6d2b11e7" + integrity sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw== + dependencies: + imurmurhash "^0.1.4" + signal-exit "^4.0.1" + +ws@7.4.6: + version "7.4.6" + resolved "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" + integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== + +ws@^7.4.6: + version "7.5.9" + resolved "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" + integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== + +ws@^8.2.2: + version "8.14.2" + resolved "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz#6c249a806eb2db7a20d26d51e7709eab7b2e6c7f" + integrity sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g== + +xml-name-validator@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" + integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== + +xml-name-validator@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" + integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== + +xmlchars@^2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" + integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== + +y18n@^4.0.0: + version "4.0.3" + resolved "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" + integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yaml@2.3.4: + version "2.3.4" + resolved "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz#53fc1d514be80aabf386dc6001eb29bf3b7523b2" + integrity sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA== + +yargs-parser@^18.1.2: + version "18.1.3" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" + integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-parser@^20.2.2: + version "20.2.9" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + +yargs@^15.3.1: + version "15.4.1" + resolved "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" + integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== + 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" + +yargs@^16.2.0: + version "16.2.0" + resolved "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + +yargs@^17.5.1: + version "17.7.2" + resolved "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + +yauzl@^2.10.0: + version "2.10.0" + resolved "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g== + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.1.0" + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==