From b61abffc360411d712d273415590ef6cf3067ba9 Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Thu, 28 Nov 2024 00:33:08 +0200 Subject: [PATCH 01/13] refactor: convert to typescript --- .eslintignore | 1 + .eslintrc.yml | 30 +- .github/workflows/ci.yml | 12 + .gitignore | 1 + .prettierrc | 7 + .swcrc-spec | 18 + docs/guide/developers.md | 14 +- docs/guide/options.md | 8 +- docs/guide/usage.md | 6 +- docs/samples/api.md | 34 +- docs/samples/basic.md | 38 +- docs/samples/drag/category.md | 20 +- docs/samples/drag/linear-ratio.md | 29 +- docs/samples/drag/linear.md | 29 +- docs/samples/drag/log.md | 18 +- docs/samples/drag/reject-outside.md | 29 +- docs/samples/drag/time.md | 38 +- docs/samples/drag/timeseries.md | 30 +- docs/samples/fetch-data.md | 56 +- docs/samples/pan/region.md | 40 +- docs/samples/pan/toggle.md | 24 +- docs/samples/wheel/category-min-range.md | 20 +- docs/samples/wheel/category.md | 20 +- docs/samples/wheel/click-zoom.md | 58 +- docs/samples/wheel/log.md | 20 +- docs/samples/wheel/over-scale-mode.md | 36 +- docs/samples/wheel/time.md | 42 +- jasmine.json | 4 + karma.conf.js => karma.conf.cjs | 20 +- package-lock.json | 2418 +++++++++++++++++++-- package.json | 47 +- rollup.config.js | 77 +- samples/.eslintrc.yml | 1 - samples/zoom-separately.html | 197 +- src/core.js | 294 --- src/core.ts | 280 +++ src/defaults.ts | 26 + src/hammer.js | 183 -- src/hammer.ts | 190 ++ src/handlers.js | 299 --- src/handlers.ts | 315 +++ src/index.esm.js | 4 - src/index.js | 6 - src/index.ts | 38 + src/index.umd.ts | 6 + types/options.d.ts => src/options.ts | 89 +- src/plugin.js | 147 -- src/plugin.ts | 152 ++ src/scale.types.js | 304 --- src/scale.types.test.ts | 27 + src/scale.types.ts | 285 +++ src/state.js | 33 - src/state.ts | 64 + src/types.ts | 6 + src/utils.js | 107 - src/utils.test.ts | 29 + src/utils.ts | 94 + test/fixtures/pan/category-x-1.js | 70 +- test/fixtures/pan/category-x-10.js | 68 +- test/fixtures/pan/category-x-25.js | 68 +- test/fixtures/pan/category-x-5.js | 80 +- test/fixtures/pan/category-x-50.js | 68 +- test/fixtures/pan/time-day-left.js | 54 +- test/fixtures/pan/time-day-right.js | 54 +- test/fixtures/zoom/category-x.js | 70 +- test/fixtures/zoom/drag.js | 50 +- test/fixtures/zoom/dragDrawTime.js | 50 +- test/fixtures/zoom/update-reset.js | 103 +- test/fixtures/zoom/zoom-reset.js | 74 +- test/index.js | 103 +- test/specs/api.spec.js | 770 +++---- test/specs/defaults.spec.js | 44 +- test/specs/fixtures.spec.js | 8 +- test/specs/module.spec.js | 34 +- test/specs/pan.spec.js | 304 +-- test/specs/zoom.drag.spec.js | 692 +++--- test/specs/zoom.pinch.spec.js | 107 +- test/specs/zoom.wheel.spec.js | 518 ++--- {types/tests => test/types}/.eslintrc.yml | 0 {types/tests => test/types}/exports.ts | 12 +- {types/tests => test/types}/tsconfig.json | 4 +- tsconfig.json | 46 +- tsconfig.spec.json | 37 + types/index.d.ts | 63 - 84 files changed, 6144 insertions(+), 3827 deletions(-) create mode 100644 .eslintignore create mode 100644 .prettierrc create mode 100644 .swcrc-spec create mode 100644 jasmine.json rename karma.conf.js => karma.conf.cjs (90%) delete mode 100644 src/core.js create mode 100644 src/core.ts create mode 100644 src/defaults.ts delete mode 100644 src/hammer.js create mode 100644 src/hammer.ts delete mode 100644 src/handlers.js create mode 100644 src/handlers.ts delete mode 100644 src/index.esm.js delete mode 100644 src/index.js create mode 100644 src/index.ts create mode 100644 src/index.umd.ts rename types/options.d.ts => src/options.ts (62%) delete mode 100644 src/plugin.js create mode 100644 src/plugin.ts delete mode 100644 src/scale.types.js create mode 100644 src/scale.types.test.ts create mode 100644 src/scale.types.ts delete mode 100644 src/state.js create mode 100644 src/state.ts create mode 100644 src/types.ts delete mode 100644 src/utils.js create mode 100644 src/utils.test.ts create mode 100644 src/utils.ts rename {types/tests => test/types}/.eslintrc.yml (100%) rename {types/tests => test/types}/exports.ts (74%) rename {types/tests => test/types}/tsconfig.json (65%) create mode 100644 tsconfig.spec.json delete mode 100644 types/index.d.ts diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000..795d828d7 --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +dist/**/* diff --git a/.eslintrc.yml b/.eslintrc.yml index f738fde0d..fa460394a 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -1,22 +1,36 @@ extends: - chartjs - plugin:markdown/recommended + - plugin:@typescript-eslint/recommended -env: - es2021: true - browser: true - node: true +parser: "@typescript-eslint/parser" parserOptions: + ecmaVersion: 2022 sourceType: module - ecmaFeatures: - impliedStrict: true - modules: true -plugins: ['html'] +env: + es2022: true + browser: true + node: true + jasmine: true + +plugins: + - "@typescript-eslint" + - prettier + - html rules: + prettier/prettier: "error" + semi: ["error", "never"] complexity: ["warn", 10] max-statements: ["warn", 30] no-var: "warn" prefer-const: ["warn", {"destructuring": "all"}] + # turning off things conflicting with prettier + indent: "off" + comma-dangle: "off" + comma-spacing: "off" + comma-style: "off" + object-curly-spacing: "off" + space-before-function-paren: "off" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 88c3397e3..524534460 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,6 +28,9 @@ jobs: - name: lint run: npm run lint + - name: typecheck + run: npm run typecheck + - name: build run: npm run build @@ -40,6 +43,15 @@ jobs: fi shell: bash + - name: Coveralls Paraller - Unit + if: matrix.os == 'ubuntu-latest' + uses: coverallsapp/github-action@v2 + with: + github-token: ${{ secrets.github_token }} + file: './coverage/unit/lcov.info' + flag-name: unit + parallel: true + - name: Coveralls Parallel - Chrome uses: coverallsapp/github-action@v2 with: diff --git a/.gitignore b/.gitignore index 3ba0279f2..0a10c454b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ # Deployment +/build /coverage /custom /dist diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 000000000..9e7d3036c --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +bracketSpacing: true +singleQuote: true +printWidth: 120 +semi: false +tabWidth: 2 +useTabs: false +trailingComma: 'es5' diff --git a/.swcrc-spec b/.swcrc-spec new file mode 100644 index 000000000..853100e8c --- /dev/null +++ b/.swcrc-spec @@ -0,0 +1,18 @@ +{ + "jsc": { + "parser": { + "syntax": "typescript", + "tsx": false, + "decorators": true, + "dynamicImport": true, + "importMeta": true + }, + "target": "es2022", + "baseUrl": "." + }, + "module": { + "type": "es6", + "resolveFully": true + }, + "sourceMaps": true +} diff --git a/docs/guide/developers.md b/docs/guide/developers.md index d1ea5cf21..86e4a9c64 100644 --- a/docs/guide/developers.md +++ b/docs/guide/developers.md @@ -65,18 +65,18 @@ Returns whether the user is currently in the middle of a drag operation or pan o You can extend chartjs-plugin-zoom with support for [custom scales](https://www.chartjs.org/docs/latest/developers/axes.html) by using the zoom plugin's `zoomFunctions`, `zoomRectFunctions`, and `panFunctions` members. These objects are indexed by scale types (scales' `id` members) and give optional handlers for zoom and pan functionality. ```js -import {Scale} from 'chart.js'; -import zoomPlugin from 'chartjs-plugin-zoom'; +import {Scale} from 'chart.js' +import zoomPlugin from 'chartjs-plugin-zoom' class MyScale extends Scale { /* extensions ... */ } -MyScale.id = 'myScale'; -MyScale.defaults = defaultConfigObject; +MyScale.id = 'myScale' +MyScale.defaults = defaultConfigObject -zoomPlugin.zoomFunctions.myScale = (scale, zoom, center, limits) => false; -zoomPlugin.zoomRectFunctions.myScale = (scale, from, to, limits) => false; -zoomPlugin.panFunctions.myScale = (scale, delta, limits) => false; +zoomPlugin.zoomFunctions.myScale = (scale, zoom, center, limits) => false +zoomPlugin.zoomRectFunctions.myScale = (scale, from, to, limits) => false +zoomPlugin.panFunctions.myScale = (scale, delta, limits) => false // zoomRectFunctions can normally be omitted, since zooming by specific pixel // coordinates rarely needs special handling. ``` diff --git a/docs/guide/options.md b/docs/guide/options.md index 27177b659..172e426fb 100644 --- a/docs/guide/options.md +++ b/docs/guide/options.md @@ -5,7 +5,7 @@ The options for chartjs-plugin-zoom should be placed in `options.plugins.zoom` i The options are split in three sub-objects, [limits](#limits), [pan](#pan) and [zoom](#zoom). ```js -const chart = new Chart('id', { +export const chart = new Chart('id', { type: 'bar', data: {}, options: { @@ -23,7 +23,7 @@ const chart = new Chart('id', { } } } -}); +}) ``` ## Pan @@ -122,7 +122,7 @@ Limits options define the limits per axis for pan and zoom. If you're using multiple or custom axes (scales), you can define limits for those, too. ```js -const chart = new Chart('id', { +export const chart = new Chart('id', { type: 'line', data: {}, options: { @@ -146,7 +146,7 @@ const chart = new Chart('id', { } } } -}); +}) ``` #### Scale Limits diff --git a/docs/guide/usage.md b/docs/guide/usage.md index 75c56bdfe..33ff18805 100644 --- a/docs/guide/usage.md +++ b/docs/guide/usage.md @@ -31,7 +31,7 @@ const config = { } } } -}; +} /* */ module.exports = { @@ -39,10 +39,10 @@ module.exports = { { name: 'Reset zoom', handler: function(chart) { - chart.resetZoom(); + chart.resetZoom() } } ], config -}; +} ``` diff --git a/docs/samples/api.md b/docs/samples/api.md index 8c4960033..7e67a67c1 100644 --- a/docs/samples/api.md +++ b/docs/samples/api.md @@ -2,7 +2,7 @@ ```js chart-editor // -const NUMBER_CFG = {count: 20, min: -100, max: 100}; +const NUMBER_CFG = {count: 20, min: -100, max: 100} const data = { datasets: [{ label: 'My First dataset', @@ -21,7 +21,7 @@ const data = { pointBorderWidth: 1, data: Utils.points(NUMBER_CFG), }] -}; +} // // @@ -38,7 +38,7 @@ const scaleOpts = { display: true, text: (ctx) => ctx.scale.axis + ' axis', } -}; +} const scales = { x: { position: 'top', @@ -46,8 +46,8 @@ const scales = { y: { position: 'right', }, -}; -Object.keys(scales).forEach(scale => Object.assign(scales[scale], scaleOpts)); +} +Object.keys(scales).forEach(scale => Object.assign(scales[scale], scaleOpts)) // // @@ -57,7 +57,7 @@ const config = { options: { scales: scales, } -}; +} // // @@ -66,50 +66,50 @@ const actions = [ { name: 'Zoom +10%', handler(chart) { - chart.zoom(1.1); + chart.zoom(1.1) } }, { name: 'Zoom -10%', handler(chart) { - chart.zoom(2 - 1 / 0.9); + chart.zoom(2 - 1 / 0.9) }, }, { name: 'Zoom x +10%', handler(chart) { - chart.zoom({x: 1.1}); + chart.zoom({x: 1.1}) } }, { name: 'Zoom x -10%', handler(chart) { - chart.zoom({x: 2 - 1 / 0.9}); + chart.zoom({x: 2 - 1 / 0.9}) }, }, { name: 'Pan x 100px (anim)', handler(chart) { - chart.pan({x: 100}, undefined, 'default'); + chart.pan({x: 100}, undefined, 'default') } }, { name: 'Pan x -100px (anim)', handler(chart) { - chart.pan({x: -100}, undefined, 'default'); + chart.pan({x: -100}, undefined, 'default') }, }, { name: 'Zoom x: 0..-100, y: 0..100', handler(chart) { - chart.zoomScale('x', {min: -100, max: 0}, 'default'); - chart.zoomScale('y', {min: 0, max: 100}, 'default'); + chart.zoomScale('x', {min: -100, max: 0}, 'default') + chart.zoomScale('y', {min: 0, max: 100}, 'default') } }, { name: 'Reset zoom', handler(chart) { - chart.resetZoom(); + chart.resetZoom() } } -]; +] // module.exports = { actions, config -}; +} ``` diff --git a/docs/samples/basic.md b/docs/samples/basic.md index 3a2ccd3a0..03f54d84d 100644 --- a/docs/samples/basic.md +++ b/docs/samples/basic.md @@ -2,7 +2,7 @@ ```js chart-editor // -const NUMBER_CFG = {count: 20, min: -100, max: 100}; +const NUMBER_CFG = {count: 20, min: -100, max: 100} const data = { datasets: [{ label: 'My First dataset', @@ -21,7 +21,7 @@ const data = { pointBorderWidth: 1, data: Utils.points(NUMBER_CFG), }] -}; +} // // @@ -38,7 +38,7 @@ const scaleOpts = { display: true, text: (ctx) => ctx.scale.axis + ' axis', } -}; +} const scales = { x: { position: 'top', @@ -46,8 +46,8 @@ const scales = { y: { position: 'right', }, -}; -Object.keys(scales).forEach(scale => Object.assign(scales[scale], scaleOpts)); +} +Object.keys(scales).forEach(scale => Object.assign(scales[scale], scaleOpts)) // // @@ -72,14 +72,14 @@ const zoomOptions = { // This update is needed to display up to date zoom level in the title. // Without this, previous zoom level is displayed. // The reason is: title uses the same beforeUpdate hook, and is evaluated before zoom. - chart.update('none'); + chart.update('none') } } -}; +} // -const panStatus = () => zoomOptions.pan.enabled ? 'enabled' : 'disabled'; -const zoomStatus = (chart) => (zoomOptions.zoom.wheel.enabled ? 'enabled' : 'disabled') + ' (' + chart.getZoomLevel() + 'x)'; +const panStatus = () => zoomOptions.pan.enabled ? 'enabled' : 'disabled' +const zoomStatus = (chart) => (zoomOptions.zoom.wheel.enabled ? 'enabled' : 'disabled') + ' (' + chart.getZoomLevel() + 'x)' // const config = { @@ -96,37 +96,37 @@ const config = { } }, onClick(e) { - console.log(e.type); + console.log(e.type) } } -}; +} // const actions = [ { name: 'Toggle zoom', handler(chart) { - zoomOptions.zoom.wheel.enabled = !zoomOptions.zoom.wheel.enabled; - zoomOptions.zoom.pinch.enabled = !zoomOptions.zoom.pinch.enabled; - chart.update(); + zoomOptions.zoom.wheel.enabled = !zoomOptions.zoom.wheel.enabled + zoomOptions.zoom.pinch.enabled = !zoomOptions.zoom.pinch.enabled + chart.update() } }, { name: 'Toggle pan', handler(chart) { - zoomOptions.pan.enabled = !zoomOptions.pan.enabled; - chart.update(); + zoomOptions.pan.enabled = !zoomOptions.pan.enabled + chart.update() }, }, { name: 'Reset zoom', handler(chart) { - chart.resetZoom(); + chart.resetZoom() } } -]; +] module.exports = { actions, config, output: 'Clicks are logged here' -}; +} ``` diff --git a/docs/samples/drag/category.md b/docs/samples/drag/category.md index ab8f60ff7..14c51c02c 100644 --- a/docs/samples/drag/category.md +++ b/docs/samples/drag/category.md @@ -4,8 +4,8 @@ Zooming is performed by clicking and selecting an area over the chart with the m ```js chart-editor // -const DATA_COUNT = 20; -const NUMBER_CFG = {count: DATA_COUNT, min: -100, max: 100}; +const DATA_COUNT = 20 +const NUMBER_CFG = {count: DATA_COUNT, min: -100, max: 100} const data = { labels: Utils.months({count: DATA_COUNT}), datasets: [{ @@ -24,7 +24,7 @@ const data = { backgroundColor: Utils.randomColor(0.5), data: Utils.numbers(NUMBER_CFG), }] -}; +} // // @@ -37,7 +37,7 @@ const scaleOpts = { display: true, text: (ctx) => ctx.scale.axis + ' axis', } -}; +} const scales = { x: { type: 'category', @@ -48,8 +48,8 @@ const scales = { callback: (val, index, ticks) => index === 0 || index === ticks.length - 1 ? null : val, }, }, -}; -Object.keys(scales).forEach(scale => Object.assign(scales[scale], scaleOpts)); +} +Object.keys(scales).forEach(scale => Object.assign(scales[scale], scaleOpts)) // // @@ -75,20 +75,20 @@ const config = { } }, } -}; +} // const actions = [ { name: 'Reset zoom', handler(chart) { - chart.resetZoom(); + chart.resetZoom() } } -]; +] module.exports = { actions, config, -}; +} ``` diff --git a/docs/samples/drag/linear-ratio.md b/docs/samples/drag/linear-ratio.md index 54094b362..f8ae22b50 100644 --- a/docs/samples/drag/linear-ratio.md +++ b/docs/samples/drag/linear-ratio.md @@ -4,7 +4,7 @@ Zooming is performed by clicking and selecting an area over the chart with the m ```js chart-editor // -const NUMBER_CFG = {count: 20, min: -100, max: 100}; +const NUMBER_CFG = {count: 20, min: -100, max: 100} const data = { datasets: [{ label: 'My First dataset', @@ -23,7 +23,7 @@ const data = { pointBorderWidth: 1, data: Utils.points(NUMBER_CFG), }] -}; +} // // @@ -37,7 +37,7 @@ const scaleOpts = { display: true, text: (ctx) => ctx.scale.axis + ' axis', } -}; +} const scales = { x: { position: 'top', @@ -45,12 +45,11 @@ const scales = { y: { position: 'right', }, -}; -Object.keys(scales).forEach(scale => Object.assign(scales[scale], scaleOpts)); +} +Object.keys(scales).forEach(scale => Object.assign(scales[scale], scaleOpts)) // // -const dragColor = Utils.randomColor(0.4); const zoomOptions = { pan: { enabled: true, @@ -67,10 +66,10 @@ const zoomOptions = { maintainAspectRatio: true, } } -}; +} // -const zoomStatus = () => zoomOptions.zoom.drag.enabled ? 'enabled' : 'disabled'; +const zoomStatus = () => zoomOptions.zoom.drag.enabled ? 'enabled' : 'disabled' // const config = { @@ -83,30 +82,30 @@ const config = { title: { display: true, position: 'bottom', - text: (ctx) => 'Zoom: ' + zoomStatus() + text: () => 'Zoom: ' + zoomStatus() } }, } -}; +} // const actions = [ { name: 'Toggle zoom', handler(chart) { - zoomOptions.zoom.drag.enabled = !zoomOptions.zoom.drag.enabled; - chart.update(); + zoomOptions.zoom.drag.enabled = !zoomOptions.zoom.drag.enabled + chart.update() } }, { name: 'Reset zoom', handler(chart) { - chart.resetZoom(); + chart.resetZoom() } } -]; +] module.exports = { actions, config, -}; +} ``` diff --git a/docs/samples/drag/linear.md b/docs/samples/drag/linear.md index 3c40f76ac..2668430ac 100644 --- a/docs/samples/drag/linear.md +++ b/docs/samples/drag/linear.md @@ -4,7 +4,7 @@ Zooming is performed by clicking and selecting an area over the chart with the m ```js chart-editor // -const NUMBER_CFG = {count: 20, min: -100, max: 100}; +const NUMBER_CFG = {count: 20, min: -100, max: 100} const data = { datasets: [{ label: 'My First dataset', @@ -23,7 +23,7 @@ const data = { pointBorderWidth: 1, data: Utils.points(NUMBER_CFG), }] -}; +} // // @@ -40,7 +40,7 @@ const scaleOpts = { display: true, text: (ctx) => ctx.scale.axis + ' axis', } -}; +} const scales = { x: { position: 'top', @@ -48,12 +48,11 @@ const scales = { y: { position: 'right', }, -}; -Object.keys(scales).forEach(scale => Object.assign(scales[scale], scaleOpts)); +} +Object.keys(scales).forEach(scale => Object.assign(scales[scale], scaleOpts)) // // -const dragColor = Utils.randomColor(0.4); const zoomOptions = { pan: { enabled: true, @@ -69,10 +68,10 @@ const zoomOptions = { backgroundColor: 'rgba(54, 162, 235, 0.3)' } } -}; +} // -const zoomStatus = () => zoomOptions.zoom.drag.enabled ? 'enabled' : 'disabled'; +const zoomStatus = () => zoomOptions.zoom.drag.enabled ? 'enabled' : 'disabled' // const config = { @@ -85,30 +84,30 @@ const config = { title: { display: true, position: 'bottom', - text: (ctx) => 'Zoom: ' + zoomStatus() + text: () => 'Zoom: ' + zoomStatus() } }, } -}; +} // const actions = [ { name: 'Toggle zoom', handler(chart) { - zoomOptions.zoom.drag.enabled = !zoomOptions.zoom.drag.enabled; - chart.update(); + zoomOptions.zoom.drag.enabled = !zoomOptions.zoom.drag.enabled + chart.update() } }, { name: 'Reset zoom', handler(chart) { - chart.resetZoom(); + chart.resetZoom() } } -]; +] module.exports = { actions, config, -}; +} ``` diff --git a/docs/samples/drag/log.md b/docs/samples/drag/log.md index 0459059b9..db26d52fb 100644 --- a/docs/samples/drag/log.md +++ b/docs/samples/drag/log.md @@ -107,7 +107,7 @@ const data = { y: -3.596e1 }] }] -}; +} // // @@ -116,11 +116,11 @@ const scales = { type: 'logarithmic', ticks: { callback: function(tick) { - const remain = tick / (Math.pow(10, Math.floor(Math.log10(tick)))); + const remain = tick / (Math.pow(10, Math.floor(Math.log10(tick)))) if (remain === 1 || remain === 2 || remain === 5) { - return tick.toString() + 'Hz'; + return tick.toString() + 'Hz' } - return ''; + return '' }, maxRotation: 0 }, @@ -129,7 +129,7 @@ const scales = { text: 'Frequency', }, } -}; +} // // @@ -154,20 +154,20 @@ const config = { } }, } -}; +} // const actions = [ { name: 'Reset zoom', handler(chart) { - chart.resetZoom(); + chart.resetZoom() } } -]; +] module.exports = { actions, config, -}; +} ``` diff --git a/docs/samples/drag/reject-outside.md b/docs/samples/drag/reject-outside.md index c499503a7..102d2cc48 100644 --- a/docs/samples/drag/reject-outside.md +++ b/docs/samples/drag/reject-outside.md @@ -4,7 +4,7 @@ Zooming is performed by clicking and selecting an area over the chart with the m ```js chart-editor // -const NUMBER_CFG = {count: 20, min: -100, max: 100}; +const NUMBER_CFG = {count: 20, min: -100, max: 100} const data = { datasets: [{ label: 'My First dataset', @@ -23,7 +23,7 @@ const data = { pointBorderWidth: 1, data: Utils.points(NUMBER_CFG), }] -}; +} // // @@ -40,7 +40,7 @@ const scaleOpts = { display: true, text: (ctx) => ctx.scale.axis + ' axis', } -}; +} const scales = { x: { position: 'top', @@ -48,12 +48,11 @@ const scales = { y: { position: 'right', }, -}; -Object.keys(scales).forEach(scale => Object.assign(scales[scale], scaleOpts)); +} +Object.keys(scales).forEach(scale => Object.assign(scales[scale], scaleOpts)) // // -const dragColor = Utils.randomColor(0.4); const zoomOptions = { pan: { enabled: true, @@ -71,10 +70,10 @@ const zoomOptions = { backgroundColor: 'rgba(54, 162, 235, 0.3)' } } -}; +} // -const zoomStatus = () => zoomOptions.zoom.drag.enabled ? 'enabled' : 'disabled'; +const zoomStatus = () => zoomOptions.zoom.drag.enabled ? 'enabled' : 'disabled' // const config = { @@ -87,30 +86,30 @@ const config = { title: { display: true, position: 'bottom', - text: (ctx) => 'Zoom: ' + zoomStatus() + text: () => 'Zoom: ' + zoomStatus() } }, } -}; +} // const actions = [ { name: 'Toggle zoom', handler(chart) { - zoomOptions.zoom.drag.enabled = !zoomOptions.zoom.drag.enabled; - chart.update(); + zoomOptions.zoom.drag.enabled = !zoomOptions.zoom.drag.enabled + chart.update() } }, { name: 'Reset zoom', handler(chart) { - chart.resetZoom(); + chart.resetZoom() } } -]; +] module.exports = { actions, config, -}; +} ``` diff --git a/docs/samples/drag/time.md b/docs/samples/drag/time.md index ce4bf866c..417c9ca4f 100644 --- a/docs/samples/drag/time.md +++ b/docs/samples/drag/time.md @@ -4,7 +4,7 @@ Zooming is performed by clicking and selecting an area over the chart with the m ```js chart-editor // -const NUMBER_CFG = {count: 500, min: 0, max: 1000}; +const NUMBER_CFG = {count: 500, min: 0, max: 1000} const data = { datasets: [{ label: 'My First dataset', @@ -15,7 +15,7 @@ const data = { pointBorderWidth: 1, data: Utils.hourlyPoints(NUMBER_CFG), }] -}; +} // // @@ -50,7 +50,7 @@ const scales = { text: (ctx) => ctx.scale.axis + ' axis', } }, -}; +} // // @@ -65,11 +65,11 @@ const zoomOptions = { }, mode: 'xy', }, -}; +} // -const panStatus = () => zoomOptions.pan.enabled ? 'enabled' : 'disabled'; -const zoomStatus = () => zoomOptions.zoom.drag.enabled ? 'enabled' : 'disabled'; +const panStatus = () => zoomOptions.pan.enabled ? 'enabled' : 'disabled' +const zoomStatus = () => zoomOptions.zoom.drag.enabled ? 'enabled' : 'disabled' // const config = { @@ -82,49 +82,49 @@ const config = { title: { display: true, position: 'bottom', - text: (ctx) => 'Zoom: ' + zoomStatus() + ', Pan: ' + panStatus() + text: () => 'Zoom: ' + zoomStatus() + ', Pan: ' + panStatus() } }, } -}; +} // const actions = [ { name: 'Toggle zoom', handler(chart) { - zoomOptions.zoom.drag.enabled = !zoomOptions.zoom.drag.enabled; - chart.update(); + zoomOptions.zoom.drag.enabled = !zoomOptions.zoom.drag.enabled + chart.update() } }, { name: 'Toggle pan', handler(chart) { - zoomOptions.pan.enabled = !zoomOptions.pan.enabled; - chart.update(); + zoomOptions.pan.enabled = !zoomOptions.pan.enabled + chart.update() }, }, { name: 'Reset zoom', handler(chart) { - chart.resetZoom(); + chart.resetZoom() } }, { name: 'Zoom to next week', handler(chart) { - chart.zoomScale('x', Utils.nextWeek(), 'default'); - chart.update(); + chart.zoomScale('x', Utils.nextWeek(), 'default') + chart.update() } }, { name: 'Zoom to 400-600', handler(chart) { - chart.zoomScale('y', {min: 400, max: 600}, 'default'); - chart.update(); + chart.zoomScale('y', {min: 400, max: 600}, 'default') + chart.update() } } -]; +] module.exports = { actions, config, -}; +} ``` diff --git a/docs/samples/drag/timeseries.md b/docs/samples/drag/timeseries.md index bf660ffa4..d8d9141ea 100644 --- a/docs/samples/drag/timeseries.md +++ b/docs/samples/drag/timeseries.md @@ -4,7 +4,7 @@ Zooming is performed by clicking and selecting an area over the chart with the m ```js chart-editor // -const NUMBER_CFG = {count: 200, min: 0, max: 100}; +const NUMBER_CFG = {count: 200, min: 0, max: 100} const data = { datasets: [{ label: 'My First dataset', @@ -15,7 +15,7 @@ const data = { pointBorderWidth: 1, data: Utils.officeHourPoints(NUMBER_CFG), }] -}; +} // // @@ -53,7 +53,7 @@ const scales = { text: (ctx) => ctx.scale.axis + ' axis', } }, -}; +} // // @@ -68,11 +68,11 @@ const zoomOptions = { }, mode: 'xy', }, -}; +} // -const panStatus = () => zoomOptions.pan.enabled ? 'enabled' : 'disabled'; -const zoomStatus = () => zoomOptions.zoom.drag.enabled ? 'enabled' : 'disabled'; +const panStatus = () => zoomOptions.pan.enabled ? 'enabled' : 'disabled' +const zoomStatus = () => zoomOptions.zoom.drag.enabled ? 'enabled' : 'disabled' // const config = { @@ -85,37 +85,37 @@ const config = { title: { display: true, position: 'bottom', - text: (ctx) => 'Zoom: ' + zoomStatus() + ', Pan: ' + panStatus() + text: () => 'Zoom: ' + zoomStatus() + ', Pan: ' + panStatus() } }, } -}; +} // const actions = [ { name: 'Toggle zoom', handler(chart) { - zoomOptions.zoom.drag.enabled = !zoomOptions.zoom.drag.enabled; - chart.update(); + zoomOptions.zoom.drag.enabled = !zoomOptions.zoom.drag.enabled + chart.update() } }, { name: 'Toggle pan', handler(chart) { - zoomOptions.pan.enabled = !zoomOptions.pan.enabled; - chart.update(); + zoomOptions.pan.enabled = !zoomOptions.pan.enabled + chart.update() }, }, { name: 'Reset zoom', handler(chart) { - chart.resetZoom(); + chart.resetZoom() } } -]; +] module.exports = { actions, config, -}; +} ``` diff --git a/docs/samples/fetch-data.md b/docs/samples/fetch-data.md index e98552f77..8bd7396d1 100644 --- a/docs/samples/fetch-data.md +++ b/docs/samples/fetch-data.md @@ -2,41 +2,41 @@ ```js chart-editor // -const start = new Date().valueOf(); -const end = start + 1000 * 60 * 60 * 24 * 2; -const allData = []; -let y = 100; +const start = new Date().valueOf() +const end = start + 1000 * 60 * 60 * 24 * 2 +const allData = [] +let y = 100 for (let x = start; x <= end; x += 1000) { - y += 5 - Math.random() * 10; - allData.push({x, y}); + y += 5 - Math.random() * 10 + allData.push({x, y}) } function fetchData(x1, x2) { - const step = Math.max(1, Math.round((x2 - x1) / 100000)); - const data = []; - let i = 0; + const step = Math.max(1, Math.round((x2 - x1) / 100000)) + const data = [] + let i = 0 while (i < allData.length && allData[i].x < x1) { - i++; + i++ } while (i < allData.length && allData[i].x <= x2) { - data.push(allData[i]); - i += step; + data.push(allData[i]) + i += step } - return data; + return data } // // -let timer; +let timer function startFetch({chart}) { - const {min, max} = chart.scales.x; - clearTimeout(timer); + const {min, max} = chart.scales.x + clearTimeout(timer) timer = setTimeout(() => { - console.log('Fetched data between ' + min + ' and ' + max); - chart.data.datasets[0].data = fetchData(min, max); - chart.stop(); // make sure animations are not running - chart.update('none'); - }, 500); + console.log('Fetched data between ' + min + ' and ' + max) + chart.data.datasets[0].data = fetchData(min, max) + chart.stop() // make sure animations are not running + chart.update('none') + }, 500) } // @@ -64,7 +64,7 @@ const scales = { type: 'linear', position: 'left', }, -}; +} // // @@ -91,10 +91,10 @@ const zoomOptions = { mode: 'x', onZoomComplete: startFetch } -}; +} // -const zoomStatus = (chart) => 'zoom level: ' + chart.getZoomLevel() + ''; +const zoomStatus = (chart) => 'zoom level: ' + chart.getZoomLevel() + '' // const config = { @@ -128,21 +128,21 @@ const config = { } } } -}; +} // const actions = [ { name: 'Reset zoom', handler(chart) { - chart.resetZoom('zoom'); + chart.resetZoom('zoom') } } -]; +] module.exports = { actions, config, output: 'console.log output' -}; +} ``` diff --git a/docs/samples/pan/region.md b/docs/samples/pan/region.md index 93a69d370..735965966 100644 --- a/docs/samples/pan/region.md +++ b/docs/samples/pan/region.md @@ -4,7 +4,7 @@ In this example pan is only accepted at the middle region (50%) of the chart. Th ```js chart-editor // -const NUMBER_CFG = {count: 20, min: -100, max: 100}; +const NUMBER_CFG = {count: 20, min: -100, max: 100} const data = { datasets: [{ label: 'My First dataset', @@ -23,7 +23,7 @@ const data = { pointBorderWidth: 1, data: Utils.points(NUMBER_CFG), }] -}; +} // // @@ -39,7 +39,7 @@ const scaleOpts = { display: true, text: (ctx) => ctx.scale.axis + ' axis', } -}; +} const scales = { x: { position: 'top', @@ -47,8 +47,8 @@ const scales = { y: { position: 'right', }, -}; -Object.keys(scales).forEach(scale => Object.assign(scales[scale], scaleOpts)); +} +Object.keys(scales).forEach(scale => Object.assign(scales[scale], scaleOpts)) // // @@ -60,12 +60,12 @@ const zoomOptions = { pan: { enabled: true, onPanStart({chart, point}) { - const area = chart.chartArea; - const w25 = area.width * 0.25; - const h25 = area.height * 0.25; + const area = chart.chartArea + const w25 = area.width * 0.25 + const h25 = area.height * 0.25 if (point.x < area.left + w25 || point.x > area.right - w25 || point.y < area.top + h25 || point.y > area.bottom - h25) { - return false; // abort + return false // abort } }, mode: 'xy', @@ -78,21 +78,21 @@ const zoomOptions = { enabled: true }, } -}; +} // // const borderPlugin = { id: 'panAreaBorder', - beforeDraw(chart, args, options) { - const {ctx, chartArea: {left, top, width, height}} = chart; - ctx.save(); - ctx.strokeStyle = 'rgba(255, 0, 0, 0.3)'; - ctx.lineWidth = 1; - ctx.strokeRect(left + width * 0.25, top + height * 0.25, width / 2, height / 2); - ctx.restore(); + beforeDraw(chart) { + const {ctx, chartArea: {left, top, width, height}} = chart + ctx.save() + ctx.strokeStyle = 'rgba(255, 0, 0, 0.3)' + ctx.lineWidth = 1 + ctx.strokeRect(left + width * 0.25, top + height * 0.25, width / 2, height / 2) + ctx.restore() } -}; +} // // @@ -106,10 +106,10 @@ const config = { }, }, plugins: [borderPlugin] -}; +} // module.exports = { config, -}; +} ``` diff --git a/docs/samples/pan/toggle.md b/docs/samples/pan/toggle.md index 40762eadd..3e2385320 100644 --- a/docs/samples/pan/toggle.md +++ b/docs/samples/pan/toggle.md @@ -4,7 +4,7 @@ In this example pan is initially disabled. ```js chart-editor // -const NUMBER_CFG = {count: 20, min: -100, max: 100}; +const NUMBER_CFG = {count: 20, min: -100, max: 100} const data = { datasets: [{ label: 'My First dataset', @@ -23,7 +23,7 @@ const data = { pointBorderWidth: 1, data: Utils.points(NUMBER_CFG), }] -}; +} // // @@ -36,7 +36,7 @@ const scaleOpts = { display: true, text: (ctx) => ctx.scale.axis + ' axis', } -}; +} const scales = { x: { position: 'top', @@ -44,8 +44,8 @@ const scales = { y: { position: 'right', }, -}; -Object.keys(scales).forEach(scale => Object.assign(scales[scale], scaleOpts)); +} +Object.keys(scales).forEach(scale => Object.assign(scales[scale], scaleOpts)) // // @@ -66,7 +66,7 @@ const zoomOptions = { enabled: true }, } -}; +} // // @@ -79,27 +79,27 @@ const config = { zoom: zoomOptions, }, }, -}; +} // const actions = [ { name: 'Toggle pan', handler(chart) { - chart.options.plugins.zoom.pan.enabled = !chart.options.plugins.zoom.pan.enabled; - chart.update(); + chart.options.plugins.zoom.pan.enabled = !chart.options.plugins.zoom.pan.enabled + chart.update() } }, { name: 'Reset zoom', handler(chart) { - chart.resetZoom('zoom'); + chart.resetZoom('zoom') } } -]; +] module.exports = { actions, config, -}; +} ``` diff --git a/docs/samples/wheel/category-min-range.md b/docs/samples/wheel/category-min-range.md index 09af5b95a..92c72b4de 100644 --- a/docs/samples/wheel/category-min-range.md +++ b/docs/samples/wheel/category-min-range.md @@ -2,8 +2,8 @@ ```js chart-editor // -const DATA_COUNT = 20; -const NUMBER_CFG = {count: DATA_COUNT, min: -100, max: 100}; +const DATA_COUNT = 20 +const NUMBER_CFG = {count: DATA_COUNT, min: -100, max: 100} const data = { labels: Utils.months({count: DATA_COUNT}), datasets: [{ @@ -22,7 +22,7 @@ const data = { backgroundColor: Utils.randomColor(0.5), data: Utils.numbers(NUMBER_CFG), }] -}; +} // // @@ -35,7 +35,7 @@ const scaleOpts = { display: true, text: (ctx) => ctx.scale.axis + ' axis', } -}; +} const scales = { x: { type: 'category', @@ -45,8 +45,8 @@ const scales = { y: { type: 'linear' }, -}; -Object.keys(scales).forEach(scale => Object.assign(scales[scale], scaleOpts)); +} +Object.keys(scales).forEach(scale => Object.assign(scales[scale], scaleOpts)) // // @@ -79,20 +79,20 @@ const config = { }, }, } -}; +} // const actions = [ { name: 'Reset zoom', handler(chart) { - chart.resetZoom(); + chart.resetZoom() } } -]; +] module.exports = { actions, config, -}; +} ``` diff --git a/docs/samples/wheel/category.md b/docs/samples/wheel/category.md index fd7e92ae7..4c86d289e 100644 --- a/docs/samples/wheel/category.md +++ b/docs/samples/wheel/category.md @@ -2,8 +2,8 @@ ```js chart-editor // -const DATA_COUNT = 20; -const NUMBER_CFG = {count: DATA_COUNT, min: -100, max: 100}; +const DATA_COUNT = 20 +const NUMBER_CFG = {count: DATA_COUNT, min: -100, max: 100} const data = { labels: Utils.months({count: DATA_COUNT}), datasets: [{ @@ -22,7 +22,7 @@ const data = { backgroundColor: Utils.randomColor(0.5), data: Utils.numbers(NUMBER_CFG), }] -}; +} // // @@ -35,7 +35,7 @@ const scaleOpts = { display: true, text: (ctx) => ctx.scale.axis + ' axis', } -}; +} const scales = { x: { type: 'category', @@ -45,8 +45,8 @@ const scales = { y: { type: 'linear' }, -}; -Object.keys(scales).forEach(scale => Object.assign(scales[scale], scaleOpts)); +} +Object.keys(scales).forEach(scale => Object.assign(scales[scale], scaleOpts)) // // @@ -74,20 +74,20 @@ const config = { } }, } -}; +} // const actions = [ { name: 'Reset zoom', handler(chart) { - chart.resetZoom(); + chart.resetZoom() } } -]; +] module.exports = { actions, config, -}; +} ``` diff --git a/docs/samples/wheel/click-zoom.md b/docs/samples/wheel/click-zoom.md index f63fac526..d329f6058 100644 --- a/docs/samples/wheel/click-zoom.md +++ b/docs/samples/wheel/click-zoom.md @@ -19,10 +19,10 @@ Tortor condimentum lacinia quis vel eros donec ac. Phasellus vestibulum lorem se ```js chart-editor // -const DATA_COUNT = 70; -const NUMBER_CFG = {count: DATA_COUNT, min: 0, max: 100}; +const DATA_COUNT = 70 +const NUMBER_CFG = {count: DATA_COUNT, min: 0, max: 100} -const labels = Utils.months({count: DATA_COUNT}); +const labels = Utils.months({count: DATA_COUNT}) const data = { labels: labels, datasets: [ @@ -44,7 +44,7 @@ const data = { stack: 'combined' } ] -}; +} // // @@ -65,26 +65,26 @@ const zoomOptions = { }, mode: 'xy', } -}; +} // // const borderPlugin = { id: 'chartAreaBorder', - beforeDraw(chart, args, options) { - const {ctx, chartArea: {left, top, width, height}} = chart; + beforeDraw(chart) { + const {ctx, chartArea: {left, top, width, height}} = chart if (chart.options.plugins.zoom.zoom.wheel.enabled) { - ctx.save(); - ctx.strokeStyle = 'red'; - ctx.lineWidth = 1; - ctx.strokeRect(left, top, width, height); - ctx.restore(); + ctx.save() + ctx.strokeStyle = 'red' + ctx.lineWidth = 1 + ctx.strokeRect(left, top, width, height) + ctx.restore() } } -}; +} // -const zoomStatus = () => 'Zoom: ' + (zoomOptions.zoom.wheel.enabled ? 'enabled' : 'disabled'); +const zoomStatus = () => 'Zoom: ' + (zoomOptions.zoom.wheel.enabled ? 'enabled' : 'disabled') // const config = { @@ -101,14 +101,14 @@ const config = { } }, onClick(e) { - const chart = e.chart; - chart.options.plugins.zoom.zoom.wheel.enabled = !chart.options.plugins.zoom.zoom.wheel.enabled; - chart.options.plugins.zoom.zoom.pinch.enabled = !chart.options.plugins.zoom.zoom.pinch.enabled; - chart.update(); + const chart = e.chart + chart.options.plugins.zoom.zoom.wheel.enabled = !chart.options.plugins.zoom.zoom.wheel.enabled + chart.options.plugins.zoom.zoom.pinch.enabled = !chart.options.plugins.zoom.zoom.pinch.enabled + chart.update() } }, plugins: [borderPlugin] -}; +} // const actions = [ @@ -116,34 +116,34 @@ const actions = [ name: 'Randomize', handler(chart) { chart.data.datasets.forEach(dataset => { - dataset.data = Utils.numbers(NUMBER_CFG); - }); - chart.update(); + dataset.data = Utils.numbers(NUMBER_CFG) + }) + chart.update() } }, { name: 'Toggle zoom', handler(chart) { - zoomOptions.zoom.wheel.enabled = !zoomOptions.zoom.wheel.enabled; - chart.update(); + zoomOptions.zoom.wheel.enabled = !zoomOptions.zoom.wheel.enabled + chart.update() } }, { name: 'Toggle pan', handler(chart) { - zoomOptions.pan.enabled = !zoomOptions.pan.enabled; - chart.update(); + zoomOptions.pan.enabled = !zoomOptions.pan.enabled + chart.update() }, }, { name: 'Reset zoom', handler(chart) { - chart.resetZoom(); + chart.resetZoom() } } -]; +] module.exports = { actions, config, -}; +} ``` Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Fringilla ut morbi tincidunt augue interdum velit euismod. Elit pellentesque habitant morbi tristique senectus et netus. Consectetur adipiscing elit pellentesque habitant morbi. Id faucibus nisl tincidunt eget nullam non nisi est sit. Blandit turpis cursus in hac habitasse. Vulputate eu scelerisque felis imperdiet proin fermentum leo vel. Ornare massa eget egestas purus. A diam sollicitudin tempor id eu nisl nunc. Augue mauris augue neque gravida in fermentum et sollicitudin. Dolor purus non enim praesent elementum facilisis leo vel fringilla. Habitant morbi tristique senectus et netus et malesuada. Nulla pharetra diam sit amet nisl suscipit adipiscing bibendum est. Gravida dictum fusce ut placerat orci nulla pellentesque. diff --git a/docs/samples/wheel/log.md b/docs/samples/wheel/log.md index 2c3a07249..17a0a27be 100644 --- a/docs/samples/wheel/log.md +++ b/docs/samples/wheel/log.md @@ -105,7 +105,7 @@ const data = { y: -3.596e1 }] }] -}; +} // // @@ -114,11 +114,11 @@ const scales = { type: 'logarithmic', ticks: { callback: function(tick) { - const remain = tick / (Math.pow(10, Math.floor(Math.log10(tick)))); + const remain = tick / (Math.pow(10, Math.floor(Math.log10(tick)))) if (remain === 1 || remain === 2 || remain === 5) { - return tick.toString() + 'Hz'; + return tick.toString() + 'Hz' } - return ''; + return '' }, maxRotation: 0 }, @@ -130,10 +130,10 @@ const scales = { y: { // constant width for the scale afterFit: (scale) => { - scale.width = 50; + scale.width = 50 }, } -}; +} // // @@ -164,20 +164,20 @@ const config = { } }, }, -}; +} // const actions = [ { name: 'Reset zoom', handler(chart) { - chart.resetZoom(); + chart.resetZoom() } } -]; +] module.exports = { actions, config, -}; +} ``` diff --git a/docs/samples/wheel/over-scale-mode.md b/docs/samples/wheel/over-scale-mode.md index 685791d77..70e17a951 100644 --- a/docs/samples/wheel/over-scale-mode.md +++ b/docs/samples/wheel/over-scale-mode.md @@ -4,7 +4,7 @@ Pan and Zoom are allowed only when mouse is over the axis. ```js chart-editor // -const NUMBER_CFG = {count: 20, min: -100, max: 100}; +const NUMBER_CFG = {count: 20, min: -100, max: 100} const data = { datasets: [{ label: 'My First dataset', @@ -23,7 +23,7 @@ const data = { pointBorderWidth: 1, data: Utils.points(NUMBER_CFG), }] -}; +} // // @@ -40,7 +40,7 @@ const scaleOpts = { display: true, text: (ctx) => ctx.scale.axis + ' axis', } -}; +} const scales = { x: { position: 'top', @@ -48,8 +48,8 @@ const scales = { y: { position: 'right', }, -}; -Object.keys(scales).forEach(scale => Object.assign(scales[scale], scaleOpts)); +} +Object.keys(scales).forEach(scale => Object.assign(scales[scale], scaleOpts)) // // @@ -69,11 +69,11 @@ const zoomOptions = { mode: 'xy', scaleMode: 'xy', } -}; +} // -const panStatus = () => zoomOptions.pan.enabled ? 'enabled' : 'disabled'; -const zoomStatus = () => zoomOptions.zoom.wheel.enabled ? 'enabled' : 'disabled'; +const panStatus = () => zoomOptions.pan.enabled ? 'enabled' : 'disabled' +const zoomStatus = () => zoomOptions.zoom.wheel.enabled ? 'enabled' : 'disabled' // const config = { @@ -86,37 +86,37 @@ const config = { title: { display: true, position: 'bottom', - text: (ctx) => 'Zoom: ' + zoomStatus() + ', Pan: ' + panStatus(), + text: () => 'Zoom: ' + zoomStatus() + ', Pan: ' + panStatus(), } }, } -}; +} // const actions = [ { name: 'Toggle zoom', handler(chart) { - zoomOptions.zoom.wheel.enabled = !zoomOptions.zoom.wheel.enabled; - zoomOptions.zoom.pinch.enabled = !zoomOptions.zoom.pinch.enabled; - chart.update(); + zoomOptions.zoom.wheel.enabled = !zoomOptions.zoom.wheel.enabled + zoomOptions.zoom.pinch.enabled = !zoomOptions.zoom.pinch.enabled + chart.update() } }, { name: 'Toggle pan', handler(chart) { - zoomOptions.pan.enabled = !zoomOptions.pan.enabled; - chart.update(); + zoomOptions.pan.enabled = !zoomOptions.pan.enabled + chart.update() }, }, { name: 'Reset zoom', handler(chart) { - chart.resetZoom(); + chart.resetZoom() } } -]; +] module.exports = { actions, config, -}; +} ``` diff --git a/docs/samples/wheel/time.md b/docs/samples/wheel/time.md index 56365548e..b99dc068f 100644 --- a/docs/samples/wheel/time.md +++ b/docs/samples/wheel/time.md @@ -2,7 +2,7 @@ ```js chart-editor // -const NUMBER_CFG = {count: 500, min: 0, max: 1000}; +const NUMBER_CFG = {count: 500, min: 0, max: 1000} const data = { datasets: [{ label: 'My First dataset', @@ -13,7 +13,7 @@ const data = { pointBorderWidth: 1, data: Utils.hourlyPoints(NUMBER_CFG), }] -}; +} // // @@ -48,7 +48,7 @@ const scales = { text: (ctx) => ctx.scale.axis + ' axis', } }, -}; +} // // @@ -66,11 +66,11 @@ const zoomOptions = { enabled: true, mode: 'xy', } -}; +} // -const panStatus = () => zoomOptions.pan.enabled ? 'enabled' : 'disabled'; -const zoomStatus = () => zoomOptions.zoom.wheel.enabled ? 'enabled' : 'disabled'; +const panStatus = () => zoomOptions.pan.enabled ? 'enabled' : 'disabled' +const zoomStatus = () => zoomOptions.zoom.wheel.enabled ? 'enabled' : 'disabled' // const config = { @@ -83,54 +83,54 @@ const config = { title: { display: true, position: 'bottom', - text: (ctx) => 'Zoom: ' + zoomStatus() + ', Pan: ' + panStatus() + text: () => 'Zoom: ' + zoomStatus() + ', Pan: ' + panStatus() } }, onClick(e) { - console.log(e.type); + console.log(e.type) } } -}; +} // const actions = [ { name: 'Toggle zoom', handler(chart) { - zoomOptions.zoom.wheel.enabled = !zoomOptions.zoom.wheel.enabled; - zoomOptions.zoom.pinch.enabled = !zoomOptions.zoom.pinch.enabled; - chart.update(); + zoomOptions.zoom.wheel.enabled = !zoomOptions.zoom.wheel.enabled + zoomOptions.zoom.pinch.enabled = !zoomOptions.zoom.pinch.enabled + chart.update() } }, { name: 'Toggle pan', handler(chart) { - zoomOptions.pan.enabled = !zoomOptions.pan.enabled; - chart.update(); + zoomOptions.pan.enabled = !zoomOptions.pan.enabled + chart.update() }, }, { name: 'Reset zoom', handler(chart) { - chart.resetZoom(); + chart.resetZoom() } }, { name: 'Zoom to next week', handler(chart) { - chart.zoomScale('x', Utils.nextWeek(), 'default'); - chart.update(); + chart.zoomScale('x', Utils.nextWeek(), 'default') + chart.update() } }, { name: 'Zoom to 400-600', handler(chart) { - chart.zoomScale('y', {min: 400, max: 600}, 'default'); - chart.update(); + chart.zoomScale('y', {min: 400, max: 600}, 'default') + chart.update() } } -]; +] module.exports = { actions, config, output: 'Clicks are logged here' -}; +} ``` diff --git a/jasmine.json b/jasmine.json new file mode 100644 index 000000000..fd8b2c31b --- /dev/null +++ b/jasmine.json @@ -0,0 +1,4 @@ +{ + "spec_dir": "build", + "spec_files": ["**/*.test.js"] +} diff --git a/karma.conf.js b/karma.conf.cjs similarity index 90% rename from karma.conf.js rename to karma.conf.cjs index 066cef81d..417758870 100644 --- a/karma.conf.js +++ b/karma.conf.cjs @@ -1,11 +1,12 @@ const istanbul = require('rollup-plugin-istanbul'); const json = require('@rollup/plugin-json'); const resolve = require('@rollup/plugin-node-resolve'); -const builds = require('./rollup.config'); const yargs = require('yargs'); const env = process.env.NODE_ENV; -module.exports = function(karma) { +module.exports = async function(karma) { + const builds = (await import('./rollup.config.js')).default; + const args = yargs .option('verbose', {default: false}) .argv; @@ -15,14 +16,12 @@ module.exports = function(karma) { // better with source mapping. In other cases, pick the minified build to // make sure that the minification process (terser) doesn't break anything. const regex = karma.autoWatch ? /\.js$/ : /\.min\.js$/; - const build = builds.filter(v => v.output.file.match(regex))[0]; + const build = builds.filter(v => v.output.format === 'umd' && v.output.file.match(regex))[0]; if (env === 'test') { - build.plugins = [ - json(), - resolve(), + build.plugins.push( istanbul({exclude: ['node_modules/**/*.js', 'package.json']}) - ]; + ); } karma.set({ @@ -60,26 +59,25 @@ module.exports = function(karma) { files: [ {pattern: 'test/fixtures/**/*.js', included: false}, - {pattern: 'test/fixtures/**/*.json', included: false}, {pattern: 'test/fixtures/**/*.png', included: false}, {pattern: 'node_modules/chart.js/dist/chart.umd.js'}, {pattern: 'node_modules/hammer-simulator/index.js'}, {pattern: 'node_modules/hammerjs/hammer.js'}, {pattern: 'node_modules/chartjs-adapter-date-fns/dist/chartjs-adapter-date-fns.bundle.js'}, {pattern: 'test/index.js'}, - {pattern: 'src/index.js'}, + {pattern: 'src/index.umd.ts'}, {pattern: 'test/specs/**/*.js'} ], preprocessors: { 'test/index.js': ['rollup'], - 'src/index.js': ['sources'] + 'src/index.umd.ts': ['sources'] }, rollupPreprocessor: { plugins: [ json(), - resolve(), + resolve({extensions: ['.js', '.ts']}), ], external: [ 'chart.js' diff --git a/package-lock.json b/package-lock.json index c7ac14ecd..b1cc3cdb5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,12 +18,17 @@ "@rollup/plugin-commonjs": "^28.0.1", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", + "@rollup/plugin-swc": "^0.4.0", "@rollup/plugin-terser": "^0.4.4", "@simonbrunel/vuepress-plugin-versions": "^0.2.0", + "@swc/cli": "^0.5.1", + "@swc/core": "^1.9.3", + "@types/jasmine": "^5.1.4", "@types/linkify-it": "^3.0.5", "@typescript-eslint/eslint-plugin": "^5.4.0", - "@typescript-eslint/parser": "^5.4.0", + "@typescript-eslint/parser": "^5.62.0", "babel-loader": "^8.3.0", + "c8": "^10.1.2", "chart.js": "^4.3.2", "chartjs-adapter-date-fns": "^3.0.0", "chartjs-test-utils": "^0.5.0", @@ -35,6 +40,7 @@ "eslint-config-chartjs": "^0.3.0", "eslint-plugin-html": "^8.1.2", "eslint-plugin-markdown": "^2.0.1", + "eslint-plugin-prettier": "^5.2.1", "hammer-simulator": "^0.0.1", "jasmine": "^5.4.0", "karma": "^6.4.4", @@ -47,12 +53,12 @@ "karma-spec-reporter": "0.0.36", "ng-hammerjs": "^2.0.8", "pixelmatch": "^6.0.0", - "rollup": "^4.12.1", + "rollup": "^4.27.3", "rollup-plugin-cleanup": "^3.2.1", "rollup-plugin-istanbul": "^5.0.0", "typedoc": "^0.26.11", "typedoc-plugin-markdown": "^4.2.10", - "typescript": "^5.6.3", + "typescript": "5.6", "vuepress": "^1.8.2", "vuepress-plugin-flexsearch": "^0.3.0", "vuepress-plugin-redirect": "^1.2.5", @@ -1609,6 +1615,12 @@ "node": ">=6.9.0" } }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, "node_modules/@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", @@ -1939,6 +1951,294 @@ "node": ">=4" } }, + "node_modules/@napi-rs/nice": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice/-/nice-1.0.1.tgz", + "integrity": "sha512-zM0mVWSXE0a0h9aKACLwKmD6nHcRiKrPpCfvaKqG1CqDEyjEawId0ocXxVzPMCAm6kkWr2P025msfxXEnt8UGQ==", + "dev": true, + "optional": true, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "optionalDependencies": { + "@napi-rs/nice-android-arm-eabi": "1.0.1", + "@napi-rs/nice-android-arm64": "1.0.1", + "@napi-rs/nice-darwin-arm64": "1.0.1", + "@napi-rs/nice-darwin-x64": "1.0.1", + "@napi-rs/nice-freebsd-x64": "1.0.1", + "@napi-rs/nice-linux-arm-gnueabihf": "1.0.1", + "@napi-rs/nice-linux-arm64-gnu": "1.0.1", + "@napi-rs/nice-linux-arm64-musl": "1.0.1", + "@napi-rs/nice-linux-ppc64-gnu": "1.0.1", + "@napi-rs/nice-linux-riscv64-gnu": "1.0.1", + "@napi-rs/nice-linux-s390x-gnu": "1.0.1", + "@napi-rs/nice-linux-x64-gnu": "1.0.1", + "@napi-rs/nice-linux-x64-musl": "1.0.1", + "@napi-rs/nice-win32-arm64-msvc": "1.0.1", + "@napi-rs/nice-win32-ia32-msvc": "1.0.1", + "@napi-rs/nice-win32-x64-msvc": "1.0.1" + } + }, + "node_modules/@napi-rs/nice-android-arm-eabi": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-android-arm-eabi/-/nice-android-arm-eabi-1.0.1.tgz", + "integrity": "sha512-5qpvOu5IGwDo7MEKVqqyAxF90I6aLj4n07OzpARdgDRfz8UbBztTByBp0RC59r3J1Ij8uzYi6jI7r5Lws7nn6w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-android-arm64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-android-arm64/-/nice-android-arm64-1.0.1.tgz", + "integrity": "sha512-GqvXL0P8fZ+mQqG1g0o4AO9hJjQaeYG84FRfZaYjyJtZZZcMjXW5TwkL8Y8UApheJgyE13TQ4YNUssQaTgTyvA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-darwin-arm64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-darwin-arm64/-/nice-darwin-arm64-1.0.1.tgz", + "integrity": "sha512-91k3HEqUl2fsrz/sKkuEkscj6EAj3/eZNCLqzD2AA0TtVbkQi8nqxZCZDMkfklULmxLkMxuUdKe7RvG/T6s2AA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-darwin-x64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-darwin-x64/-/nice-darwin-x64-1.0.1.tgz", + "integrity": "sha512-jXnMleYSIR/+TAN/p5u+NkCA7yidgswx5ftqzXdD5wgy/hNR92oerTXHc0jrlBisbd7DpzoaGY4cFD7Sm5GlgQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-freebsd-x64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-freebsd-x64/-/nice-freebsd-x64-1.0.1.tgz", + "integrity": "sha512-j+iJ/ezONXRQsVIB/FJfwjeQXX7A2tf3gEXs4WUGFrJjpe/z2KB7sOv6zpkm08PofF36C9S7wTNuzHZ/Iiccfw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-arm-gnueabihf": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm-gnueabihf/-/nice-linux-arm-gnueabihf-1.0.1.tgz", + "integrity": "sha512-G8RgJ8FYXYkkSGQwywAUh84m946UTn6l03/vmEXBYNJxQJcD+I3B3k5jmjFG/OPiU8DfvxutOP8bi+F89MCV7Q==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-arm64-gnu": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm64-gnu/-/nice-linux-arm64-gnu-1.0.1.tgz", + "integrity": "sha512-IMDak59/W5JSab1oZvmNbrms3mHqcreaCeClUjwlwDr0m3BoR09ZiN8cKFBzuSlXgRdZ4PNqCYNeGQv7YMTjuA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-arm64-musl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm64-musl/-/nice-linux-arm64-musl-1.0.1.tgz", + "integrity": "sha512-wG8fa2VKuWM4CfjOjjRX9YLIbysSVV1S3Kgm2Fnc67ap/soHBeYZa6AGMeR5BJAylYRjnoVOzV19Cmkco3QEPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-ppc64-gnu": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-ppc64-gnu/-/nice-linux-ppc64-gnu-1.0.1.tgz", + "integrity": "sha512-lxQ9WrBf0IlNTCA9oS2jg/iAjQyTI6JHzABV664LLrLA/SIdD+I1i3Mjf7TsnoUbgopBcCuDztVLfJ0q9ubf6Q==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-riscv64-gnu": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-riscv64-gnu/-/nice-linux-riscv64-gnu-1.0.1.tgz", + "integrity": "sha512-3xs69dO8WSWBb13KBVex+yvxmUeEsdWexxibqskzoKaWx9AIqkMbWmE2npkazJoopPKX2ULKd8Fm9veEn0g4Ig==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-s390x-gnu": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-s390x-gnu/-/nice-linux-s390x-gnu-1.0.1.tgz", + "integrity": "sha512-lMFI3i9rlW7hgToyAzTaEybQYGbQHDrpRkg+1gJWEpH0PLAQoZ8jiY0IzakLfNWnVda1eTYYlxxFYzW8Rqczkg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-x64-gnu": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-x64-gnu/-/nice-linux-x64-gnu-1.0.1.tgz", + "integrity": "sha512-XQAJs7DRN2GpLN6Fb+ZdGFeYZDdGl2Fn3TmFlqEL5JorgWKrQGRUrpGKbgZ25UeZPILuTKJ+OowG2avN8mThBA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-x64-musl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-x64-musl/-/nice-linux-x64-musl-1.0.1.tgz", + "integrity": "sha512-/rodHpRSgiI9o1faq9SZOp/o2QkKQg7T+DK0R5AkbnI/YxvAIEHf2cngjYzLMQSQgUhxym+LFr+UGZx4vK4QdQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-win32-arm64-msvc": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-arm64-msvc/-/nice-win32-arm64-msvc-1.0.1.tgz", + "integrity": "sha512-rEcz9vZymaCB3OqEXoHnp9YViLct8ugF+6uO5McifTedjq4QMQs3DHz35xBEGhH3gJWEsXMUbzazkz5KNM5YUg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-win32-ia32-msvc": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-ia32-msvc/-/nice-win32-ia32-msvc-1.0.1.tgz", + "integrity": "sha512-t7eBAyPUrWL8su3gDxw9xxxqNwZzAqKo0Szv3IjVQd1GpXXVkb6vBBQUuxfIYaXMzZLwlxRQ7uzM2vdUE9ULGw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-win32-x64-msvc": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-x64-msvc/-/nice-win32-x64-msvc-1.0.1.tgz", + "integrity": "sha512-JlF+uDcatt3St2ntBG8H02F1mM45i5SF9W+bIKiReVE6wiy3o16oBP/yxt+RZ+N6LbCImJXJ6bXNO2kn9AXicg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1984,6 +2284,18 @@ "node": ">=14" } }, + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/@rollup/plugin-commonjs": { "version": "28.0.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.1.tgz", @@ -2054,6 +2366,28 @@ } } }, + "node_modules/@rollup/plugin-swc": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-swc/-/plugin-swc-0.4.0.tgz", + "integrity": "sha512-oAtqXa8rOl7BOK1Rz3rRxI+LIL53S9SqO2KSq2UUUzWgOgXg6492Jh5mL2mv/f9cpit8zFWdwILuVeozZ0C8mg==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "smob": "^1.4.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "@swc/core": "^1.3.0", + "rollup": "^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, "node_modules/@rollup/plugin-terser": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-0.4.4.tgz", @@ -2099,9 +2433,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.27.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.27.2.tgz", - "integrity": "sha512-Tj+j7Pyzd15wAdSJswvs5CJzJNV+qqSUcr/aCD+jpQSBtXvGnV0pnrjoc8zFTe9fcKCatkpFpOO7yAzpO998HA==", + "version": "4.27.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.27.3.tgz", + "integrity": "sha512-EzxVSkIvCFxUd4Mgm4xR9YXrcp976qVaHnqom/Tgm+vU79k4vV4eYTjmRvGfeoW8m9LVcsAy/lGjcgVegKEhLQ==", "cpu": [ "arm" ], @@ -2112,9 +2446,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.27.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.27.2.tgz", - "integrity": "sha512-xsPeJgh2ThBpUqlLgRfiVYBEf/P1nWlWvReG+aBWfNv3XEBpa6ZCmxSVnxJgLgkNz4IbxpLy64h2gCmAAQLneQ==", + "version": "4.27.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.27.3.tgz", + "integrity": "sha512-LJc5pDf1wjlt9o/Giaw9Ofl+k/vLUaYsE2zeQGH85giX2F+wn/Cg8b3c5CDP3qmVmeO5NzwVUzQQxwZvC2eQKw==", "cpu": [ "arm64" ], @@ -2125,9 +2459,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.27.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.27.2.tgz", - "integrity": "sha512-KnXU4m9MywuZFedL35Z3PuwiTSn/yqRIhrEA9j+7OSkji39NzVkgxuxTYg5F8ryGysq4iFADaU5osSizMXhU2A==", + "version": "4.27.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.27.3.tgz", + "integrity": "sha512-OuRysZ1Mt7wpWJ+aYKblVbJWtVn3Cy52h8nLuNSzTqSesYw1EuN6wKp5NW/4eSre3mp12gqFRXOKTcN3AI3LqA==", "cpu": [ "arm64" ], @@ -2138,9 +2472,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.27.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.27.2.tgz", - "integrity": "sha512-Hj77A3yTvUeCIx/Vi+4d4IbYhyTwtHj07lVzUgpUq9YpJSEiGJj4vXMKwzJ3w5zp5v3PFvpJNgc/J31smZey6g==", + "version": "4.27.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.27.3.tgz", + "integrity": "sha512-xW//zjJMlJs2sOrCmXdB4d0uiilZsOdlGQIC/jjmMWT47lkLLoB1nsNhPUcnoqyi5YR6I4h+FjBpILxbEy8JRg==", "cpu": [ "x64" ], @@ -2151,9 +2485,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.27.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.27.2.tgz", - "integrity": "sha512-RjgKf5C3xbn8gxvCm5VgKZ4nn0pRAIe90J0/fdHUsgztd3+Zesb2lm2+r6uX4prV2eUByuxJNdt647/1KPRq5g==", + "version": "4.27.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.27.3.tgz", + "integrity": "sha512-58E0tIcwZ+12nK1WiLzHOD8I0d0kdrY/+o7yFVPRHuVGY3twBwzwDdTIBGRxLmyjciMYl1B/U515GJy+yn46qw==", "cpu": [ "arm64" ], @@ -2164,9 +2498,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.27.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.27.2.tgz", - "integrity": "sha512-duq21FoXwQtuws+V9H6UZ+eCBc7fxSpMK1GQINKn3fAyd9DFYKPJNcUhdIKOrMFjLEJgQskoMoiuizMt+dl20g==", + "version": "4.27.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.27.3.tgz", + "integrity": "sha512-78fohrpcVwTLxg1ZzBMlwEimoAJmY6B+5TsyAZ3Vok7YabRBUvjYTsRXPTjGEvv/mfgVBepbW28OlMEz4w8wGA==", "cpu": [ "x64" ], @@ -2177,9 +2511,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.27.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.27.2.tgz", - "integrity": "sha512-6npqOKEPRZkLrMcvyC/32OzJ2srdPzCylJjiTJT2c0bwwSGm7nz2F9mNQ1WrAqCBZROcQn91Fno+khFhVijmFA==", + "version": "4.27.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.27.3.tgz", + "integrity": "sha512-h2Ay79YFXyQi+QZKo3ISZDyKaVD7uUvukEHTOft7kh00WF9mxAaxZsNs3o/eukbeKuH35jBvQqrT61fzKfAB/Q==", "cpu": [ "arm" ], @@ -2190,9 +2524,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.27.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.27.2.tgz", - "integrity": "sha512-V9Xg6eXtgBtHq2jnuQwM/jr2mwe2EycnopO8cbOvpzFuySCGtKlPCI3Hj9xup/pJK5Q0388qfZZy2DqV2J8ftw==", + "version": "4.27.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.27.3.tgz", + "integrity": "sha512-Sv2GWmrJfRY57urktVLQ0VKZjNZGogVtASAgosDZ1aUB+ykPxSi3X1nWORL5Jk0sTIIwQiPH7iE3BMi9zGWfkg==", "cpu": [ "arm" ], @@ -2203,9 +2537,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.27.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.27.2.tgz", - "integrity": "sha512-uCFX9gtZJoQl2xDTpRdseYuNqyKkuMDtH6zSrBTA28yTfKyjN9hQ2B04N5ynR8ILCoSDOrG/Eg+J2TtJ1e/CSA==", + "version": "4.27.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.27.3.tgz", + "integrity": "sha512-FPoJBLsPW2bDNWjSrwNuTPUt30VnfM8GPGRoLCYKZpPx0xiIEdFip3dH6CqgoT0RnoGXptaNziM0WlKgBc+OWQ==", "cpu": [ "arm64" ], @@ -2216,9 +2550,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.27.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.27.2.tgz", - "integrity": "sha512-/PU9P+7Rkz8JFYDHIi+xzHabOu9qEWR07L5nWLIUsvserrxegZExKCi2jhMZRd0ATdboKylu/K5yAXbp7fYFvA==", + "version": "4.27.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.27.3.tgz", + "integrity": "sha512-TKxiOvBorYq4sUpA0JT+Fkh+l+G9DScnG5Dqx7wiiqVMiRSkzTclP35pE6eQQYjP4Gc8yEkJGea6rz4qyWhp3g==", "cpu": [ "arm64" ], @@ -2229,9 +2563,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.27.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.27.2.tgz", - "integrity": "sha512-eCHmol/dT5odMYi/N0R0HC8V8QE40rEpkyje/ZAXJYNNoSfrObOvG/Mn+s1F/FJyB7co7UQZZf6FuWnN6a7f4g==", + "version": "4.27.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.27.3.tgz", + "integrity": "sha512-v2M/mPvVUKVOKITa0oCFksnQQ/TqGrT+yD0184/cWHIu0LoIuYHwox0Pm3ccXEz8cEQDLk6FPKd1CCm+PlsISw==", "cpu": [ "ppc64" ], @@ -2242,9 +2576,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.27.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.27.2.tgz", - "integrity": "sha512-DEP3Njr9/ADDln3kNi76PXonLMSSMiCir0VHXxmGSHxCxDfQ70oWjHcJGfiBugzaqmYdTC7Y+8Int6qbnxPBIQ==", + "version": "4.27.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.27.3.tgz", + "integrity": "sha512-LdrI4Yocb1a/tFVkzmOE5WyYRgEBOyEhWYJe4gsDWDiwnjYKjNs7PS6SGlTDB7maOHF4kxevsuNBl2iOcj3b4A==", "cpu": [ "riscv64" ], @@ -2255,9 +2589,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.27.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.27.2.tgz", - "integrity": "sha512-NHGo5i6IE/PtEPh5m0yw5OmPMpesFnzMIS/lzvN5vknnC1sXM5Z/id5VgcNPgpD+wHmIcuYYgW+Q53v+9s96lQ==", + "version": "4.27.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.27.3.tgz", + "integrity": "sha512-d4wVu6SXij/jyiwPvI6C4KxdGzuZOvJ6y9VfrcleHTwo68fl8vZC5ZYHsCVPUi4tndCfMlFniWgwonQ5CUpQcA==", "cpu": [ "s390x" ], @@ -2268,9 +2602,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.27.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.27.2.tgz", - "integrity": "sha512-PaW2DY5Tan+IFvNJGHDmUrORadbe/Ceh8tQxi8cmdQVCCYsLoQo2cuaSj+AU+YRX8M4ivS2vJ9UGaxfuNN7gmg==", + "version": "4.27.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.27.3.tgz", + "integrity": "sha512-/6bn6pp1fsCGEY5n3yajmzZQAh+mW4QPItbiWxs69zskBzJuheb3tNynEjL+mKOsUSFK11X4LYF2BwwXnzWleA==", "cpu": [ "x64" ], @@ -2281,9 +2615,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.27.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.27.2.tgz", - "integrity": "sha512-dOlWEMg2gI91Qx5I/HYqOD6iqlJspxLcS4Zlg3vjk1srE67z5T2Uz91yg/qA8sY0XcwQrFzWWiZhMNERylLrpQ==", + "version": "4.27.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.27.3.tgz", + "integrity": "sha512-nBXOfJds8OzUT1qUreT/en3eyOXd2EH5b0wr2bVB5999qHdGKkzGzIyKYaKj02lXk6wpN71ltLIaQpu58YFBoQ==", "cpu": [ "x64" ], @@ -2294,9 +2628,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.27.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.27.2.tgz", - "integrity": "sha512-euMIv/4x5Y2/ImlbGl88mwKNXDsvzbWUlT7DFky76z2keajCtcbAsN9LUdmk31hAoVmJJYSThgdA0EsPeTr1+w==", + "version": "4.27.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.27.3.tgz", + "integrity": "sha512-ogfbEVQgIZOz5WPWXF2HVb6En+kWzScuxJo/WdQTqEgeyGkaa2ui5sQav9Zkr7bnNCLK48uxmmK0TySm22eiuw==", "cpu": [ "arm64" ], @@ -2307,9 +2641,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.27.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.27.2.tgz", - "integrity": "sha512-RsnE6LQkUHlkC10RKngtHNLxb7scFykEbEwOFDjr3CeCMG+Rr+cKqlkKc2/wJ1u4u990urRHCbjz31x84PBrSQ==", + "version": "4.27.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.27.3.tgz", + "integrity": "sha512-ecE36ZBMLINqiTtSNQ1vzWc5pXLQHlf/oqGp/bSbi7iedcjcNb6QbCBNG73Euyy2C+l/fn8qKWEwxr+0SSfs3w==", "cpu": [ "ia32" ], @@ -2320,9 +2654,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.27.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.27.2.tgz", - "integrity": "sha512-foJM5vv+z2KQmn7emYdDLyTbkoO5bkHZE1oth2tWbQNGW7mX32d46Hz6T0MqXdWS2vBZhaEtHqdy9WYwGfiliA==", + "version": "4.27.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.27.3.tgz", + "integrity": "sha512-vliZLrDmYKyaUoMzEbMTg2JkerfBjn03KmAw9CykO0Zzkzoyd7o3iZNam/TpyWNjNT+Cz2iO3P9Smv2wgrR+Eg==", "cpu": [ "x64" ], @@ -2332,6 +2666,12 @@ "win32" ] }, + "node_modules/@sec-ant/readable-stream": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", + "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", + "dev": true + }, "node_modules/@shigma/stringify-object": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/@shigma/stringify-object/-/stringify-object-3.3.0.tgz", @@ -2432,18 +2772,325 @@ "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", "dev": true }, - "node_modules/@szmarczak/http-timer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", - "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", - "dev": true, - "dependencies": { - "defer-to-connect": "^1.0.1" + "node_modules/@swc/cli": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@swc/cli/-/cli-0.5.1.tgz", + "integrity": "sha512-sxSXyjqFImYrqjhZSPymjmM/9V6auZG67UsDwbe7FZaBlyfW8ka3QG/zRjpJJ9+8Ahns/kKb8bXPKQq7V2MtBw==", + "dev": true, + "dependencies": { + "@swc/counter": "^0.1.3", + "@xhmikosr/bin-wrapper": "^13.0.5", + "commander": "^8.3.0", + "fast-glob": "^3.2.5", + "minimatch": "^9.0.3", + "piscina": "^4.3.0", + "semver": "^7.3.8", + "slash": "3.0.0", + "source-map": "^0.7.3" + }, + "bin": { + "spack": "bin/spack.js", + "swc": "bin/swc.js", + "swcx": "bin/swcx.js" + }, + "engines": { + "node": ">= 16.14.0" + }, + "peerDependencies": { + "@swc/core": "^1.2.66", + "chokidar": "^3.5.1" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@swc/cli/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@swc/cli/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@swc/cli/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@swc/cli/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/cli/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@swc/core": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.9.3.tgz", + "integrity": "sha512-oRj0AFePUhtatX+BscVhnzaAmWjpfAeySpM1TCbxA1rtBDeH/JDhi5yYzAKneDYtVtBvA7ApfeuzhMC9ye4xSg==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@swc/counter": "^0.1.3", + "@swc/types": "^0.1.17" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.9.3", + "@swc/core-darwin-x64": "1.9.3", + "@swc/core-linux-arm-gnueabihf": "1.9.3", + "@swc/core-linux-arm64-gnu": "1.9.3", + "@swc/core-linux-arm64-musl": "1.9.3", + "@swc/core-linux-x64-gnu": "1.9.3", + "@swc/core-linux-x64-musl": "1.9.3", + "@swc/core-win32-arm64-msvc": "1.9.3", + "@swc/core-win32-ia32-msvc": "1.9.3", + "@swc/core-win32-x64-msvc": "1.9.3" + }, + "peerDependencies": { + "@swc/helpers": "*" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.9.3.tgz", + "integrity": "sha512-hGfl/KTic/QY4tB9DkTbNuxy5cV4IeejpPD4zo+Lzt4iLlDWIeANL4Fkg67FiVceNJboqg48CUX+APhDHO5G1w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.9.3.tgz", + "integrity": "sha512-IaRq05ZLdtgF5h9CzlcgaNHyg4VXuiStnOFpfNEMuI5fm5afP2S0FHq8WdakUz5WppsbddTdplL+vpeApt/WCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.9.3.tgz", + "integrity": "sha512-Pbwe7xYprj/nEnZrNBvZfjnTxlBIcfApAGdz2EROhjpPj+FBqBa3wOogqbsuGGBdCphf8S+KPprL1z+oDWkmSQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.9.3.tgz", + "integrity": "sha512-AQ5JZiwNGVV/2K2TVulg0mw/3LYfqpjZO6jDPtR2evNbk9Yt57YsVzS+3vHSlUBQDRV9/jqMuZYVU3P13xrk+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.9.3.tgz", + "integrity": "sha512-tzVH480RY6RbMl/QRgh5HK3zn1ZTFsThuxDGo6Iuk1MdwIbdFYUY034heWUTI4u3Db97ArKh0hNL0xhO3+PZdg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.9.3.tgz", + "integrity": "sha512-ivXXBRDXDc9k4cdv10R21ccBmGebVOwKXT/UdH1PhxUn9m/h8erAWjz5pcELwjiMf27WokqPgaWVfaclDbgE+w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.9.3.tgz", + "integrity": "sha512-ILsGMgfnOz1HwdDz+ZgEuomIwkP1PHT6maigZxaCIuC6OPEhKE8uYna22uU63XvYcLQvZYDzpR3ms47WQPuNEg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.9.3.tgz", + "integrity": "sha512-e+XmltDVIHieUnNJHtspn6B+PCcFOMYXNJB1GqoCcyinkEIQNwC8KtWgMqUucUbEWJkPc35NHy9k8aCXRmw9Kg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.9.3.tgz", + "integrity": "sha512-rqpzNfpAooSL4UfQnHhkW8aL+oyjqJniDP0qwZfGnjDoJSbtPysHg2LpcOBEdSnEH+uIZq6J96qf0ZFD8AGfXA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.9.3.tgz", + "integrity": "sha512-3YJJLQ5suIEHEKc1GHtqVq475guiyqisKSoUnoaRtxkDaW5g1yvPt9IoSLOe2mRs7+FFhGGU693RsBUSwOXSdQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "dev": true + }, + "node_modules/@swc/types": { + "version": "0.1.17", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.17.tgz", + "integrity": "sha512-V5gRru+aD8YVyCOMAjMpWR1Ui577DD5KSJsHP8RAxopAH22jFz6GZd/qxqjO6MJHQhcsjvjOFXyDhyLQUnMveQ==", + "dev": true, + "dependencies": { + "@swc/counter": "^0.1.3" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "dev": true, + "dependencies": { + "defer-to-connect": "^1.0.1" }, "engines": { "node": ">=6" } }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", + "dev": true + }, "node_modules/@trysound/sax": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", @@ -2557,6 +3204,12 @@ "integrity": "sha512-t2szdkwmg2JJyuCM20e8kR2X59WCE5Zkl4bzm1u1Oukjm79zpbiAv+QjnwLnuuV0WHEcX2NgUItu0pAMKuOPww==", "dev": true }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", + "dev": true + }, "node_modules/@types/http-errors": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", @@ -2572,6 +3225,18 @@ "@types/node": "*" } }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true + }, + "node_modules/@types/jasmine": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-5.1.4.tgz", + "integrity": "sha512-px7OMFO/ncXxixDe1zR13V1iycqWae0MxTaw62RpFlksUi5QuNWgQJFkTQjIOvrmutJbI7Fp2Y2N1F6D2R4G6w==", + "dev": true + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -3319,6 +3984,22 @@ "url": "https://opencollective.com/postcss/" } }, + "node_modules/@vue/component-compiler-utils/node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "optional": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/@vue/component-compiler-utils/node_modules/yallist": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", @@ -4403,144 +5084,601 @@ "integrity": "sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==", "dev": true, "dependencies": { - "@webassemblyjs/wast-printer": "1.9.0" + "@webassemblyjs/wast-printer": "1.9.0" + } + }, + "node_modules/@webassemblyjs/helper-fsm": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz", + "integrity": "sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-module-context": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz", + "integrity": "sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.9.0" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz", + "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz", + "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz", + "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==", + "dev": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz", + "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==", + "dev": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz", + "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==", + "dev": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz", + "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/helper-wasm-section": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-opt": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "@webassemblyjs/wast-printer": "1.9.0" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz", + "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz", + "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz", + "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" + } + }, + "node_modules/@webassemblyjs/wast-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz", + "integrity": "sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/floating-point-hex-parser": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-code-frame": "1.9.0", + "@webassemblyjs/helper-fsm": "1.9.0", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz", + "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xhmikosr/archive-type": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@xhmikosr/archive-type/-/archive-type-7.0.0.tgz", + "integrity": "sha512-sIm84ZneCOJuiy3PpWR5bxkx3HaNt1pqaN+vncUBZIlPZCq8ASZH+hBVdu5H8znR7qYC6sKwx+ie2Q7qztJTxA==", + "dev": true, + "dependencies": { + "file-type": "^19.0.0" + }, + "engines": { + "node": "^14.14.0 || >=16.0.0" + } + }, + "node_modules/@xhmikosr/bin-check": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@xhmikosr/bin-check/-/bin-check-7.0.3.tgz", + "integrity": "sha512-4UnCLCs8DB+itHJVkqFp9Zjg+w/205/J2j2wNBsCEAm/BuBmtua2hhUOdAMQE47b1c7P9Xmddj0p+X1XVsfHsA==", + "dev": true, + "dependencies": { + "execa": "^5.1.1", + "isexe": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@xhmikosr/bin-check/node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "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" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/@xhmikosr/bin-check/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@xhmikosr/bin-check/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@xhmikosr/bin-check/node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@xhmikosr/bin-wrapper": { + "version": "13.0.5", + "resolved": "https://registry.npmjs.org/@xhmikosr/bin-wrapper/-/bin-wrapper-13.0.5.tgz", + "integrity": "sha512-DT2SAuHDeOw0G5bs7wZbQTbf4hd8pJ14tO0i4cWhRkIJfgRdKmMfkDilpaJ8uZyPA0NVRwasCNAmMJcWA67osw==", + "dev": true, + "dependencies": { + "@xhmikosr/bin-check": "^7.0.3", + "@xhmikosr/downloader": "^15.0.1", + "@xhmikosr/os-filter-obj": "^3.0.0", + "bin-version-check": "^5.1.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@xhmikosr/decompress": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@xhmikosr/decompress/-/decompress-10.0.1.tgz", + "integrity": "sha512-6uHnEEt5jv9ro0CDzqWlFgPycdE+H+kbJnwyxgZregIMLQ7unQSCNVsYG255FoqU8cP46DyggI7F7LohzEl8Ag==", + "dev": true, + "dependencies": { + "@xhmikosr/decompress-tar": "^8.0.1", + "@xhmikosr/decompress-tarbz2": "^8.0.1", + "@xhmikosr/decompress-targz": "^8.0.1", + "@xhmikosr/decompress-unzip": "^7.0.0", + "graceful-fs": "^4.2.11", + "make-dir": "^4.0.0", + "strip-dirs": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@xhmikosr/decompress-tar": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@xhmikosr/decompress-tar/-/decompress-tar-8.0.1.tgz", + "integrity": "sha512-dpEgs0cQKJ2xpIaGSO0hrzz3Kt8TQHYdizHsgDtLorWajuHJqxzot9Hbi0huRxJuAGG2qiHSQkwyvHHQtlE+fg==", + "dev": true, + "dependencies": { + "file-type": "^19.0.0", + "is-stream": "^2.0.1", + "tar-stream": "^3.1.7" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@xhmikosr/decompress-tar/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@xhmikosr/decompress-tarbz2": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@xhmikosr/decompress-tarbz2/-/decompress-tarbz2-8.0.1.tgz", + "integrity": "sha512-OF+6DysDZP5YTDO8uHuGG6fMGZjc+HszFPBkVltjoje2Cf60hjBg/YP5OQndW1hfwVWOdP7f3CnJiPZHJUTtEg==", + "dev": true, + "dependencies": { + "@xhmikosr/decompress-tar": "^8.0.1", + "file-type": "^19.0.0", + "is-stream": "^2.0.1", + "seek-bzip": "^2.0.0", + "unbzip2-stream": "^1.4.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@xhmikosr/decompress-tarbz2/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@xhmikosr/decompress-targz": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@xhmikosr/decompress-targz/-/decompress-targz-8.0.1.tgz", + "integrity": "sha512-mvy5AIDIZjQ2IagMI/wvauEiSNHhu/g65qpdM4EVoYHUJBAmkQWqcPJa8Xzi1aKVTmOA5xLJeDk7dqSjlHq8Mg==", + "dev": true, + "dependencies": { + "@xhmikosr/decompress-tar": "^8.0.1", + "file-type": "^19.0.0", + "is-stream": "^2.0.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@xhmikosr/decompress-targz/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@xhmikosr/decompress-unzip": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@xhmikosr/decompress-unzip/-/decompress-unzip-7.0.0.tgz", + "integrity": "sha512-GQMpzIpWTsNr6UZbISawsGI0hJ4KA/mz5nFq+cEoPs12UybAqZWKbyIaZZyLbJebKl5FkLpsGBkrplJdjvUoSQ==", + "dev": true, + "dependencies": { + "file-type": "^19.0.0", + "get-stream": "^6.0.1", + "yauzl": "^3.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@xhmikosr/decompress-unzip/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@xhmikosr/decompress/node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@xhmikosr/decompress/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@xhmikosr/downloader": { + "version": "15.0.1", + "resolved": "https://registry.npmjs.org/@xhmikosr/downloader/-/downloader-15.0.1.tgz", + "integrity": "sha512-fiuFHf3Dt6pkX8HQrVBsK0uXtkgkVlhrZEh8b7VgoDqFf+zrgFBPyrwCqE/3nDwn3hLeNz+BsrS7q3mu13Lp1g==", + "dev": true, + "dependencies": { + "@xhmikosr/archive-type": "^7.0.0", + "@xhmikosr/decompress": "^10.0.1", + "content-disposition": "^0.5.4", + "defaults": "^3.0.0", + "ext-name": "^5.0.0", + "file-type": "^19.0.0", + "filenamify": "^6.0.0", + "get-stream": "^6.0.1", + "got": "^13.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@xhmikosr/downloader/node_modules/@sindresorhus/is": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", + "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@xhmikosr/downloader/node_modules/@szmarczak/http-timer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", + "dev": true, + "dependencies": { + "defer-to-connect": "^2.0.1" + }, + "engines": { + "node": ">=14.16" } }, - "node_modules/@webassemblyjs/helper-fsm": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz", - "integrity": "sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-module-context": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz", - "integrity": "sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==", + "node_modules/@xhmikosr/downloader/node_modules/cacheable-request": { + "version": "10.2.14", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz", + "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.9.0" + "@types/http-cache-semantics": "^4.0.2", + "get-stream": "^6.0.1", + "http-cache-semantics": "^4.1.1", + "keyv": "^4.5.3", + "mimic-response": "^4.0.0", + "normalize-url": "^8.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=14.16" } }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz", - "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz", - "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==", + "node_modules/@xhmikosr/downloader/node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-buffer": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/wasm-gen": "1.9.0" + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz", - "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==", + "node_modules/@xhmikosr/downloader/node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", "dev": true, - "dependencies": { - "@xtuc/ieee754": "^1.2.0" + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz", - "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==", + "node_modules/@xhmikosr/downloader/node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", "dev": true, - "dependencies": { - "@xtuc/long": "4.2.2" + "engines": { + "node": ">=10" } }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz", - "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==", - "dev": true + "node_modules/@xhmikosr/downloader/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz", - "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==", + "node_modules/@xhmikosr/downloader/node_modules/got": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/got/-/got-13.0.0.tgz", + "integrity": "sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-buffer": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/helper-wasm-section": "1.9.0", - "@webassemblyjs/wasm-gen": "1.9.0", - "@webassemblyjs/wasm-opt": "1.9.0", - "@webassemblyjs/wasm-parser": "1.9.0", - "@webassemblyjs/wast-printer": "1.9.0" + "@sindresorhus/is": "^5.2.0", + "@szmarczak/http-timer": "^5.0.1", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^10.2.8", + "decompress-response": "^6.0.0", + "form-data-encoder": "^2.1.2", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" } }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz", - "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==", + "node_modules/@xhmikosr/downloader/node_modules/lowercase-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/ieee754": "1.9.0", - "@webassemblyjs/leb128": "1.9.0", - "@webassemblyjs/utf8": "1.9.0" + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz", - "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==", + "node_modules/@xhmikosr/downloader/node_modules/mimic-response": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", + "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-buffer": "1.9.0", - "@webassemblyjs/wasm-gen": "1.9.0", - "@webassemblyjs/wasm-parser": "1.9.0" + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz", - "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==", + "node_modules/@xhmikosr/downloader/node_modules/normalize-url": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.1.tgz", + "integrity": "sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==", "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-api-error": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/ieee754": "1.9.0", - "@webassemblyjs/leb128": "1.9.0", - "@webassemblyjs/utf8": "1.9.0" + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@webassemblyjs/wast-parser": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz", - "integrity": "sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==", + "node_modules/@xhmikosr/downloader/node_modules/p-cancelable": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", + "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", + "dev": true, + "engines": { + "node": ">=12.20" + } + }, + "node_modules/@xhmikosr/downloader/node_modules/responselike": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", + "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/floating-point-hex-parser": "1.9.0", - "@webassemblyjs/helper-api-error": "1.9.0", - "@webassemblyjs/helper-code-frame": "1.9.0", - "@webassemblyjs/helper-fsm": "1.9.0", - "@xtuc/long": "4.2.2" + "lowercase-keys": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz", - "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==", + "node_modules/@xhmikosr/os-filter-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@xhmikosr/os-filter-obj/-/os-filter-obj-3.0.0.tgz", + "integrity": "sha512-siPY6BD5dQ2SZPl3I0OZBHL27ZqZvLEosObsZRQ1NUB8qcxegwt0T9eKtV96JMFQpIz1elhkzqOg4c/Ri6Dp9A==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/wast-parser": "1.9.0", - "@xtuc/long": "4.2.2" + "arch": "^3.0.0" + }, + "engines": { + "node": "^14.14.0 || >=16.0.0" } }, "node_modules/@xtuc/ieee754": { @@ -4806,6 +5944,26 @@ "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true }, + "node_modules/arch": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/arch/-/arch-3.0.0.tgz", + "integrity": "sha512-AmIAC+Wtm2AU8lGfTtHsw0Y9Qtftx2YXEEtiBP10xFUtMOA+sHHx6OAddyL52mUKh1vsXQ6/w1mVDptZCyUt4Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -5132,6 +6290,12 @@ "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", "dev": true }, + "node_modules/b4a": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", + "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==", + "dev": true + }, "node_modules/babel-loader": { "version": "8.4.1", "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.4.1.tgz", @@ -5205,6 +6369,13 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/bare-events": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.0.tgz", + "integrity": "sha512-/E8dDe9dsbLyh2qrZ64PEPadOQ0F4gbl1sUJOrmph7xOiIxfY8vwab/4bFLh4Y88/Hk/ujKcrQKc+ps0mv873A==", + "dev": true, + "optional": true + }, "node_modules/base": { "version": "0.11.2", "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", @@ -5301,6 +6472,110 @@ "node": "*" } }, + "node_modules/bin-version": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bin-version/-/bin-version-6.0.0.tgz", + "integrity": "sha512-nk5wEsP4RiKjG+vF+uG8lFsEn4d7Y6FVDamzzftSunXOoOcOOkzcWdKVlGgFFwlUQCj63SgnUkLLGF8v7lufhw==", + "dev": true, + "dependencies": { + "execa": "^5.0.0", + "find-versions": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bin-version-check": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bin-version-check/-/bin-version-check-5.1.0.tgz", + "integrity": "sha512-bYsvMqJ8yNGILLz1KP9zKLzQ6YpljV3ln1gqhuLkUtyfGi3qXKGuK2p+U4NAvjVFzDFiBBtOpCOSFNuYYEGZ5g==", + "dev": true, + "dependencies": { + "bin-version": "^6.0.0", + "semver": "^7.5.3", + "semver-truncate": "^3.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bin-version-check/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/bin-version/node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "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" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/bin-version/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bin-version/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bin-version/node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -5593,6 +6868,15 @@ "isarray": "^1.0.0" } }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -5647,6 +6931,144 @@ "node": ">= 0.8" } }, + "node_modules/c8": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/c8/-/c8-10.1.2.tgz", + "integrity": "sha512-Qr6rj76eSshu5CgRYvktW0uM0CFY0yi4Fd5D0duDXO6sYinyopmftUiJVuzBQxQcwQLor7JWDVRP+dUfCmzgJw==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@istanbuljs/schema": "^0.1.3", + "find-up": "^5.0.0", + "foreground-child": "^3.1.1", + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.1.6", + "test-exclude": "^7.0.1", + "v8-to-istanbul": "^9.0.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1" + }, + "bin": { + "c8": "bin/c8.js" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "monocart-coverage-reports": "^2" + }, + "peerDependenciesMeta": { + "monocart-coverage-reports": { + "optional": true + } + } + }, + "node_modules/c8/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/c8/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/c8/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/c8/node_modules/test-exclude": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz", + "integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^10.4.1", + "minimatch": "^9.0.4" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/c8/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/c8/node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/c8/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "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" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/c8/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, "node_modules/cac": { "version": "6.7.14", "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", @@ -5911,6 +7333,15 @@ "semver": "bin/semver" } }, + "node_modules/cacheable-lookup": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", + "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", + "dev": true, + "engines": { + "node": ">=14.16" + } + }, "node_modules/cacheable-request": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", @@ -8150,6 +9581,18 @@ "node": ">=6" } }, + "node_modules/defaults": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-3.0.0.tgz", + "integrity": "sha512-RsqXDEAALjfRTro+IFNKpcPCt0/Cy2FqHSIlnomiJp9YGadpQnrtbRpSgN2+np21qHcIKiva4fiOQGjS9/qR/A==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/defer-to-connect": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", @@ -9320,6 +10763,36 @@ "eslint": ">=6.0.0" } }, + "node_modules/eslint-plugin-prettier": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz", + "integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.9.1" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, "node_modules/eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -9780,6 +11253,31 @@ "node": ">= 0.8" } }, + "node_modules/ext-list": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/ext-list/-/ext-list-2.2.2.tgz", + "integrity": "sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==", + "dev": true, + "dependencies": { + "mime-db": "^1.28.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ext-name": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ext-name/-/ext-name-5.0.0.tgz", + "integrity": "sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==", + "dev": true, + "dependencies": { + "ext-list": "^2.0.0", + "sort-keys-length": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -9857,6 +11355,18 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "dev": true + }, "node_modules/fast-glob": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", @@ -10031,6 +11541,52 @@ "node": ">= 4" } }, + "node_modules/file-type": { + "version": "19.6.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-19.6.0.tgz", + "integrity": "sha512-VZR5I7k5wkD0HgFnMsq5hOsSc710MJMu5Nc5QYsbe38NN5iPV/XTObYLc/cpttRTf6lX538+5uO1ZQRhYibiZQ==", + "dev": true, + "dependencies": { + "get-stream": "^9.0.1", + "strtok3": "^9.0.1", + "token-types": "^6.0.0", + "uint8array-extras": "^1.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, + "node_modules/file-type/node_modules/get-stream": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", + "dev": true, + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/file-type/node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", @@ -10038,6 +11594,33 @@ "dev": true, "optional": true }, + "node_modules/filename-reserved-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-3.0.0.tgz", + "integrity": "sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/filenamify": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-6.0.0.tgz", + "integrity": "sha512-vqIlNogKeyD3yzrm0yhRMQg8hOVwYcYRfjEoODd49iCprMn4HL85gK3HcykQE53EPIpX3HcAbGA5ELQv216dAQ==", + "dev": true, + "dependencies": { + "filename-reserved-regex": "^3.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -10128,6 +11711,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/find-versions": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-5.1.0.tgz", + "integrity": "sha512-+iwzCJ7C5v5KgcBuueqVoNiHVoQpwiUK5XFLjf0affFTep+Wcw93tPvmb8tqujDNmzhBDPddnWV/qgWSXgq+Hg==", + "dev": true, + "dependencies": { + "semver-regex": "^4.0.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/flat-cache": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", @@ -10259,6 +11857,15 @@ "node": ">= 0.12" } }, + "node_modules/form-data-encoder": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", + "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", + "dev": true, + "engines": { + "node": ">= 14.17" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -11236,12 +12843,34 @@ "npm": ">=1.3.7" } }, + "node_modules/http2-wrapper": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", + "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", + "dev": true, + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, "node_modules/https-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==", "dev": true }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -11527,6 +13156,15 @@ "integrity": "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==", "dev": true }, + "node_modules/inspect-with-kind": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/inspect-with-kind/-/inspect-with-kind-1.0.5.tgz", + "integrity": "sha512-MAQUJuIo7Xqk8EVNP+6d3CKq9c80hi4tjIbIAT6lmGW9W6WzlHiu9PS8uSuUYU+Do+j1baiFp3H25XEVxDIG2g==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.2" + } + }, "node_modules/internal-ip": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", @@ -13214,6 +14852,12 @@ "source-map": "^0.6.1" } }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -13415,7 +15059,16 @@ "mime-db": "1.52.0" }, "engines": { - "node": ">= 0.6" + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" } }, "node_modules/mimic-response": { @@ -14178,6 +15831,21 @@ "wrappy": "1" } }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/oniguruma-to-es": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-0.1.2.tgz", @@ -14562,6 +16230,25 @@ "node": ">=0.12" } }, + "node_modules/peek-readable": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.3.1.tgz", + "integrity": "sha512-GVlENSDW6KHaXcd9zkZltB7tCLosKB/4Hg0fqBJkAoBgYG2Tn1xtMgXtSUuMU9AK/gCm/tTdT8mgAeF4YNeeqw==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true + }, "node_modules/perf-regexes": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/perf-regexes/-/perf-regexes-1.0.1.tgz", @@ -14631,6 +16318,15 @@ "node": ">=0.10.0" } }, + "node_modules/piscina": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-4.7.0.tgz", + "integrity": "sha512-b8hvkpp9zS0zsfa939b/jXbe64Z2gZv0Ha7FYPNUiDIB1y2AtxcOZdfP8xN8HFjUaqQiT9gRlfjAsoL8vdJ1Iw==", + "dev": true, + "optionalDependencies": { + "@napi-rs/nice": "^1.0.1" + } + }, "node_modules/pixelmatch": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-6.0.0.tgz", @@ -16282,21 +17978,33 @@ } }, "node_modules/prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", "dev": true, - "optional": true, + "peer": true, "bin": { - "prettier": "bin-prettier.js" + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">=10.13.0" + "node": ">=14" }, "funding": { "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/pretty-error": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.2.tgz", @@ -16561,6 +18269,24 @@ } ] }, + "node_modules/queue-tick": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", + "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", + "dev": true + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -17083,6 +18809,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true + }, "node_modules/resolve-cwd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", @@ -17223,9 +18955,9 @@ } }, "node_modules/rollup": { - "version": "4.27.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.27.2.tgz", - "integrity": "sha512-KreA+PzWmk2yaFmZVwe6GB2uBD86nXl86OsDkt1bJS9p3vqWuEQ6HnJJ+j/mZi/q0920P99/MVRlB4L3crpF5w==", + "version": "4.27.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.27.3.tgz", + "integrity": "sha512-SLsCOnlmGt9VoZ9Ek8yBK8tAdmPHeppkw+Xa7yDlCEhDTvwYei03JlWo1fdc7YTfLZ4tD8riJCUyAgTbszk1fQ==", "dev": true, "dependencies": { "@types/estree": "1.0.6" @@ -17238,24 +18970,24 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.27.2", - "@rollup/rollup-android-arm64": "4.27.2", - "@rollup/rollup-darwin-arm64": "4.27.2", - "@rollup/rollup-darwin-x64": "4.27.2", - "@rollup/rollup-freebsd-arm64": "4.27.2", - "@rollup/rollup-freebsd-x64": "4.27.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.27.2", - "@rollup/rollup-linux-arm-musleabihf": "4.27.2", - "@rollup/rollup-linux-arm64-gnu": "4.27.2", - "@rollup/rollup-linux-arm64-musl": "4.27.2", - "@rollup/rollup-linux-powerpc64le-gnu": "4.27.2", - "@rollup/rollup-linux-riscv64-gnu": "4.27.2", - "@rollup/rollup-linux-s390x-gnu": "4.27.2", - "@rollup/rollup-linux-x64-gnu": "4.27.2", - "@rollup/rollup-linux-x64-musl": "4.27.2", - "@rollup/rollup-win32-arm64-msvc": "4.27.2", - "@rollup/rollup-win32-ia32-msvc": "4.27.2", - "@rollup/rollup-win32-x64-msvc": "4.27.2", + "@rollup/rollup-android-arm-eabi": "4.27.3", + "@rollup/rollup-android-arm64": "4.27.3", + "@rollup/rollup-darwin-arm64": "4.27.3", + "@rollup/rollup-darwin-x64": "4.27.3", + "@rollup/rollup-freebsd-arm64": "4.27.3", + "@rollup/rollup-freebsd-x64": "4.27.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.27.3", + "@rollup/rollup-linux-arm-musleabihf": "4.27.3", + "@rollup/rollup-linux-arm64-gnu": "4.27.3", + "@rollup/rollup-linux-arm64-musl": "4.27.3", + "@rollup/rollup-linux-powerpc64le-gnu": "4.27.3", + "@rollup/rollup-linux-riscv64-gnu": "4.27.3", + "@rollup/rollup-linux-s390x-gnu": "4.27.3", + "@rollup/rollup-linux-x64-gnu": "4.27.3", + "@rollup/rollup-linux-x64-musl": "4.27.3", + "@rollup/rollup-win32-arm64-msvc": "4.27.3", + "@rollup/rollup-win32-ia32-msvc": "4.27.3", + "@rollup/rollup-win32-x64-msvc": "4.27.3", "fsevents": "~2.3.2" } }, @@ -17490,6 +19222,28 @@ "node": ">=4" } }, + "node_modules/seek-bzip": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-2.0.0.tgz", + "integrity": "sha512-SMguiTnYrhpLdk3PwfzHeotrcwi8bNV4iemL9tx9poR/yeaMYwB9VzR1w7b57DuWpuqR8n6oZboi0hj3AxZxQg==", + "dev": true, + "dependencies": { + "commander": "^6.0.0" + }, + "bin": { + "seek-bunzip": "bin/seek-bunzip", + "seek-table": "bin/seek-bzip-table" + } + }, + "node_modules/seek-bzip/node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", @@ -17535,6 +19289,45 @@ "node": ">=8" } }, + "node_modules/semver-regex": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-4.0.5.tgz", + "integrity": "sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semver-truncate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/semver-truncate/-/semver-truncate-3.0.0.tgz", + "integrity": "sha512-LJWA9kSvMolR51oDE6PN3kALBNaUdkxzAGcexw8gjMA8xr5zUqK0JiR3CgARSqanYF3Z1YHvsErb1KDgh+v7Rg==", + "dev": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semver-truncate/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/send": { "version": "0.19.0", "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", @@ -18095,6 +19888,39 @@ "node": ">=4" } }, + "node_modules/sort-keys-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sort-keys-length/-/sort-keys-length-1.0.1.tgz", + "integrity": "sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw==", + "dev": true, + "dependencies": { + "sort-keys": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sort-keys-length/node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sort-keys-length/node_modules/sort-keys": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", + "integrity": "sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==", + "dev": true, + "dependencies": { + "is-plain-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/sort-keys/node_modules/is-plain-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", @@ -18424,6 +20250,20 @@ "node": ">=8.0" } }, + "node_modules/streamx": { + "version": "2.20.2", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.20.2.tgz", + "integrity": "sha512-aDGDLU+j9tJcUdPGOaHmVF1u/hhI+CsGkT02V3OKlHDV7IukOI+nTWAGkiZEKCO35rWN1wIr4tS7YFr1f4qSvA==", + "dev": true, + "dependencies": { + "fast-fifo": "^1.3.2", + "queue-tick": "^1.0.1", + "text-decoder": "^1.1.0" + }, + "optionalDependencies": { + "bare-events": "^2.2.0" + } + }, "node_modules/strict-uri-encode": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", @@ -18598,6 +20438,25 @@ "node": ">=0.10.0" } }, + "node_modules/strip-dirs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-3.0.0.tgz", + "integrity": "sha512-I0sdgcFTfKQlUPZyAqPJmSG3HLO9rWDFnxonnIbskYNM3DwFOeTNB5KzVq3dA1GdRAc/25b5Y7UO2TQfKWw4aQ==", + "dev": true, + "dependencies": { + "inspect-with-kind": "^1.0.5", + "is-plain-obj": "^1.1.0" + } + }, + "node_modules/strip-dirs/node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/strip-eof": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", @@ -18607,6 +20466,15 @@ "node": ">=0.10.0" } }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -18619,6 +20487,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strtok3": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-9.1.1.tgz", + "integrity": "sha512-FhwotcEqjr241ZbjFzjlIYg6c5/L/s4yBGWSMvJ9UoExiSqL+FnFA/CaeZx17WGaZMS/4SOZp8wH18jSS4R4lw==", + "dev": true, + "dependencies": { + "@tokenizer/token": "^0.3.0", + "peek-readable": "^5.3.1" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/stylehacks": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz", @@ -18921,6 +20806,28 @@ "node": ">=4" } }, + "node_modules/synckit": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz", + "integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==", + "dev": true, + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/synckit/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + }, "node_modules/tapable": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", @@ -18930,6 +20837,17 @@ "node": ">=6" } }, + "node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "dev": true, + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, "node_modules/term-size": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", @@ -19150,6 +21068,12 @@ "node": ">=6.0.0" } }, + "node_modules/text-decoder": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.1.tgz", + "integrity": "sha512-x9v3H/lTKIJKQQe7RPQkLfKAnc9lUTkWDypIQgTzPJAq+5/GCDHonmshfvlsNSj58yyshbIJJDLmU15qNERrXQ==", + "dev": true + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -19337,6 +21261,23 @@ "node": ">=0.6" } }, + "node_modules/token-types": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.0.0.tgz", + "integrity": "sha512-lbDrTLVsHhOMljPscd0yitpozq7Ga2M5Cvez5AjGg8GASBjtt6iERCAJ93yommPmz62fb45oFIXHEZ3u9bfJEA==", + "dev": true, + "dependencies": { + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/toml": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz", @@ -19688,6 +21629,18 @@ "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", "dev": true }, + "node_modules/uint8array-extras": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.4.0.tgz", + "integrity": "sha512-ZPtzy0hu4cZjv3z5NW9gfKnNLjoz4y6uv4HlelAjDK7sY/xOkKZv9xK/WQpcsBB3jEybChz9DPC2U/+cusjJVQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -19703,6 +21656,40 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/unbzip2-stream": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", + "dev": true, + "dependencies": { + "buffer": "^5.2.1", + "through": "^2.3.8" + } + }, + "node_modules/unbzip2-stream/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/undici-types": { "version": "6.19.8", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", @@ -20261,6 +22248,20 @@ "uuid": "bin/uuid" } }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -20548,6 +22549,22 @@ "prettier": "^1.18.2 || ^2.0.0" } }, + "node_modules/vue/node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "optional": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/vue2-perfect-scrollbar": { "version": "1.5.56", "resolved": "https://registry.npmjs.org/vue2-perfect-scrollbar/-/vue2-perfect-scrollbar-1.5.56.tgz", @@ -23019,6 +25036,19 @@ "node": ">=10" } }, + "node_modules/yauzl": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.2.0.tgz", + "integrity": "sha512-Ow9nuGZE+qp1u4JIPvg+uCiUr7xGQWdff7JQSk5VGYTAZMDe2q8lxJ10ygv10qmSj031Ty/6FNJpLO4o1Sgc+w==", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3", + "pend": "~1.2.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index df25256d3..06d019b91 100644 --- a/package.json +++ b/package.json @@ -4,34 +4,43 @@ "description": "Plugin that enables zoom and pan functionality in Chart.js charts.", "version": "2.2.0", "license": "MIT", + "type": "module", "jsdelivr": "dist/chartjs-plugin-zoom.min.js", "unpkg": "dist/chartjs-plugin-zoom.min.js", "main": "dist/chartjs-plugin-zoom.js", "module": "dist/chartjs-plugin-zoom.esm.js", - "types": "types/index.d.ts", + "types": "dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/chartjs-plugin-zoom-esm.js", + "require": "./dist/chartjs-plugin-zoom.js" + } + }, "repository": { "type": "git", "url": "https://github.com/chartjs/chartjs-plugin-zoom.git" }, "scripts": { "autobuild": "rollup -c -w", - "build": "rollup -c", - "dev": "karma start --auto-watch --no-single-run --browsers chrome", - "dev:ff": "karma start --auto-watch --no-single-run --browsers firefox", + "emitDeclarations": "tsc --emitDeclarationOnly", + "build": "rollup -c && npm run emitDeclarations", + "dev": "karma start ./karma.conf.cjs --auto-watch --no-single-run --browsers chrome", + "dev:ff": "karma start ./karma.conf.cjs --auto-watch --no-single-run --browsers firefox", "docs": "npm run build && vuepress build docs --no-cache", "docs:dev": "concurrently \"npm run autobuild\" \"vuepress dev docs --no-cache\"", - "lint-js": "eslint \"samples/**/*.html\" \"test/**/*.js\" \"src/**/*.js\"", - "lint-md": "eslint \"**/*.md\"", - "lint-tsc": "tsc", - "lint-types": "eslint \"types/**/*.ts\" && tsc -p types/tests/", - "lint": "concurrently \"npm:lint-*\"", - "test": "cross-env NODE_ENV=test concurrently \"npm:test-*\"", - "test-karma": "karma start --auto-watch --single-run --coverage", - "test-types": "tsc -p types/tests/" + "lint": "eslint \"src/**/*.ts\" \"test/**/*.js\" \"**/*.md\" \"samples/**/*.html\"", + "typecheck": "tsc -p tsconfig.json --noEmit", + "test": "npm run test-types && npm run test-unit && npm run test-karma", + "pretest-unit": "swc --config-file .swcrc-spec src -d build", + "test-unit": "JASMINE_CONFIG_PATH=jasmine.json c8 --all --src=src --reporter=text --reporter=lcov -o=coverage/unit jasmine", + "test-karma": "cross-env NODE_ENV=test karma start ./karma.conf.cjs --auto-watch --single-run --coverage", + "test-types": "tsc -p test/types/ --noEmit" }, "files": [ "dist/*.js", - "types/*.d.ts" + "dist/*.d.ts", + "dist/*.map" ], "devDependencies": { "@babel/core": "^7.20.2", @@ -39,12 +48,17 @@ "@rollup/plugin-commonjs": "^28.0.1", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", + "@rollup/plugin-swc": "^0.4.0", "@rollup/plugin-terser": "^0.4.4", "@simonbrunel/vuepress-plugin-versions": "^0.2.0", + "@swc/cli": "^0.5.1", + "@swc/core": "^1.9.3", + "@types/jasmine": "^5.1.4", "@types/linkify-it": "^3.0.5", "@typescript-eslint/eslint-plugin": "^5.4.0", - "@typescript-eslint/parser": "^5.4.0", + "@typescript-eslint/parser": "^5.62.0", "babel-loader": "^8.3.0", + "c8": "^10.1.2", "chart.js": "^4.3.2", "chartjs-adapter-date-fns": "^3.0.0", "chartjs-test-utils": "^0.5.0", @@ -56,6 +70,7 @@ "eslint-config-chartjs": "^0.3.0", "eslint-plugin-html": "^8.1.2", "eslint-plugin-markdown": "^2.0.1", + "eslint-plugin-prettier": "^5.2.1", "hammer-simulator": "^0.0.1", "jasmine": "^5.4.0", "karma": "^6.4.4", @@ -68,12 +83,12 @@ "karma-spec-reporter": "0.0.36", "ng-hammerjs": "^2.0.8", "pixelmatch": "^6.0.0", - "rollup": "^4.12.1", + "rollup": "^4.27.3", "rollup-plugin-cleanup": "^3.2.1", "rollup-plugin-istanbul": "^5.0.0", "typedoc": "^0.26.11", "typedoc-plugin-markdown": "^4.2.10", - "typescript": "^5.6.3", + "typescript": "5.6", "vuepress": "^1.8.2", "vuepress-plugin-flexsearch": "^0.3.0", "vuepress-plugin-redirect": "^1.2.5", diff --git a/rollup.config.js b/rollup.config.js index d930f1144..89f70c322 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,10 +1,12 @@ -const commonjs = require('@rollup/plugin-commonjs'); -const cleanup = require('rollup-plugin-cleanup'); -const json = require('@rollup/plugin-json'); -const resolve = require('@rollup/plugin-node-resolve'); -const terser = require('@rollup/plugin-terser'); +import commonjs from '@rollup/plugin-commonjs'; +import cleanup from 'rollup-plugin-cleanup'; +import json from '@rollup/plugin-json'; +import resolve from '@rollup/plugin-node-resolve'; +import swc from '@rollup/plugin-swc'; +import terser from '@rollup/plugin-terser'; +import {readFileSync} from 'fs'; -const pkg = require('./package.json'); +const pkg = JSON.parse(readFileSync('./package.json')); const dependencies = Object.keys(pkg.dependencies); const peerDependencies = Object.keys(pkg.peerDependencies); const allDependencies = dependencies.concat(peerDependencies); @@ -24,60 +26,67 @@ const globals = { }; allDependencies.push('chart.js/helpers'); -module.exports = [ +const plugins = (minify) => [ + commonjs({ + include: 'node_modules/**', + }), + json(), + resolve({extensions: ['.ts']}), + swc({ + jsc: { + parser: { + syntax: 'typescript', + tsx: false, + }, + target: 'es2022' + }, + module: { + type: 'es6', + }, + sourceMaps: true + }), + minify ? terser({output: {comments: 'some'}}) : cleanup({comments: ['some']}), +]; + +export default [ { - input: 'src/index.js', + input: 'src/index.umd.ts', output: { name, file: `dist/${pkg.name}.js`, banner, format: 'umd', indent: false, - globals + globals, + sourcemap: true, }, - plugins: [ - commonjs({ - include: 'node_modules/**', - }), - json(), - resolve(), - cleanup({comments: ['some']}), - ], + plugins: plugins(false), external: allDependencies }, { - input: 'src/index.js', + input: 'src/index.umd.ts', output: { name, file: `dist/${pkg.name}.min.js`, banner, format: 'umd', indent: false, - globals + globals, + sourcemap: true, }, - plugins: [ - commonjs({ - include: 'node_modules/**', - }), - json(), - resolve(), - terser({output: {comments: 'some'}}) - ], + plugins: plugins(true), external: allDependencies }, { - input: 'src/index.esm.js', - plugins: [ - json(), - resolve(), - cleanup({comments: ['some']}), - ], + input: 'src/index.ts', + plugins: plugins(false), output: { name, file: `dist/${pkg.name}.esm.js`, banner, format: 'esm', - indent: false + indent: false, + sourcemap: true, }, external: allDependencies }, diff --git a/samples/.eslintrc.yml b/samples/.eslintrc.yml index b06319766..61e7abab9 100644 --- a/samples/.eslintrc.yml +++ b/samples/.eslintrc.yml @@ -7,6 +7,5 @@ globals: randomScalingFactor: true rules: - indent: ["error", "tab", {flatTernaryExpressions: true}] no-new: "off" no-var: "off" diff --git a/samples/zoom-separately.html b/samples/zoom-separately.html index 6405e86b5..d151f7830 100644 --- a/samples/zoom-separately.html +++ b/samples/zoom-separately.html @@ -20,110 +20,113 @@ diff --git a/src/core.js b/src/core.js deleted file mode 100644 index b30977847..000000000 --- a/src/core.js +++ /dev/null @@ -1,294 +0,0 @@ -import {each, callback as call, sign, valueOrDefault} from 'chart.js/helpers'; -import {panFunctions, updateRange, zoomFunctions, zoomRectFunctions} from './scale.types'; -import {getState} from './state'; -import {directionEnabled, getEnabledScalesByPoint} from './utils'; - -function shouldUpdateScaleLimits(scale, originalScaleLimits, updatedScaleLimits) { - const {id, options: {min, max}} = scale; - if (!originalScaleLimits[id] || !updatedScaleLimits[id]) { - return true; - } - const previous = updatedScaleLimits[id]; - return previous.min !== min || previous.max !== max; -} - -function removeMissingScales(limits, scales) { - each(limits, (opt, key) => { - if (!scales[key]) { - delete limits[key]; - } - }); -} - -function storeOriginalScaleLimits(chart, state) { - const {scales} = chart; - const {originalScaleLimits, updatedScaleLimits} = state; - - each(scales, function(scale) { - if (shouldUpdateScaleLimits(scale, originalScaleLimits, updatedScaleLimits)) { - originalScaleLimits[scale.id] = { - min: {scale: scale.min, options: scale.options.min}, - max: {scale: scale.max, options: scale.options.max}, - }; - } - }); - - removeMissingScales(originalScaleLimits, scales); - removeMissingScales(updatedScaleLimits, scales); - return originalScaleLimits; -} - -function doZoom(scale, amount, center, limits) { - const fn = zoomFunctions[scale.type] || zoomFunctions.default; - call(fn, [scale, amount, center, limits]); -} - -function doZoomRect(scale, from, to, limits) { - const fn = zoomRectFunctions[scale.type] || zoomRectFunctions.default; - call(fn, [scale, from, to, limits]); -} - -function getCenter(chart) { - const ca = chart.chartArea; - return { - x: (ca.left + ca.right) / 2, - y: (ca.top + ca.bottom) / 2, - }; -} - -/** - * @param {import('chart.js').Chart} chart The Chart instance - * @param {import('../types').ZoomAmount} amount The zoom percentage or percentages and focal point - * @param {import('chart.js').UpdateMode} [transition] Which transition mode to use. Defaults to 'none' - * @param {import('../types/options').ZoomTrigger} [trigger] What triggered the zoom. Defaults to 'api' - */ -export function zoom(chart, amount, transition = 'none', trigger = 'api') { - const {x = 1, y = 1, focalPoint = getCenter(chart)} = typeof amount === 'number' ? {x: amount, y: amount} : amount; - const state = getState(chart); - const {options: {limits, zoom: zoomOptions}} = state; - - storeOriginalScaleLimits(chart, state); - - const xEnabled = x !== 1; - const yEnabled = y !== 1; - const enabledScales = getEnabledScalesByPoint(zoomOptions, focalPoint, chart); - - // @ts-expect-error No overload matches this call - each(enabledScales || chart.scales, function(scale) { - if (scale.isHorizontal() && xEnabled) { - doZoom(scale, x, focalPoint, limits); - } else if (!scale.isHorizontal() && yEnabled) { - doZoom(scale, y, focalPoint, limits); - } - }); - - chart.update(transition); - - // @ts-expect-error args not assignable to unknown[] - call(zoomOptions.onZoom, [{chart, trigger}]); -} - -/** - * @param {import('chart.js').Chart} chart The Chart instance - * @param {import('chart.js').Point} p0 First corner of the rect - * @param {import('chart.js').Point} p1 Opposite corner of the rect - * @param {import('chart.js').UpdateMode} [transition] - * @param {import('../types/options').ZoomTrigger} [trigger] What triggered the zoom. Defaults to 'api' - */ -export function zoomRect(chart, p0, p1, transition = 'none', trigger = 'api') { - const state = getState(chart); - const {options: {limits, zoom: zoomOptions}} = state; - const {mode = 'xy'} = zoomOptions; - - storeOriginalScaleLimits(chart, state); - const xEnabled = directionEnabled(mode, 'x', chart); - const yEnabled = directionEnabled(mode, 'y', chart); - - each(chart.scales, function(scale) { - if (scale.isHorizontal() && xEnabled) { - doZoomRect(scale, p0.x, p1.x, limits); - } else if (!scale.isHorizontal() && yEnabled) { - doZoomRect(scale, p0.y, p1.y, limits); - } - }); - - chart.update(transition); - - // @ts-expect-error args not assignable to unknown[] - call(zoomOptions.onZoom, [{chart, trigger}]); -} - -/** - * @param {import('chart.js').Chart} chart The Chart instance - * @param {string} scaleId - * @param {import('../types').ScaleRange} range - * @param {import('chart.js').UpdateMode} [transition] - * @param {import('../types/options').ZoomTrigger} [trigger] What triggered the zoom. Defaults to 'api' - */ -export function zoomScale(chart, scaleId, range, transition = 'none', trigger = 'api') { - const state = getState(chart); - storeOriginalScaleLimits(chart, state); - const scale = chart.scales[scaleId]; - updateRange(scale, range, undefined, true); - chart.update(transition); - - // @ts-expect-error args not assignable to unknown[] - call(state.options.zoom?.onZoom, [{chart, trigger}]); -} - -/** - * @param {import('chart.js').Chart} chart The Chart instance - * @param {import('chart.js').UpdateMode} transition - */ -export function resetZoom(chart, transition = 'default') { - const state = getState(chart); - const originalScaleLimits = storeOriginalScaleLimits(chart, state); - - each(chart.scales, function(scale) { - const scaleOptions = scale.options; - if (originalScaleLimits[scale.id]) { - scaleOptions.min = originalScaleLimits[scale.id].min.options; - scaleOptions.max = originalScaleLimits[scale.id].max.options; - } else { - delete scaleOptions.min; - delete scaleOptions.max; - } - delete state.updatedScaleLimits[scale.id]; - }); - chart.update(transition); - - // @ts-expect-error args not assignable to unknown[] - call(state.options.zoom.onZoomComplete, [{chart}]); -} - -function getOriginalRange(state, scaleId) { - const original = state.originalScaleLimits[scaleId]; - if (!original) { - return; - } - const {min, max} = original; - return valueOrDefault(max.options, max.scale) - valueOrDefault(min.options, min.scale); -} - -/** - * @param {import('chart.js').Chart} chart The Chart instance - */ -export function getZoomLevel(chart) { - const state = getState(chart); - let min = 1; - let max = 1; - each(chart.scales, function(scale) { - const origRange = getOriginalRange(state, scale.id); - if (origRange) { - const level = Math.round(origRange / (scale.max - scale.min) * 100) / 100; - min = Math.min(min, level); - max = Math.max(max, level); - } - }); - return min < 1 ? min : max; -} - -function panScale(scale, delta, limits, state) { - const {panDelta} = state; - // Add possible cumulative delta from previous pan attempts where scale did not change - const storedDelta = panDelta[scale.id] || 0; - if (sign(storedDelta) === sign(delta)) { - delta += storedDelta; - } - const fn = panFunctions[scale.type] || panFunctions.default; - if (call(fn, [scale, delta, limits])) { - // The scale changed, reset cumulative delta - panDelta[scale.id] = 0; - } else { - // The scale did not change, store cumulative delta - panDelta[scale.id] = delta; - } -} - -/** - * @param {import('chart.js').Chart} chart The Chart instance - * @param {import('../types').PanAmount} delta - * @param {import('chart.js').Scale[]} [enabledScales] - * @param {import('chart.js').UpdateMode} [transition] - */ -export function pan(chart, delta, enabledScales, transition = 'none') { - const {x = 0, y = 0} = typeof delta === 'number' ? {x: delta, y: delta} : delta; - const state = getState(chart); - const {options: {pan: panOptions, limits}} = state; - const {onPan} = panOptions || {}; - - storeOriginalScaleLimits(chart, state); - - const xEnabled = x !== 0; - const yEnabled = y !== 0; - - // @ts-expect-error No overload matches this call - each(enabledScales || chart.scales, function(scale) { - if (scale.isHorizontal() && xEnabled) { - panScale(scale, x, limits, state); - } else if (!scale.isHorizontal() && yEnabled) { - panScale(scale, y, limits, state); - } - }); - - chart.update(transition); - - // @ts-expect-error args not assignable to unknown[] - call(onPan, [{chart}]); -} - -/** - * @param {import('chart.js').Chart} chart The Chart instance - */ -export function getInitialScaleBounds(chart) { - const state = getState(chart); - storeOriginalScaleLimits(chart, state); - const scaleBounds = {}; - for (const scaleId of Object.keys(chart.scales)) { - const {min, max} = state.originalScaleLimits[scaleId] || {min: {}, max: {}}; - scaleBounds[scaleId] = {min: min.scale, max: max.scale}; - } - - return scaleBounds; -} - -/** - * @param {import('chart.js').Chart} chart The Chart instance - */ -export function getZoomedScaleBounds(chart) { - const state = getState(chart); - const scaleBounds = {}; - for (const scaleId of Object.keys(chart.scales)) { - scaleBounds[scaleId] = state.updatedScaleLimits[scaleId]; - } - - return scaleBounds; -} - -/** - * @param {import('chart.js').Chart} chart The Chart instance - */ -export function isZoomedOrPanned(chart) { - const scaleBounds = getInitialScaleBounds(chart); - for (const scaleId of Object.keys(chart.scales)) { - const {min: originalMin, max: originalMax} = scaleBounds[scaleId]; - - if (originalMin !== undefined && chart.scales[scaleId].min !== originalMin) { - return true; - } - - if (originalMax !== undefined && chart.scales[scaleId].max !== originalMax) { - return true; - } - } - - return false; -} - -/** - * @param {import('chart.js').Chart} chart The Chart instance - */ -export function isZoomingOrPanning(chart) { - const state = getState(chart); - return state.panning || state.dragging; -} diff --git a/src/core.ts b/src/core.ts new file mode 100644 index 000000000..b83d8510e --- /dev/null +++ b/src/core.ts @@ -0,0 +1,280 @@ +import { isNumber, sign } from 'chart.js/helpers' +import { panFunctions, updateRange, zoomFunctions, zoomRectFunctions } from './scale.types.js' +import { getState, type OriginalScaleLimits, type ScaleRange, type State, type UpdatedScaleLimits } from './state.js' +import { directionEnabled, getEnabledScalesByPoint } from './utils.js' +import type { Chart, Point, Scale, UpdateMode } from 'chart.js' +import type { LimitOptions, ZoomTrigger } from './options.js' +import type { ZoomAmount } from './types.js' + +function shouldUpdateScaleLimits( + scale: Scale, + originalScaleLimits: OriginalScaleLimits, + updatedScaleLimits: UpdatedScaleLimits +) { + const { + id, + options: { min, max }, + } = scale + if (!originalScaleLimits[id] || !updatedScaleLimits[id]) { + return true + } + const previous = updatedScaleLimits[id] + return previous.min !== min || previous.max !== max +} + +function removeMissingScales(limits: OriginalScaleLimits | UpdatedScaleLimits, scales: Record) { + for (const key of Object.keys(limits)) { + if (!scales[key]) { + delete limits[key] + } + } +} + +function storeOriginalScaleLimits(chart: Chart, state: State) { + const { scales } = chart + const { originalScaleLimits, updatedScaleLimits } = state + + for (const scale of Object.values(scales)) { + if (shouldUpdateScaleLimits(scale, originalScaleLimits, updatedScaleLimits)) { + originalScaleLimits[scale.id] = { + min: { scale: scale.min, options: scale.options.min }, + max: { scale: scale.max, options: scale.options.max }, + } + } + } + + removeMissingScales(originalScaleLimits, scales) + removeMissingScales(updatedScaleLimits, scales) + return originalScaleLimits +} + +function doZoom(scale: Scale, amount: number, center: Point, limits: LimitOptions) { + const fn = zoomFunctions[scale.type] || zoomFunctions.default + fn?.(scale, amount, center, limits) +} + +function doZoomRect(scale: Scale, from: number, to: number, limits: LimitOptions) { + const fn = zoomRectFunctions[scale.type] || zoomRectFunctions.default + fn?.(scale, from, to, limits) +} + +function getCenter(chart: Chart) { + const ca = chart.chartArea + return { + x: (ca.left + ca.right) / 2, + y: (ca.top + ca.bottom) / 2, + } +} + +export function zoom(chart: Chart, amount: ZoomAmount, transition: UpdateMode = 'none', trigger: ZoomTrigger = 'api') { + const { x = 1, y = 1, focalPoint = getCenter(chart) } = typeof amount === 'number' ? { x: amount, y: amount } : amount + const state = getState(chart) + const { + options: { limits = {}, zoom: zoomOptions }, + } = state + + storeOriginalScaleLimits(chart, state) + + const xEnabled = x !== 1 + const yEnabled = y !== 1 + const enabledScales = getEnabledScalesByPoint(zoomOptions, focalPoint, chart) + + for (const scale of enabledScales) { + if (scale.isHorizontal() && xEnabled) { + doZoom(scale, x, focalPoint, limits) + } else if (!scale.isHorizontal() && yEnabled) { + doZoom(scale, y, focalPoint, limits) + } + } + + chart.update(transition) + + zoomOptions?.onZoom?.({ chart, trigger }) +} + +export function zoomRect( + chart: Chart, + p0: Point, + p1: Point, + transition: UpdateMode = 'none', + trigger: ZoomTrigger = 'api' +) { + const state = getState(chart) + const { + options: { limits = {}, zoom: zoomOptions = {} }, + } = state + const { mode = 'xy' } = zoomOptions + + storeOriginalScaleLimits(chart, state) + const xEnabled = directionEnabled(mode, 'x', chart) + const yEnabled = directionEnabled(mode, 'y', chart) + + for (const scale of Object.values(chart.scales)) { + if (scale.isHorizontal() && xEnabled) { + doZoomRect(scale, p0.x, p1.x, limits) + } else if (!scale.isHorizontal() && yEnabled) { + doZoomRect(scale, p0.y, p1.y, limits) + } + } + + chart.update(transition) + + zoomOptions.onZoom?.({ chart, trigger }) +} + +export function zoomScale( + chart: Chart, + scaleId: string, + range: ScaleRange, + transition: UpdateMode = 'none', + trigger: ZoomTrigger = 'api' +) { + const state = getState(chart) + storeOriginalScaleLimits(chart, state) + const scale = chart.scales[scaleId] + updateRange(scale, range, undefined, true) + chart.update(transition) + + state.options.zoom?.onZoom?.({ chart, trigger }) +} + +export function resetZoom(chart: Chart, transition: UpdateMode = 'default') { + const state = getState(chart) + const originalScaleLimits = storeOriginalScaleLimits(chart, state) + + for (const scale of Object.values(chart.scales)) { + const scaleOptions = scale.options + if (originalScaleLimits[scale.id]) { + scaleOptions.min = originalScaleLimits[scale.id].min.options + scaleOptions.max = originalScaleLimits[scale.id].max.options + } else { + delete scaleOptions.min + delete scaleOptions.max + } + delete state.updatedScaleLimits[scale.id] + } + chart.update(transition) + + state.options.zoom?.onZoomComplete?.({ chart }) +} + +function getOriginalRange(state: State, scaleId: string): number | undefined { + const original = state.originalScaleLimits[scaleId] + if (!original) { + return undefined + } + const { min, max } = original + if (isNumber(max.options) && isNumber(min.options)) { + return max.options - min.options + } + if (isNumber(max.scale) && isNumber(min.scale)) { + return max.scale - min.scale + } + return undefined +} + +export function getZoomLevel(chart: Chart) { + const state = getState(chart) + let min = 1 + let max = 1 + for (const scale of Object.values(chart.scales)) { + const origRange = getOriginalRange(state, scale.id) + if (origRange) { + const level = Math.round((origRange / (scale.max - scale.min)) * 100) / 100 + min = Math.min(min, level) + max = Math.max(max, level) + } + } + return min < 1 ? min : max +} + +function panScale(scale: Scale, delta: number, limits: LimitOptions, state: State) { + const { panDelta } = state + // Add possible cumulative delta from previous pan attempts where scale did not change + const storedDelta = panDelta[scale.id] || 0 + if (sign(storedDelta) === sign(delta)) { + delta += storedDelta + } + const fn = panFunctions[scale.type] || panFunctions.default + if (fn?.(scale, delta, limits)) { + // The scale changed, reset cumulative delta + panDelta[scale.id] = 0 + } else { + // The scale did not change, store cumulative delta + panDelta[scale.id] = delta + } +} + +type PanAmount = number | Partial + +export function pan(chart: Chart, delta: PanAmount, enabledScales?: Scale[], transition: UpdateMode = 'none') { + const { x = 0, y = 0 } = typeof delta === 'number' ? { x: delta, y: delta } : delta + const state = getState(chart) + const { + options: { pan: panOptions, limits = {} }, + } = state + const { onPan } = panOptions || {} + + storeOriginalScaleLimits(chart, state) + + const xEnabled = x !== 0 + const yEnabled = y !== 0 + + const scales = enabledScales || Object.values(chart.scales) + + for (const scale of scales) { + if (scale.isHorizontal() && xEnabled) { + panScale(scale, x, limits, state) + } else if (!scale.isHorizontal() && yEnabled) { + panScale(scale, y, limits, state) + } + } + + chart.update(transition) + + onPan?.({ chart }) +} + +export function getInitialScaleBounds(chart: Chart) { + const state = getState(chart) + storeOriginalScaleLimits(chart, state) + const scaleBounds: Record = {} + for (const scaleId of Object.keys(chart.scales)) { + const { min, max } = state.originalScaleLimits[scaleId] || { min: {}, max: {} } + scaleBounds[scaleId] = { min: min.scale, max: max.scale } + } + + return scaleBounds +} + +export function getZoomedScaleBounds(chart: Chart) { + const state = getState(chart) + const scaleBounds: Record = {} + for (const scaleId of Object.keys(chart.scales)) { + scaleBounds[scaleId] = state.updatedScaleLimits[scaleId] + } + + return scaleBounds +} + +export function isZoomedOrPanned(chart: Chart) { + const scaleBounds = getInitialScaleBounds(chart) + for (const scaleId of Object.keys(chart.scales)) { + const { min: originalMin, max: originalMax } = scaleBounds[scaleId] + + if (originalMin !== undefined && chart.scales[scaleId].min !== originalMin) { + return true + } + + if (originalMax !== undefined && chart.scales[scaleId].max !== originalMax) { + return true + } + } + + return false +} + +export function isZoomingOrPanning(chart: Chart) { + const state = getState(chart) + return state.panning || state.dragging +} diff --git a/src/defaults.ts b/src/defaults.ts new file mode 100644 index 000000000..8a247ff5b --- /dev/null +++ b/src/defaults.ts @@ -0,0 +1,26 @@ +import type { ZoomPluginOptions } from './options' + +export const defaults: ZoomPluginOptions = { + pan: { + enabled: false, + mode: 'xy', + threshold: 10, + modifierKey: null, + }, + zoom: { + wheel: { + enabled: false, + speed: 0.1, + modifierKey: null, + }, + drag: { + enabled: false, + drawTime: 'beforeDatasetsDraw', + modifierKey: null, + }, + pinch: { + enabled: false, + }, + mode: 'xy', + }, +} diff --git a/src/hammer.js b/src/hammer.js deleted file mode 100644 index 428e41c71..000000000 --- a/src/hammer.js +++ /dev/null @@ -1,183 +0,0 @@ -import {callback as call, getRelativePosition} from 'chart.js/helpers'; -import Hammer from 'hammerjs'; -import {pan, zoom} from './core'; -import {getState} from './state'; -import {directionEnabled, getEnabledScalesByPoint, getModifierKey, keyNotPressed, keyPressed} from './utils'; - -function createEnabler(chart, state) { - return function(recognizer, event) { - const {pan: panOptions, zoom: zoomOptions = {}} = state.options; - if (!panOptions || !panOptions.enabled) { - return false; - } - const srcEvent = event && event.srcEvent; - if (!srcEvent) { // Sometimes Hammer queries this with a null event. - return true; - } - if (!state.panning && event.pointerType === 'mouse' && ( - keyNotPressed(getModifierKey(panOptions), srcEvent) || keyPressed(getModifierKey(zoomOptions.drag), srcEvent)) - ) { - call(panOptions.onPanRejected, [{chart, event}]); - return false; - } - return true; - }; -} - -function pinchAxes(p0, p1) { - // fingers position difference - const pinchX = Math.abs(p0.clientX - p1.clientX); - const pinchY = Math.abs(p0.clientY - p1.clientY); - - // diagonal fingers will change both (xy) axes - const p = pinchX / pinchY; - let x, y; - if (p > 0.3 && p < 1.7) { - x = y = true; - } else if (pinchX > pinchY) { - x = true; - } else { - y = true; - } - return {x, y}; -} - -function handlePinch(chart, state, e) { - if (state.scale) { - const {center, pointers} = e; - // Hammer reports the total scaling. We need the incremental amount - const zoomPercent = 1 / state.scale * e.scale; - const rect = e.target.getBoundingClientRect(); - const pinch = pinchAxes(pointers[0], pointers[1]); - const mode = state.options.zoom.mode; - const amount = { - x: pinch.x && directionEnabled(mode, 'x', chart) ? zoomPercent : 1, - y: pinch.y && directionEnabled(mode, 'y', chart) ? zoomPercent : 1, - focalPoint: { - x: center.x - rect.left, - y: center.y - rect.top - } - }; - - zoom(chart, amount, 'zoom', 'pinch'); - - // Keep track of overall scale - state.scale = e.scale; - } -} - -function startPinch(chart, state, event) { - if (state.options.zoom.pinch.enabled) { - const point = getRelativePosition(event, chart); - if (call(state.options.zoom.onZoomStart, [{chart, event, point}]) === false) { - state.scale = null; - call(state.options.zoom.onZoomRejected, [{chart, event}]); - } else { - state.scale = 1; - } - } -} - -function endPinch(chart, state, e) { - if (state.scale) { - handlePinch(chart, state, e); - state.scale = null; // reset - call(state.options.zoom.onZoomComplete, [{chart}]); - } -} - -function handlePan(chart, state, e) { - const delta = state.delta; - if (delta) { - state.panning = true; - pan(chart, {x: e.deltaX - delta.x, y: e.deltaY - delta.y}, state.panScales); - state.delta = {x: e.deltaX, y: e.deltaY}; - } -} - -function startPan(chart, state, event) { - const {enabled, onPanStart, onPanRejected} = state.options.pan; - if (!enabled) { - return; - } - const rect = event.target.getBoundingClientRect(); - const point = { - x: event.center.x - rect.left, - y: event.center.y - rect.top - }; - - if (call(onPanStart, [{chart, event, point}]) === false) { - return call(onPanRejected, [{chart, event}]); - } - - state.panScales = getEnabledScalesByPoint(state.options.pan, point, chart); - state.delta = {x: 0, y: 0}; - handlePan(chart, state, event); -} - -function endPan(chart, state) { - state.delta = null; - if (state.panning) { - state.panning = false; - state.filterNextClick = true; - call(state.options.pan.onPanComplete, [{chart}]); - } -} - -const hammers = new WeakMap(); -export function startHammer(chart, options) { - const state = getState(chart); - const canvas = chart.canvas; - const {pan: panOptions, zoom: zoomOptions} = options; - - const mc = new Hammer.Manager(canvas); - if (zoomOptions && zoomOptions.pinch.enabled) { - mc.add(new Hammer.Pinch()); - mc.on('pinchstart', (e) => startPinch(chart, state, e)); - mc.on('pinch', (e) => handlePinch(chart, state, e)); - mc.on('pinchend', (e) => endPinch(chart, state, e)); - } - - if (panOptions && panOptions.enabled) { - mc.add(new Hammer.Pan({ - threshold: panOptions.threshold, - enable: createEnabler(chart, state) - })); - mc.on('panstart', (e) => startPan(chart, state, e)); - mc.on('panmove', (e) => handlePan(chart, state, e)); - mc.on('panend', () => endPan(chart, state)); - } - - hammers.set(chart, mc); -} - -export function stopHammer(chart) { - const mc = hammers.get(chart); - if (mc) { - mc.remove('pinchstart'); - mc.remove('pinch'); - mc.remove('pinchend'); - mc.remove('panstart'); - mc.remove('pan'); - mc.remove('panend'); - mc.destroy(); - hammers.delete(chart); - } -} - -export function hammerOptionsChanged(oldOptions, newOptions) { - const {pan: oldPan, zoom: oldZoom} = oldOptions; - const {pan: newPan, zoom: newZoom} = newOptions; - - if (oldZoom?.zoom?.pinch?.enabled !== newZoom?.zoom?.pinch?.enabled) { - return true; - } - if (oldPan?.enabled !== newPan?.enabled) { - return true; - } - if (oldPan?.threshold !== newPan?.threshold) { - return true; - } - - return false; -} diff --git a/src/hammer.ts b/src/hammer.ts new file mode 100644 index 000000000..681a2536a --- /dev/null +++ b/src/hammer.ts @@ -0,0 +1,190 @@ +import { getRelativePosition } from 'chart.js/helpers' +import Hammer from 'hammerjs' +import { pan, zoom } from './core' +import { getState, type State } from './state' +import { directionEnabled, getEnabledScalesByPoint, getModifierKey, keyNotPressed, keyPressed } from './utils' +import type { Chart } from 'chart.js' +import type { ZoomPluginOptions } from './options' + +function createEnabler(chart: Chart, state: State) { + return function (_recognizer: any, event: HammerInput) { + const { pan: panOptions, zoom: zoomOptions = {} } = state.options + if (!panOptions || !panOptions.enabled) { + return false + } + const srcEvent = event && event.srcEvent + if (!srcEvent) { + // Sometimes Hammer queries this with a null event. + return true + } + if ( + !state.panning && + event.pointerType === 'mouse' && + (keyNotPressed(getModifierKey(panOptions), srcEvent) || keyPressed(getModifierKey(zoomOptions.drag), srcEvent)) + ) { + panOptions.onPanRejected?.({ chart, event }) + return false + } + return true + } +} + +function pinchAxes(p0: { clientX: number; clientY: number }, p1: { clientX: number; clientY: number }) { + // fingers position difference + const pinchX = Math.abs(p0.clientX - p1.clientX) + const pinchY = Math.abs(p0.clientY - p1.clientY) + + // diagonal fingers will change both (xy) axes + const p = pinchX / pinchY + let x, y + if (p > 0.3 && p < 1.7) { + x = y = true + } else if (pinchX > pinchY) { + x = true + } else { + y = true + } + return { x, y } +} + +function handlePinch(chart: Chart, state: State, e: HammerInput) { + if (state.scale) { + const { center, pointers } = e + // Hammer reports the total scaling. We need the incremental amount + const zoomPercent = (1 / state.scale) * e.scale + const rect = e.target.getBoundingClientRect() + const pinch = pinchAxes(pointers[0], pointers[1]) + const mode = state.options.zoom?.mode + const amount = { + x: pinch.x && directionEnabled(mode, 'x', chart) ? zoomPercent : 1, + y: pinch.y && directionEnabled(mode, 'y', chart) ? zoomPercent : 1, + focalPoint: { + x: center.x - rect.left, + y: center.y - rect.top, + }, + } + + zoom(chart, amount, 'zoom', 'pinch') + + // Keep track of overall scale + state.scale = e.scale + } +} + +function startPinch(chart: Chart, state: State, e: HammerInput) { + if (state.options.zoom?.pinch?.enabled) { + const point = getRelativePosition(e.srcEvent, chart as any) // TODO: would expect Chart type to be valid for getRelativePosition + if (state.options.zoom?.onZoomStart?.({ chart, event: e.srcEvent, point }) === false) { + state.scale = null + state.options.zoom?.onZoomRejected?.({ chart, event: e.srcEvent }) + } else { + state.scale = 1 + } + } +} + +function endPinch(chart: Chart, state: State, e: HammerInput) { + if (state.scale) { + handlePinch(chart, state, e) + state.scale = null // reset + state.options.zoom?.onZoomComplete?.({ chart }) + } +} + +function handlePan(chart: Chart, state: State, e: HammerInput) { + const delta = state.delta + if (delta) { + state.panning = true + pan(chart, { x: e.deltaX - delta.x, y: e.deltaY - delta.y }, state.panScales) + state.delta = { x: e.deltaX, y: e.deltaY } + } +} + +function startPan(chart: Chart, state: State, event: HammerInput) { + const { enabled, onPanStart, onPanRejected } = state.options.pan ?? {} + if (!enabled) { + return + } + const rect = event.target.getBoundingClientRect() + const point = { + x: event.center.x - rect.left, + y: event.center.y - rect.top, + } + + if (onPanStart?.({ chart, event, point }) === false) { + return onPanRejected?.({ chart, event }) + } + + state.panScales = getEnabledScalesByPoint(state.options.pan, point, chart) + state.delta = { x: 0, y: 0 } + handlePan(chart, state, event) +} + +function endPan(chart: Chart, state: State) { + state.delta = null + if (state.panning) { + state.panning = false + state.filterNextClick = true + state.options.pan?.onPanComplete?.({ chart }) + } +} + +const hammers = new WeakMap() +export function startHammer(chart: Chart, options: ZoomPluginOptions) { + const state = getState(chart) + const canvas = chart.canvas + const { pan: panOptions, zoom: zoomOptions } = options + + const mc = new Hammer.Manager(canvas) + if (zoomOptions?.pinch?.enabled) { + mc.add(new Hammer.Pinch()) + mc.on('pinchstart', (e) => startPinch(chart, state, e)) + mc.on('pinch', (e) => handlePinch(chart, state, e)) + mc.on('pinchend', (e) => endPinch(chart, state, e)) + } + + if (panOptions && panOptions.enabled) { + mc.add( + new Hammer.Pan({ + threshold: panOptions.threshold, + enable: createEnabler(chart, state), + }) + ) + mc.on('panstart', (e) => startPan(chart, state, e)) + mc.on('panmove', (e) => handlePan(chart, state, e)) + mc.on('panend', () => endPan(chart, state)) + } + + hammers.set(chart, mc) +} + +export function stopHammer(chart: Chart) { + const mc = hammers.get(chart) + if (mc) { + mc.remove('pinchstart') + mc.remove('pinch') + mc.remove('pinchend') + mc.remove('panstart') + mc.remove('pan') + mc.remove('panend') + mc.destroy() + hammers.delete(chart) + } +} + +export function hammerOptionsChanged(oldOptions: ZoomPluginOptions, newOptions: ZoomPluginOptions) { + const { pan: oldPan, zoom: oldZoom } = oldOptions + const { pan: newPan, zoom: newZoom } = newOptions + + if (oldZoom?.pinch?.enabled !== newZoom?.pinch?.enabled) { + return true + } + if (oldPan?.enabled !== newPan?.enabled) { + return true + } + if (oldPan?.threshold !== newPan?.threshold) { + return true + } + + return false +} diff --git a/src/handlers.js b/src/handlers.js deleted file mode 100644 index 6e7e091c0..000000000 --- a/src/handlers.js +++ /dev/null @@ -1,299 +0,0 @@ -import {directionEnabled, debounce, keyNotPressed, getModifierKey, keyPressed} from './utils'; -import {zoom, zoomRect} from './core'; -import {callback as call, getRelativePosition, _isPointInArea} from 'chart.js/helpers'; -import {getState} from './state'; - -/** - * @param {number} x - * @param {number} from - * @param {number} to - * @returns {number} - */ -const clamp = (x, from, to) => Math.min(to, Math.max(from, x)); - -function removeHandler(chart, type) { - const {handlers} = getState(chart); - const handler = handlers[type]; - if (handler && handler.target) { - handler.target.removeEventListener(type, handler); - delete handlers[type]; - } -} - -function addHandler(chart, target, type, handler) { - const {handlers, options} = getState(chart); - const oldHandler = handlers[type]; - if (oldHandler && oldHandler.target === target) { - // already attached - return; - } - removeHandler(chart, type); - handlers[type] = (event) => handler(chart, event, options); - handlers[type].target = target; - - // `passive: false` for wheel events, to prevent chrome warnings. Use default value for other events. - const passive = type === 'wheel' ? false : undefined; - target.addEventListener(type, handlers[type], {passive}); -} - -export function mouseMove(chart, event) { - const state = getState(chart); - if (state.dragStart) { - state.dragging = true; - state.dragEnd = event; - chart.update('none'); - } -} - -function keyDown(chart, event) { - const state = getState(chart); - if (!state.dragStart || event.key !== 'Escape') { - return; - } - - removeHandler(chart, 'keydown'); - state.dragging = false; - state.dragStart = state.dragEnd = null; - chart.update('none'); -} - -function getPointPosition(event, chart) { - if (event.target !== chart.canvas) { - const canvasArea = chart.canvas.getBoundingClientRect(); - return { - x: event.clientX - canvasArea.left, - y: event.clientY - canvasArea.top, - }; - } - return getRelativePosition(event, chart); -} - -/** - * @param {import('chart.js').Chart} chart - * @param {*} event - * @param {import('../types/options').ZoomOptions} zoomOptions - */ -function zoomStart(chart, event, zoomOptions) { - const {onZoomStart, onZoomRejected} = zoomOptions; - if (onZoomStart) { - const point = getPointPosition(event, chart); - // @ts-expect-error args not assignable to unknown[] - if (call(onZoomStart, [{chart, event, point}]) === false) { - // @ts-expect-error args not assignable to unknown[] - call(onZoomRejected, [{chart, event}]); - return false; - } - } -} - -export function mouseDown(chart, event) { - if (chart.legend) { - const point = getRelativePosition(event, chart); - if (_isPointInArea(point, chart.legend)) { - return; - } - } - const state = getState(chart); - const {pan: panOptions, zoom: zoomOptions = {}} = state.options; - if ( - event.button !== 0 || - keyPressed(getModifierKey(panOptions), event) || - keyNotPressed(getModifierKey(zoomOptions.drag), event) - ) { - // @ts-expect-error args not assignable to unknown[] - return call(zoomOptions.onZoomRejected, [{chart, event}]); - } - - if (zoomStart(chart, event, zoomOptions) === false) { - return; - } - state.dragStart = event; - - addHandler(chart, chart.canvas.ownerDocument, 'mousemove', mouseMove); - addHandler(chart, window.document, 'keydown', keyDown); -} - -function applyAspectRatio({begin, end}, aspectRatio) { - let width = end.x - begin.x; - let height = end.y - begin.y; - const ratio = Math.abs(width / height); - - if (ratio > aspectRatio) { - width = Math.sign(width) * Math.abs(height * aspectRatio); - } else if (ratio < aspectRatio) { - height = Math.sign(height) * Math.abs(width / aspectRatio); - } - - end.x = begin.x + width; - end.y = begin.y + height; -} - -function applyMinMaxProps(rect, chartArea, points, {min, max, prop}) { - rect[min] = clamp(Math.min(points.begin[prop], points.end[prop]), chartArea[min], chartArea[max]); - rect[max] = clamp(Math.max(points.begin[prop], points.end[prop]), chartArea[min], chartArea[max]); -} - -function getRelativePoints(chart, pointEvents, maintainAspectRatio) { - const points = { - begin: getPointPosition(pointEvents.dragStart, chart), - end: getPointPosition(pointEvents.dragEnd, chart), - }; - - if (maintainAspectRatio) { - const aspectRatio = chart.chartArea.width / chart.chartArea.height; - applyAspectRatio(points, aspectRatio); - } - - return points; -} - -export function computeDragRect(chart, mode, pointEvents, maintainAspectRatio) { - const xEnabled = directionEnabled(mode, 'x', chart); - const yEnabled = directionEnabled(mode, 'y', chart); - const {top, left, right, bottom, width: chartWidth, height: chartHeight} = chart.chartArea; - const rect = {top, left, right, bottom}; - - const points = getRelativePoints(chart, pointEvents, maintainAspectRatio && xEnabled && yEnabled); - - if (xEnabled) { - applyMinMaxProps(rect, chart.chartArea, points, {min: 'left', max: 'right', prop: 'x'}); - } - - if (yEnabled) { - applyMinMaxProps(rect, chart.chartArea, points, {min: 'top', max: 'bottom', prop: 'y'}); - } - - const width = rect.right - rect.left; - const height = rect.bottom - rect.top; - - return { - ...rect, - width, - height, - zoomX: xEnabled && width ? 1 + ((chartWidth - width) / chartWidth) : 1, - zoomY: yEnabled && height ? 1 + ((chartHeight - height) / chartHeight) : 1 - }; -} - -export function mouseUp(chart, event) { - const state = getState(chart); - if (!state.dragStart) { - return; - } - - removeHandler(chart, 'mousemove'); - const {mode, onZoomComplete, drag: {threshold = 0, maintainAspectRatio}} = state.options.zoom; - const rect = computeDragRect(chart, mode, {dragStart: state.dragStart, dragEnd: event}, maintainAspectRatio); - const distanceX = directionEnabled(mode, 'x', chart) ? rect.width : 0; - const distanceY = directionEnabled(mode, 'y', chart) ? rect.height : 0; - const distance = Math.sqrt(distanceX * distanceX + distanceY * distanceY); - - // Remove drag start and end before chart update to stop drawing selected area - state.dragStart = state.dragEnd = null; - - if (distance <= threshold) { - state.dragging = false; - chart.update('none'); - return; - } - - zoomRect(chart, {x: rect.left, y: rect.top}, {x: rect.right, y: rect.bottom}, 'zoom', 'drag'); - - state.dragging = false; - state.filterNextClick = true; - // @ts-expect-error args not assignable to unknown[] - call(onZoomComplete, [{chart}]); -} - -/** - * @param {import('chart.js').Chart} chart - * @param {*} event - * @param {import('../types/options').ZoomOptions} zoomOptions - */ -function wheelPreconditions(chart, event, zoomOptions) { - // Before preventDefault, check if the modifier key required and pressed - if (keyNotPressed(getModifierKey(zoomOptions.wheel), event)) { - // @ts-expect-error args not assignable to unknown[] - call(zoomOptions.onZoomRejected, [{chart, event}]); - return; - } - - if (zoomStart(chart, event, zoomOptions) === false) { - return; - } - - // Prevent the event from triggering the default behavior (e.g. content scrolling). - if (event.cancelable) { - event.preventDefault(); - } - - // Firefox always fires the wheel event twice: - // First without the delta and right after that once with the delta properties. - if (event.deltaY === undefined) { - return; - } - return true; -} - -export function wheel(chart, event) { - const {handlers: {onZoomComplete}, options: {zoom: zoomOptions}} = getState(chart); - - if (!wheelPreconditions(chart, event, zoomOptions)) { - return; - } - - const rect = event.target.getBoundingClientRect(); - const speed = zoomOptions.wheel.speed; - const percentage = event.deltaY >= 0 ? 2 - 1 / (1 - speed) : 1 + speed; - const amount = { - x: percentage, - y: percentage, - focalPoint: { - x: event.clientX - rect.left, - y: event.clientY - rect.top - } - }; - - zoom(chart, amount, 'zoom', 'wheel'); - - call(onZoomComplete, [{chart}]); -} - -function addDebouncedHandler(chart, name, handler, delay) { - if (handler) { - getState(chart).handlers[name] = debounce(() => call(handler, [{chart}]), delay); - } -} - -export function addListeners(chart, options) { - const canvas = chart.canvas; - const {wheel: wheelOptions, drag: dragOptions, onZoomComplete} = options.zoom; - - // Install listeners. Do this dynamically based on options so that we can turn zoom on and off - // We also want to make sure listeners aren't always on. E.g. if you're scrolling down a page - // and the mouse goes over a chart you don't want it intercepted unless the plugin is enabled - if (wheelOptions.enabled) { - addHandler(chart, canvas, 'wheel', wheel); - addDebouncedHandler(chart, 'onZoomComplete', onZoomComplete, 250); - } else { - removeHandler(chart, 'wheel'); - } - if (dragOptions.enabled) { - addHandler(chart, canvas, 'mousedown', mouseDown); - addHandler(chart, canvas.ownerDocument, 'mouseup', mouseUp); - } else { - removeHandler(chart, 'mousedown'); - removeHandler(chart, 'mousemove'); - removeHandler(chart, 'mouseup'); - removeHandler(chart, 'keydown'); - } -} - -export function removeListeners(chart) { - removeHandler(chart, 'mousedown'); - removeHandler(chart, 'mousemove'); - removeHandler(chart, 'mouseup'); - removeHandler(chart, 'wheel'); - removeHandler(chart, 'click'); - removeHandler(chart, 'keydown'); -} diff --git a/src/handlers.ts b/src/handlers.ts new file mode 100644 index 000000000..7814ee1ec --- /dev/null +++ b/src/handlers.ts @@ -0,0 +1,315 @@ +import { directionEnabled, debounce, keyNotPressed, getModifierKey, keyPressed } from './utils' +import { zoom, zoomRect } from './core' +import { getRelativePosition, _isPointInArea } from 'chart.js/helpers' +import { getState, type HandlerFunctions, type HandlerName } from './state' +import type { Chart, ChartArea, Point } from 'chart.js' +import type { ModeOption, ZoomOptions, ZoomPluginOptions } from './options' + +const clamp = (x: number, from: number, to: number): number => Math.min(to, Math.max(from, x)) + +function removeHandler(chart: Chart, type: HandlerName) { + const { handlers, targets } = getState(chart) + const handler = handlers[type] + const target = targets[type] + if (handler && target) { + target.removeEventListener(type, handler) + delete handlers[type] + } +} + +type EventHandler = (chart: Chart, event: T, options: ZoomPluginOptions) => void + +function addHandler( + chart: Chart, + target: HTMLCanvasElement | Document, + type: HandlerName, + handler: EventHandler +) { + const { handlers, options, targets } = getState(chart) + const oldHandler = handlers[type] + if (oldHandler && targets[type] === target) { + // already attached + return + } + removeHandler(chart, type) + handlers[type] = (event) => handler(chart, event as T, options) + targets[type] = target + + // `passive: false` for wheel events, to prevent chrome warnings. Use default value for other events. + const passive = type === 'wheel' ? false : undefined + target.addEventListener(type, handlers[type], { passive }) +} + +export function mouseMove(chart: Chart, event: MouseEvent) { + const state = getState(chart) + if (state.dragStart) { + state.dragging = true + state.dragEnd = event + chart.update('none') + } +} + +function keyDown(chart: Chart, event: KeyboardEvent) { + const state = getState(chart) + if (!state.dragStart || event.key !== 'Escape') { + return + } + + removeHandler(chart, 'keydown') + state.dragging = false + state.dragStart = state.dragEnd = undefined + chart.update('none') +} + +function getPointPosition(event: MouseEvent, chart: Chart) { + if (event.target !== chart.canvas) { + const canvasArea = chart.canvas.getBoundingClientRect() + return { + x: event.clientX - canvasArea.left, + y: event.clientY - canvasArea.top, + } + } + return getRelativePosition(event, chart as any) // TODO: would expect Chart type to be valid for getRelativePosition +} + +function zoomStart(chart: Chart, event: MouseEvent, zoomOptions: ZoomOptions): boolean | void { + const { onZoomStart, onZoomRejected } = zoomOptions + if (onZoomStart) { + const point = getPointPosition(event, chart) + if (onZoomStart?.({ chart, event, point }) === false) { + onZoomRejected?.({ chart, event }) + return false + } + } +} + +export function mouseDown(chart: Chart, event: MouseEvent): void { + if (chart.legend) { + const point = getRelativePosition(event, chart as any) // TODO: would expect Chart type to be valid for getRelativePosition + if (_isPointInArea(point, chart.legend)) { + return + } + } + const state = getState(chart) + const { pan: panOptions, zoom: zoomOptions = {} } = state.options + if ( + event.button !== 0 || + keyPressed(getModifierKey(panOptions), event) || + keyNotPressed(getModifierKey(zoomOptions.drag), event) + ) { + return zoomOptions.onZoomRejected?.({ chart, event }) + } + + if (zoomStart(chart, event, zoomOptions) === false) { + return + } + state.dragStart = event + + addHandler(chart, chart.canvas.ownerDocument, 'mousemove', mouseMove) + addHandler(chart, window.document, 'keydown', keyDown) +} + +function applyAspectRatio( + { begin, end }: { begin: { x: number; y: number }; end: { x: number; y: number } }, + aspectRatio: number +) { + let width = end.x - begin.x + let height = end.y - begin.y + const ratio = Math.abs(width / height) + + if (ratio > aspectRatio) { + width = Math.sign(width) * Math.abs(height * aspectRatio) + } else if (ratio < aspectRatio) { + height = Math.sign(height) * Math.abs(width / aspectRatio) + } + + end.x = begin.x + width + end.y = begin.y + height +} + +type Rect = { top?: number; left?: number; right?: number; bottom?: number } +function applyMinMaxProps( + rect: Rect, + chartArea: ChartArea, + points: { begin: Point; end: Point }, + { min, max, prop }: { min: keyof Rect; max: keyof Rect; prop: 'x' | 'y' } +) { + rect[min] = clamp(Math.min(points.begin[prop], points.end[prop]), chartArea[min], chartArea[max]) + rect[max] = clamp(Math.max(points.begin[prop], points.end[prop]), chartArea[min], chartArea[max]) +} + +function getRelativePoints( + chart: Chart, + pointEvents: { dragStart: MouseEvent; dragEnd: MouseEvent }, + maintainAspectRatio?: boolean +) { + const points = { + begin: getPointPosition(pointEvents.dragStart, chart), + end: getPointPosition(pointEvents.dragEnd, chart), + } + + if (maintainAspectRatio) { + const aspectRatio = chart.chartArea.width / chart.chartArea.height + applyAspectRatio(points, aspectRatio) + } + + return points +} + +export function computeDragRect( + chart: Chart, + mode: ModeOption | undefined, + pointEvents: { dragStart: MouseEvent; dragEnd: MouseEvent }, + maintainAspectRatio: boolean | undefined +) { + const xEnabled = directionEnabled(mode, 'x', chart) + const yEnabled = directionEnabled(mode, 'y', chart) + const { top, left, right, bottom, width: chartWidth, height: chartHeight } = chart.chartArea + const rect = { top, left, right, bottom } + + const points = getRelativePoints(chart, pointEvents, maintainAspectRatio && xEnabled && yEnabled) + + if (xEnabled) { + applyMinMaxProps(rect, chart.chartArea, points, { min: 'left', max: 'right', prop: 'x' }) + } + + if (yEnabled) { + applyMinMaxProps(rect, chart.chartArea, points, { min: 'top', max: 'bottom', prop: 'y' }) + } + + const width = rect.right - rect.left + const height = rect.bottom - rect.top + + return { + ...rect, + width, + height, + zoomX: xEnabled && width ? 1 + (chartWidth - width) / chartWidth : 1, + zoomY: yEnabled && height ? 1 + (chartHeight - height) / chartHeight : 1, + } +} + +export function mouseUp(chart: Chart, event: MouseEvent) { + const state = getState(chart) + if (!state.dragStart) { + return + } + + removeHandler(chart, 'mousemove') + const { mode, onZoomComplete, drag } = state.options.zoom ?? {} + const { threshold = 0, maintainAspectRatio } = drag ?? {} + const rect = computeDragRect(chart, mode, { dragStart: state.dragStart, dragEnd: event }, maintainAspectRatio) + const distanceX = directionEnabled(mode, 'x', chart) ? rect.width : 0 + const distanceY = directionEnabled(mode, 'y', chart) ? rect.height : 0 + const distance = Math.sqrt(distanceX * distanceX + distanceY * distanceY) + + // Remove drag start and end before chart update to stop drawing selected area + state.dragStart = state.dragEnd = undefined + + if (distance <= threshold) { + state.dragging = false + chart.update('none') + return + } + + zoomRect(chart, { x: rect.left, y: rect.top }, { x: rect.right, y: rect.bottom }, 'zoom', 'drag') + + state.dragging = false + state.filterNextClick = true + onZoomComplete?.({ chart }) +} + +function wheelPreconditions(chart: Chart, event: WheelEvent, zoomOptions: ZoomOptions): true | void { + // Before preventDefault, check if the modifier key required and pressed + if (keyNotPressed(getModifierKey(zoomOptions.wheel), event)) { + zoomOptions.onZoomRejected?.({ chart, event }) + return + } + + if (zoomStart(chart, event, zoomOptions) === false) { + return + } + + // Prevent the event from triggering the default behavior (e.g. content scrolling). + if (event.cancelable) { + event.preventDefault() + } + + // Firefox always fires the wheel event twice: + // First without the delta and right after that once with the delta properties. + if (event.deltaY === undefined) { + return + } + return true +} + +export function wheel(chart: Chart, event: WheelEvent & { target?: HTMLCanvasElement }) { + const { + handlers: { onZoomComplete }, + options: { zoom: zoomOptions = {} }, + } = getState(chart) + + if (!wheelPreconditions(chart, event, zoomOptions)) { + return + } + + const rect = event.target?.getBoundingClientRect() + const speed = zoomOptions?.wheel?.speed ?? 0.1 + const percentage = event.deltaY >= 0 ? 2 - 1 / (1 - speed) : 1 + speed + const amount = { + x: percentage, + y: percentage, + focalPoint: { + x: event.clientX - rect.left, + y: event.clientY - rect.top, + }, + } + + zoom(chart, amount, 'zoom', 'wheel') + + onZoomComplete?.(event) +} + +function addDebouncedHandler( + chart: Chart, + name: HandlerName, + handler: HandlerFunctions['onZoomComplete'] | undefined, + delay: number +) { + if (handler) { + getState(chart).handlers[name] = debounce(() => handler?.({ chart }), delay) + } +} + +export function addListeners(chart: Chart, options: ZoomPluginOptions) { + const canvas = chart.canvas + const { wheel: wheelOptions, drag: dragOptions, onZoomComplete } = options.zoom ?? {} + + // Install listeners. Do this dynamically based on options so that we can turn zoom on and off + // We also want to make sure listeners aren't always on. E.g. if you're scrolling down a page + // and the mouse goes over a chart you don't want it intercepted unless the plugin is enabled + if (wheelOptions?.enabled) { + addHandler(chart, canvas, 'wheel', wheel) + addDebouncedHandler(chart, 'onZoomComplete', onZoomComplete, 250) + } else { + removeHandler(chart, 'wheel') + } + if (dragOptions?.enabled) { + addHandler(chart, canvas, 'mousedown', mouseDown) + addHandler(chart, canvas.ownerDocument, 'mouseup', mouseUp) + } else { + removeHandler(chart, 'mousedown') + removeHandler(chart, 'mousemove') + removeHandler(chart, 'mouseup') + removeHandler(chart, 'keydown') + } +} + +export function removeListeners(chart: Chart) { + removeHandler(chart, 'mousedown') + removeHandler(chart, 'mousemove') + removeHandler(chart, 'mouseup') + removeHandler(chart, 'wheel') + removeHandler(chart, 'click') + removeHandler(chart, 'keydown') +} diff --git a/src/index.esm.js b/src/index.esm.js deleted file mode 100644 index 3ff12100f..000000000 --- a/src/index.esm.js +++ /dev/null @@ -1,4 +0,0 @@ -import plugin from './plugin'; - -export default plugin; -export {pan, zoom, zoomRect, zoomScale, resetZoom} from './core'; diff --git a/src/index.js b/src/index.js deleted file mode 100644 index 1282a6221..000000000 --- a/src/index.js +++ /dev/null @@ -1,6 +0,0 @@ -import {Chart} from 'chart.js'; -import Zoom from './plugin'; - -Chart.register(Zoom); - -export default Zoom; diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 000000000..e4aebcdc4 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,38 @@ +/* eslint-disable no-unused-vars, @typescript-eslint/no-unused-vars */ +import plugin from './plugin' + +import type { ZoomPluginOptions } from './options' +import type { ScaleRange } from './state' +import type { DistributiveArray, PanAmount, ZoomAmount } from './types' +import type { ChartType, ChartTypeRegistry, Point, Scale, UpdateMode } from 'chart.js' + +declare module 'chart.js' { + interface PluginOptionsByType { + zoom: ZoomPluginOptions + } + + // eslint-disable-next-line no-shadow + enum UpdateModeEnum { + zoom = 'zoom', + } + + interface Chart< + TType extends ChartType = keyof ChartTypeRegistry, + TData = DistributiveArray, + TLabel = unknown, + > { + pan(pan: PanAmount, scales?: Scale[], mode?: UpdateMode): void + zoom(zoom: ZoomAmount, mode?: UpdateMode): void + zoomRect(p0: Point, p1: Point, mode?: UpdateMode): void + zoomScale(id: string, range: ScaleRange, mode?: UpdateMode): void + resetZoom(mode?: UpdateMode): void + getZoomLevel(): number + getInitialScaleBounds(): Record + getZoomedScaleBounds(): Record> + isZoomedOrPanned(): boolean + isZoomingOrPanning(): boolean + } +} + +export default plugin +export { pan, zoom, zoomRect, zoomScale, resetZoom } from './core' diff --git a/src/index.umd.ts b/src/index.umd.ts new file mode 100644 index 000000000..103248e47 --- /dev/null +++ b/src/index.umd.ts @@ -0,0 +1,6 @@ +import { Chart } from 'chart.js' +import Zoom from './plugin' + +Chart.register(Zoom) + +export default Zoom diff --git a/types/options.d.ts b/src/options.ts similarity index 62% rename from types/options.d.ts rename to src/options.ts index 88a911b8b..fd2ae6983 100644 --- a/types/options.d.ts +++ b/src/options.ts @@ -1,76 +1,77 @@ -import { Chart, Color, Point } from 'chart.js'; -import { Input as HammerInput } from 'hammerjs'; +import type { Chart, Color, Point } from 'chart.js' -export type Mode = 'x' | 'y' | 'xy'; -export type ModifierKey = 'ctrl' | 'alt' | 'shift' | 'meta'; -export type DrawTime = 'afterDraw' | 'afterDatasetsDraw' | 'beforeDraw' | 'beforeDatasetsDraw'; +export type Mode = 'x' | 'y' | 'xy' +export type ModeFn = (context: { chart: Chart }) => Mode +export type ModeOption = Mode | ModeFn +export type ModifierKey = 'ctrl' | 'alt' | 'shift' | 'meta' +export type DrawTime = 'afterDraw' | 'afterDatasetsDraw' | 'beforeDraw' | 'beforeDatasetsDraw' export type ZoomTrigger = 'api' | 'drag' | 'wheel' | 'pinch' export interface WheelOptions { /** * Enable the zoom via mouse wheel */ - enabled?: boolean; + enabled?: boolean /** * Speed of zoom via mouse wheel * (percentage of zoom on a wheel event) */ - speed?: number; + speed?: number /** * Modifier key required for zooming with mouse */ - modifierKey?: ModifierKey; + modifierKey?: ModifierKey | null } export interface DragOptions { /** * Enable the zoom via drag */ - enabled?: boolean; + enabled?: boolean /** * Minimal zoom distance required before actually applying zoom */ - threshold?: number; + threshold?: number /** * Border color of the drag area */ - borderColor?: Color; + borderColor?: Color /** * Border width of the drag area */ - borderWidth?: number; + borderWidth?: number /** * Background color of the drag area */ - backgroundColor?: Color; + backgroundColor?: Color /** * Modifier key required for drag-to-zoom */ - modifierKey?: ModifierKey; + modifierKey?: ModifierKey | null /** * Draw time required for drag-to-zoom */ - drawTime?: DrawTime; + drawTime?: DrawTime /** * Maintain aspect ratio of the drag rectangle */ - maintainAspectRatio?: boolean; + maintainAspectRatio?: boolean } export interface PinchOptions { /** * Enable the zoom via pinch */ - enabled?: boolean; + enabled?: boolean } /** @@ -86,43 +87,43 @@ export interface ZoomOptions { * return 'xy'; * }, */ - mode?: Mode | { (chart: Chart): Mode }; + mode?: ModeOption /** * Options of the mouse wheel mode */ - wheel?: WheelOptions; + wheel?: WheelOptions /** * Options of the drag-to-zoom mode */ - drag?: DragOptions; + drag?: DragOptions /** * Options of the pinch mode */ - pinch?: PinchOptions; + pinch?: PinchOptions - scaleMode?: Mode | { (chart: Chart): Mode }; + scaleMode?: ModeOption /** @deprecated Use scaleMode instead */ - overScaleMode?: Mode | { (chart: Chart): Mode }; + overScaleMode?: ModeOption /** * Function called while the user is zooming */ - onZoom?: (context: { chart: Chart, trigger: ZoomTrigger }) => void; + onZoom?: (context: { chart: Chart; trigger: ZoomTrigger }) => void /** * Function called once zooming is completed */ - onZoomComplete?: (context: { chart: Chart }) => void; + onZoomComplete?: (context: { chart: Chart }) => void /** * Function called when wheel input occurs without modifier key */ - onZoomRejected?: (context: { chart: Chart, event: Event }) => void; + onZoomRejected?: (context: { chart: Chart; event: Event }) => void - onZoomStart?: (context: { chart: Chart, event: Event, point: Point }) => boolean | undefined; + onZoomStart?: (context: { chart: Chart; event: Event; point: Point }) => boolean | undefined } /** @@ -132,7 +133,7 @@ export interface PanOptions { /** * Boolean to enable panning */ - enabled?: boolean; + enabled?: boolean /** * Panning directions. Remove the appropriate direction to disable @@ -143,54 +144,54 @@ export interface PanOptions { * return 'xy'; * }, */ - mode?: Mode | { (chart: Chart): Mode }; + mode?: ModeOption /** * Modifier key required for panning with mouse */ - modifierKey?: ModifierKey; + modifierKey?: ModifierKey | null - scaleMode?: Mode | { (chart: Chart): Mode }; + scaleMode?: ModeOption /** @deprecated Use scaleMode instead */ - overScaleMode?: Mode | { (chart: Chart): Mode }; + overScaleMode?: ModeOption /** * Minimal pan distance required before actually applying pan */ - threshold?: number; + threshold?: number /** * Function called while the user is panning */ - onPan?: (context: { chart: Chart }) => void; + onPan?: (context: { chart: Chart }) => void /** * Function called once panning is completed */ - onPanComplete?: (context: { chart: Chart }) => void; + onPanComplete?: (context: { chart: Chart }) => void /** * Function called when pan fails because modifier key was not detected. * event is the Hammer event that failed - see https://hammerjs.github.io/api#event-object */ - onPanRejected?: (context: { chart: Chart, event: HammerInput }) => void; + onPanRejected?: (context: { chart: Chart; event: HammerInput }) => void - onPanStart?: (context: { chart: Chart, event: HammerInput, point: Point }) => boolean | undefined; + onPanStart?: (context: { chart: Chart; event: HammerInput; point: Point }) => boolean | undefined } export interface ScaleLimits { - min?: number | 'original'; - max?: number | 'original'; - minRange?: number; + min?: number | 'original' + max?: number | 'original' + minRange?: number } export interface LimitOptions { // Scale limits, indexed by the scale's ID (key) or by axis (x/y) - [axisId: string]: ScaleLimits; + [axisId: string]: ScaleLimits } export interface ZoomPluginOptions { - pan?: PanOptions; - limits?: LimitOptions; - zoom?: ZoomOptions; + pan?: PanOptions + limits?: LimitOptions + zoom?: ZoomOptions } diff --git a/src/plugin.js b/src/plugin.js deleted file mode 100644 index 1e533cce2..000000000 --- a/src/plugin.js +++ /dev/null @@ -1,147 +0,0 @@ -import Hammer from 'hammerjs'; -import {addListeners, computeDragRect, removeListeners} from './handlers'; -import {hammerOptionsChanged, startHammer, stopHammer} from './hammer'; -import {pan, zoom, resetZoom, zoomScale, getZoomLevel, getInitialScaleBounds, getZoomedScaleBounds, isZoomedOrPanned, isZoomingOrPanning, zoomRect} from './core'; -import {panFunctions, zoomFunctions, zoomRectFunctions} from './scale.types'; -import {getState, removeState} from './state'; -import {version} from '../package.json'; - -function draw(chart, caller, options) { - const dragOptions = options.zoom.drag; - const {dragStart, dragEnd} = getState(chart); - - if (dragOptions.drawTime !== caller || !dragEnd) { - return; - } - const {left, top, width, height} = computeDragRect(chart, options.zoom.mode, {dragStart, dragEnd}, dragOptions.maintainAspectRatio); - const ctx = chart.ctx; - - ctx.save(); - ctx.beginPath(); - ctx.fillStyle = dragOptions.backgroundColor || 'rgba(225,225,225,0.3)'; - ctx.fillRect(left, top, width, height); - - if (dragOptions.borderWidth > 0) { - ctx.lineWidth = dragOptions.borderWidth; - ctx.strokeStyle = dragOptions.borderColor || 'rgba(225,225,225)'; - ctx.strokeRect(left, top, width, height); - } - ctx.restore(); -} - -export default { - id: 'zoom', - - version, - - defaults: { - pan: { - enabled: false, - mode: 'xy', - threshold: 10, - modifierKey: null, - }, - zoom: { - wheel: { - enabled: false, - speed: 0.1, - modifierKey: null - }, - drag: { - enabled: false, - drawTime: 'beforeDatasetsDraw', - modifierKey: null - }, - pinch: { - enabled: false - }, - mode: 'xy', - } - }, - - start: function(chart, _args, options) { - const state = getState(chart); - state.options = options; - - if (Object.prototype.hasOwnProperty.call(options.zoom, 'enabled')) { - console.warn('The option `zoom.enabled` is no longer supported. Please use `zoom.wheel.enabled`, `zoom.drag.enabled`, or `zoom.pinch.enabled`.'); - } - if (Object.prototype.hasOwnProperty.call(options.zoom, 'overScaleMode') - || Object.prototype.hasOwnProperty.call(options.pan, 'overScaleMode')) { - console.warn('The option `overScaleMode` is deprecated. Please use `scaleMode` instead (and update `mode` as desired).'); - } - - if (Hammer) { - startHammer(chart, options); - } - - chart.pan = (delta, panScales, transition) => pan(chart, delta, panScales, transition); - chart.zoom = (args, transition) => zoom(chart, args, transition); - chart.zoomRect = (p0, p1, transition) => zoomRect(chart, p0, p1, transition); - chart.zoomScale = (id, range, transition) => zoomScale(chart, id, range, transition); - chart.resetZoom = (transition) => resetZoom(chart, transition); - chart.getZoomLevel = () => getZoomLevel(chart); - chart.getInitialScaleBounds = () => getInitialScaleBounds(chart); - chart.getZoomedScaleBounds = () => getZoomedScaleBounds(chart); - chart.isZoomedOrPanned = () => isZoomedOrPanned(chart); - chart.isZoomingOrPanning = () => isZoomingOrPanning(chart); - }, - - beforeEvent(chart, {event}) { - if (isZoomingOrPanning(chart)) { - // cancel any event handling while panning or dragging - return false; - } - // cancel the next click or mouseup after drag or pan - if (event.type === 'click' || event.type === 'mouseup') { - const state = getState(chart); - if (state.filterNextClick) { - state.filterNextClick = false; - return false; - } - } - }, - - beforeUpdate: function(chart, args, options) { - const state = getState(chart); - const previousOptions = state.options; - state.options = options; - - // Hammer needs to be restarted when certain options change. - if (hammerOptionsChanged(previousOptions, options)) { - stopHammer(chart); - startHammer(chart, options); - } - - addListeners(chart, options); - }, - - beforeDatasetsDraw(chart, _args, options) { - draw(chart, 'beforeDatasetsDraw', options); - }, - - afterDatasetsDraw(chart, _args, options) { - draw(chart, 'afterDatasetsDraw', options); - }, - - beforeDraw(chart, _args, options) { - draw(chart, 'beforeDraw', options); - }, - - afterDraw(chart, _args, options) { - draw(chart, 'afterDraw', options); - }, - - stop: function(chart) { - removeListeners(chart); - - if (Hammer) { - stopHammer(chart); - } - removeState(chart); - }, - - panFunctions, - zoomFunctions, - zoomRectFunctions, -}; diff --git a/src/plugin.ts b/src/plugin.ts new file mode 100644 index 000000000..48185e776 --- /dev/null +++ b/src/plugin.ts @@ -0,0 +1,152 @@ +import Hammer from 'hammerjs' +import { addListeners, computeDragRect, removeListeners } from './handlers' +import { hammerOptionsChanged, startHammer, stopHammer } from './hammer' +import { + pan, + zoom, + resetZoom, + zoomScale, + getZoomLevel, + getInitialScaleBounds, + getZoomedScaleBounds, + isZoomedOrPanned, + isZoomingOrPanning, + zoomRect, +} from './core' +import { panFunctions, zoomFunctions, zoomRectFunctions } from './scale.types' +import { getState, removeState } from './state' +import { version } from '../package.json' +import type { Chart, ChartEvent } from 'chart.js' +import type { ZoomPluginOptions } from './options' +import { defaults } from './defaults' + +function draw(chart: Chart, caller: string, options: ZoomPluginOptions) { + const dragOptions = options.zoom?.drag + const { dragStart, dragEnd } = getState(chart) + + if (dragOptions?.drawTime !== caller || !dragStart || !dragEnd) { + return + } + const { left, top, width, height } = computeDragRect( + chart, + options.zoom?.mode, + { dragStart, dragEnd }, + dragOptions.maintainAspectRatio + ) + const ctx = chart.ctx + + ctx.save() + ctx.beginPath() + ctx.fillStyle = dragOptions.backgroundColor || 'rgba(225,225,225,0.3)' + ctx.fillRect(left, top, width, height) + + if (dragOptions.borderWidth) { + ctx.lineWidth = dragOptions.borderWidth + ctx.strokeStyle = dragOptions.borderColor || 'rgba(225,225,225)' + ctx.strokeRect(left, top, width, height) + } + ctx.restore() +} + +export default { + id: 'zoom', + + version, + + defaults, + + start: function (chart: Chart, _args: unknown, options: ZoomPluginOptions) { + const state = getState(chart) + state.options = options + + if (Object.prototype.hasOwnProperty.call(options.zoom, 'enabled')) { + console.warn( + 'The option `zoom.enabled` is no longer supported. Please use `zoom.wheel.enabled`, `zoom.drag.enabled`, or `zoom.pinch.enabled`.' + ) + } + if ( + Object.prototype.hasOwnProperty.call(options.zoom, 'overScaleMode') || + Object.prototype.hasOwnProperty.call(options.pan, 'overScaleMode') + ) { + console.warn( + 'The option `overScaleMode` is deprecated. Please use `scaleMode` instead (and update `mode` as desired).' + ) + } + + if (Hammer) { + startHammer(chart, options) + } + + chart.pan = (delta, panScales, transition) => pan(chart, delta, panScales, transition) + chart.zoom = (args, transition) => zoom(chart, args, transition) + chart.zoomRect = (p0, p1, transition) => zoomRect(chart, p0, p1, transition) + chart.zoomScale = (id, range, transition) => zoomScale(chart, id, range, transition) + chart.resetZoom = (transition) => resetZoom(chart, transition) + chart.getZoomLevel = () => getZoomLevel(chart) + chart.getInitialScaleBounds = () => getInitialScaleBounds(chart) + chart.getZoomedScaleBounds = () => getZoomedScaleBounds(chart) + chart.isZoomedOrPanned = () => isZoomedOrPanned(chart) + chart.isZoomingOrPanning = () => isZoomingOrPanning(chart) + }, + + beforeEvent( + chart: Chart, + { event }: { event: ChartEvent; replay: boolean; cancelable: true; inChartArea: boolean } + ): boolean | void { + if (isZoomingOrPanning(chart)) { + // cancel any event handling while panning or dragging + return false + } + // cancel the next click or mouseup after drag or pan + if (event.type === 'click' || event.type === 'mouseup') { + const state = getState(chart) + if (state.filterNextClick) { + state.filterNextClick = false + return false + } + } + }, + + beforeUpdate: function (chart: Chart, _args: unknown, options: ZoomPluginOptions) { + const state = getState(chart) + const previousOptions = state.options + state.options = options + + // Hammer needs to be restarted when certain options change. + if (hammerOptionsChanged(previousOptions, options)) { + stopHammer(chart) + startHammer(chart, options) + } + + addListeners(chart, options) + }, + + beforeDatasetsDraw(chart: Chart, _args: unknown, options: ZoomPluginOptions) { + draw(chart, 'beforeDatasetsDraw', options) + }, + + afterDatasetsDraw(chart: Chart, _args: unknown, options: ZoomPluginOptions) { + draw(chart, 'afterDatasetsDraw', options) + }, + + beforeDraw(chart: Chart, _args: unknown, options: ZoomPluginOptions) { + draw(chart, 'beforeDraw', options) + }, + + afterDraw(chart: Chart, _args: unknown, options: ZoomPluginOptions) { + draw(chart, 'afterDraw', options) + }, + + stop: function (chart: Chart) { + removeListeners(chart) + + if (Hammer) { + stopHammer(chart) + } + removeState(chart) + }, + + panFunctions, + zoomFunctions, + zoomRectFunctions, +} diff --git a/src/scale.types.js b/src/scale.types.js deleted file mode 100644 index c964b58a3..000000000 --- a/src/scale.types.js +++ /dev/null @@ -1,304 +0,0 @@ -import {almostEquals, valueOrDefault} from 'chart.js/helpers'; -import {getState} from './state'; - -/** - * @typedef {import('chart.js').Point} Point - * @typedef {import('chart.js').Scale} Scale - * @typedef {import('../types/options').LimitOptions} LimitOptions - * @typedef {{min: number, max: number}} ScaleRange - * @typedef {import('../types/options').ScaleLimits} ScaleLimits - */ - -/** - * - * @param {number} val - * @param {number} min - * @param {number} range - * @param {number} newRange - * @returns {ScaleRange} - */ -function zoomDelta(val, min, range, newRange) { - const minPercent = Math.max(0, Math.min(1, (val - min) / range || 0)); - const maxPercent = 1 - minPercent; - - return { - min: newRange * minPercent, - max: newRange * maxPercent - }; -} - -/** - * @param {Scale} scale - * @param {Point} point - * @returns number | undefined - */ -function getValueAtPoint(scale, point) { - const pixel = scale.isHorizontal() ? point.x : point.y; - - return scale.getValueForPixel(pixel); -} - -/** - * @param {Scale} scale - * @param {number} zoom - * @param {Point} center - * @returns {ScaleRange} - */ -function linearZoomDelta(scale, zoom, center) { - const range = scale.max - scale.min; - const newRange = range * (zoom - 1); - const centerValue = getValueAtPoint(scale, center); - - return zoomDelta(centerValue, scale.min, range, newRange); -} - -/** - * @param {Scale} scale - * @param {number} zoom - * @param {Point} center - * @returns {ScaleRange} - */ -function logarithmicZoomRange(scale, zoom, center) { - const centerValue = getValueAtPoint(scale, center); - - // Return the original range, if value could not be determined. - if (centerValue === undefined) { - return {min: scale.min, max: scale.max}; - } - - const logMin = Math.log10(scale.min); - const logMax = Math.log10(scale.max); - const logCenter = Math.log10(centerValue); - const logRange = logMax - logMin; - const newLogRange = logRange * (zoom - 1); - const delta = zoomDelta(logCenter, logMin, logRange, newLogRange); - - return { - min: Math.pow(10, logMin + delta.min), - max: Math.pow(10, logMax - delta.max), - }; -} - -/** - * @param {Scale} scale - * @param {LimitOptions|undefined} limits - * @returns {ScaleLimits} - */ -function getScaleLimits(scale, limits) { - return limits && (limits[scale.id] || limits[scale.axis]) || {}; -} - -function getLimit(state, scale, scaleLimits, prop, fallback) { - let limit = scaleLimits[prop]; - if (limit === 'original') { - const original = state.originalScaleLimits[scale.id][prop]; - limit = valueOrDefault(original.options, original.scale); - } - return valueOrDefault(limit, fallback); -} - -/** - * @param {Scale} scale - * @param {number} pixel0 - * @param {number} pixel1 - * @returns {ScaleRange} - */ -function linearRange(scale, pixel0, pixel1) { - const v0 = scale.getValueForPixel(pixel0); - const v1 = scale.getValueForPixel(pixel1); - return { - min: Math.min(v0, v1), - max: Math.max(v0, v1) - }; -} - -/** - * @param {number} range - * @param {{ min: number; max: number; minLimit: number; maxLimit: number; }} options - * @param {{ min: { scale?: number; options?: number; }; max: { scale?: number; options?: number; }}} [originalLimits] - */ -function fixRange(range, {min, max, minLimit, maxLimit}, originalLimits) { - const offset = (range - max + min) / 2; - min -= offset; - max += offset; - - // In case the values are really close to the original values, use the original values. - const origMin = originalLimits.min.options ?? originalLimits.min.scale; - const origMax = originalLimits.max.options ?? originalLimits.max.scale; - - const epsilon = range / 1e6; - if (almostEquals(min, origMin, epsilon)) { - min = origMin; - } - if (almostEquals(max, origMax, epsilon)) { - max = origMax; - } - - // Apply limits - if (min < minLimit) { - min = minLimit; - max = Math.min(minLimit + range, maxLimit); - } else if (max > maxLimit) { - max = maxLimit; - min = Math.max(maxLimit - range, minLimit); - } - - return {min, max}; -} - -/** - * @param {Scale} scale - * @param {ScaleRange} minMax - * @param {LimitOptions} [limits] - * @param {boolean|'pan'} [zoom] - * @returns {boolean} - */ -export function updateRange(scale, {min, max}, limits, zoom = false) { - const state = getState(scale.chart); - const {options: scaleOpts} = scale; - - const scaleLimits = getScaleLimits(scale, limits); - const {minRange = 0} = scaleLimits; - const minLimit = getLimit(state, scale, scaleLimits, 'min', -Infinity); - const maxLimit = getLimit(state, scale, scaleLimits, 'max', Infinity); - - if (zoom === 'pan' && (min < minLimit || max > maxLimit)) { - // At limit: No change but return true to indicate no need to store the delta. - return true; - } - - const scaleRange = scale.max - scale.min; - const range = zoom ? Math.max(max - min, minRange) : scaleRange; - - if (zoom && range === minRange && scaleRange <= minRange) { - // At range limit: No change but return true to indicate no need to store the delta. - return true; - } - - const newRange = fixRange(range, {min, max, minLimit, maxLimit}, state.originalScaleLimits[scale.id]); - - scaleOpts.min = newRange.min; - scaleOpts.max = newRange.max; - - state.updatedScaleLimits[scale.id] = newRange; - - // return true if the scale range is changed - return scale.parse(newRange.min) !== scale.min || scale.parse(newRange.max) !== scale.max; -} - -function zoomNumericalScale(scale, zoom, center, limits) { - const delta = linearZoomDelta(scale, zoom, center); - const newRange = {min: scale.min + delta.min, max: scale.max - delta.max}; - return updateRange(scale, newRange, limits, true); -} - -function zoomLogarithmicScale(scale, zoom, center, limits) { - const newRange = logarithmicZoomRange(scale, zoom, center); - return updateRange(scale, newRange, limits, true); -} - -/** - * @param {Scale} scale - * @param {number} from - * @param {number} to - * @param {LimitOptions} [limits] - */ -function zoomRectNumericalScale(scale, from, to, limits) { - updateRange(scale, linearRange(scale, from, to), limits, true); -} - -const integerChange = (v) => v === 0 || isNaN(v) ? 0 : v < 0 ? Math.min(Math.round(v), -1) : Math.max(Math.round(v), 1); - -function existCategoryFromMaxZoom(scale) { - const labels = scale.getLabels(); - const maxIndex = labels.length - 1; - - if (scale.min > 0) { - scale.min -= 1; - } - if (scale.max < maxIndex) { - scale.max += 1; - } -} - -function zoomCategoryScale(scale, zoom, center, limits) { - const delta = linearZoomDelta(scale, zoom, center); - if (scale.min === scale.max && zoom < 1) { - existCategoryFromMaxZoom(scale); - } - const newRange = {min: scale.min + integerChange(delta.min), max: scale.max - integerChange(delta.max)}; - return updateRange(scale, newRange, limits, true); -} - -function scaleLength(scale) { - return scale.isHorizontal() ? scale.width : scale.height; -} - -function panCategoryScale(scale, delta, limits) { - const labels = scale.getLabels(); - const lastLabelIndex = labels.length - 1; - let {min, max} = scale; - // The visible range. Ticks can be skipped, and thus not reliable. - const range = Math.max(max - min, 1); - // How many pixels of delta is required before making a step. stepSize, but limited to max 1/10 of the scale length. - const stepDelta = Math.round(scaleLength(scale) / Math.max(range, 10)); - const stepSize = Math.round(Math.abs(delta / stepDelta)); - let applied; - if (delta < -stepDelta) { - max = Math.min(max + stepSize, lastLabelIndex); - min = range === 1 ? max : max - range; - applied = max === lastLabelIndex; - } else if (delta > stepDelta) { - min = Math.max(0, min - stepSize); - max = range === 1 ? min : min + range; - applied = min === 0; - } - - return updateRange(scale, {min, max}, limits) || applied; -} - -const OFFSETS = { - second: 500, // 500 ms - minute: 30 * 1000, // 30 s - hour: 30 * 60 * 1000, // 30 m - day: 12 * 60 * 60 * 1000, // 12 h - week: 3.5 * 24 * 60 * 60 * 1000, // 3.5 d - month: 15 * 24 * 60 * 60 * 1000, // 15 d - quarter: 60 * 24 * 60 * 60 * 1000, // 60 d - year: 182 * 24 * 60 * 60 * 1000 // 182 d -}; - -function panNumericalScale(scale, delta, limits, pan = false) { - const {min: prevStart, max: prevEnd, options} = scale; - const round = options.time && options.time.round; - const offset = OFFSETS[round] || 0; - const newMin = scale.getValueForPixel(scale.getPixelForValue(prevStart + offset) - delta); - const newMax = scale.getValueForPixel(scale.getPixelForValue(prevEnd + offset) - delta); - if (isNaN(newMin) || isNaN(newMax)) { - // NaN can happen for 0-dimension scales (either because they were configured - // with min === max or because the chart has 0 plottable area). - return true; - } - return updateRange(scale, {min: newMin, max: newMax}, limits, pan ? 'pan' : false); -} - -function panNonLinearScale(scale, delta, limits) { - return panNumericalScale(scale, delta, limits, true); -} - -export const zoomFunctions = { - category: zoomCategoryScale, - default: zoomNumericalScale, - logarithmic: zoomLogarithmicScale, -}; - -export const zoomRectFunctions = { - default: zoomRectNumericalScale, -}; - -export const panFunctions = { - category: panCategoryScale, - default: panNumericalScale, - logarithmic: panNonLinearScale, - timeseries: panNonLinearScale, -}; diff --git a/src/scale.types.test.ts b/src/scale.types.test.ts new file mode 100644 index 000000000..ec7d5065a --- /dev/null +++ b/src/scale.types.test.ts @@ -0,0 +1,27 @@ +import { zoomDelta } from './scale.types' + +describe('utils', () => { + describe('zoomDelta', () => { + for (const { val, min, range, newRange, expected } of [ + { val: undefined, min: 100, range: 100, newRange: -10, expected: { min: -0, max: -10 } }, + { val: undefined, min: undefined, range: 1, newRange: 1, expected: { min: 0, max: 1 } }, + { val: undefined, min: undefined, range: undefined, newRange: 1, expected: { min: 0, max: 1 } }, + { val: undefined, min: undefined, range: undefined, newRange: undefined, expected: { min: NaN, max: NaN } }, + + { val: 0, min: 0, range: 0, newRange: 1, expected: { min: 0, max: 1 } }, + + { val: 0, min: 0, range: 2, newRange: -1, expected: { min: -0, max: -1 } }, + + { val: 0, min: 0, range: 1, newRange: 2, expected: { min: 0, max: 2 } }, + { val: 1, min: 0, range: 1, newRange: 2, expected: { min: 2, max: 0 } }, + { val: 1, min: 1, range: 1, newRange: 2, expected: { min: 0, max: 2 } }, + + { val: 1, min: 0, range: 2, newRange: 4, expected: { min: 2, max: 2 } }, + ]) { + it(`returns ${expected} for ${val}, ${min}, ${range}, ${newRange},`, () => { + // @ts-expect-error using invalid values + expect(zoomDelta(val, min, range, newRange)).toEqual(expected) + }) + } + }) +}) diff --git a/src/scale.types.ts b/src/scale.types.ts new file mode 100644 index 000000000..0625c99a0 --- /dev/null +++ b/src/scale.types.ts @@ -0,0 +1,285 @@ +import { almostEquals, isNullOrUndef, isNumber, valueOrDefault } from 'chart.js/helpers' +import { getState, type ScaleRange, type State } from './state' +import type { Point, Scale, TimeScale, TimeUnit } from 'chart.js' +import type { LimitOptions, ScaleLimits } from './options' + +export type ZoomFunction = (scale: Scale, zoom: number, center: Point, limits: LimitOptions) => boolean +export type ZoomRectFunction = (scale: Scale, from: number, to: number, limits: LimitOptions) => boolean +export type PanFunction = (scale: Scale, delta: number, limits: LimitOptions) => boolean + +const isTimeScale = (scale: Scale): scale is TimeScale => scale.type === 'time' + +const isNotNumber = (value?: number): value is undefined => value === undefined || isNaN(value) + +export function zoomDelta( + val: number | undefined, + min: number | undefined, + range: number, + newRange: number +): ScaleRange { + const minPercent = range && isNumber(val) && isNumber(min) ? Math.max(0, Math.min(1, (val - min) / range)) : 0 + const maxPercent = 1 - minPercent + + return { + min: newRange * minPercent, + max: newRange * maxPercent, + } +} + +function getValueAtPoint(scale: Scale, point: Point): number | undefined { + const pixel = scale.isHorizontal() ? point.x : point.y + + return scale.getValueForPixel(pixel) +} + +function linearZoomDelta(scale: Scale, zoom: number, center: Point): ScaleRange { + const range = scale.max - scale.min + const newRange = range * (zoom - 1) + const centerValue = getValueAtPoint(scale, center) + + return zoomDelta(centerValue, scale.min, range, newRange) +} + +function logarithmicZoomRange(scale: Scale, zoom: number, center: Point) { + const centerValue = getValueAtPoint(scale, center) + + // Return the original range, if value could not be determined. + if (centerValue === undefined) { + return { min: scale.min, max: scale.max } + } + + const logMin = Math.log10(scale.min) + const logMax = Math.log10(scale.max) + const logCenter = Math.log10(centerValue) + const logRange = logMax - logMin + const newLogRange = logRange * (zoom - 1) + const delta = zoomDelta(logCenter, logMin, logRange, newLogRange) + + return { + min: Math.pow(10, logMin + delta.min), + max: Math.pow(10, logMax - delta.max), + } +} + +function getScaleLimits(scale: Scale, limits?: LimitOptions): ScaleLimits { + return limits?.[scale.id] || limits?.[scale.axis] || {} +} + +function getLimit(state: State, scale: Scale, scaleLimits: ScaleLimits, prop: 'min' | 'max', fallback: number): number { + let limit = scaleLimits[prop] + if (limit === 'original') { + const original = state.originalScaleLimits[scale.id][prop] + if (isNumber(original.options)) { + return original.options + } + + if (!isNullOrUndef(original.options)) { + const parsed = scale.parse(original.options) + if (isNumber(parsed)) { + return parsed + } + } + + limit = original.scale + } + return valueOrDefault(limit, fallback) +} + +function linearRange(scale: Scale, pixel0: number, pixel1: number): ScaleRange { + const v0 = scale.getValueForPixel(pixel0) ?? scale.min + const v1 = scale.getValueForPixel(pixel1) ?? scale.max + return { + min: Math.min(v0, v1), + max: Math.max(v0, v1), + } +} + +function fixRange( + range: number, + { min, max, minLimit, maxLimit }: { min: number; max: number; minLimit: number; maxLimit: number }, + state: State, + scale: Scale +) { + const offset = (range - max + min) / 2 + min -= offset + max += offset + + // In case the values are really close to the original values, use the original values. + const origLimits: ScaleLimits = { min: 'original', max: 'original' } + const origMin = getLimit(state, scale, origLimits, 'min', Number.NEGATIVE_INFINITY) + const origMax = getLimit(state, scale, origLimits, 'max', Number.POSITIVE_INFINITY) + + const epsilon = range / 1e6 + if (almostEquals(min, origMin, epsilon)) { + min = origMin + } + if (almostEquals(max, origMax, epsilon)) { + max = origMax + } + + // Apply limits + if (min < minLimit) { + min = minLimit + max = Math.min(minLimit + range, maxLimit) + } else if (max > maxLimit) { + max = maxLimit + min = Math.max(maxLimit - range, minLimit) + } + + return { min, max } +} + +export function updateRange( + scale: Scale, + { min, max }: ScaleRange, + limits?: LimitOptions, + zoom: boolean | 'pan' = false +): boolean { + const state = getState(scale.chart) + const { options: scaleOpts } = scale + + const scaleLimits = getScaleLimits(scale, limits) + const { minRange = 0 } = scaleLimits + const minLimit = getLimit(state, scale, scaleLimits, 'min', -Infinity) + const maxLimit = getLimit(state, scale, scaleLimits, 'max', Infinity) + + if (zoom === 'pan' && (min < minLimit || max > maxLimit)) { + // At limit: No change but return true to indicate no need to store the delta. + return true + } + + const scaleRange = scale.max - scale.min + const range = zoom ? Math.max(max - min, minRange) : scaleRange + + if (zoom && range === minRange && scaleRange <= minRange) { + // At range limit: No change but return true to indicate no need to store the delta. + return true + } + + const newRange = fixRange(range, { min, max, minLimit, maxLimit }, state, scale) + + scaleOpts.min = newRange.min + scaleOpts.max = newRange.max + + state.updatedScaleLimits[scale.id] = newRange + + // return true if the scale range is changed + return scale.parse(newRange.min) !== scale.min || scale.parse(newRange.max) !== scale.max +} + +function zoomNumericalScale(scale: Scale, zoom: number, center: Point, limits: LimitOptions) { + const delta = linearZoomDelta(scale, zoom, center) + const newRange = { min: scale.min + delta.min, max: scale.max - delta.max } + return updateRange(scale, newRange, limits, true) +} + +function zoomLogarithmicScale(scale: Scale, zoom: number, center: Point, limits: LimitOptions) { + const newRange = logarithmicZoomRange(scale, zoom, center) + return updateRange(scale, newRange, limits, true) +} + +function zoomRectNumericalScale(scale: Scale, from: number, to: number, limits: LimitOptions) { + return updateRange(scale, linearRange(scale, from, to), limits, true) +} + +const integerChange = (v: number) => + v === 0 || isNaN(v) ? 0 : v < 0 ? Math.min(Math.round(v), -1) : Math.max(Math.round(v), 1) + +function existCategoryFromMaxZoom(scale: Scale) { + const labels = scale.getLabels() + const maxIndex = labels.length - 1 + + if (scale.min > 0) { + scale.min -= 1 + } + if (scale.max < maxIndex) { + scale.max += 1 + } +} + +function zoomCategoryScale(scale: Scale, zoom: number, center: Point, limits: LimitOptions) { + const delta = linearZoomDelta(scale, zoom, center) + if (scale.min === scale.max && zoom < 1) { + existCategoryFromMaxZoom(scale) + } + const newRange = { min: scale.min + integerChange(delta.min), max: scale.max - integerChange(delta.max) } + + return updateRange(scale, newRange, limits, true) +} + +function scaleLength(scale: Scale) { + return scale.isHorizontal() ? scale.width : scale.height +} + +function panCategoryScale(scale: Scale, delta: number, limits: LimitOptions) { + const labels = scale.getLabels() + const lastLabelIndex = labels.length - 1 + let { min, max } = scale + // The visible range. Ticks can be skipped, and thus not reliable. + const range = Math.max(max - min, 1) + // How many pixels of delta is required before making a step. stepSize, but limited to max 1/10 of the scale length. + const stepDelta = Math.round(scaleLength(scale) / Math.max(range, 10)) + const stepSize = Math.round(Math.abs(delta / stepDelta)) + let applied + if (delta < -stepDelta) { + max = Math.min(max + stepSize, lastLabelIndex) + min = range === 1 ? max : max - range + applied = max === lastLabelIndex + } else if (delta > stepDelta) { + min = Math.max(0, min - stepSize) + max = range === 1 ? min : min + range + applied = min === 0 + } + + return updateRange(scale, { min, max }, limits) || Boolean(applied) +} + +const OFFSETS: Record = { + millisecond: 0, + second: 500, // 500 ms + minute: 30 * 1000, // 30 s + hour: 30 * 60 * 1000, // 30 m + day: 12 * 60 * 60 * 1000, // 12 h + week: 3.5 * 24 * 60 * 60 * 1000, // 3.5 d + month: 15 * 24 * 60 * 60 * 1000, // 15 d + quarter: 60 * 24 * 60 * 60 * 1000, // 60 d + year: 182 * 24 * 60 * 60 * 1000, // 182 d +} + +function panNumericalScale(scale: Scale, delta: number, limits: LimitOptions, pan = false) { + const { min: prevStart, max: prevEnd } = scale + let offset = 0 + if (isTimeScale(scale)) { + const round = scale.options.time?.round + offset = round ? OFFSETS[round] : 0 + } + const newMin = scale.getValueForPixel(scale.getPixelForValue(prevStart + offset) - delta) + const newMax = scale.getValueForPixel(scale.getPixelForValue(prevEnd + offset) - delta) + if (isNotNumber(newMin) || isNotNumber(newMax)) { + // NaN can happen for 0-dimension scales (either because they were configured + // with min === max or because the chart has 0 plottable area). + return true + } + return updateRange(scale, { min: newMin, max: newMax }, limits, pan ? 'pan' : false) +} + +function panNonLinearScale(scale: Scale, delta: number, limits: LimitOptions) { + return panNumericalScale(scale, delta, limits, true) +} + +export const zoomFunctions: Record = { + category: zoomCategoryScale, + default: zoomNumericalScale, + logarithmic: zoomLogarithmicScale, +} + +export const zoomRectFunctions: Record = { + default: zoomRectNumericalScale, +} + +export const panFunctions: Record = { + category: panCategoryScale, + default: panNumericalScale, + logarithmic: panNonLinearScale, + timeseries: panNonLinearScale, +} diff --git a/src/state.js b/src/state.js deleted file mode 100644 index 0b8e0d027..000000000 --- a/src/state.js +++ /dev/null @@ -1,33 +0,0 @@ -/** - * @typedef {import("chart.js").Chart} Chart - * @typedef {{originalScaleLimits: any; updatedScaleLimits: any; handlers: any; panDelta: any; dragging: boolean; panning: boolean; options?: import("../types/options").ZoomPluginOptions, dragStart?: any, dragEnd?: any, filterNextClick?: boolean}} ZoomPluginState - */ - -/** - * @type WeakMap - */ -const chartStates = new WeakMap(); - -/** - * @param {import("chart.js").Chart} chart - * @returns {ZoomPluginState} - */ -export function getState(chart) { - let state = chartStates.get(chart); - if (!state) { - state = { - originalScaleLimits: {}, - updatedScaleLimits: {}, - handlers: {}, - panDelta: {}, - dragging: false, - panning: false - }; - chartStates.set(chart, state); - } - return state; -} - -export function removeState(chart) { - chartStates.delete(chart); -} diff --git a/src/state.ts b/src/state.ts new file mode 100644 index 000000000..94ea535d9 --- /dev/null +++ b/src/state.ts @@ -0,0 +1,64 @@ +import { Chart, Scale, type Point } from 'chart.js' +import type { ZoomPluginOptions } from './options' + +export type ScaleRange = { min: number; max: number } +export type OriginalLimits = { min: { scale?: number; options?: unknown }; max: { scale?: number; options?: unknown } } +export type OriginalScaleLimits = Record +export type UpdatedScaleLimits = Record + +export type HandlerFunctions = { + click: (chart: Chart, event: MouseEvent, options: ZoomPluginOptions) => void + keydown: (chart: Chart, event: KeyboardEvent) => void + mousedown: (chart: Chart, event: MouseEvent, options: ZoomPluginOptions) => void + mousemove: (chart: Chart, event: MouseEvent, options: ZoomPluginOptions) => void + mouseup: (chart: Chart, event: MouseEvent, options: ZoomPluginOptions) => void + onZoomComplete: ({ chart }: { chart: Chart }) => void + wheel: (chart: Chart, event: WheelEvent) => void +} +export type HandlerName = keyof HandlerFunctions +export type HandlerFunction = HandlerFunctions[HandlerName] +export type Handler = EventListener +export type Handlers = Partial> + +export type HandlerTarget = Partial> + +export interface State { + originalScaleLimits: OriginalScaleLimits + updatedScaleLimits: UpdatedScaleLimits + handlers: Handlers + targets: HandlerTarget + panDelta: Record + dragging: boolean + panning: boolean + options: ZoomPluginOptions + dragStart?: MouseEvent + dragEnd?: MouseEvent + filterNextClick?: boolean + scale?: number | null + delta?: Point | null + panScales?: Scale[] +} + +const chartStates = new WeakMap() + +export function getState(chart: Chart): State { + let state = chartStates.get(chart) + if (!state) { + state = { + originalScaleLimits: {}, + updatedScaleLimits: {}, + handlers: {}, + options: {}, + targets: {}, + panDelta: {}, + dragging: false, + panning: false, + } + chartStates.set(chart, state) + } + return state +} + +export function removeState(chart: Chart) { + chartStates.delete(chart) +} diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 000000000..235be5c3c --- /dev/null +++ b/src/types.ts @@ -0,0 +1,6 @@ +import type { Point } from 'chart.js' + +export type ZoomAmount = number | (Partial & { focalPoint?: Point }) +export type PanAmount = number | Partial +export type ScaleRange = { min: number; max: number } +export type DistributiveArray = [T] extends [unknown] ? Array : never diff --git a/src/utils.js b/src/utils.js deleted file mode 100644 index 3d16880c5..000000000 --- a/src/utils.js +++ /dev/null @@ -1,107 +0,0 @@ -import {each} from 'chart.js/helpers'; - -export const getModifierKey = opts => opts && opts.enabled && opts.modifierKey; -export const keyPressed = (key, event) => key && event[key + 'Key']; -export const keyNotPressed = (key, event) => key && !event[key + 'Key']; - -/** - * @param {string|function} mode can be 'x', 'y' or 'xy' - * @param {string} dir can be 'x' or 'y' - * @param {import('chart.js').Chart} chart instance of the chart in question - * @returns {boolean} - */ -export function directionEnabled(mode, dir, chart) { - if (mode === undefined) { - return true; - } else if (typeof mode === 'string') { - return mode.indexOf(dir) !== -1; - } else if (typeof mode === 'function') { - return mode({chart}).indexOf(dir) !== -1; - } - - return false; -} - -function directionsEnabled(mode, chart) { - if (typeof mode === 'function') { - mode = mode({chart}); - } - if (typeof mode === 'string') { - return {x: mode.indexOf('x') !== -1, y: mode.indexOf('y') !== -1}; - } - - return {x: false, y: false}; -} - -/** - * Debounces calling `fn` for `delay` ms - * @param {function} fn - Function to call. No arguments are passed. - * @param {number} delay - Delay in ms. 0 = immediate invocation. - * @returns {function} - */ -export function debounce(fn, delay) { - let timeout; - return function() { - clearTimeout(timeout); - timeout = setTimeout(fn, delay); - return delay; - }; -} - -/** - * Checks which axis is under the mouse cursor. - * @param {{x: number, y: number}} point - the mouse location - * @param {import('chart.js').Chart} [chart] instance of the chart in question - * @return {import('chart.js').Scale} - */ -function getScaleUnderPoint({x, y}, chart) { - const scales = chart.scales; - const scaleIds = Object.keys(scales); - for (let i = 0; i < scaleIds.length; i++) { - const scale = scales[scaleIds[i]]; - if (y >= scale.top && y <= scale.bottom && x >= scale.left && x <= scale.right) { - return scale; - } - } - return null; -} - -/** - * Evaluate the chart's mode, scaleMode, and overScaleMode properties to - * determine which axes are eligible for scaling. - * options.overScaleMode can be a function if user want zoom only one scale of many for example. - * @param options - Zoom or pan options - * @param {{x: number, y: number}} point - the mouse location - * @param {import('chart.js').Chart} [chart] instance of the chart in question - * @return {import('chart.js').Scale[]} - */ -export function getEnabledScalesByPoint(options, point, chart) { - const {mode = 'xy', scaleMode, overScaleMode} = options || {}; - const scale = getScaleUnderPoint(point, chart); - - const enabled = directionsEnabled(mode, chart); - const scaleEnabled = directionsEnabled(scaleMode, chart); - - // Convert deprecated overScaleEnabled to new scaleEnabled. - if (overScaleMode) { - const overScaleEnabled = directionsEnabled(overScaleMode, chart); - for (const axis of ['x', 'y']) { - if (overScaleEnabled[axis]) { - scaleEnabled[axis] = enabled[axis]; - enabled[axis] = false; - } - } - } - - if (scale && scaleEnabled[scale.axis]) { - return [scale]; - } - - const enabledScales = []; - each(chart.scales, function(scaleItem) { - if (enabled[scaleItem.axis]) { - enabledScales.push(scaleItem); - } - }); - return enabledScales; -} diff --git a/src/utils.test.ts b/src/utils.test.ts new file mode 100644 index 000000000..9f855e4f6 --- /dev/null +++ b/src/utils.test.ts @@ -0,0 +1,29 @@ +import type { Chart } from 'chart.js' +import type { ModeOption } from './options.js' +import { directionEnabled } from './utils.js' + +describe('utils', () => { + describe('directionEnabled', () => { + const chart = {} as Chart + const testCases: Array<{ mode: ModeOption | undefined; dir: 'x' | 'y'; expected: boolean }> = [ + { mode: 'xy', dir: 'x', expected: true }, + { mode: 'x', dir: 'x', expected: true }, + { mode: 'y', dir: 'x', expected: false }, + + { mode: 'xy', dir: 'y', expected: true }, + { mode: 'x', dir: 'y', expected: false }, + { mode: 'y', dir: 'y', expected: true }, + ] + + for (const { mode, dir, expected } of testCases) { + it(`returns ${expected} when mode is ${mode} and required direction is ${dir}`, () => { + expect(directionEnabled(mode, dir, chart)).toEqual(expected) + + const modeFn = jasmine.createSpy('mode').and.returnValue(mode) + + expect(directionEnabled(modeFn, dir, chart)).toEqual(expected) + expect(modeFn).toHaveBeenCalledWith({ chart }) + }) + } + }) +}) diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 000000000..9a7c576d9 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,94 @@ +import type { Chart, Point, Scale } from 'chart.js' +import { each } from 'chart.js/helpers' +import type { DragOptions, ModeOption, ModifierKey, PanOptions } from './options' + +const eventKey = (key: ModifierKey): 'altKey' | 'ctrlKey' | 'metaKey' | 'shiftKey' => `${key}Key` + +export const getModifierKey = (opts?: DragOptions | PanOptions): ModifierKey | undefined => + opts?.enabled && opts.modifierKey ? opts.modifierKey : undefined +export const keyPressed = (key: ModifierKey | undefined, event: TouchEvent | MouseEvent | PointerEvent) => + key && event[eventKey(key)] +export const keyNotPressed = (key: ModifierKey | undefined, event: TouchEvent | MouseEvent | PointerEvent) => + key && !event[eventKey(key)] + +export function directionEnabled(mode: ModeOption | undefined, dir: 'x' | 'y', chart: Chart): boolean { + if (mode === undefined) { + return true + } else if (typeof mode === 'string') { + return mode.indexOf(dir) !== -1 + } else if (typeof mode === 'function') { + return mode({ chart }).indexOf(dir) !== -1 + } + + return false +} + +function directionsEnabled(mode: ModeOption | undefined, chart: Chart) { + if (typeof mode === 'function') { + mode = mode({ chart }) + } + if (typeof mode === 'string') { + return { x: mode.indexOf('x') !== -1, y: mode.indexOf('y') !== -1 } + } + + return { x: false, y: false } +} + +export function debounce(fn: () => void, delay: number | undefined) { + let timeout: number | NodeJS.Timeout + return function () { + clearTimeout(timeout) + timeout = setTimeout(fn, delay) + return delay + } +} + +function getScaleUnderPoint({ x, y }: Point, chart: Chart): Scale | null { + const scales = chart.scales + const scaleIds = Object.keys(scales) + for (let i = 0; i < scaleIds.length; i++) { + const scale = scales[scaleIds[i]] + if (y >= scale.top && y <= scale.bottom && x >= scale.left && x <= scale.right) { + return scale + } + } + return null +} + +/** + * Evaluate the chart's mode, scaleMode, and overScaleMode properties to + * determine which axes are eligible for scaling. + * options.overScaleMode can be a function if user want zoom only one scale of many for example. + */ +export function getEnabledScalesByPoint(options: PanOptions | undefined, point: Point, chart: Chart): Scale[] { + const { mode = 'xy', scaleMode, overScaleMode } = options || {} + const scale = getScaleUnderPoint(point, chart) + + const enabled = directionsEnabled(mode, chart) + const scaleEnabled = directionsEnabled(scaleMode, chart) + + // Convert deprecated overScaleEnabled to new scaleEnabled. + if (overScaleMode) { + const overScaleEnabled = directionsEnabled(overScaleMode, chart) + for (const axis of ['x', 'y'] as const) { + if (overScaleEnabled[axis]) { + scaleEnabled[axis] = enabled[axis] + enabled[axis] = false + } + } + } + + if (scale && scaleEnabled[scale.axis as 'x' | 'y']) { + return [scale] + } + + const enabledScales: Scale[] = [] + + each(chart.scales, function (scaleItem) { + if (enabled[scaleItem.axis as 'x' | 'y']) { + enabledScales.push(scaleItem) + } + }) + + return enabledScales || Object.values(chart.scales) +} diff --git a/test/fixtures/pan/category-x-1.js b/test/fixtures/pan/category-x-1.js index d4df0fd26..5680ce1d5 100644 --- a/test/fixtures/pan/category-x-1.js +++ b/test/fixtures/pan/category-x-1.js @@ -1,14 +1,14 @@ -const labels = []; -const data = []; +const labels = [] +const data = [] for (let i = 1; i <= 100; i++) { - labels.push('Label ' + i); - data.push(Math.sin(i / 100 * Math.PI) * 10); + labels.push('Label ' + i) + data.push(Math.sin((i / 100) * Math.PI) * 10) } -const canvas = document.createElement('canvas'); -canvas.width = 512; -canvas.height = 512; -const ctx = canvas.getContext('2d'); +const canvas = document.createElement('canvas') +canvas.width = 512 +canvas.height = 512 +const ctx = canvas.getContext('2d') module.exports = { tolerance: 0.02, @@ -16,12 +16,14 @@ module.exports = { type: 'bar', data: { labels, - datasets: [{ - data, - barPercentage: 1, - categoryPercentage: 1, - backgroundColor: c => `rgba(${255 - c.index * 4}, 0, 0, 1)` - }] + datasets: [ + { + data, + barPercentage: 1, + categoryPercentage: 1, + backgroundColor: (c) => `rgba(${255 - c.index * 4}, 0, 0, 1)`, + }, + ], }, options: { events: [], @@ -29,9 +31,9 @@ module.exports = { x: { display: false, min: 'Label 1', - max: 'Label 1' + max: 'Label 1', }, - y: {display: false, max: 10} + y: { display: false, max: 10 }, }, plugins: { legend: false, @@ -40,29 +42,29 @@ module.exports = { pan: { enabled: true, mode: 'x', - } - } + }, + }, }, layout: { - padding: 2 - } - } + padding: 2, + }, + }, }, options: { spriteText: true, run(chart) { - const steps = 16; - const n = Math.sqrt(steps); - const side = 512 / n; + const steps = 16 + const n = Math.sqrt(steps) + const side = 512 / n for (let i = 0; i < steps; i++) { - const col = i % n; - const row = Math.floor(i / n); - chart.pan({x: -100}); - chart.update(); - ctx.drawImage(chart.canvas, col * side, row * side, side, side); + const col = i % n + const row = Math.floor(i / n) + chart.pan({ x: -100 }) + chart.update() + ctx.drawImage(chart.canvas, col * side, row * side, side, side) } - Chart.helpers.clearCanvas(chart.canvas); - chart.ctx.drawImage(canvas, 0, 0); - } - } -}; + Chart.helpers.clearCanvas(chart.canvas) + chart.ctx.drawImage(canvas, 0, 0) + }, + }, +} diff --git a/test/fixtures/pan/category-x-10.js b/test/fixtures/pan/category-x-10.js index b9fa3a1b2..92ad0b86d 100644 --- a/test/fixtures/pan/category-x-10.js +++ b/test/fixtures/pan/category-x-10.js @@ -1,14 +1,14 @@ -const labels = []; -const data = []; +const labels = [] +const data = [] for (let i = 1; i <= 100; i++) { - labels.push('Label ' + i); - data.push(Math.sin(i / 100 * Math.PI) * 10); + labels.push('Label ' + i) + data.push(Math.sin((i / 100) * Math.PI) * 10) } -const canvas = document.createElement('canvas'); -canvas.width = 512; -canvas.height = 512; -const ctx = canvas.getContext('2d'); +const canvas = document.createElement('canvas') +canvas.width = 512 +canvas.height = 512 +const ctx = canvas.getContext('2d') module.exports = { tolerance: 0.02, @@ -16,12 +16,14 @@ module.exports = { type: 'bar', data: { labels, - datasets: [{ - data, - barPercentage: 1, - categoryPercentage: 1, - backgroundColor: c => c.index < 50 ? 'blue' : 'red' - }] + datasets: [ + { + data, + barPercentage: 1, + categoryPercentage: 1, + backgroundColor: (c) => (c.index < 50 ? 'blue' : 'red'), + }, + ], }, options: { events: [], @@ -31,7 +33,7 @@ module.exports = { min: 'Label 1', max: 'Label 10', }, - y: {display: false, max: 10} + y: { display: false, max: 10 }, }, plugins: { legend: false, @@ -40,29 +42,29 @@ module.exports = { pan: { enabled: true, mode: 'x', - } - } + }, + }, }, layout: { - padding: 2 - } - } + padding: 2, + }, + }, }, options: { spriteText: true, run(chart) { - const steps = 16; - const n = Math.sqrt(steps); - const side = 512 / n; + const steps = 16 + const n = Math.sqrt(steps) + const side = 512 / n for (let i = 0; i < steps; i++) { - const col = i % n; - const row = Math.floor(i / n); - chart.pan({x: -200}); - chart.update(); - ctx.drawImage(chart.canvas, col * side, row * side, side, side); + const col = i % n + const row = Math.floor(i / n) + chart.pan({ x: -200 }) + chart.update() + ctx.drawImage(chart.canvas, col * side, row * side, side, side) } - Chart.helpers.clearCanvas(chart.canvas); - chart.ctx.drawImage(canvas, 0, 0); - } - } -}; + Chart.helpers.clearCanvas(chart.canvas) + chart.ctx.drawImage(canvas, 0, 0) + }, + }, +} diff --git a/test/fixtures/pan/category-x-25.js b/test/fixtures/pan/category-x-25.js index 1a74b2b18..b8c9f9c21 100644 --- a/test/fixtures/pan/category-x-25.js +++ b/test/fixtures/pan/category-x-25.js @@ -1,14 +1,14 @@ -const labels = []; -const data = []; +const labels = [] +const data = [] for (let i = 1; i <= 100; i++) { - labels.push('Label ' + i); - data.push(Math.sin(i / 100 * Math.PI) * 10); + labels.push('Label ' + i) + data.push(Math.sin((i / 100) * Math.PI) * 10) } -const canvas = document.createElement('canvas'); -canvas.width = 512; -canvas.height = 512; -const ctx = canvas.getContext('2d'); +const canvas = document.createElement('canvas') +canvas.width = 512 +canvas.height = 512 +const ctx = canvas.getContext('2d') module.exports = { tolerance: 0.02, @@ -16,12 +16,14 @@ module.exports = { type: 'bar', data: { labels, - datasets: [{ - data, - barPercentage: 1, - categoryPercentage: 1, - backgroundColor: c => c.index < 50 ? 'blue' : 'red' - }] + datasets: [ + { + data, + barPercentage: 1, + categoryPercentage: 1, + backgroundColor: (c) => (c.index < 50 ? 'blue' : 'red'), + }, + ], }, options: { events: [], @@ -31,7 +33,7 @@ module.exports = { min: 'Label 75', max: 'Label 100', }, - y: {display: false, max: 10} + y: { display: false, max: 10 }, }, plugins: { legend: false, @@ -40,29 +42,29 @@ module.exports = { pan: { enabled: true, mode: 'x', - } - } + }, + }, }, layout: { - padding: 2 - } - } + padding: 2, + }, + }, }, options: { spriteText: true, run(chart) { - const steps = 16; - const n = Math.sqrt(steps); - const side = 512 / n; + const steps = 16 + const n = Math.sqrt(steps) + const side = 512 / n for (let i = 0; i < steps; i++) { - const col = i % n; - const row = Math.floor(i / n); - chart.pan({x: 50}); - chart.update(); - ctx.drawImage(chart.canvas, col * side, row * side, side, side); + const col = i % n + const row = Math.floor(i / n) + chart.pan({ x: 50 }) + chart.update() + ctx.drawImage(chart.canvas, col * side, row * side, side, side) } - Chart.helpers.clearCanvas(chart.canvas); - chart.ctx.drawImage(canvas, 0, 0); - } - } -}; + Chart.helpers.clearCanvas(chart.canvas) + chart.ctx.drawImage(canvas, 0, 0) + }, + }, +} diff --git a/test/fixtures/pan/category-x-5.js b/test/fixtures/pan/category-x-5.js index 19210de02..01da44613 100644 --- a/test/fixtures/pan/category-x-5.js +++ b/test/fixtures/pan/category-x-5.js @@ -1,14 +1,14 @@ -const labels = []; -const data = []; +const labels = [] +const data = [] for (let i = 1; i <= 100; i++) { - labels.push('Label ' + i); - data.push(Math.sin(i / 100 * Math.PI) * 10); + labels.push('Label ' + i) + data.push(Math.sin((i / 100) * Math.PI) * 10) } -const canvas = document.createElement('canvas'); -canvas.width = 512; -canvas.height = 512; -const ctx = canvas.getContext('2d'); +const canvas = document.createElement('canvas') +canvas.width = 512 +canvas.height = 512 +const ctx = canvas.getContext('2d') module.exports = { tolerance: 0.02, @@ -16,12 +16,14 @@ module.exports = { type: 'bar', data: { labels, - datasets: [{ - data, - barPercentage: 1, - categoryPercentage: 1, - backgroundColor: c => c.index < 50 ? 'blue' : 'red' - }] + datasets: [ + { + data, + barPercentage: 1, + categoryPercentage: 1, + backgroundColor: (c) => (c.index < 50 ? 'blue' : 'red'), + }, + ], }, options: { events: [], @@ -29,9 +31,9 @@ module.exports = { x: { display: false, min: 'Label 1', - max: 'Label 5' + max: 'Label 5', }, - y: {display: false, max: 10} + y: { display: false, max: 10 }, }, plugins: { legend: false, @@ -40,38 +42,38 @@ module.exports = { pan: { enabled: true, mode: 'x', - } - } + }, + }, }, layout: { - padding: 2 - } - } + padding: 2, + }, + }, }, options: { spriteText: true, run(chart) { - const steps = 16; - const n = Math.sqrt(steps); - const side = 512 / n; + const steps = 16 + const n = Math.sqrt(steps) + const side = 512 / n return new Promise((resolve) => { - let i = 0; + let i = 0 const next = () => { if (i < steps) { - const col = i % n; - const row = Math.floor(i / n); - i++; + const col = i % n + const row = Math.floor(i / n) + i++ - ctx.drawImage(chart.canvas, col * side, row * side, side, side); - Simulator.gestures.pan(chart.canvas, {deltaX: -350, deltaY: 0, duration: 50}, next); + ctx.drawImage(chart.canvas, col * side, row * side, side, side) + Simulator.gestures.pan(chart.canvas, { deltaX: -350, deltaY: 0, duration: 50 }, next) } else { - Chart.helpers.clearCanvas(chart.canvas); - chart.ctx.drawImage(canvas, 0, 0); - resolve(); + Chart.helpers.clearCanvas(chart.canvas) + chart.ctx.drawImage(canvas, 0, 0) + resolve() } - }; - next(); - }); - } - } -}; + } + next() + }) + }, + }, +} diff --git a/test/fixtures/pan/category-x-50.js b/test/fixtures/pan/category-x-50.js index 48bf941a8..42a995cfe 100644 --- a/test/fixtures/pan/category-x-50.js +++ b/test/fixtures/pan/category-x-50.js @@ -1,14 +1,14 @@ -const labels = []; -const data = []; +const labels = [] +const data = [] for (let i = 1; i <= 100; i++) { - labels.push('Label ' + i); - data.push(Math.sin(i / 100 * Math.PI) * 10); + labels.push('Label ' + i) + data.push(Math.sin((i / 100) * Math.PI) * 10) } -const canvas = document.createElement('canvas'); -canvas.width = 512; -canvas.height = 512; -const ctx = canvas.getContext('2d'); +const canvas = document.createElement('canvas') +canvas.width = 512 +canvas.height = 512 +const ctx = canvas.getContext('2d') module.exports = { tolerance: 0.02, @@ -16,12 +16,14 @@ module.exports = { type: 'bar', data: { labels, - datasets: [{ - data, - barPercentage: 1, - categoryPercentage: 1, - backgroundColor: c => c.index < 50 ? 'blue' : 'red' - }] + datasets: [ + { + data, + barPercentage: 1, + categoryPercentage: 1, + backgroundColor: (c) => (c.index < 50 ? 'blue' : 'red'), + }, + ], }, options: { events: [], @@ -31,7 +33,7 @@ module.exports = { min: 'Label 50', max: 'Label 100', }, - y: {display: false, max: 10} + y: { display: false, max: 10 }, }, plugins: { legend: false, @@ -40,28 +42,28 @@ module.exports = { pan: { enabled: true, mode: 'x', - } - } + }, + }, }, layout: { - padding: 2 - } - } + padding: 2, + }, + }, }, options: { run(chart) { - const steps = 16; - const n = Math.sqrt(steps); - const side = 512 / n; + const steps = 16 + const n = Math.sqrt(steps) + const side = 512 / n for (let i = 0; i < steps; i++) { - const col = i % n; - const row = Math.floor(i / n); - chart.pan({x: 25}); - chart.update(); - ctx.drawImage(chart.canvas, col * side, row * side, side, side); + const col = i % n + const row = Math.floor(i / n) + chart.pan({ x: 25 }) + chart.update() + ctx.drawImage(chart.canvas, col * side, row * side, side, side) } - Chart.helpers.clearCanvas(chart.canvas); - chart.ctx.drawImage(canvas, 0, 0); - } - } -}; + Chart.helpers.clearCanvas(chart.canvas) + chart.ctx.drawImage(canvas, 0, 0) + }, + }, +} diff --git a/test/fixtures/pan/time-day-left.js b/test/fixtures/pan/time-day-left.js index d39468e88..4b3d1fd00 100644 --- a/test/fixtures/pan/time-day-left.js +++ b/test/fixtures/pan/time-day-left.js @@ -1,7 +1,7 @@ -const canvas = document.createElement('canvas'); -canvas.width = 512; -canvas.height = 512; -const ctx = canvas.getContext('2d'); +const canvas = document.createElement('canvas') +canvas.width = 512 +canvas.height = 512 +const ctx = canvas.getContext('2d') module.exports = { config: { @@ -11,9 +11,9 @@ module.exports = { datasets: [ { backgroundColor: ['red', 'green', 'blue', 'orange'], - data: [1, 2, 3, 4] - } - ] + data: [1, 2, 3, 4], + }, + ], }, options: { animation: false, @@ -21,7 +21,7 @@ module.exports = { scales: { y: { display: false, - max: 5 + max: 5, }, x: { type: 'time', @@ -30,8 +30,8 @@ module.exports = { time: { unit: 'day', round: 'day', - } - } + }, + }, }, plugins: { legend: false, @@ -39,28 +39,28 @@ module.exports = { pan: { enabled: true, mode: 'x', - } - } - } - } + }, + }, + }, + }, }, options: { spriteText: true, run(chart) { - const steps = 4; - const n = Math.sqrt(steps); - const side = 512 / n; + const steps = 4 + const n = Math.sqrt(steps) + const side = 512 / n for (let i = 0; i < steps; i++) { - const col = i % n; - const row = Math.floor(i / n); + const col = i % n + const row = Math.floor(i / n) if (i > 0) { - chart.pan({x: 150}); - chart.update(); + chart.pan({ x: 150 }) + chart.update() } - ctx.drawImage(chart.canvas, col * side, row * side, side, side); + ctx.drawImage(chart.canvas, col * side, row * side, side, side) } - Chart.helpers.clearCanvas(chart.canvas); - chart.ctx.drawImage(canvas, 0, 0); - } - } -}; + Chart.helpers.clearCanvas(chart.canvas) + chart.ctx.drawImage(canvas, 0, 0) + }, + }, +} diff --git a/test/fixtures/pan/time-day-right.js b/test/fixtures/pan/time-day-right.js index 6ba120195..d63d35316 100644 --- a/test/fixtures/pan/time-day-right.js +++ b/test/fixtures/pan/time-day-right.js @@ -1,7 +1,7 @@ -const canvas = document.createElement('canvas'); -canvas.width = 512; -canvas.height = 512; -const ctx = canvas.getContext('2d'); +const canvas = document.createElement('canvas') +canvas.width = 512 +canvas.height = 512 +const ctx = canvas.getContext('2d') module.exports = { config: { @@ -11,9 +11,9 @@ module.exports = { datasets: [ { backgroundColor: ['red', 'green', 'blue', 'orange'], - data: [1, 2, 3, 4] - } - ] + data: [1, 2, 3, 4], + }, + ], }, options: { animation: false, @@ -21,7 +21,7 @@ module.exports = { scales: { y: { display: false, - max: 5 + max: 5, }, x: { type: 'time', @@ -30,8 +30,8 @@ module.exports = { time: { unit: 'day', round: 'day', - } - } + }, + }, }, plugins: { legend: false, @@ -39,28 +39,28 @@ module.exports = { pan: { enabled: true, mode: 'x', - } - } - } - } + }, + }, + }, + }, }, options: { spriteText: true, run(chart) { - const steps = 4; - const n = Math.sqrt(steps); - const side = 512 / n; + const steps = 4 + const n = Math.sqrt(steps) + const side = 512 / n for (let i = 0; i < steps; i++) { - const col = i % n; - const row = Math.floor(i / n); + const col = i % n + const row = Math.floor(i / n) if (i > 0) { - chart.pan({x: -150}); - chart.update(); + chart.pan({ x: -150 }) + chart.update() } - ctx.drawImage(chart.canvas, col * side, row * side, side, side); + ctx.drawImage(chart.canvas, col * side, row * side, side, side) } - Chart.helpers.clearCanvas(chart.canvas); - chart.ctx.drawImage(canvas, 0, 0); - } - } -}; + Chart.helpers.clearCanvas(chart.canvas) + chart.ctx.drawImage(canvas, 0, 0) + }, + }, +} diff --git a/test/fixtures/zoom/category-x.js b/test/fixtures/zoom/category-x.js index 221527f7e..c8053460d 100644 --- a/test/fixtures/zoom/category-x.js +++ b/test/fixtures/zoom/category-x.js @@ -1,31 +1,33 @@ -const labels = []; -const data = []; +const labels = [] +const data = [] for (let i = 1; i <= 100; i++) { - labels.push('Label ' + i); - data.push(Math.sin(i / 100 * Math.PI) * 10); + labels.push('Label ' + i) + data.push(Math.sin((i / 100) * Math.PI) * 10) } -const canvas = document.createElement('canvas'); -canvas.width = 512; -canvas.height = 512; -const ctx = canvas.getContext('2d'); +const canvas = document.createElement('canvas') +canvas.width = 512 +canvas.height = 512 +const ctx = canvas.getContext('2d') module.exports = { config: { type: 'bar', data: { labels, - datasets: [{ - data, - barPercentage: 1, - categoryPercentage: 1, - backgroundColor: 'red' - }] + datasets: [ + { + data, + barPercentage: 1, + categoryPercentage: 1, + backgroundColor: 'red', + }, + ], }, options: { scales: { - x: {display: false}, - y: {display: false} + x: { display: false }, + y: { display: false }, }, plugins: { legend: false, @@ -35,32 +37,32 @@ module.exports = { enabled: true, }, mode: 'x', - } - } + }, + }, }, layout: { - padding: 2 - } - } + padding: 2, + }, + }, }, options: { run(chart) { - const steps = 16; - const n = Math.sqrt(steps); - const side = 512 / n; + const steps = 16 + const n = Math.sqrt(steps) + const side = 512 / n for (let i = 0; i < steps; i++) { - const col = i % n; - const row = Math.floor(i / n); + const col = i % n + const row = Math.floor(i / n) jasmine.triggerWheelEvent(chart, { x: 255, y: 255, - deltaY: -1 - }); - ctx.drawImage(chart.canvas, col * side, row * side, side, side); + deltaY: -1, + }) + ctx.drawImage(chart.canvas, col * side, row * side, side, side) } - Chart.helpers.clearCanvas(chart.canvas); - chart.ctx.drawImage(canvas, 0, 0); - } - } -}; + Chart.helpers.clearCanvas(chart.canvas) + chart.ctx.drawImage(canvas, 0, 0) + }, + }, +} diff --git a/test/fixtures/zoom/drag.js b/test/fixtures/zoom/drag.js index cad22e0aa..f9e312bc6 100644 --- a/test/fixtures/zoom/drag.js +++ b/test/fixtures/zoom/drag.js @@ -1,6 +1,6 @@ -const data = []; +const data = [] for (let i = 0; i < 100; i++) { - data.push({x: i, y: Math.sin(i / 25 * Math.PI) * 10}); + data.push({ x: i, y: Math.sin((i / 25) * Math.PI) * 10 }) } module.exports = { @@ -8,15 +8,17 @@ module.exports = { config: { type: 'line', data: { - datasets: [{ - data, - borderColor: 'red' - }] + datasets: [ + { + data, + borderColor: 'red', + }, + ], }, options: { scales: { - x: {type: 'linear', display: false}, - y: {display: false} + x: { type: 'linear', display: false }, + y: { display: false }, }, plugins: { legend: false, @@ -26,29 +28,29 @@ module.exports = { enabled: true, backgroundColor: 'yellow', borderColor: 'black', - borderWidth: 1 + borderWidth: 1, }, - } - } + }, + }, }, layout: { - padding: 2 - } - } + padding: 2, + }, + }, }, options: { run(chart) { - const scaleX = chart.scales.x; - const scaleY = chart.scales.y; + const scaleX = chart.scales.x + const scaleY = chart.scales.y jasmine.triggerMouseEvent(chart, 'mousedown', { x: scaleX.getPixelForValue(5), - y: scaleY.getPixelForValue(10) - }); + y: scaleY.getPixelForValue(10), + }) jasmine.triggerMouseEvent(chart, 'mousemove', { x: scaleX.getPixelForValue(60), - y: scaleY.getPixelForValue(0) - }); - chart.render = function() { }; - } - } -}; + y: scaleY.getPixelForValue(0), + }) + chart.render = () => undefined + }, + }, +} diff --git a/test/fixtures/zoom/dragDrawTime.js b/test/fixtures/zoom/dragDrawTime.js index 56d011e97..9d537b822 100644 --- a/test/fixtures/zoom/dragDrawTime.js +++ b/test/fixtures/zoom/dragDrawTime.js @@ -1,6 +1,6 @@ -const data = []; +const data = [] for (let i = 0; i < 100; i++) { - data.push({x: i, y: Math.sin(i / 25 * Math.PI) * 10}); + data.push({ x: i, y: Math.sin((i / 25) * Math.PI) * 10 }) } module.exports = { @@ -8,15 +8,17 @@ module.exports = { config: { type: 'line', data: { - datasets: [{ - data, - borderColor: 'red' - }] + datasets: [ + { + data, + borderColor: 'red', + }, + ], }, options: { scales: { - x: {type: 'linear', display: false}, - y: {display: false} + x: { type: 'linear', display: false }, + y: { display: false }, }, plugins: { legend: false, @@ -27,29 +29,29 @@ module.exports = { backgroundColor: 'yellow', borderColor: 'black', borderWidth: 1, - drawTime: 'afterDraw' + drawTime: 'afterDraw', }, - } - } + }, + }, }, layout: { - padding: 2 - } - } + padding: 2, + }, + }, }, options: { run(chart) { - const scaleX = chart.scales.x; - const scaleY = chart.scales.y; + const scaleX = chart.scales.x + const scaleY = chart.scales.y jasmine.triggerMouseEvent(chart, 'mousedown', { x: scaleX.getPixelForValue(5), - y: scaleY.getPixelForValue(10) - }); + y: scaleY.getPixelForValue(10), + }) jasmine.triggerMouseEvent(chart, 'mousemove', { x: scaleX.getPixelForValue(60), - y: scaleY.getPixelForValue(0) - }); - chart.render = function() { }; - } - } -}; + y: scaleY.getPixelForValue(0), + }) + chart.render = () => undefined + }, + }, +} diff --git a/test/fixtures/zoom/update-reset.js b/test/fixtures/zoom/update-reset.js index 774a6f186..18968a667 100644 --- a/test/fixtures/zoom/update-reset.js +++ b/test/fixtures/zoom/update-reset.js @@ -1,25 +1,25 @@ -const labels = ['a', 'b', 'c', 'd']; -const data = [2, 4, 8, 16]; +const labels = ['a', 'b', 'c', 'd'] +const data = [2, 4, 8, 16] -const canvas = document.createElement('canvas'); -const side = 256; -canvas.height = 768; -canvas.width = 768; -const ctx = canvas.getContext('2d'); +const canvas = document.createElement('canvas') +const side = 256 +canvas.height = 768 +canvas.width = 768 +const ctx = canvas.getContext('2d') -let x = 0; -let y = 0; +let x = 0 +let y = 0 function snapshot(chart, descr) { - ctx.drawImage(chart.canvas, x, y, side, side); - ctx.textBaseline = 'middle'; - ctx.textAlign = 'center'; - ctx.font = 'normal normal 12px arial'; - ctx.fillText(descr, x + side / 2, y + side / 2); + ctx.drawImage(chart.canvas, x, y, side, side) + ctx.textBaseline = 'middle' + ctx.textAlign = 'center' + ctx.font = 'normal normal 12px arial' + ctx.fillText(descr, x + side / 2, y + side / 2) - x += side; + x += side if (x + side > canvas.width) { - y += side; - x = 0; + y += side + x = 0 } } @@ -29,61 +29,62 @@ module.exports = { type: 'line', data: { labels, - datasets: [{ - data, - borderColor: 'red', - borderWidth: 10 - }] + datasets: [ + { + data, + borderColor: 'red', + borderWidth: 10, + }, + ], }, options: { scales: { - y: {} + y: {}, }, plugins: { legend: false, }, - } + }, }, options: { spriteText: true, run(chart) { - snapshot(chart, 'original'); + snapshot(chart, 'original') - chart.zoomScale('y', {min: 3, max: 9}); - snapshot(chart, 'zoom 3..9'); + chart.zoomScale('y', { min: 3, max: 9 }) + snapshot(chart, 'zoom 3..9') - chart.resetZoom(); - snapshot(chart, 'reset'); + chart.resetZoom() + snapshot(chart, 'reset') chart.options.scales.y = { min: 0, - max: 25 - }; - chart.update(); - snapshot(chart, 'update 0..25'); + max: 25, + } + chart.update() + snapshot(chart, 'update 0..25') - chart.resetZoom(); - snapshot(chart, 'reset'); + chart.resetZoom() + snapshot(chart, 'reset') - chart.zoomScale('y', {min: 5, max: 15}); - snapshot(chart, 'zoom 5..15'); + chart.zoomScale('y', { min: 5, max: 15 }) + snapshot(chart, 'zoom 5..15') - chart.resetZoom(); - snapshot(chart, 'reset'); + chart.resetZoom() + snapshot(chart, 'reset') chart.options.scales.y = { min: 1, - max: 10 - }; - chart.update(); - snapshot(chart, 'update 1..10'); - - chart.resetZoom(); - snapshot(chart, 'reset'); + max: 10, + } + chart.update() + snapshot(chart, 'update 1..10') - Chart.helpers.clearCanvas(chart.canvas); - chart.ctx.drawImage(canvas, 0, 0, 512, 512); - } - } -}; + chart.resetZoom() + snapshot(chart, 'reset') + Chart.helpers.clearCanvas(chart.canvas) + chart.ctx.drawImage(canvas, 0, 0, 512, 512) + }, + }, +} diff --git a/test/fixtures/zoom/zoom-reset.js b/test/fixtures/zoom/zoom-reset.js index 2da5b8f0d..9f79db375 100644 --- a/test/fixtures/zoom/zoom-reset.js +++ b/test/fixtures/zoom/zoom-reset.js @@ -1,70 +1,72 @@ -const labels = []; -const data = []; +const labels = [] +const data = [] for (let i = 1; i <= 100; i++) { - labels.push('Label ' + i); - data.push(Math.sin(i / 100 * Math.PI) * 10); + labels.push('Label ' + i) + data.push(Math.sin((i / 100) * Math.PI) * 10) } -const canvas = document.createElement('canvas'); -canvas.width = 512; -canvas.height = 512; -const ctx = canvas.getContext('2d'); +const canvas = document.createElement('canvas') +canvas.width = 512 +canvas.height = 512 +const ctx = canvas.getContext('2d') module.exports = { config: { type: 'bar', data: { labels, - datasets: [{ - data, - barPercentage: 1, - categoryPercentage: 1, - backgroundColor: 'red' - }] + datasets: [ + { + data, + barPercentage: 1, + categoryPercentage: 1, + backgroundColor: 'red', + }, + ], }, options: { scales: { - x: {display: false}, - y: {display: false} + x: { display: false }, + y: { display: false }, }, plugins: { legend: false, zoom: { zoom: { wheel: { - enabled: true + enabled: true, }, mode: 'x', - } - } + }, + }, }, layout: { - padding: 2 - } - } + padding: 2, + }, + }, }, options: { run(chart) { - const steps = 4; - const n = Math.sqrt(steps); - const side = 512 / n; + const steps = 4 + const n = Math.sqrt(steps) + const side = 512 / n for (let i = 0; i < steps; i++) { - const col = i % n; - const row = Math.floor(i / n); + const col = i % n + const row = Math.floor(i / n) if (i > 0 && i < steps - 1) { jasmine.triggerWheelEvent(chart, { x: 255, y: 255, - deltaY: -1 - }); + deltaY: -1, + }) } else { - chart.resetZoom(); + chart.resetZoom() } - ctx.drawImage(chart.canvas, col * side, row * side, side, side); + ctx.drawImage(chart.canvas, col * side, row * side, side, side) } - Chart.helpers.clearCanvas(chart.canvas); - chart.ctx.drawImage(canvas, 0, 0); - } - } -}; + Chart.helpers.clearCanvas(chart.canvas) + chart.ctx.drawImage(canvas, 0, 0) + }, + }, +} diff --git a/test/index.js b/test/index.js index 0c770829c..8db0a2f1e 100644 --- a/test/index.js +++ b/test/index.js @@ -1,54 +1,67 @@ -import {acquireChart, releaseChart, specsFromFixtures, triggerMouseEvent, addMatchers, releaseCharts} from 'chartjs-test-utils'; +import { + acquireChart, + releaseChart, + specsFromFixtures, + triggerMouseEvent, + addMatchers, + releaseCharts, +} from 'chartjs-test-utils' // force ratio=1 for tests on high-res/retina devices // ref https://github.com/chartjs/Chart.js/issues/4515 -window.devicePixelRatio = 1; +window.devicePixelRatio = 1 -window.acquireChart = acquireChart; -window.releaseChart = releaseChart; +window.acquireChart = acquireChart +window.releaseChart = releaseChart jasmine.fixture = { - specs: specsFromFixtures -}; -jasmine.triggerMouseEvent = triggerMouseEvent; - -jasmine.triggerWheelEvent = function(chart, init = {}) { - const node = chart.canvas; - const rect = node.getBoundingClientRect(); - const event = new WheelEvent('wheel', Object.assign({}, init, { - clientX: rect.left + init.x, - clientY: rect.top + init.y, - cancelable: true, - bubbles: true, - view: window - })); - - node.dispatchEvent(event); -}; - -jasmine.dispatchEvent = function(chart, type, pt, init = {}) { - const node = chart.canvas; - const rect = node.getBoundingClientRect(); - const event = new MouseEvent(type, Object.assign({}, init, { - clientX: rect.left + pt.x, - clientY: rect.top + pt.y, - cancelable: true, - bubbles: true, - view: window - })); - - node.dispatchEvent(event); -}; - -beforeEach(function() { - addMatchers(); -}); - -afterEach(function() { - releaseCharts(); -}); + specs: specsFromFixtures, +} +jasmine.triggerMouseEvent = triggerMouseEvent + +jasmine.triggerWheelEvent = function (chart, init = {}) { + const node = chart.canvas + const rect = node.getBoundingClientRect() + const event = new WheelEvent( + 'wheel', + Object.assign({}, init, { + clientX: rect.left + init.x, + clientY: rect.top + init.y, + cancelable: true, + bubbles: true, + view: window, + }) + ) + + node.dispatchEvent(event) +} + +jasmine.dispatchEvent = function (chart, type, pt, init = {}) { + const node = chart.canvas + const rect = node.getBoundingClientRect() + const event = new MouseEvent( + type, + Object.assign({}, init, { + clientX: rect.left + pt.x, + clientY: rect.top + pt.y, + cancelable: true, + bubbles: true, + view: window, + }) + ) + + node.dispatchEvent(event) +} + +beforeEach(function () { + addMatchers() +}) + +afterEach(function () { + releaseCharts() +}) beforeAll(() => { // Disable colors plugin for tests. - window.Chart.defaults.plugins.colors.enabled = false; -}); + window.Chart.defaults.plugins.colors.enabled = false +}) diff --git a/test/specs/api.spec.js b/test/specs/api.spec.js index 1b1505902..01cf16c16 100644 --- a/test/specs/api.spec.js +++ b/test/specs/api.spec.js @@ -1,137 +1,136 @@ -describe('api', function() { - it('should add methods to chart instance', function() { - const chart = window.acquireChart({type: 'line'}); - - expect(typeof chart.pan).toBe('function'); - expect(typeof chart.zoom).toBe('function'); - expect(typeof chart.zoomScale).toBe('function'); - expect(typeof chart.zoomRect).toBe('function'); - expect(typeof chart.resetZoom).toBe('function'); - expect(typeof chart.getZoomLevel).toBe('function'); - expect(typeof chart.getInitialScaleBounds).toBe('function'); - expect(typeof chart.getZoomedScaleBounds).toBe('function'); - expect(typeof chart.isZoomedOrPanned).toBe('function'); - expect(typeof chart.isZoomingOrPanning).toBe('function'); - - }); - - describe('zoom and resetZoom', function() { - it('should accept zoom percentage as single parameter', function() { +describe('api', function () { + it('should add methods to chart instance', function () { + const chart = window.acquireChart({ type: 'line' }) + + expect(typeof chart.pan).toBe('function') + expect(typeof chart.zoom).toBe('function') + expect(typeof chart.zoomScale).toBe('function') + expect(typeof chart.zoomRect).toBe('function') + expect(typeof chart.resetZoom).toBe('function') + expect(typeof chart.getZoomLevel).toBe('function') + expect(typeof chart.getInitialScaleBounds).toBe('function') + expect(typeof chart.getZoomedScaleBounds).toBe('function') + expect(typeof chart.isZoomedOrPanned).toBe('function') + expect(typeof chart.isZoomingOrPanning).toBe('function') + }) + + describe('zoom and resetZoom', function () { + it('should accept zoom percentage as single parameter', function () { const chart = window.acquireChart({ type: 'scatter', options: { scales: { x: { min: 0, - max: 100 + max: 100, }, y: { min: 0, - max: 100 - } - } - } - }); - expect(chart.scales.x.min).toBe(0); - expect(chart.scales.x.max).toBe(100); - expect(chart.scales.y.min).toBe(0); - expect(chart.scales.y.max).toBe(100); - - chart.zoom(1.5); - expect(chart.scales.x.min).toBe(25); - expect(chart.scales.x.max).toBe(75); - expect(chart.scales.y.min).toBe(25); - expect(chart.scales.y.max).toBe(75); - - chart.zoom(0.5); - expect(chart.scales.x.min).toBe(12.5); - expect(chart.scales.x.max).toBe(87.5); - expect(chart.scales.y.min).toBe(12.5); - expect(chart.scales.y.max).toBe(87.5); - - chart.resetZoom(); - expect(chart.scales.x.min).toBe(0); - expect(chart.scales.x.max).toBe(100); - expect(chart.scales.y.min).toBe(0); - expect(chart.scales.y.max).toBe(100); - }); - - it('should accept different percentages per axis', function() { + max: 100, + }, + }, + }, + }) + expect(chart.scales.x.min).toBe(0) + expect(chart.scales.x.max).toBe(100) + expect(chart.scales.y.min).toBe(0) + expect(chart.scales.y.max).toBe(100) + + chart.zoom(1.5) + expect(chart.scales.x.min).toBe(25) + expect(chart.scales.x.max).toBe(75) + expect(chart.scales.y.min).toBe(25) + expect(chart.scales.y.max).toBe(75) + + chart.zoom(0.5) + expect(chart.scales.x.min).toBe(12.5) + expect(chart.scales.x.max).toBe(87.5) + expect(chart.scales.y.min).toBe(12.5) + expect(chart.scales.y.max).toBe(87.5) + + chart.resetZoom() + expect(chart.scales.x.min).toBe(0) + expect(chart.scales.x.max).toBe(100) + expect(chart.scales.y.min).toBe(0) + expect(chart.scales.y.max).toBe(100) + }) + + it('should accept different percentages per axis', function () { const chart = window.acquireChart({ type: 'scatter', options: { scales: { x: { min: 0, - max: 100 + max: 100, }, y: { min: 0, - max: 100 - } - } - } - }); - expect(chart.scales.x.min).toBe(0); - expect(chart.scales.x.max).toBe(100); - expect(chart.scales.y.min).toBe(0); - expect(chart.scales.y.max).toBe(100); - - chart.zoom({x: 1.5, y: 1.25}); - expect(chart.scales.x.min).toBe(25); - expect(chart.scales.x.max).toBe(75); - expect(chart.scales.y.min).toBe(12.5); - expect(chart.scales.y.max).toBe(87.5); - - chart.zoom({x: 0.75, y: 0.3}); - expect(chart.scales.x.min).toBe(18.75); - expect(chart.scales.x.max).toBe(81.25); - expect(chart.scales.y.min).toBe(-13.75); - expect(chart.scales.y.max).toBe(113.75); - - chart.resetZoom(); - expect(chart.scales.x.min).toBe(0); - expect(chart.scales.x.max).toBe(100); - expect(chart.scales.y.min).toBe(0); - expect(chart.scales.y.max).toBe(100); - }); - - it('should honor fitted scale updates on reset', function() { + max: 100, + }, + }, + }, + }) + expect(chart.scales.x.min).toBe(0) + expect(chart.scales.x.max).toBe(100) + expect(chart.scales.y.min).toBe(0) + expect(chart.scales.y.max).toBe(100) + + chart.zoom({ x: 1.5, y: 1.25 }) + expect(chart.scales.x.min).toBe(25) + expect(chart.scales.x.max).toBe(75) + expect(chart.scales.y.min).toBe(12.5) + expect(chart.scales.y.max).toBe(87.5) + + chart.zoom({ x: 0.75, y: 0.3 }) + expect(chart.scales.x.min).toBe(18.75) + expect(chart.scales.x.max).toBe(81.25) + expect(chart.scales.y.min).toBe(-13.75) + expect(chart.scales.y.max).toBe(113.75) + + chart.resetZoom() + expect(chart.scales.x.min).toBe(0) + expect(chart.scales.x.max).toBe(100) + expect(chart.scales.y.min).toBe(0) + expect(chart.scales.y.max).toBe(100) + }) + + it('should honor fitted scale updates on reset', function () { const chart = window.acquireChart({ type: 'scatter', data: { datasets: [ { data: [ - {x: 0, y: 0}, - {x: 100, y: 100} - ] - } - ] - } - }); - - chart.zoom(1.5); - chart.data.datasets[0].data[0].x = -100; - chart.update(); - chart.resetZoom(); - expect(chart.scales.x.min).toBe(-100); - expect(chart.scales.x.max).toBe(100); - }); - - it('should no-op with fully constrained limits', function() { + { x: 0, y: 0 }, + { x: 100, y: 100 }, + ], + }, + ], + }, + }) + + chart.zoom(1.5) + chart.data.datasets[0].data[0].x = -100 + chart.update() + chart.resetZoom() + expect(chart.scales.x.min).toBe(-100) + expect(chart.scales.x.max).toBe(100) + }) + + it('should no-op with fully constrained limits', function () { const chart = window.acquireChart({ type: 'scatter', options: { scales: { x: { min: 0, - max: 100 + max: 100, }, y: { min: 0, - max: 100 - } + max: 100, + }, }, plugins: { zoom: { @@ -139,32 +138,32 @@ describe('api', function() { x: { min: 0, max: 100, - minRange: 100 - } - } - } - } - } - }); - - chart.zoom(1.5); - expect(chart.scales.x.min).toBe(0); - expect(chart.scales.x.max).toBe(100); - }); - - it('should no-op whan laready at limit', function() { + minRange: 100, + }, + }, + }, + }, + }, + }) + + chart.zoom(1.5) + expect(chart.scales.x.min).toBe(0) + expect(chart.scales.x.max).toBe(100) + }) + + it('should no-op whan laready at limit', function () { const chart = window.acquireChart({ type: 'scatter', options: { scales: { x: { min: 0, - max: 100 + max: 100, }, y: { min: 0, - max: 100 - } + max: 100, + }, }, plugins: { zoom: { @@ -173,82 +172,85 @@ describe('api', function() { x: { min: 0, max: 100, - minRange: 50 - } - } - } - } - } - }); - - chart.zoom({x: 1.5, focalPoint: {x: chart.scales.x.getPixelForValue(100)}}); - expect(chart.scales.x.min).toBe(50); - expect(chart.scales.x.max).toBe(100); - - chart.zoom({x: 1.5, focalPoint: {x: chart.scales.x.getPixelForValue(100)}}); - expect(chart.scales.x.min).toBe(50); - expect(chart.scales.x.max).toBe(100); - - chart.zoom({x: 1.5, focalPoint: {x: chart.scales.x.getPixelForValue(50)}}); - expect(chart.scales.x.min).toBe(50); - expect(chart.scales.x.max).toBe(100); - }); - - it('should honor zoom changes against a limit', function() { + minRange: 50, + }, + }, + }, + }, + }, + }) + + chart.zoom({ x: 1.5, focalPoint: { x: chart.scales.x.getPixelForValue(100) } }) + expect(chart.scales.x.min).toBe(50) + expect(chart.scales.x.max).toBe(100) + + chart.zoom({ x: 1.5, focalPoint: { x: chart.scales.x.getPixelForValue(100) } }) + expect(chart.scales.x.min).toBe(50) + expect(chart.scales.x.max).toBe(100) + + chart.zoom({ x: 1.5, focalPoint: { x: chart.scales.x.getPixelForValue(50) } }) + expect(chart.scales.x.min).toBe(50) + expect(chart.scales.x.max).toBe(100) + }) + + it('should honor zoom changes against a limit', function () { const chart = window.acquireChart({ type: 'scatter', options: { scales: { x: { min: 0, - max: 100 + max: 100, }, y: { min: 0, - max: 100 - } + max: 100, + }, }, plugins: { zoom: { limits: { x: { min: 0, - max: 100 - } - } - } - } - } - }); + max: 100, + }, + }, + }, + }, + }, + }) chart.zoom({ x: 1.99, focalPoint: { - x: chart.scales.x.getPixelForValue(0) - } - }); - expect(chart.scales.x.min).toBe(0); - expect(chart.scales.x.max).toBe(1); + x: chart.scales.x.getPixelForValue(0), + }, + }) + expect(chart.scales.x.min).toBe(0) + expect(chart.scales.x.max).toBe(1) chart.zoom({ x: -98, focalPoint: { - x: chart.scales.x.getPixelForValue(1) - } - }); - expect(chart.scales.x.min).toBe(0); - expect(chart.scales.x.max).toBe(100); - }); - }); - - describe('zoomScale', function() { - it('should call onZoom', function() { - const zoomSpy = jasmine.createSpy('start'); + x: chart.scales.x.getPixelForValue(1), + }, + }) + expect(chart.scales.x.min).toBe(0) + expect(chart.scales.x.max).toBe(100) + }) + }) + + describe('zoomScale', function () { + it('should call onZoom', function () { + const zoomSpy = jasmine.createSpy('start') const chart = window.acquireChart({ type: 'scatter', data: { datasets: [ { - data: [{x: 1, y: 1}, {x: 10, y: 10}], + data: [ + { x: 1, y: 1 }, + { x: 10, y: 10 }, + ], }, ], }, @@ -257,245 +259,249 @@ describe('api', function() { zoom: { zoom: { onZoom: zoomSpy, - } - } - } - } - }); + }, + }, + }, + }, + }) - chart.zoomScale('x', {min: 2, max: 10}, 'default'); + chart.zoomScale('x', { min: 2, max: 10 }, 'default') - expect(zoomSpy).toHaveBeenCalledWith({chart, trigger: 'api'}); - }); - }); + expect(zoomSpy).toHaveBeenCalledWith({ chart, trigger: 'api' }) + }) + }) - describe('getInitialScaleBounds', function() { - it('should provide the correct initial scale bounds regardless of the zoom level', function() { + describe('getInitialScaleBounds', function () { + it('should provide the correct initial scale bounds regardless of the zoom level', function () { const chart = window.acquireChart({ type: 'scatter', options: { scales: { x: { min: 0, - max: 100 + max: 100, }, y: { min: 0, - max: 100 - } - } - } - }); - - chart.zoom(1); - expect(chart.getInitialScaleBounds().x.min).toBe(0); - expect(chart.getInitialScaleBounds().x.max).toBe(100); - expect(chart.getInitialScaleBounds().y.min).toBe(0); - expect(chart.getInitialScaleBounds().y.max).toBe(100); - - chart.zoom({x: 1.5, y: 1.25}); - expect(chart.getInitialScaleBounds().x.min).toBe(0); - expect(chart.getInitialScaleBounds().x.max).toBe(100); - expect(chart.getInitialScaleBounds().y.min).toBe(0); - expect(chart.getInitialScaleBounds().y.max).toBe(100); - }); - - it('should provide updated scale bounds upon data update', function() { + max: 100, + }, + }, + }, + }) + + chart.zoom(1) + expect(chart.getInitialScaleBounds().x.min).toBe(0) + expect(chart.getInitialScaleBounds().x.max).toBe(100) + expect(chart.getInitialScaleBounds().y.min).toBe(0) + expect(chart.getInitialScaleBounds().y.max).toBe(100) + + chart.zoom({ x: 1.5, y: 1.25 }) + expect(chart.getInitialScaleBounds().x.min).toBe(0) + expect(chart.getInitialScaleBounds().x.max).toBe(100) + expect(chart.getInitialScaleBounds().y.min).toBe(0) + expect(chart.getInitialScaleBounds().y.max).toBe(100) + }) + + it('should provide updated scale bounds upon data update', function () { const chart = window.acquireChart({ type: 'scatter', data: { datasets: [ { data: [ - {x: 0, y: 0}, - {x: 100, y: 100} - ] - } - ] - } - }); - - expect(chart.getInitialScaleBounds().x.min).toBe(0); - expect(chart.getInitialScaleBounds().x.max).toBe(100); - expect(chart.getInitialScaleBounds().y.min).toBe(0); - expect(chart.getInitialScaleBounds().y.max).toBe(100); - - chart.data.datasets[0].data[0].x = -100; - chart.update(); - - expect(chart.getInitialScaleBounds().x.min).toBe(-100); - expect(chart.getInitialScaleBounds().x.max).toBe(100); - expect(chart.getInitialScaleBounds().y.min).toBe(0); - expect(chart.getInitialScaleBounds().y.max).toBe(100); - }); - }); - - describe('isZoomedOrPanned', function() { - it('should return whether or not the page is currently zoomed', function() { + { x: 0, y: 0 }, + { x: 100, y: 100 }, + ], + }, + ], + }, + }) + + expect(chart.getInitialScaleBounds().x.min).toBe(0) + expect(chart.getInitialScaleBounds().x.max).toBe(100) + expect(chart.getInitialScaleBounds().y.min).toBe(0) + expect(chart.getInitialScaleBounds().y.max).toBe(100) + + chart.data.datasets[0].data[0].x = -100 + chart.update() + + expect(chart.getInitialScaleBounds().x.min).toBe(-100) + expect(chart.getInitialScaleBounds().x.max).toBe(100) + expect(chart.getInitialScaleBounds().y.min).toBe(0) + expect(chart.getInitialScaleBounds().y.max).toBe(100) + }) + }) + + describe('isZoomedOrPanned', function () { + it('should return whether or not the page is currently zoomed', function () { const chart = window.acquireChart({ type: 'scatter', options: { scales: { x: { min: 0, - max: 100 + max: 100, }, y: { min: 0, - max: 100 - } - } - } - }); + max: 100, + }, + }, + }, + }) - expect(chart.isZoomedOrPanned()).toBe(false); + expect(chart.isZoomedOrPanned()).toBe(false) - chart.zoom(1); - expect(chart.isZoomedOrPanned()).toBe(false); + chart.zoom(1) + expect(chart.isZoomedOrPanned()).toBe(false) - chart.zoom({x: 1.5, y: 1.25}); - expect(chart.isZoomedOrPanned()).toBe(true); + chart.zoom({ x: 1.5, y: 1.25 }) + expect(chart.isZoomedOrPanned()).toBe(true) - chart.zoom({x: 0.25, y: 0.5}); - expect(chart.isZoomedOrPanned()).toBe(true); + chart.zoom({ x: 0.25, y: 0.5 }) + expect(chart.isZoomedOrPanned()).toBe(true) - chart.resetZoom(); - expect(chart.isZoomedOrPanned()).toBe(false); - }); + chart.resetZoom() + expect(chart.isZoomedOrPanned()).toBe(false) + }) - it('should return whether or not the page is currently panned', function() { + it('should return whether or not the page is currently panned', function () { const chart = window.acquireChart({ type: 'scatter', options: { scales: { x: { min: 0, - max: 100 + max: 100, }, y: { min: 0, - max: 100 - } - } - } - }); + max: 100, + }, + }, + }, + }) - expect(chart.isZoomedOrPanned()).toBe(false); + expect(chart.isZoomedOrPanned()).toBe(false) - chart.pan({x: 0, y: 0}); - expect(chart.isZoomedOrPanned()).toBe(false); + chart.pan({ x: 0, y: 0 }) + expect(chart.isZoomedOrPanned()).toBe(false) - chart.pan({x: 10}); - expect(chart.isZoomedOrPanned()).toBe(true); + chart.pan({ x: 10 }) + expect(chart.isZoomedOrPanned()).toBe(true) - chart.resetZoom(); - expect(chart.isZoomedOrPanned()).toBe(false); - }); + chart.resetZoom() + expect(chart.isZoomedOrPanned()).toBe(false) + }) - it('should work with updated data and fitted scales', function() { + it('should work with updated data and fitted scales', function () { const chart = window.acquireChart({ type: 'scatter', data: { datasets: [ { data: [ - {x: 0, y: 0}, - {x: 100, y: 100} - ] - } - ] - } - }); + { x: 0, y: 0 }, + { x: 100, y: 100 }, + ], + }, + ], + }, + }) // This sequence of operations captures a previous bug. - chart.zoom(1.5); - chart.data.datasets[0].data[0].x = -100; - chart.update(); - chart.resetZoom(); - expect(chart.scales.x.min).toBe(-100); - expect(chart.scales.x.max).toBe(100); - expect(chart.isZoomedOrPanned()).toBe(false); - }); - }); - - describe('getZoomedScaleBounds', function() { - it('should return the zoom range, or undefined if not zoomed', function() { + chart.zoom(1.5) + chart.data.datasets[0].data[0].x = -100 + chart.update() + chart.resetZoom() + expect(chart.scales.x.min).toBe(-100) + expect(chart.scales.x.max).toBe(100) + expect(chart.isZoomedOrPanned()).toBe(false) + }) + }) + + describe('getZoomedScaleBounds', function () { + it('should return the zoom range, or undefined if not zoomed', function () { const chart = window.acquireChart({ type: 'scatter', options: { scales: { x: { min: 0, - max: 100 + max: 100, }, y: { min: 0, - max: 100 - } - } - } - }); - expect(chart.getZoomedScaleBounds().x).toBeUndefined(); - - chart.zoom(1.5); - expect(chart.getZoomedScaleBounds().x).toEqual({min: 25, max: 75}); - - chart.resetZoom(); - expect(chart.getZoomedScaleBounds().x).toBeUndefined(); - }); - }); - - describe('with category scale', function() { - it('should zoom up to and out from single category', function() { + max: 100, + }, + }, + }, + }) + expect(chart.getZoomedScaleBounds().x).toBeUndefined() + + chart.zoom(1.5) + expect(chart.getZoomedScaleBounds().x).toEqual({ min: 25, max: 75 }) + + chart.resetZoom() + expect(chart.getZoomedScaleBounds().x).toBeUndefined() + }) + }) + + describe('with category scale', function () { + it('should zoom up to and out from single category', function () { const chart = window.acquireChart({ type: 'bar', data: { labels: ['a', 'b', 'c', 'd', 'e'], - datasets: [{ - data: [1, 2, 3, 2, 1] - }] + datasets: [ + { + data: [1, 2, 3, 2, 1], + }, + ], }, options: { scales: { x: { min: 'b', - max: 'd' - } + max: 'd', + }, }, - } - }); - expect(chart.scales.x.min).toBe(1); - expect(chart.scales.x.max).toBe(3); - chart.zoom(1.1); - expect(chart.scales.x.min).toBe(2); - expect(chart.scales.x.max).toBe(2); - chart.zoom(0.9); - expect(chart.scales.x.min).toBe(1); - expect(chart.scales.x.max).toBe(3); - chart.zoom(0.9); - expect(chart.scales.x.min).toBe(0); - expect(chart.scales.x.max).toBe(4); - chart.resetZoom(); - expect(chart.scales.x.min).toBe(1); - expect(chart.scales.x.max).toBe(3); - }); - - it('should not exceed limits', function() { + }, + }) + expect(chart.scales.x.min).toBe(1) + expect(chart.scales.x.max).toBe(3) + chart.zoom(1.1) + expect(chart.scales.x.min).toBe(2) + expect(chart.scales.x.max).toBe(2) + chart.zoom(0.9) + expect(chart.scales.x.min).toBe(1) + expect(chart.scales.x.max).toBe(3) + chart.zoom(0.9) + expect(chart.scales.x.min).toBe(0) + expect(chart.scales.x.max).toBe(4) + chart.resetZoom() + expect(chart.scales.x.min).toBe(1) + expect(chart.scales.x.max).toBe(3) + }) + + it('should not exceed limits', function () { const chart = window.acquireChart({ type: 'bar', data: { labels: ['0', '1', '2', '3', '4', '5', '6'], - datasets: [{ - data: [1, 2, 3, 2, 1, 0, 1] - }] + datasets: [ + { + data: [1, 2, 3, 2, 1, 0, 1], + }, + ], }, options: { indexAxis: 'y', scales: { y: { min: 2, - max: 4 - } + max: 4, + }, }, plugins: { zoom: { @@ -503,77 +509,79 @@ describe('api', function() { y: { min: 1, max: 5, - minRange: 1 - } + minRange: 1, + }, }, zoom: { wheel: { enabled: true, }, - mode: 'y' - } - } - } - } - }); - expect(chart.scales.y.min).toBe(2); - expect(chart.scales.y.max).toBe(4); - chart.zoom(1.1); - expect(chart.scales.y.min).toBe(3); - expect(chart.scales.y.max).toBe(4); - chart.pan(-100); - expect(chart.scales.y.min).toBe(4); - expect(chart.scales.y.max).toBe(5); - chart.zoom(0.9); - expect(chart.scales.y.min).toBe(3); - expect(chart.scales.y.max).toBe(5); - chart.zoom(0.9); - expect(chart.scales.y.min).toBe(1); - expect(chart.scales.y.max).toBe(5); - chart.zoom(0.9); - expect(chart.scales.y.min).toBe(1); - expect(chart.scales.y.max).toBe(5); - chart.pan(-100); - expect(chart.scales.y.min).toBe(1); - expect(chart.scales.y.max).toBe(5); - chart.pan(100); - expect(chart.scales.y.min).toBe(1); - expect(chart.scales.y.max).toBe(5); - }); - }); - - describe('with logarithmic scale', function() { - it('should zoom in and out', function() { + mode: 'y', + }, + }, + }, + }, + }) + expect(chart.scales.y.min).toBe(2) + expect(chart.scales.y.max).toBe(4) + chart.zoom(1.1) + expect(chart.scales.y.min).toBe(3) + expect(chart.scales.y.max).toBe(4) + chart.pan(-100) + expect(chart.scales.y.min).toBe(4) + expect(chart.scales.y.max).toBe(5) + chart.zoom(0.9) + expect(chart.scales.y.min).toBe(3) + expect(chart.scales.y.max).toBe(5) + chart.zoom(0.9) + expect(chart.scales.y.min).toBe(1) + expect(chart.scales.y.max).toBe(5) + chart.zoom(0.9) + expect(chart.scales.y.min).toBe(1) + expect(chart.scales.y.max).toBe(5) + chart.pan(-100) + expect(chart.scales.y.min).toBe(1) + expect(chart.scales.y.max).toBe(5) + chart.pan(100) + expect(chart.scales.y.min).toBe(1) + expect(chart.scales.y.max).toBe(5) + }) + }) + + describe('with logarithmic scale', function () { + it('should zoom in and out', function () { const chart = window.acquireChart({ type: 'bar', data: { labels: ['a', 'b', 'c', 'd', 'e'], - datasets: [{ - data: [1, 22, 3333, 22, 1], - }] + datasets: [ + { + data: [1, 22, 3333, 22, 1], + }, + ], }, options: { scales: { y: { type: 'logarithmic', - } + }, }, - } - }); - expect(chart.scales.y.min).toBe(0.1); - expect(chart.scales.y.max).toBe(4000); - chart.zoom(1.1); - expect(chart.scales.y.min).toBeCloseTo(0.17, 2); - expect(chart.scales.y.max).toBeCloseTo(2355, 0); - chart.zoom(0.9); - expect(chart.scales.y.min).toBeCloseTo(0.105, 3); - expect(chart.scales.y.max).toBeCloseTo(3794, 0); - chart.zoom(0.9); - expect(chart.scales.y.min).toBeCloseTo(0.06, 2); - expect(chart.scales.y.max).toBeCloseTo(6410, 0); - chart.resetZoom(); - expect(chart.scales.y.min).toBe(0.1); - expect(chart.scales.y.max).toBe(4000); - }); - }); -}); + }, + }) + expect(chart.scales.y.min).toBe(0.1) + expect(chart.scales.y.max).toBe(4000) + chart.zoom(1.1) + expect(chart.scales.y.min).toBeCloseTo(0.17, 2) + expect(chart.scales.y.max).toBeCloseTo(2355, 0) + chart.zoom(0.9) + expect(chart.scales.y.min).toBeCloseTo(0.105, 3) + expect(chart.scales.y.max).toBeCloseTo(3794, 0) + chart.zoom(0.9) + expect(chart.scales.y.min).toBeCloseTo(0.06, 2) + expect(chart.scales.y.max).toBeCloseTo(6410, 0) + chart.resetZoom() + expect(chart.scales.y.min).toBe(0.1) + expect(chart.scales.y.max).toBe(4000) + }) + }) +}) diff --git a/test/specs/defaults.spec.js b/test/specs/defaults.spec.js index b82009fe4..2de21c5ab 100644 --- a/test/specs/defaults.spec.js +++ b/test/specs/defaults.spec.js @@ -1,4 +1,4 @@ -describe('defaults', function() { +describe('defaults', function () { const expected = { pan: { enabled: false, @@ -10,37 +10,39 @@ describe('defaults', function() { wheel: { enabled: false, speed: 0.1, - modifierKey: null + modifierKey: null, }, drag: { enabled: false, drawTime: 'beforeDatasetsDraw', - modifierKey: null + modifierKey: null, }, pinch: { - enabled: false + enabled: false, }, - mode: 'xy' - } - }; + mode: 'xy', + }, + } - it('should be registered as global plugin options', function() { - expect(Chart.defaults.plugins.zoom).toEqual(expected); - }); + it('should be registered as global plugin options', function () { + expect(Chart.defaults.plugins.zoom).toEqual(expected) + }) - it('should be called with default options', function() { - const plugin = Chart.registry.getPlugin('zoom'); - const spy = spyOn(plugin, 'beforeUpdate'); + it('should be called with default options', function () { + const plugin = Chart.registry.getPlugin('zoom') + const spy = spyOn(plugin, 'beforeUpdate') const chart = window.acquireChart({ type: 'line', data: { - datasets: [{ - data: [] - }] - } - }); + datasets: [ + { + data: [], + }, + ], + }, + }) - expect(spy).toHaveBeenCalledWith(chart, {cancelable: true, mode: undefined}, expected); - }); -}); + expect(spy).toHaveBeenCalledWith(chart, { cancelable: true, mode: undefined }, expected) + }) +}) diff --git a/test/specs/fixtures.spec.js b/test/specs/fixtures.spec.js index 7161c43e1..8cecda592 100644 --- a/test/specs/fixtures.spec.js +++ b/test/specs/fixtures.spec.js @@ -1,4 +1,4 @@ -describe('fixtures', function() { - describe('zoom', jasmine.fixture.specs('zoom')); - describe('pan', jasmine.fixture.specs('pan')); -}); +describe('fixtures', function () { + describe('zoom', jasmine.fixture.specs('zoom')) + describe('pan', jasmine.fixture.specs('pan')) +}) diff --git a/test/specs/module.spec.js b/test/specs/module.spec.js index ff992f792..6219a92d1 100644 --- a/test/specs/module.spec.js +++ b/test/specs/module.spec.js @@ -1,20 +1,20 @@ -describe('module', function() { - it ('should be globally exported in ChartZoom', function() { - expect(typeof window.ChartZoom).toBe('object'); - }); +describe('module', function () { + it('should be globally exported in ChartZoom', function () { + expect(typeof window.ChartZoom).toBe('object') + }) - it ('should be referenced with id "zoom"', function() { - expect(window.ChartZoom.id).toBe('zoom'); - }); + it('should be referenced with id "zoom"', function () { + expect(window.ChartZoom.id).toBe('zoom') + }) - it ('should expose zoomFunctions, zoomRectFunctions, and panFunctions', function() { - expect(window.ChartZoom.zoomFunctions instanceof Object).toBe(true); - expect(window.ChartZoom.zoomRectFunctions instanceof Object).toBe(true); - expect(window.ChartZoom.panFunctions instanceof Object).toBe(true); - }); + it('should expose zoomFunctions, zoomRectFunctions, and panFunctions', function () { + expect(window.ChartZoom.zoomFunctions instanceof Object).toBe(true) + expect(window.ChartZoom.zoomRectFunctions instanceof Object).toBe(true) + expect(window.ChartZoom.panFunctions instanceof Object).toBe(true) + }) - it ('should be globally registered', function() { - const plugin = Chart.registry.getPlugin('zoom'); - expect(plugin).toBe(window.ChartZoom); - }); -}); + it('should be globally registered', function () { + const plugin = Chart.registry.getPlugin('zoom') + expect(plugin).toBe(window.ChartZoom) + }) +}) diff --git a/test/specs/pan.spec.js b/test/specs/pan.spec.js index ca5d42b68..0eecc9d1d 100644 --- a/test/specs/pan.spec.js +++ b/test/specs/pan.spec.js @@ -1,22 +1,28 @@ -describe('pan', function() { +describe('pan', function () { const data = { labels: ['a', 'b', 'c', 'd', 'e'], - datasets: [{ - data: [{ - x: 1, - y: 3 - }, { - x: 2, - y: 2 - }, { - x: 3, - y: 1 - }] - }] - }; + datasets: [ + { + data: [ + { + x: 1, + y: 3, + }, + { + x: 2, + y: 2, + }, + { + x: 3, + y: 1, + }, + ], + }, + ], + } - describe('delta', function() { - it('should be applied cumulatively', function() { + describe('delta', function () { + it('should be applied cumulatively', function () { const chart = window.acquireChart({ type: 'line', data, @@ -26,32 +32,32 @@ describe('pan', function() { pan: { enabled: true, mode: 'x', - } - } + }, + }, }, scales: { x: { min: 1, - max: 2 - } - } - } - }); - const scale = chart.scales.x; - expect(scale.min).toBe(1); - expect(scale.max).toBe(2); - chart.pan(20); - expect(scale.min).toBe(1); - expect(scale.max).toBe(2); - chart.pan(20); - expect(scale.min).toBe(1); - expect(scale.max).toBe(2); - chart.pan(20); - expect(scale.min).toBe(0); - expect(scale.max).toBe(1); - }); + max: 2, + }, + }, + }, + }) + const scale = chart.scales.x + expect(scale.min).toBe(1) + expect(scale.max).toBe(2) + chart.pan(20) + expect(scale.min).toBe(1) + expect(scale.max).toBe(2) + chart.pan(20) + expect(scale.min).toBe(1) + expect(scale.max).toBe(2) + chart.pan(20) + expect(scale.min).toBe(0) + expect(scale.max).toBe(1) + }) - it('should not give credit', function() { + it('should not give credit', function () { const chart = window.acquireChart({ type: 'scatter', data, @@ -60,38 +66,38 @@ describe('pan', function() { zoom: { limits: { x: { - max: 4 - } + max: 4, + }, }, pan: { enabled: true, mode: 'x', - } - } + }, + }, }, scales: { x: { min: 1, - max: 3 - } - } - } - }); - const scale = chart.scales.x; - expect(scale.min).toBe(1); - expect(scale.max).toBe(3); - chart.pan(-2000); - expect(scale.min).toBe(2); - expect(scale.max).toBe(4); - chart.pan(-2000); - expect(scale.min).toBe(2); - expect(scale.max).toBe(4); - chart.pan(50); - expect(scale.min).toBeLessThan(2); - expect(scale.max).toBe(scale.min + 2); - }); + max: 3, + }, + }, + }, + }) + const scale = chart.scales.x + expect(scale.min).toBe(1) + expect(scale.max).toBe(3) + chart.pan(-2000) + expect(scale.min).toBe(2) + expect(scale.max).toBe(4) + chart.pan(-2000) + expect(scale.min).toBe(2) + expect(scale.max).toBe(4) + chart.pan(50) + expect(scale.min).toBeLessThan(2) + expect(scale.max).toBe(scale.min + 2) + }) - it('should handle zero-dimension scales', function() { + it('should handle zero-dimension scales', function () { const chart = window.acquireChart({ type: 'line', data, @@ -101,29 +107,29 @@ describe('pan', function() { pan: { enabled: true, mode: 'y', - } - } + }, + }, }, scales: { y: { type: 'linear', min: 2, - max: 2 - } - } - } - }); - const scale = chart.scales.y; - expect(scale.min).toBe(2); - expect(scale.max).toBe(2); - chart.pan(50); - expect(scale.min).toBe(2); - expect(scale.max).toBe(2); - expect(scale.options.min).toBe(2); - expect(scale.options.max).toBe(2); - }); + max: 2, + }, + }, + }, + }) + const scale = chart.scales.y + expect(scale.min).toBe(2) + expect(scale.max).toBe(2) + chart.pan(50) + expect(scale.min).toBe(2) + expect(scale.max).toBe(2) + expect(scale.options.min).toBe(2) + expect(scale.options.max).toBe(2) + }) - it('should respect original limits', function() { + it('should respect original limits', function () { const chart = window.acquireChart({ type: 'line', data, @@ -138,27 +144,27 @@ describe('pan', function() { x: { min: 'original', max: 'original', - } + }, }, - } + }, }, scales: { x: { min: 1, - max: 2 - } - } - } - }); - const scale = chart.scales.x; - expect(scale.min).toBe(1); - expect(scale.max).toBe(2); - chart.pan(100); - expect(scale.min).toBe(1); - expect(scale.max).toBe(2); - }); + max: 2, + }, + }, + }, + }) + const scale = chart.scales.x + expect(scale.min).toBe(1) + expect(scale.max).toBe(2) + chart.pan(100) + expect(scale.min).toBe(1) + expect(scale.max).toBe(2) + }) - it('should respect original limits for nonlinear scales', function() { + it('should respect original limits for nonlinear scales', function () { const chart = window.acquireChart({ type: 'line', data, @@ -173,31 +179,31 @@ describe('pan', function() { x: { min: 'original', max: 'original', - } + }, }, - } + }, }, scales: { x: { type: 'logarithmic', min: 1, - max: 10 - } - } - } - }); - const scale = chart.scales.x; - expect(scale.min).toBe(1); - expect(scale.max).toBe(10); - chart.pan(100); - expect(scale.min).toBe(1); - expect(scale.max).toBe(10); - }); - }); + max: 10, + }, + }, + }, + }) + const scale = chart.scales.x + expect(scale.min).toBe(1) + expect(scale.max).toBe(10) + chart.pan(100) + expect(scale.min).toBe(1) + expect(scale.max).toBe(10) + }) + }) - describe('events', function() { - it('should call onPanStart', function(done) { - const startSpy = jasmine.createSpy('started'); + describe('events', function () { + it('should call onPanStart', function (done) { + const startSpy = jasmine.createSpy('started') const chart = window.acquireChart({ type: 'scatter', data, @@ -207,22 +213,22 @@ describe('pan', function() { pan: { enabled: true, mode: 'xy', - onPanStart: startSpy - } - } - } - } - }); + onPanStart: startSpy, + }, + }, + }, + }, + }) - Simulator.gestures.pan(chart.canvas, {deltaX: -350, deltaY: 0, duration: 50}, function() { - expect(startSpy).toHaveBeenCalled(); - expect(chart.scales.x.min).not.toBe(1); - done(); - }); - }); + Simulator.gestures.pan(chart.canvas, { deltaX: -350, deltaY: 0, duration: 50 }, function () { + expect(startSpy).toHaveBeenCalled() + expect(chart.scales.x.min).not.toBe(1) + done() + }) + }) - it('should call onPanRejected when onStartPan returns false', function(done) { - const rejectSpy = jasmine.createSpy('rejected'); + it('should call onPanRejected when onStartPan returns false', function (done) { + const rejectSpy = jasmine.createSpy('rejected') const chart = window.acquireChart({ type: 'scatter', data, @@ -233,21 +239,21 @@ describe('pan', function() { enabled: true, mode: 'xy', onPanStart: () => false, - onPanRejected: rejectSpy - } - } - } - } - }); + onPanRejected: rejectSpy, + }, + }, + }, + }, + }) - Simulator.gestures.pan(chart.canvas, {deltaX: -350, deltaY: 0, duration: 50}, function() { - expect(rejectSpy).toHaveBeenCalled(); - expect(chart.scales.x.min).toBe(1); - done(); - }); - }); + Simulator.gestures.pan(chart.canvas, { deltaX: -350, deltaY: 0, duration: 50 }, function () { + expect(rejectSpy).toHaveBeenCalled() + expect(chart.scales.x.min).toBe(1) + done() + }) + }) - it('should call onPanComplete', function(done) { + it('should call onPanComplete', function (done) { const chart = window.acquireChart({ type: 'scatter', data, @@ -258,15 +264,15 @@ describe('pan', function() { enabled: true, mode: 'xy', onPanComplete(ctx) { - expect(ctx.chart.scales.x.min).not.toBe(1); - done(); - } - } - } - } - } - }); - Simulator.gestures.pan(chart.canvas, {deltaX: -350, deltaY: 0, duration: 50}); - }); - }); -}); + expect(ctx.chart.scales.x.min).not.toBe(1) + done() + }, + }, + }, + }, + }, + }) + Simulator.gestures.pan(chart.canvas, { deltaX: -350, deltaY: 0, duration: 50 }) + }) + }) +}) diff --git a/test/specs/zoom.drag.spec.js b/test/specs/zoom.drag.spec.js index a5b25019e..d56d69702 100644 --- a/test/specs/zoom.drag.spec.js +++ b/test/specs/zoom.drag.spec.js @@ -1,23 +1,29 @@ -describe('zoom with drag', function() { +describe('zoom with drag', function () { const data = { - datasets: [{ - data: [{ - x: 1, - y: 3 - }, { - x: 2, - y: 2 - }, { - x: 3, - y: 1 - }] - }] - }; - - describe('on linear scale', function() { - let chart, scaleX, scaleY; - - it('should be applied on X scale when mode = x', function() { + datasets: [ + { + data: [ + { + x: 1, + y: 3, + }, + { + x: 2, + y: 2, + }, + { + x: 3, + y: 1, + }, + ], + }, + ], + } + + describe('on linear scale', function () { + let chart, scaleX, scaleY + + it('should be applied on X scale when mode = x', function () { chart = window.acquireChart({ type: 'line', data, @@ -25,45 +31,45 @@ describe('zoom with drag', function() { scales: { xScale0: { id: 'xScale0', - type: 'linear' + type: 'linear', }, yScale0: { id: 'yScale0', - type: 'linear' - } + type: 'linear', + }, }, plugins: { zoom: { zoom: { drag: { - enabled: true + enabled: true, }, - mode: 'x' - } - } - } - } - }); + mode: 'x', + }, + }, + }, + }, + }) - scaleX = chart.scales.xScale0; - scaleY = chart.scales.yScale0; + scaleX = chart.scales.xScale0 + scaleY = chart.scales.yScale0 jasmine.triggerMouseEvent(chart, 'mousedown', { x: scaleX.getPixelForValue(1.5), - y: scaleY.getPixelForValue(1.1) - }); + y: scaleY.getPixelForValue(1.1), + }) jasmine.triggerMouseEvent(chart, 'mouseup', { x: scaleX.getPixelForValue(2.8), - y: scaleY.getPixelForValue(1.7) - }); + y: scaleY.getPixelForValue(1.7), + }) - expect(scaleX.options.min).toBeCloseTo(1.5); - expect(scaleX.options.max).toBeCloseTo(2.8); - expect(scaleY.options.min).toBeUndefined(); - expect(scaleY.options.max).toBeUndefined(); - }); + expect(scaleX.options.min).toBeCloseTo(1.5) + expect(scaleX.options.max).toBeCloseTo(2.8) + expect(scaleY.options.min).toBeUndefined() + expect(scaleY.options.max).toBeUndefined() + }) - it('should be applied on X scale when mode = f() => x', function() { + it('should be applied on X scale when mode = f() => x', function () { chart = window.acquireChart({ type: 'line', data, @@ -71,47 +77,47 @@ describe('zoom with drag', function() { scales: { xScale0: { id: 'xScale0', - type: 'linear' + type: 'linear', }, yScale0: { id: 'yScale0', - type: 'linear' - } + type: 'linear', + }, }, plugins: { zoom: { zoom: { drag: { - enabled: true + enabled: true, }, - mode: function() { - return 'x'; - } - } - } - } - } - }); + mode: function () { + return 'x' + }, + }, + }, + }, + }, + }) - scaleX = chart.scales.xScale0; - scaleY = chart.scales.yScale0; + scaleX = chart.scales.xScale0 + scaleY = chart.scales.yScale0 jasmine.triggerMouseEvent(chart, 'mousedown', { x: scaleX.getPixelForValue(1.5), - y: scaleY.getPixelForValue(1.1) - }); + y: scaleY.getPixelForValue(1.1), + }) jasmine.triggerMouseEvent(chart, 'mouseup', { x: scaleX.getPixelForValue(2.8), - y: scaleY.getPixelForValue(1.7) - }); + y: scaleY.getPixelForValue(1.7), + }) - expect(scaleX.options.min).toBeCloseTo(1.5); - expect(scaleX.options.max).toBeCloseTo(2.8); - expect(scaleY.options.min).toBeUndefined(); - expect(scaleY.options.max).toBeUndefined(); - }); + expect(scaleX.options.min).toBeCloseTo(1.5) + expect(scaleX.options.max).toBeCloseTo(2.8) + expect(scaleY.options.min).toBeUndefined() + expect(scaleY.options.max).toBeUndefined() + }) - it('should be applied on Y scale when mode = y', function() { + it('should be applied on Y scale when mode = y', function () { chart = window.acquireChart({ type: 'line', data, @@ -119,45 +125,45 @@ describe('zoom with drag', function() { scales: { xScale0: { id: 'xScale0', - type: 'linear' + type: 'linear', }, yScale0: { id: 'yScale0', - type: 'linear' - } + type: 'linear', + }, }, plugins: { zoom: { zoom: { drag: { - enabled: true + enabled: true, }, - mode: 'y' - } - } - } - } - }); + mode: 'y', + }, + }, + }, + }, + }) - scaleX = chart.scales.xScale0; - scaleY = chart.scales.yScale0; + scaleX = chart.scales.xScale0 + scaleY = chart.scales.yScale0 jasmine.triggerMouseEvent(chart, 'mousedown', { x: scaleX.getPixelForValue(1.5), - y: scaleY.getPixelForValue(1.1) - }); + y: scaleY.getPixelForValue(1.1), + }) jasmine.triggerMouseEvent(chart, 'mouseup', { x: scaleX.getPixelForValue(2.8), - y: scaleY.getPixelForValue(1.7) - }); + y: scaleY.getPixelForValue(1.7), + }) - expect(scaleX.options.min).toBeUndefined(); - expect(scaleX.options.max).toBeUndefined(); - expect(scaleY.options.min).toBeCloseTo(1.1); - expect(scaleY.options.max).toBeCloseTo(1.7); - }); + expect(scaleX.options.min).toBeUndefined() + expect(scaleX.options.max).toBeUndefined() + expect(scaleY.options.min).toBeCloseTo(1.1) + expect(scaleY.options.max).toBeCloseTo(1.7) + }) - it('should be applied on Y scale when mode = f() => y', function() { + it('should be applied on Y scale when mode = f() => y', function () { chart = window.acquireChart({ type: 'line', data, @@ -165,47 +171,47 @@ describe('zoom with drag', function() { scales: { xScale0: { id: 'xScale0', - type: 'linear' + type: 'linear', }, yScale0: { id: 'yScale0', - type: 'linear' - } + type: 'linear', + }, }, plugins: { zoom: { zoom: { drag: { - enabled: true + enabled: true, }, - mode: function() { - return 'y'; - } - } - } - } - } - }); + mode: function () { + return 'y' + }, + }, + }, + }, + }, + }) - scaleX = chart.scales.xScale0; - scaleY = chart.scales.yScale0; + scaleX = chart.scales.xScale0 + scaleY = chart.scales.yScale0 jasmine.triggerMouseEvent(chart, 'mousedown', { x: scaleX.getPixelForValue(1.5), - y: scaleY.getPixelForValue(1.1) - }); + y: scaleY.getPixelForValue(1.1), + }) jasmine.triggerMouseEvent(chart, 'mouseup', { x: scaleX.getPixelForValue(2.8), - y: scaleY.getPixelForValue(1.7) - }); + y: scaleY.getPixelForValue(1.7), + }) - expect(scaleX.options.min).toBeUndefined(); - expect(scaleX.options.max).toBeUndefined(); - expect(scaleY.options.min).toBeCloseTo(1.1); - expect(scaleY.options.max).toBeCloseTo(1.7); - }); + expect(scaleX.options.min).toBeUndefined() + expect(scaleX.options.max).toBeUndefined() + expect(scaleY.options.min).toBeCloseTo(1.1) + expect(scaleY.options.max).toBeCloseTo(1.7) + }) - it('should be applied on X and Y scales when mode = xy', function() { + it('should be applied on X and Y scales when mode = xy', function () { chart = window.acquireChart({ type: 'line', data, @@ -213,51 +219,51 @@ describe('zoom with drag', function() { scales: { xScale0: { id: 'xScale0', - type: 'linear' + type: 'linear', }, yScale0: { id: 'yScale0', - type: 'linear' - } + type: 'linear', + }, }, plugins: { zoom: { zoom: { drag: { - enabled: true + enabled: true, }, - mode: 'xy' - } - } - } - } - }); + mode: 'xy', + }, + }, + }, + }, + }) - scaleX = chart.scales.xScale0; - scaleY = chart.scales.yScale0; + scaleX = chart.scales.xScale0 + scaleY = chart.scales.yScale0 jasmine.triggerMouseEvent(chart, 'mousedown', { x: scaleX.getPixelForValue(1.5), - y: scaleY.getPixelForValue(1.1) - }); + y: scaleY.getPixelForValue(1.1), + }) jasmine.triggerMouseEvent(chart, 'mouseup', { x: scaleX.getPixelForValue(2.8), - y: scaleY.getPixelForValue(1.7) - }); + y: scaleY.getPixelForValue(1.7), + }) - expect(scaleX.options.min).toBeCloseTo(1.5); - expect(scaleX.options.max).toBeCloseTo(2.8); - expect(scaleY.options.min).toBeCloseTo(1.1); - expect(scaleY.options.max).toBeCloseTo(1.7); - }); - }); + expect(scaleX.options.min).toBeCloseTo(1.5) + expect(scaleX.options.max).toBeCloseTo(2.8) + expect(scaleY.options.min).toBeCloseTo(1.1) + expect(scaleY.options.max).toBeCloseTo(1.7) + }) + }) - describe('with modifierKey', function() { + describe('with modifierKey', function () { for (const key of ['ctrl', 'alt', 'shift', 'meta']) { for (const pressed of [true, false]) { - let chart, scaleX, scaleY; - it(`should ${pressed ? '' : 'not '}change ${pressed ? 'with' : 'without'} key ${key}`, async function() { - const rejectedSpy = jasmine.createSpy('wheelFailed'); + let chart, scaleX, scaleY + it(`should ${pressed ? '' : 'not '}change ${pressed ? 'with' : 'without'} key ${key}`, async function () { + const rejectedSpy = jasmine.createSpy('wheelFailed') chart = window.acquireChart({ type: 'line', data, @@ -266,11 +272,11 @@ describe('zoom with drag', function() { x: { type: 'linear', min: 0, - max: 10 + max: 10, }, y: { - type: 'linear' - } + type: 'linear', + }, }, plugins: { zoom: { @@ -280,54 +286,54 @@ describe('zoom with drag', function() { modifierKey: key, }, mode: 'x', - onZoomRejected: rejectedSpy - } - } - } - } - }); + onZoomRejected: rejectedSpy, + }, + }, + }, + }, + }) - scaleX = chart.scales.x; - scaleY = chart.scales.y; + scaleX = chart.scales.x + scaleY = chart.scales.y - const oldMinX = scaleX.options.min; - const oldMaxX = scaleX.options.max; + const oldMinX = scaleX.options.min + const oldMaxX = scaleX.options.max const pt = { x: scaleX.getPixelForValue(1.5), y: scaleY.getPixelForValue(1.1), - }; - const pt2 = {x: pt.x + 20, y: pt.y + 20}; - const init = {}; + } + const pt2 = { x: pt.x + 20, y: pt.y + 20 } + const init = {} if (pressed) { - init[key + 'Key'] = true; + init[key + 'Key'] = true } - jasmine.dispatchEvent(chart, 'mousedown', pt, init); - jasmine.dispatchEvent(chart, 'mousemove', pt2, init); - jasmine.dispatchEvent(chart, 'mouseup', pt2, init); + jasmine.dispatchEvent(chart, 'mousedown', pt, init) + jasmine.dispatchEvent(chart, 'mousemove', pt2, init) + jasmine.dispatchEvent(chart, 'mouseup', pt2, init) if (pressed) { - expect(scaleX.options.min).not.toEqual(oldMinX); - expect(scaleX.options.max).not.toEqual(oldMaxX); - expect(rejectedSpy).not.toHaveBeenCalled(); + expect(scaleX.options.min).not.toEqual(oldMinX) + expect(scaleX.options.max).not.toEqual(oldMaxX) + expect(rejectedSpy).not.toHaveBeenCalled() } else { - expect(scaleX.options.min).toEqual(oldMinX); - expect(scaleX.options.max).toEqual(oldMaxX); - expect(rejectedSpy).toHaveBeenCalled(); + expect(scaleX.options.min).toEqual(oldMinX) + expect(scaleX.options.max).toEqual(oldMaxX) + expect(rejectedSpy).toHaveBeenCalled() } - }); + }) } } - }); + }) - describe('drag with pan.modifierKey', function() { + describe('drag with pan.modifierKey', function () { for (const key of ['ctrl', 'alt', 'shift', 'meta']) { for (const pressed of [true, false]) { - let chart, scaleX, scaleY; - it(`should ${pressed ? 'not ' : ''}change ${pressed ? 'without' : 'with'} key ${key}`, async function() { - const rejectedSpy = jasmine.createSpy('zoomRejected'); - const clickSpy = jasmine.createSpy('clicked'); + let chart, scaleX, scaleY + it(`should ${pressed ? 'not ' : ''}change ${pressed ? 'without' : 'with'} key ${key}`, async function () { + const rejectedSpy = jasmine.createSpy('zoomRejected') + const clickSpy = jasmine.createSpy('clicked') chart = window.acquireChart({ type: 'line', data, @@ -336,11 +342,11 @@ describe('zoom with drag', function() { x: { type: 'linear', min: 0, - max: 10 + max: 10, }, y: { - type: 'linear' - } + type: 'linear', + }, }, plugins: { zoom: { @@ -353,166 +359,174 @@ describe('zoom with drag', function() { enabled: true, }, mode: 'x', - onZoomRejected: rejectedSpy - } - } + onZoomRejected: rejectedSpy, + }, + }, }, - onClick: clickSpy - } - }); + onClick: clickSpy, + }, + }) - scaleX = chart.scales.x; - scaleY = chart.scales.y; + scaleX = chart.scales.x + scaleY = chart.scales.y - const oldMinX = scaleX.options.min; - const oldMaxX = scaleX.options.max; + const oldMinX = scaleX.options.min + const oldMaxX = scaleX.options.max const pt = { x: scaleX.getPixelForValue(1.5), y: scaleY.getPixelForValue(1.1), - }; - const pt2 = {x: pt.x + 20, y: pt.y + 20}; - const init = {}; + } + const pt2 = { x: pt.x + 20, y: pt.y + 20 } + const init = {} if (pressed) { - init[key + 'Key'] = true; + init[key + 'Key'] = true } - jasmine.dispatchEvent(chart, 'mousedown', pt, init); - jasmine.dispatchEvent(chart, 'mousemove', pt2, init); - jasmine.dispatchEvent(chart, 'mouseup', pt2, init); + jasmine.dispatchEvent(chart, 'mousedown', pt, init) + jasmine.dispatchEvent(chart, 'mousemove', pt2, init) + jasmine.dispatchEvent(chart, 'mouseup', pt2, init) if (pressed) { - expect(scaleX.options.min).toEqual(oldMinX); - expect(scaleX.options.max).toEqual(oldMaxX); - expect(rejectedSpy).toHaveBeenCalled(); + expect(scaleX.options.min).toEqual(oldMinX) + expect(scaleX.options.max).toEqual(oldMaxX) + expect(rejectedSpy).toHaveBeenCalled() } else { - expect(scaleX.options.min).not.toEqual(oldMinX); - expect(scaleX.options.max).not.toEqual(oldMaxX); - expect(rejectedSpy).not.toHaveBeenCalled(); + expect(scaleX.options.min).not.toEqual(oldMinX) + expect(scaleX.options.max).not.toEqual(oldMaxX) + expect(rejectedSpy).not.toHaveBeenCalled() } - expect(clickSpy).not.toHaveBeenCalled(); - }); + expect(clickSpy).not.toHaveBeenCalled() + }) } } - }); - - describe('coordinate handling', function() { - let chart, scaleX, scaleY; - - it('handles dragging to the right edge of the chart', function() { - chart = window.acquireChart({ - type: 'line', - data, - options: { - scales: { - xScale0: { - id: 'xScale0', - type: 'linear', - min: 0, - max: 4 + }) + + describe('coordinate handling', function () { + let chart, scaleX, scaleY + + it('handles dragging to the right edge of the chart', function () { + chart = window.acquireChart( + { + type: 'line', + data, + options: { + scales: { + xScale0: { + id: 'xScale0', + type: 'linear', + min: 0, + max: 4, + }, + yScale0: { + id: 'yScale0', + type: 'linear', + min: 0, + max: 4, + }, }, - yScale0: { - id: 'yScale0', - type: 'linear', - min: 0, - max: 4, - } - }, - plugins: { - zoom: { + plugins: { zoom: { - drag: { - enabled: true + zoom: { + drag: { + enabled: true, + }, + mode: 'xy', }, - mode: 'xy' - } - } - } + }, + }, + }, + }, + { + wrapper: { style: 'position: absolute; left: 50px; top: 50px;' }, } - }, { - wrapper: {style: 'position: absolute; left: 50px; top: 50px;'} - }); + ) - scaleX = chart.scales.xScale0; - scaleY = chart.scales.yScale0; + scaleX = chart.scales.xScale0 + scaleY = chart.scales.yScale0 jasmine.triggerMouseEvent(chart, 'mousedown', { x: scaleX.getPixelForValue(1.5), - y: scaleY.getPixelForValue(1.2) - }); + y: scaleY.getPixelForValue(1.2), + }) jasmine.triggerMouseEvent(chart, 'mouseup', { x: scaleX.getPixelForValue(4) + 5, - y: scaleY.getPixelForValue(1.7) - }); - - expect(scaleX.options.min).toBeCloseTo(1.5); - expect(scaleX.options.max).toBeCloseTo(4); - expect(scaleY.options.min).toBeCloseTo(1.2); - expect(scaleY.options.max).toBeCloseTo(1.7); - }); - - it('handles dragging off the right edge of the chart canvas', function() { - chart = window.acquireChart({ - type: 'line', - data, - options: { - scales: { - xScale0: { - id: 'xScale0', - type: 'linear', - min: 0, - max: 4 + y: scaleY.getPixelForValue(1.7), + }) + + expect(scaleX.options.min).toBeCloseTo(1.5) + expect(scaleX.options.max).toBeCloseTo(4) + expect(scaleY.options.min).toBeCloseTo(1.2) + expect(scaleY.options.max).toBeCloseTo(1.7) + }) + + it('handles dragging off the right edge of the chart canvas', function () { + chart = window.acquireChart( + { + type: 'line', + data, + options: { + scales: { + xScale0: { + id: 'xScale0', + type: 'linear', + min: 0, + max: 4, + }, + yScale0: { + id: 'yScale0', + type: 'linear', + min: 0, + max: 4, + }, }, - yScale0: { - id: 'yScale0', - type: 'linear', - min: 0, - max: 4, - } - }, - plugins: { - zoom: { + plugins: { zoom: { - drag: { - enabled: true + zoom: { + drag: { + enabled: true, + }, + mode: 'xy', }, - mode: 'xy' - } - } - } + }, + }, + }, + }, + { + wrapper: { style: 'position: absolute; left: 50px; top: 50px;' }, } - }, { - wrapper: {style: 'position: absolute; left: 50px; top: 50px;'} - }); + ) - scaleX = chart.scales.xScale0; - scaleY = chart.scales.yScale0; + scaleX = chart.scales.xScale0 + scaleY = chart.scales.yScale0 jasmine.triggerMouseEvent(chart, 'mousedown', { x: scaleX.getPixelForValue(1.5), - y: scaleY.getPixelForValue(1.2) - }); - - const rect = chart.canvas.getBoundingClientRect(); - document.documentElement.dispatchEvent(new MouseEvent('mouseup', { - clientX: rect.right, - clientY: rect.top + scaleY.getPixelForValue(1.7), - cancelable: true, - bubbles: true, - view: window - })); - - expect(scaleX.options.min).toBeCloseTo(1.5); - expect(scaleX.options.max).toBeCloseTo(4); - expect(scaleY.options.min).toBeCloseTo(1.2); - expect(scaleY.options.max).toBeCloseTo(1.7); - }); - }); - - describe('events', function() { - it('should call onZoomStart, onZoom and onZoomComplete', function(done) { - const startSpy = jasmine.createSpy('start'); - const zoomSpy = jasmine.createSpy('zoom'); + y: scaleY.getPixelForValue(1.2), + }) + + const rect = chart.canvas.getBoundingClientRect() + document.documentElement.dispatchEvent( + new MouseEvent('mouseup', { + clientX: rect.right, + clientY: rect.top + scaleY.getPixelForValue(1.7), + cancelable: true, + bubbles: true, + view: window, + }) + ) + + expect(scaleX.options.min).toBeCloseTo(1.5) + expect(scaleX.options.max).toBeCloseTo(4) + expect(scaleY.options.min).toBeCloseTo(1.2) + expect(scaleY.options.max).toBeCloseTo(1.7) + }) + }) + + describe('events', function () { + it('should call onZoomStart, onZoom and onZoomComplete', function (done) { + const startSpy = jasmine.createSpy('start') + const zoomSpy = jasmine.createSpy('zoom') const chart = window.acquireChart({ type: 'scatter', data, @@ -526,40 +540,40 @@ describe('zoom with drag', function() { mode: 'xy', onZoomStart: startSpy, onZoom: zoomSpy, - onZoomComplete: () => done() - } - } - } - } - }); + onZoomComplete: () => done(), + }, + }, + }, + }, + }) const pt = { x: chart.scales.x.getPixelForValue(1.5), y: chart.scales.y.getPixelForValue(1.1), - }; - const pt2 = {x: pt.x + 20, y: pt.y + 20}; + } + const pt2 = { x: pt.x + 20, y: pt.y + 20 } - expect(chart.isZoomingOrPanning()).toBe(false); + expect(chart.isZoomingOrPanning()).toBe(false) - jasmine.dispatchEvent(chart, 'mousedown', pt); - jasmine.dispatchEvent(chart, 'mousemove', pt2); + jasmine.dispatchEvent(chart, 'mousedown', pt) + jasmine.dispatchEvent(chart, 'mousemove', pt2) - expect(chart.isZoomingOrPanning()).toBe(true); + expect(chart.isZoomingOrPanning()).toBe(true) - jasmine.dispatchEvent(chart, 'mouseup', pt2); + jasmine.dispatchEvent(chart, 'mouseup', pt2) // Drag state isn't cleared until a timeout fires (later), so we can't // easily test this here. // expect(chart.isZoomingOrPanning()).toBe(false); - expect(startSpy).toHaveBeenCalled(); - expect(zoomSpy).toHaveBeenCalledWith({chart, trigger: 'drag'}); - }); + expect(startSpy).toHaveBeenCalled() + expect(zoomSpy).toHaveBeenCalledWith({ chart, trigger: 'drag' }) + }) - it('should call onZoomRejected when onZoomStart returns false', function() { - const zoomSpy = jasmine.createSpy('zoom'); - const rejectSpy = jasmine.createSpy('reject'); - const doneSpy = jasmine.createSpy('done'); + it('should call onZoomRejected when onZoomStart returns false', function () { + const zoomSpy = jasmine.createSpy('zoom') + const rejectSpy = jasmine.createSpy('reject') + const doneSpy = jasmine.createSpy('done') const chart = window.acquireChart({ type: 'scatter', data, @@ -574,33 +588,33 @@ describe('zoom with drag', function() { onZoomStart: () => false, onZoom: zoomSpy, onZoomComplete: doneSpy, - onZoomRejected: rejectSpy - } - } - } - } - }); + onZoomRejected: rejectSpy, + }, + }, + }, + }, + }) const pt = { x: chart.scales.x.getPixelForValue(1.5), y: chart.scales.y.getPixelForValue(1.1), - }; - const pt2 = {x: pt.x + 20, y: pt.y + 20}; + } + const pt2 = { x: pt.x + 20, y: pt.y + 20 } - expect(chart.isZoomingOrPanning()).toBe(false); + expect(chart.isZoomingOrPanning()).toBe(false) - jasmine.dispatchEvent(chart, 'mousedown', pt); + jasmine.dispatchEvent(chart, 'mousedown', pt) - expect(chart.isZoomingOrPanning()).toBe(false); + expect(chart.isZoomingOrPanning()).toBe(false) - jasmine.dispatchEvent(chart, 'mousemove', pt2); - jasmine.dispatchEvent(chart, 'mouseup', pt2); + jasmine.dispatchEvent(chart, 'mousemove', pt2) + jasmine.dispatchEvent(chart, 'mouseup', pt2) - expect(chart.isZoomingOrPanning()).toBe(false); + expect(chart.isZoomingOrPanning()).toBe(false) - expect(rejectSpy).toHaveBeenCalled(); - expect(zoomSpy).not.toHaveBeenCalled(); - expect(doneSpy).not.toHaveBeenCalled(); - }); - }); -}); + expect(rejectSpy).toHaveBeenCalled() + expect(zoomSpy).not.toHaveBeenCalled() + expect(doneSpy).not.toHaveBeenCalled() + }) + }) +}) diff --git a/test/specs/zoom.pinch.spec.js b/test/specs/zoom.pinch.spec.js index fab012acc..10394ff6c 100644 --- a/test/specs/zoom.pinch.spec.js +++ b/test/specs/zoom.pinch.spec.js @@ -1,22 +1,28 @@ describe('pinch', () => { const data = { - datasets: [{ - data: [{ - x: 1, - y: 3 - }, { - x: 2, - y: 2 - }, { - x: 3, - y: 1 - }] - }] - }; + datasets: [ + { + data: [ + { + x: 1, + y: 3, + }, + { + x: 2, + y: 2, + }, + { + x: 3, + y: 1, + }, + ], + }, + ], + } describe('events', () => { - it('should call onZoomStart', function(done) { - const startSpy = jasmine.createSpy('started'); + it('should call onZoomStart', function (done) { + const startSpy = jasmine.createSpy('started') const chart = window.acquireChart({ type: 'scatter', data, @@ -30,20 +36,20 @@ describe('pinch', () => { enabled: true, }, }, - } - } - } - }); + }, + }, + }, + }) - Simulator.gestures.pinch(chart.canvas, {pos: [chart.width / 2, chart.height / 2]}, function() { - expect(startSpy).toHaveBeenCalled(); - expect(chart.scales.x.min).not.toBe(1); - done(); - }); - }); + Simulator.gestures.pinch(chart.canvas, { pos: [chart.width / 2, chart.height / 2] }, function () { + expect(startSpy).toHaveBeenCalled() + expect(chart.scales.x.min).not.toBe(1) + done() + }) + }) - it('should call onZoomRejected when onStartZoom returns false', function(done) { - const rejectSpy = jasmine.createSpy('rejected'); + it('should call onZoomRejected when onStartZoom returns false', function (done) { + const rejectSpy = jasmine.createSpy('rejected') const chart = window.acquireChart({ type: 'scatter', data, @@ -56,21 +62,21 @@ describe('pinch', () => { onZoomRejected: rejectSpy, pinch: { enabled: true, - } - } - } - } - } - }); + }, + }, + }, + }, + }, + }) - Simulator.gestures.pinch(chart.canvas, {}, function() { - expect(rejectSpy).toHaveBeenCalled(); - expect(chart.scales.x.min).toBe(1); - done(); - }); - }); + Simulator.gestures.pinch(chart.canvas, {}, function () { + expect(rejectSpy).toHaveBeenCalled() + expect(chart.scales.x.min).toBe(1) + done() + }) + }) - it('should call onZoomComplete', function(done) { + it('should call onZoomComplete', function (done) { const chart = window.acquireChart({ type: 'scatter', data, @@ -80,19 +86,18 @@ describe('pinch', () => { zoom: { mode: 'xy', onZoomComplete(ctx) { - expect(ctx.chart.scales.x.min).not.toBe(1); - done(); + expect(ctx.chart.scales.x.min).not.toBe(1) + done() }, pinch: { enabled: true, }, }, - } - } - } - }); - Simulator.gestures.pinch(chart.canvas, {}); - }); - - }); -}); + }, + }, + }, + }) + Simulator.gestures.pinch(chart.canvas, {}) + }) + }) +}) diff --git a/test/specs/zoom.wheel.spec.js b/test/specs/zoom.wheel.spec.js index acdab0d09..43392e736 100644 --- a/test/specs/zoom.wheel.spec.js +++ b/test/specs/zoom.wheel.spec.js @@ -1,25 +1,31 @@ -describe('zoom with wheel', function() { +describe('zoom with wheel', function () { const data = { - datasets: [{ - data: [{ - x: 1, - y: 3 - }, { - x: 2, - y: 2 - }, { - x: 3, - y: 1 - }] - }] - }; - - describe('with modifierKey', function() { + datasets: [ + { + data: [ + { + x: 1, + y: 3, + }, + { + x: 2, + y: 2, + }, + { + x: 3, + y: 1, + }, + ], + }, + ], + } + + describe('with modifierKey', function () { for (const key of ['ctrl', 'alt', 'shift', 'meta']) { for (const pressed of [true, false]) { - let chart, scaleX, scaleY; - it(`should ${pressed ? '' : 'not '}change ${pressed ? 'with' : 'without'} key ${key}`, async function() { - const rejectedSpy = jasmine.createSpy('wheelFailed'); + let chart, scaleX, scaleY + it(`should ${pressed ? '' : 'not '}change ${pressed ? 'with' : 'without'} key ${key}`, async function () { + const rejectedSpy = jasmine.createSpy('wheelFailed') chart = window.acquireChart({ type: 'line', data, @@ -28,11 +34,11 @@ describe('zoom with wheel', function() { x: { type: 'linear', min: 0, - max: 10 + max: 10, }, y: { - type: 'linear' - } + type: 'linear', + }, }, plugins: { zoom: { @@ -42,45 +48,45 @@ describe('zoom with wheel', function() { modifierKey: key, }, mode: 'x', - onZoomRejected: rejectedSpy - } - } - } - } - }); + onZoomRejected: rejectedSpy, + }, + }, + }, + }, + }) - scaleX = chart.scales.x; - scaleY = chart.scales.y; + scaleX = chart.scales.x + scaleY = chart.scales.y - const oldMinX = scaleX.options.min; - const oldMaxX = scaleX.options.max; + const oldMinX = scaleX.options.min + const oldMaxX = scaleX.options.max const wheelEv = { x: scaleX.getPixelForValue(1.5), y: scaleY.getPixelForValue(1.1), - deltaY: 1 - }; + deltaY: 1, + } if (pressed) { - wheelEv[key + 'Key'] = true; + wheelEv[key + 'Key'] = true } - jasmine.triggerWheelEvent(chart, wheelEv); + jasmine.triggerWheelEvent(chart, wheelEv) if (pressed) { - expect(scaleX.options.min).not.toEqual(oldMinX); - expect(scaleX.options.max).not.toEqual(oldMaxX); - expect(rejectedSpy).not.toHaveBeenCalled(); + expect(scaleX.options.min).not.toEqual(oldMinX) + expect(scaleX.options.max).not.toEqual(oldMaxX) + expect(rejectedSpy).not.toHaveBeenCalled() } else { - expect(scaleX.options.min).toEqual(oldMinX); - expect(scaleX.options.max).toEqual(oldMaxX); - expect(rejectedSpy).toHaveBeenCalled(); + expect(scaleX.options.min).toEqual(oldMinX) + expect(scaleX.options.max).toEqual(oldMaxX) + expect(rejectedSpy).toHaveBeenCalled() } - }); + }) } } - }); + }) - describe('with overScaleMode = y and mode = xy', function() { + describe('with overScaleMode = y and mode = xy', function () { const config = { type: 'line', data, @@ -89,11 +95,11 @@ describe('zoom with wheel', function() { x: { type: 'linear', min: 1, - max: 10 + max: 10, }, y: { - type: 'linear' - } + type: 'linear', + }, }, plugins: { zoom: { @@ -102,69 +108,69 @@ describe('zoom with wheel', function() { enabled: true, }, mode: 'xy', - overScaleMode: 'y' - } - } - } - } - }; + overScaleMode: 'y', + }, + }, + }, + }, + } - describe('Wheel under Y scale', function() { - it('should be applied on Y, but not on X scales.', function() { - const chart = window.acquireChart(config); + describe('Wheel under Y scale', function () { + it('should be applied on Y, but not on X scales.', function () { + const chart = window.acquireChart(config) - const scaleX = chart.scales.x; - const scaleY = chart.scales.y; + const scaleX = chart.scales.x + const scaleY = chart.scales.y - const oldMinX = scaleX.options.min; - const oldMaxX = scaleX.options.max; - const oldMinY = scaleY.options.min; - const oldMaxY = scaleY.options.max; + const oldMinX = scaleX.options.min + const oldMaxX = scaleX.options.max + const oldMinY = scaleY.options.min + const oldMaxY = scaleY.options.max const wheelEv = { x: scaleY.left + (scaleY.right - scaleY.left) / 2, y: scaleY.top + (scaleY.bottom - scaleY.top) / 2, - deltaY: 1 - }; + deltaY: 1, + } - jasmine.triggerWheelEvent(chart, wheelEv); + jasmine.triggerWheelEvent(chart, wheelEv) - expect(scaleX.options.min).toEqual(oldMinX); - expect(scaleX.options.max).toEqual(oldMaxX); - expect(scaleY.options.min).not.toEqual(oldMinY); - expect(scaleY.options.max).not.toEqual(oldMaxY); - }); - }); + expect(scaleX.options.min).toEqual(oldMinX) + expect(scaleX.options.max).toEqual(oldMaxX) + expect(scaleY.options.min).not.toEqual(oldMinY) + expect(scaleY.options.max).not.toEqual(oldMaxY) + }) + }) - describe('Wheel not under Y scale', function() { - it('should be applied on X, but not on Y scales.', function() { - const chart = window.acquireChart(config); + describe('Wheel not under Y scale', function () { + it('should be applied on X, but not on Y scales.', function () { + const chart = window.acquireChart(config) - const scaleX = chart.scales.x; - const scaleY = chart.scales.y; + const scaleX = chart.scales.x + const scaleY = chart.scales.y - const oldMinX = scaleX.options.min; - const oldMaxX = scaleX.options.max; - const oldMinY = scaleY.options.min; - const oldMaxY = scaleY.options.max; + const oldMinX = scaleX.options.min + const oldMaxX = scaleX.options.max + const oldMinY = scaleY.options.min + const oldMaxY = scaleY.options.max const wheelEv = { x: scaleX.getPixelForValue(1.5), y: scaleY.getPixelForValue(1.1), - deltaY: 1 - }; + deltaY: 1, + } - jasmine.triggerWheelEvent(chart, wheelEv); + jasmine.triggerWheelEvent(chart, wheelEv) - expect(scaleX.options.min).not.toEqual(oldMinX); - expect(scaleX.options.max).not.toEqual(oldMaxX); - expect(scaleY.options.min).toEqual(oldMinY); - expect(scaleY.options.max).toEqual(oldMaxY); - }); - }); - }); + expect(scaleX.options.min).not.toEqual(oldMinX) + expect(scaleX.options.max).not.toEqual(oldMaxX) + expect(scaleY.options.min).toEqual(oldMinY) + expect(scaleY.options.max).toEqual(oldMaxY) + }) + }) + }) - describe('with scaleMode = y and mode = xy', function() { + describe('with scaleMode = y and mode = xy', function () { const config = { type: 'line', data, @@ -173,11 +179,11 @@ describe('zoom with wheel', function() { x: { type: 'linear', min: 1, - max: 10 + max: 10, }, y: { - type: 'linear' - } + type: 'linear', + }, }, plugins: { zoom: { @@ -186,82 +192,80 @@ describe('zoom with wheel', function() { enabled: true, }, mode: 'xy', - scaleMode: 'y' - } - } - } - } - }; + scaleMode: 'y', + }, + }, + }, + }, + } - describe('Wheel under Y scale', function() { - it('should be applied on Y, but not on X scales.', function() { - const chart = window.acquireChart(config); + describe('Wheel under Y scale', function () { + it('should be applied on Y, but not on X scales.', function () { + const chart = window.acquireChart(config) - const scaleX = chart.scales.x; - const scaleY = chart.scales.y; + const scaleX = chart.scales.x + const scaleY = chart.scales.y - const oldMinX = scaleX.options.min; - const oldMaxX = scaleX.options.max; - const oldMinY = scaleY.options.min; - const oldMaxY = scaleY.options.max; + const oldMinX = scaleX.options.min + const oldMaxX = scaleX.options.max + const oldMinY = scaleY.options.min + const oldMaxY = scaleY.options.max const wheelEv = { x: scaleY.left + (scaleY.right - scaleY.left) / 2, y: scaleY.top + (scaleY.bottom - scaleY.top) / 2, - deltaY: 1 - }; + deltaY: 1, + } - jasmine.triggerWheelEvent(chart, wheelEv); + jasmine.triggerWheelEvent(chart, wheelEv) - expect(scaleX.options.min).toEqual(oldMinX); - expect(scaleX.options.max).toEqual(oldMaxX); - expect(scaleY.options.min).not.toEqual(oldMinY); - expect(scaleY.options.max).not.toEqual(oldMaxY); - }); - }); + expect(scaleX.options.min).toEqual(oldMinX) + expect(scaleX.options.max).toEqual(oldMaxX) + expect(scaleY.options.min).not.toEqual(oldMinY) + expect(scaleY.options.max).not.toEqual(oldMaxY) + }) + }) - describe('Wheel not under Y scale', function() { - it('should be applied on X and Y scales.', function() { - const chart = window.acquireChart(config); + describe('Wheel not under Y scale', function () { + it('should be applied on X and Y scales.', function () { + const chart = window.acquireChart(config) - const scaleX = chart.scales.x; - const scaleY = chart.scales.y; + const scaleX = chart.scales.x + const scaleY = chart.scales.y - const oldMinX = scaleX.options.min; - const oldMaxX = scaleX.options.max; - const oldMinY = scaleY.options.min; - const oldMaxY = scaleY.options.max; + const oldMinX = scaleX.options.min + const oldMaxX = scaleX.options.max + const oldMinY = scaleY.options.min + const oldMaxY = scaleY.options.max const wheelEv = { x: scaleX.getPixelForValue(1.5), y: scaleY.getPixelForValue(1.1), - deltaY: 1 - }; + deltaY: 1, + } - jasmine.triggerWheelEvent(chart, wheelEv); + jasmine.triggerWheelEvent(chart, wheelEv) - expect(scaleX.options.min).not.toEqual(oldMinX); - expect(scaleX.options.max).not.toEqual(oldMaxX); - expect(scaleY.options.min).not.toEqual(oldMinY); - expect(scaleY.options.max).not.toEqual(oldMaxY); - }); - }); - }); + expect(scaleX.options.min).not.toEqual(oldMinX) + expect(scaleX.options.max).not.toEqual(oldMaxX) + expect(scaleY.options.min).not.toEqual(oldMinY) + expect(scaleY.options.max).not.toEqual(oldMaxY) + }) + }) + }) - describe('with logarithmic scale', function() { - it('should zoom correctly when mouse in center of chart', function() { + describe('with logarithmic scale', function () { + it('should zoom correctly when mouse in center of chart', function () { const config = { type: 'line', data: { - datasets: [ - {data: [1, 10, 100, 1000, 10000]} - ], + datasets: [{ data: [1, 10, 100, 1000, 10000] }], }, options: { scales: { y: { - type: 'logarithmic' - } + type: 'logarithmic', + }, }, plugins: { zoom: { @@ -270,73 +274,73 @@ describe('zoom with wheel', function() { wheel: { enabled: true, }, - } - } - } - } - }; - const chart = window.acquireChart(config); - const scaleY = chart.scales.y; + }, + }, + }, + }, + } + const chart = window.acquireChart(config) + const scaleY = chart.scales.y const zoomIn = { x: Math.round(scaleY.left + (scaleY.right - scaleY.left) / 2), y: Math.round(scaleY.top + (scaleY.bottom - scaleY.top) / 2), - deltaY: -1 - }; + deltaY: -1, + } const zoomOut = { ...zoomIn, - deltaY: 1 - }; + deltaY: 1, + } - expect(scaleY.min).toBe(1); - expect(scaleY.max).toBe(10000); + expect(scaleY.min).toBe(1) + expect(scaleY.max).toBe(10000) - jasmine.triggerWheelEvent(chart, zoomIn); + jasmine.triggerWheelEvent(chart, zoomIn) - expect(scaleY.min).toBeCloseTo(1.6, 1); - expect(scaleY.max).toBeCloseTo(6310, -1); + expect(scaleY.min).toBeCloseTo(1.6, 1) + expect(scaleY.max).toBeCloseTo(6310, -1) - jasmine.triggerWheelEvent(chart, zoomIn); + jasmine.triggerWheelEvent(chart, zoomIn) - expect(scaleY.min).toBeCloseTo(2.4, 1); - expect(scaleY.max).toBeCloseTo(4170, -1); + expect(scaleY.min).toBeCloseTo(2.4, 1) + expect(scaleY.max).toBeCloseTo(4170, -1) - jasmine.triggerWheelEvent(chart, zoomOut); - jasmine.triggerWheelEvent(chart, zoomOut); + jasmine.triggerWheelEvent(chart, zoomOut) + jasmine.triggerWheelEvent(chart, zoomOut) - expect(scaleY.min).toBe(1); - expect(scaleY.max).toBe(10000); + expect(scaleY.min).toBe(1) + expect(scaleY.max).toBe(10000) - chart.resetZoom(); + chart.resetZoom() - expect(scaleY.min).toBe(1); - expect(scaleY.max).toBe(10000); + expect(scaleY.min).toBe(1) + expect(scaleY.max).toBe(10000) - jasmine.triggerWheelEvent(chart, zoomOut); + jasmine.triggerWheelEvent(chart, zoomOut) - expect(scaleY.min).toBeCloseTo(0.6, 1); - expect(scaleY.max).toBeCloseTo(16700, -2); + expect(scaleY.min).toBeCloseTo(0.6, 1) + expect(scaleY.max).toBeCloseTo(16700, -2) - jasmine.triggerWheelEvent(chart, zoomIn); + jasmine.triggerWheelEvent(chart, zoomIn) - expect(scaleY.min).toBeCloseTo(1); - expect(scaleY.max).toBeCloseTo(10000); - }); - }); + expect(scaleY.min).toBeCloseTo(1) + expect(scaleY.max).toBeCloseTo(10000) + }) + }) - it('should respect aspectRatio when mode = xy', function() { + it('should respect aspectRatio when mode = xy', function () { const chart = window.acquireChart({ type: 'line', data, options: { scales: { x: { - type: 'linear' + type: 'linear', }, y: { - type: 'linear' - } + type: 'linear', + }, }, plugins: { legend: false, @@ -347,35 +351,35 @@ describe('zoom with wheel', function() { enabled: true, maintainAspectRatio: true, }, - mode: 'xy' - } - } - } - } - }); + mode: 'xy', + }, + }, + }, + }, + }) - const scaleX = chart.scales.x; - const scaleY = chart.scales.y; + const scaleX = chart.scales.x + const scaleY = chart.scales.y jasmine.triggerMouseEvent(chart, 'mousedown', { x: scaleX.getPixelForValue(1.5), - y: scaleY.getPixelForValue(1.1) - }); + y: scaleY.getPixelForValue(1.1), + }) jasmine.triggerMouseEvent(chart, 'mouseup', { x: scaleX.getPixelForValue(2.8), - y: scaleY.getPixelForValue(1.7) - }); - - expect(scaleX.options.min).toBeCloseTo(1.5); - expect(scaleX.options.max).toBeCloseTo(2.1); - expect(scaleY.options.min).toBeCloseTo(1.1); - expect(scaleY.options.max).toBeCloseTo(1.7); - }); - - describe('events', function() { - it('should call onZoomStart, onZoom and onZoomComplete', function(done) { - const startSpy = jasmine.createSpy('start'); - const zoomSpy = jasmine.createSpy('zoom'); + y: scaleY.getPixelForValue(1.7), + }) + + expect(scaleX.options.min).toBeCloseTo(1.5) + expect(scaleX.options.max).toBeCloseTo(2.1) + expect(scaleY.options.min).toBeCloseTo(1.1) + expect(scaleY.options.max).toBeCloseTo(1.7) + }) + + describe('events', function () { + it('should call onZoomStart, onZoom and onZoomComplete', function (done) { + const startSpy = jasmine.createSpy('start') + const zoomSpy = jasmine.createSpy('zoom') const chart = window.acquireChart({ type: 'scatter', data, @@ -389,27 +393,27 @@ describe('zoom with wheel', function() { mode: 'xy', onZoomStart: startSpy, onZoom: zoomSpy, - onZoomComplete: () => done() - } - } - } - } - }); + onZoomComplete: () => done(), + }, + }, + }, + }, + }) const wheelEv = { x: chart.scales.x.getPixelForValue(1.5), y: chart.scales.y.getPixelForValue(1.1), - deltaY: 1 - }; + deltaY: 1, + } - jasmine.triggerWheelEvent(chart, wheelEv); + jasmine.triggerWheelEvent(chart, wheelEv) - expect(startSpy).toHaveBeenCalled(); - expect(zoomSpy).toHaveBeenCalledWith({chart, trigger: 'wheel'}); - expect(chart.scales.x.min).not.toBe(1); - }); + expect(startSpy).toHaveBeenCalled() + expect(zoomSpy).toHaveBeenCalledWith({ chart, trigger: 'wheel' }) + expect(chart.scales.x.min).not.toBe(1) + }) - it('should detect configuration change', function() { - const startSpy = jasmine.createSpy('started'); + it('should detect configuration change', function () { + const startSpy = jasmine.createSpy('started') const chart = window.acquireChart({ type: 'scatter', data, @@ -421,31 +425,31 @@ describe('zoom with wheel', function() { enabled: false, }, mode: 'xy', - onZoomStart: startSpy - } - } - } - } - }); + onZoomStart: startSpy, + }, + }, + }, + }, + }) const wheelEv = { x: chart.scales.x.getPixelForValue(1.5), y: chart.scales.y.getPixelForValue(1.1), - deltaY: 1 - }; - jasmine.triggerWheelEvent(chart, wheelEv); - expect(startSpy).not.toHaveBeenCalled(); - expect(chart.scales.x.min).toBe(1); - - chart.options.plugins.zoom.zoom.wheel.enabled = true; - chart.update(); - - jasmine.triggerWheelEvent(chart, wheelEv); - expect(startSpy).toHaveBeenCalled(); - expect(chart.scales.x.min).not.toBe(1); - }); - - it('should call onZoomRejected when onZoomStart returns false', function() { - const rejectSpy = jasmine.createSpy('rejected'); + deltaY: 1, + } + jasmine.triggerWheelEvent(chart, wheelEv) + expect(startSpy).not.toHaveBeenCalled() + expect(chart.scales.x.min).toBe(1) + + chart.options.plugins.zoom.zoom.wheel.enabled = true + chart.update() + + jasmine.triggerWheelEvent(chart, wheelEv) + expect(startSpy).toHaveBeenCalled() + expect(chart.scales.x.min).not.toBe(1) + }) + + it('should call onZoomRejected when onZoomStart returns false', function () { + const rejectSpy = jasmine.createSpy('rejected') const chart = window.acquireChart({ type: 'scatter', data, @@ -458,20 +462,20 @@ describe('zoom with wheel', function() { }, mode: 'xy', onZoomStart: () => false, - onZoomRejected: rejectSpy - } - } - } - } - }); + onZoomRejected: rejectSpy, + }, + }, + }, + }, + }) const wheelEv = { x: chart.scales.x.getPixelForValue(1.5), y: chart.scales.y.getPixelForValue(1.1), - deltaY: 1 - }; - jasmine.triggerWheelEvent(chart, wheelEv); - expect(rejectSpy).toHaveBeenCalled(); - expect(chart.scales.x.min).toBe(1); - }); - }); -}); + deltaY: 1, + } + jasmine.triggerWheelEvent(chart, wheelEv) + expect(rejectSpy).toHaveBeenCalled() + expect(chart.scales.x.min).toBe(1) + }) + }) +}) diff --git a/types/tests/.eslintrc.yml b/test/types/.eslintrc.yml similarity index 100% rename from types/tests/.eslintrc.yml rename to test/types/.eslintrc.yml diff --git a/types/tests/exports.ts b/test/types/exports.ts similarity index 74% rename from types/tests/exports.ts rename to test/types/exports.ts index 03d2a6c05..bfdb16ae1 100644 --- a/types/tests/exports.ts +++ b/test/types/exports.ts @@ -1,5 +1,5 @@ -import { Chart } from 'chart.js'; -import Zoom, { pan, zoom, resetZoom } from '../index'; +import {Chart} from 'chart.js'; +import Zoom, {pan, zoom, resetZoom} from '../../src'; Chart.register(Zoom); Chart.unregister(Zoom); @@ -35,7 +35,7 @@ const chart = new Chart('id', { pan: { enabled: true, mode: 'x', - onPanStart({ event }) { + onPanStart({event}) { return event.distance > 2; } }, @@ -53,11 +53,11 @@ const chart = new Chart('id', { chart.resetZoom(); chart.zoom(1.1); -chart.zoom({ x: 1, y: 1.1, focalPoint: { x: 10, y: 10 } }, 'zoom'); +chart.zoom({x: 1, y: 1.1, focalPoint: {x: 10, y: 10}}, 'zoom'); chart.pan(10); -chart.pan({ x: 10, y: 20 }, [chart.scales.x]); +chart.pan({x: 10, y: 20}, [chart.scales.x]); pan(chart, -42, undefined, 'zoom'); -zoom(chart, { x: 1, y: 1.1, focalPoint: { x: 10, y: 10 } }, 'zoom'); +zoom(chart, {x: 1, y: 1.1, focalPoint: {x: 10, y: 10}}, 'zoom'); resetZoom(chart, 'none'); diff --git a/types/tests/tsconfig.json b/test/types/tsconfig.json similarity index 65% rename from types/tests/tsconfig.json rename to test/types/tsconfig.json index 292618bd5..e4d84d654 100644 --- a/types/tests/tsconfig.json +++ b/test/types/tsconfig.json @@ -4,7 +4,9 @@ "moduleResolution": "Node", "alwaysStrict": true, "strict": true, - "noEmit": true + "noEmit": true, + "allowSyntheticDefaultImports": true, + "resolveJsonModule": true, }, "include": [ "*.ts", diff --git a/tsconfig.json b/tsconfig.json index 73abbf1d2..20148b6a4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,16 +1,39 @@ { "compilerOptions": { - "target": "ES6", - "moduleResolution": "Node", - "allowSyntheticDefaultImports": true, - "allowJs": true, - "checkJs": true, - "noEmit": true, + /* Type Checking */ "alwaysStrict": true, - "skipLibCheck": true, + "strict": true, "strictBindCallApply": true, "strictFunctionTypes": true, - "resolveJsonModule": true + "noFallthroughCasesInSwitch": true, + "noImplicitOverride": true, + "noImplicitReturns": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + + /* Modules */ + "module": "ES2022", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "rootDir": "src", + + /* Emit */ + "declaration": true, + "declarationMap": true, + "verbatimModuleSyntax": true, + "inlineSourceMap": true, + "outDir": "dist", + + /* JavaScript Support */ + "allowJs": true, + "checkJs": true, + + /* Interop Constraints */ + "allowSyntheticDefaultImports": true, + + /* Language and Environment */ + "target": "ES2022", + "lib": ["ES2022", "DOM"], }, "typedocOptions": { "name": "chartjs-plugin-zoom", @@ -21,7 +44,10 @@ "out": "./dist/docs/typedoc" }, "include": [ - "./src/**/*.js", + "./src/**/*.ts", "./types" - ] + ], + "exclude": [ + "./src/**/*.spec.ts", + ], } diff --git a/tsconfig.spec.json b/tsconfig.spec.json new file mode 100644 index 000000000..979e8c056 --- /dev/null +++ b/tsconfig.spec.json @@ -0,0 +1,37 @@ +{ + "compilerOptions": { + /* Type Checking */ + "alwaysStrict": true, + "strict": true, + "strictBindCallApply": true, + "strictFunctionTypes": true, + "noFallthroughCasesInSwitch": true, + "noImplicitOverride": true, + "noImplicitReturns": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + + /* Modules */ + "module": "ES2022", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "rootDir": "src", + + /* Emit */ + "declaration": true, + "declarationMap": true, + "verbatimModuleSyntax": true, + "inlineSourceMap": true, + "outDir": "build", + + /* Interop Constraints */ + "allowSyntheticDefaultImports": true, + + /* Language and Environment */ + "target": "ES2022", + "lib": ["ES2022", "DOM"], + }, + "include": [ + "./src/**/*.spec.ts", + ] +} diff --git a/types/index.d.ts b/types/index.d.ts deleted file mode 100644 index 3a118c5de..000000000 --- a/types/index.d.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { Plugin, ChartType, Chart, Scale, UpdateMode, ScaleTypeRegistry, ChartTypeRegistry } from 'chart.js'; -import { LimitOptions, ZoomPluginOptions } from './options'; - -type Point = { x: number, y: number }; -type DistributiveArray = [T] extends [unknown] ? Array : never - -export type PanAmount = number | Partial; -export type ScaleRange = { min: number, max: number }; -export type ZoomAmount = number | Partial & { focalPoint?: Point }; - -declare module 'chart.js' { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - interface PluginOptionsByType { - zoom: ZoomPluginOptions; - } - - enum UpdateModeEnum { - zoom = 'zoom' - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - interface Chart, TLabel = unknown> { - pan(pan: PanAmount, scales?: Scale[], mode?: UpdateMode): void; - zoom(zoom: ZoomAmount, mode?: UpdateMode): void; - zoomRect(p0: Point, p1: Point, mode?: UpdateMode): void; - zoomScale(id: string, range: ScaleRange, mode?: UpdateMode): void; - resetZoom(mode?: UpdateMode): void; - getZoomLevel(): number; - getInitialScaleBounds(): Record; - getZoomedScaleBounds(): Record; - isZoomedOrPanned(): boolean; - isZoomingOrPanning(): boolean; - } -} - -export type ZoomFunction = (scale: Scale, zoom: number, center: Point, limits: LimitOptions) => boolean; -export type ZoomRectFunction = (scale: Scale, from: number, to: number, limits: LimitOptions) => boolean; -export type PanFunction = (scale: Scale, delta: number, limits: LimitOptions) => boolean; - -type ScaleFunctions = { - [scaleType in keyof ScaleTypeRegistry]?: T | undefined; -} & { - default: T; -}; - -declare const Zoom: Plugin & { - zoomFunctions: ScaleFunctions; - zoomRectFunctions: ScaleFunctions; - panFunctions: ScaleFunctions; -}; - -export default Zoom; - -export function pan(chart: Chart, amount: PanAmount, scales?: Scale[], mode?: UpdateMode): void; -export function zoom(chart: Chart, amount: ZoomAmount, mode?: UpdateMode): void; -export function zoomRect(chart: Chart, p0: Point, p1: Point, mode?: UpdateMode): void; -export function zoomScale(chart: Chart, scaleId: string, range: ScaleRange, mode?: UpdateMode): void; -export function resetZoom(chart: Chart, mode?: UpdateMode): void; -export function getZoomLevel(chart: Chart): number; -export function getInitialScaleBounds(chart: Chart): Record; -export function getZoomedScaleBounds(chart: Chart): Record; -export function isZoomedOrPanned(chart: Chart): boolean; -export function isZoomingOrPanning(chart: Chart): boolean; From 408bf14104ebfa12a4f2059f9ec3232d93500829 Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Fri, 29 Nov 2024 20:50:13 +0200 Subject: [PATCH 02/13] fix: windows lint --- .prettierrc | 1 + 1 file changed, 1 insertion(+) diff --git a/.prettierrc b/.prettierrc index 9e7d3036c..c8184aff5 100644 --- a/.prettierrc +++ b/.prettierrc @@ -5,3 +5,4 @@ semi: false tabWidth: 2 useTabs: false trailingComma: 'es5' +endOfLine: 'auto' From ad0488ffdbe33d62afd284ac75181026d058f369 Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Fri, 29 Nov 2024 21:09:19 +0200 Subject: [PATCH 03/13] fix: docs-build (= remove typedoc) --- docs/.vuepress/{config.js => config.ts} | 22 +- package-lock.json | 793 +++--------------------- package.json | 8 +- scripts/docs-config.sh | 2 +- tsconfig.json | 4 +- 5 files changed, 107 insertions(+), 722 deletions(-) rename docs/.vuepress/{config.js => config.ts} (91%) diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.ts similarity index 91% rename from docs/.vuepress/config.js rename to docs/.vuepress/config.ts index b4bc28290..516080743 100755 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.ts @@ -1,8 +1,10 @@ -const path = require('path'); +import * as path from 'path'; +import { DefaultThemeConfig, defineConfig, PluginTuple } from 'vuepress/config'; + const docsVersion = "VERSION"; const base = process.env.NODE_ENV === "development" ? '/chartjs-plugin-zoom/master/' : `/chartjs-plugin-zoom/${docsVersion}/`; -module.exports = { +export default defineConfig({ title: 'chartjs-plugin-zoom', description: 'A zoom and pan plugin for Chart.js >= 3.0.0', theme: 'chartjs', @@ -19,10 +21,11 @@ module.exports = { {base: '/samples', alternative: ['basic']}, ], }], + /* [ 'vuepress-plugin-typedoc', { - entryPoints: ['../../types/index.d.ts'], + entryPoints: ['../../src/index.ts'], hideInPageTOC: true, tsconfig: 'tsconfig.json', sidebar: { @@ -31,10 +34,11 @@ module.exports = { }, }, ], + */ ['@simonbrunel/vuepress-plugin-versions', { filters: { suffix: (tag) => tag ? ` (${tag})` : '', - title: (v, vars) => window.location.href.includes('master') ? 'Development (master)' : v + (vars.tag ? ` (${tag})` : ''), + title: (v, vars) => window.location.href.includes('master') ? 'Development (master)' : v + (vars.tag ? ` (${vars.tag})` : ''), }, menu: { text: '{{version|title}}', @@ -72,7 +76,7 @@ module.exports = { ] }, }], - ], + ] as PluginTuple[], chainWebpack: (config) => { config.module .rule('chart.js') @@ -109,7 +113,7 @@ module.exports = { nav: [ {text: 'Home', link: '/'}, {text: 'Guide', link: '/guide/'}, - {text: 'API', link: '/api/'}, + // {text: 'API', link: '/api/'}, {text: 'Samples', link: `/samples/`}, { text: 'Ecosystem', @@ -163,6 +167,6 @@ module.exports = { 'fetch-data', 'api', ], - } - } -}; + } as any + } as DefaultThemeConfig +}); diff --git a/package-lock.json b/package-lock.json index b1cc3cdb5..3094b4082 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,8 +23,7 @@ "@simonbrunel/vuepress-plugin-versions": "^0.2.0", "@swc/cli": "^0.5.1", "@swc/core": "^1.9.3", - "@types/jasmine": "^5.1.4", - "@types/linkify-it": "^3.0.5", + "@types/jasmine": "^5.1.5", "@typescript-eslint/eslint-plugin": "^5.4.0", "@typescript-eslint/parser": "^5.62.0", "babel-loader": "^8.3.0", @@ -53,16 +52,13 @@ "karma-spec-reporter": "0.0.36", "ng-hammerjs": "^2.0.8", "pixelmatch": "^6.0.0", - "rollup": "^4.27.3", + "rollup": "^4.27.4", "rollup-plugin-cleanup": "^3.2.1", "rollup-plugin-istanbul": "^5.0.0", - "typedoc": "^0.26.11", - "typedoc-plugin-markdown": "^4.2.10", "typescript": "5.6", "vuepress": "^1.8.2", "vuepress-plugin-flexsearch": "^0.3.0", "vuepress-plugin-redirect": "^1.2.5", - "vuepress-plugin-typedoc": "^0.9.2", "vuepress-theme-chartjs": "^0.2.0" }, "peerDependencies": { @@ -2433,9 +2429,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.27.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.27.3.tgz", - "integrity": "sha512-EzxVSkIvCFxUd4Mgm4xR9YXrcp976qVaHnqom/Tgm+vU79k4vV4eYTjmRvGfeoW8m9LVcsAy/lGjcgVegKEhLQ==", + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.27.4.tgz", + "integrity": "sha512-2Y3JT6f5MrQkICUyRVCw4oa0sutfAsgaSsb0Lmmy1Wi2y7X5vT9Euqw4gOsCyy0YfKURBg35nhUKZS4mDcfULw==", "cpu": [ "arm" ], @@ -2446,9 +2442,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.27.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.27.3.tgz", - "integrity": "sha512-LJc5pDf1wjlt9o/Giaw9Ofl+k/vLUaYsE2zeQGH85giX2F+wn/Cg8b3c5CDP3qmVmeO5NzwVUzQQxwZvC2eQKw==", + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.27.4.tgz", + "integrity": "sha512-wzKRQXISyi9UdCVRqEd0H4cMpzvHYt1f/C3CoIjES6cG++RHKhrBj2+29nPF0IB5kpy9MS71vs07fvrNGAl/iA==", "cpu": [ "arm64" ], @@ -2459,9 +2455,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.27.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.27.3.tgz", - "integrity": "sha512-OuRysZ1Mt7wpWJ+aYKblVbJWtVn3Cy52h8nLuNSzTqSesYw1EuN6wKp5NW/4eSre3mp12gqFRXOKTcN3AI3LqA==", + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.27.4.tgz", + "integrity": "sha512-PlNiRQapift4LNS8DPUHuDX/IdXiLjf8mc5vdEmUR0fF/pyy2qWwzdLjB+iZquGr8LuN4LnUoSEvKRwjSVYz3Q==", "cpu": [ "arm64" ], @@ -2472,9 +2468,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.27.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.27.3.tgz", - "integrity": "sha512-xW//zjJMlJs2sOrCmXdB4d0uiilZsOdlGQIC/jjmMWT47lkLLoB1nsNhPUcnoqyi5YR6I4h+FjBpILxbEy8JRg==", + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.27.4.tgz", + "integrity": "sha512-o9bH2dbdgBDJaXWJCDTNDYa171ACUdzpxSZt+u/AAeQ20Nk5x+IhA+zsGmrQtpkLiumRJEYef68gcpn2ooXhSQ==", "cpu": [ "x64" ], @@ -2485,9 +2481,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.27.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.27.3.tgz", - "integrity": "sha512-58E0tIcwZ+12nK1WiLzHOD8I0d0kdrY/+o7yFVPRHuVGY3twBwzwDdTIBGRxLmyjciMYl1B/U515GJy+yn46qw==", + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.27.4.tgz", + "integrity": "sha512-NBI2/i2hT9Q+HySSHTBh52da7isru4aAAo6qC3I7QFVsuhxi2gM8t/EI9EVcILiHLj1vfi+VGGPaLOUENn7pmw==", "cpu": [ "arm64" ], @@ -2498,9 +2494,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.27.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.27.3.tgz", - "integrity": "sha512-78fohrpcVwTLxg1ZzBMlwEimoAJmY6B+5TsyAZ3Vok7YabRBUvjYTsRXPTjGEvv/mfgVBepbW28OlMEz4w8wGA==", + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.27.4.tgz", + "integrity": "sha512-wYcC5ycW2zvqtDYrE7deary2P2UFmSh85PUpAx+dwTCO9uw3sgzD6Gv9n5X4vLaQKsrfTSZZ7Z7uynQozPVvWA==", "cpu": [ "x64" ], @@ -2511,9 +2507,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.27.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.27.3.tgz", - "integrity": "sha512-h2Ay79YFXyQi+QZKo3ISZDyKaVD7uUvukEHTOft7kh00WF9mxAaxZsNs3o/eukbeKuH35jBvQqrT61fzKfAB/Q==", + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.27.4.tgz", + "integrity": "sha512-9OwUnK/xKw6DyRlgx8UizeqRFOfi9mf5TYCw1uolDaJSbUmBxP85DE6T4ouCMoN6pXw8ZoTeZCSEfSaYo+/s1w==", "cpu": [ "arm" ], @@ -2524,9 +2520,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.27.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.27.3.tgz", - "integrity": "sha512-Sv2GWmrJfRY57urktVLQ0VKZjNZGogVtASAgosDZ1aUB+ykPxSi3X1nWORL5Jk0sTIIwQiPH7iE3BMi9zGWfkg==", + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.27.4.tgz", + "integrity": "sha512-Vgdo4fpuphS9V24WOV+KwkCVJ72u7idTgQaBoLRD0UxBAWTF9GWurJO9YD9yh00BzbkhpeXtm6na+MvJU7Z73A==", "cpu": [ "arm" ], @@ -2537,9 +2533,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.27.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.27.3.tgz", - "integrity": "sha512-FPoJBLsPW2bDNWjSrwNuTPUt30VnfM8GPGRoLCYKZpPx0xiIEdFip3dH6CqgoT0RnoGXptaNziM0WlKgBc+OWQ==", + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.27.4.tgz", + "integrity": "sha512-pleyNgyd1kkBkw2kOqlBx+0atfIIkkExOTiifoODo6qKDSpnc6WzUY5RhHdmTdIJXBdSnh6JknnYTtmQyobrVg==", "cpu": [ "arm64" ], @@ -2550,9 +2546,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.27.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.27.3.tgz", - "integrity": "sha512-TKxiOvBorYq4sUpA0JT+Fkh+l+G9DScnG5Dqx7wiiqVMiRSkzTclP35pE6eQQYjP4Gc8yEkJGea6rz4qyWhp3g==", + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.27.4.tgz", + "integrity": "sha512-caluiUXvUuVyCHr5DxL8ohaaFFzPGmgmMvwmqAITMpV/Q+tPoaHZ/PWa3t8B2WyoRcIIuu1hkaW5KkeTDNSnMA==", "cpu": [ "arm64" ], @@ -2563,9 +2559,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.27.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.27.3.tgz", - "integrity": "sha512-v2M/mPvVUKVOKITa0oCFksnQQ/TqGrT+yD0184/cWHIu0LoIuYHwox0Pm3ccXEz8cEQDLk6FPKd1CCm+PlsISw==", + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.27.4.tgz", + "integrity": "sha512-FScrpHrO60hARyHh7s1zHE97u0KlT/RECzCKAdmI+LEoC1eDh/RDji9JgFqyO+wPDb86Oa/sXkily1+oi4FzJQ==", "cpu": [ "ppc64" ], @@ -2576,9 +2572,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.27.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.27.3.tgz", - "integrity": "sha512-LdrI4Yocb1a/tFVkzmOE5WyYRgEBOyEhWYJe4gsDWDiwnjYKjNs7PS6SGlTDB7maOHF4kxevsuNBl2iOcj3b4A==", + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.27.4.tgz", + "integrity": "sha512-qyyprhyGb7+RBfMPeww9FlHwKkCXdKHeGgSqmIXw9VSUtvyFZ6WZRtnxgbuz76FK7LyoN8t/eINRbPUcvXB5fw==", "cpu": [ "riscv64" ], @@ -2589,9 +2585,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.27.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.27.3.tgz", - "integrity": "sha512-d4wVu6SXij/jyiwPvI6C4KxdGzuZOvJ6y9VfrcleHTwo68fl8vZC5ZYHsCVPUi4tndCfMlFniWgwonQ5CUpQcA==", + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.27.4.tgz", + "integrity": "sha512-PFz+y2kb6tbh7m3A7nA9++eInGcDVZUACulf/KzDtovvdTizHpZaJty7Gp0lFwSQcrnebHOqxF1MaKZd7psVRg==", "cpu": [ "s390x" ], @@ -2602,9 +2598,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.27.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.27.3.tgz", - "integrity": "sha512-/6bn6pp1fsCGEY5n3yajmzZQAh+mW4QPItbiWxs69zskBzJuheb3tNynEjL+mKOsUSFK11X4LYF2BwwXnzWleA==", + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.27.4.tgz", + "integrity": "sha512-Ni8mMtfo+o/G7DVtweXXV/Ol2TFf63KYjTtoZ5f078AUgJTmaIJnj4JFU7TK/9SVWTaSJGxPi5zMDgK4w+Ez7Q==", "cpu": [ "x64" ], @@ -2615,9 +2611,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.27.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.27.3.tgz", - "integrity": "sha512-nBXOfJds8OzUT1qUreT/en3eyOXd2EH5b0wr2bVB5999qHdGKkzGzIyKYaKj02lXk6wpN71ltLIaQpu58YFBoQ==", + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.27.4.tgz", + "integrity": "sha512-5AeeAF1PB9TUzD+3cROzFTnAJAcVUGLuR8ng0E0WXGkYhp6RD6L+6szYVX+64Rs0r72019KHZS1ka1q+zU/wUw==", "cpu": [ "x64" ], @@ -2628,9 +2624,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.27.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.27.3.tgz", - "integrity": "sha512-ogfbEVQgIZOz5WPWXF2HVb6En+kWzScuxJo/WdQTqEgeyGkaa2ui5sQav9Zkr7bnNCLK48uxmmK0TySm22eiuw==", + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.27.4.tgz", + "integrity": "sha512-yOpVsA4K5qVwu2CaS3hHxluWIK5HQTjNV4tWjQXluMiiiu4pJj4BN98CvxohNCpcjMeTXk/ZMJBRbgRg8HBB6A==", "cpu": [ "arm64" ], @@ -2641,9 +2637,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.27.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.27.3.tgz", - "integrity": "sha512-ecE36ZBMLINqiTtSNQ1vzWc5pXLQHlf/oqGp/bSbi7iedcjcNb6QbCBNG73Euyy2C+l/fn8qKWEwxr+0SSfs3w==", + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.27.4.tgz", + "integrity": "sha512-KtwEJOaHAVJlxV92rNYiG9JQwQAdhBlrjNRp7P9L8Cb4Rer3in+0A+IPhJC9y68WAi9H0sX4AiG2NTsVlmqJeQ==", "cpu": [ "ia32" ], @@ -2654,9 +2650,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.27.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.27.3.tgz", - "integrity": "sha512-vliZLrDmYKyaUoMzEbMTg2JkerfBjn03KmAw9CykO0Zzkzoyd7o3iZNam/TpyWNjNT+Cz2iO3P9Smv2wgrR+Eg==", + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.27.4.tgz", + "integrity": "sha512-3j4jx1TppORdTAoBJRd+/wJRGCPC0ETWkXOecJ6PPZLj6SptXkrXcNqdj0oclbKML6FkQltdz7bBA3rUSirZug==", "cpu": [ "x64" ], @@ -2695,57 +2691,6 @@ "node": ">=6" } }, - "node_modules/@shikijs/core": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.23.0.tgz", - "integrity": "sha512-J4Fo22oBlfRHAXec+1AEzcowv+Qdf4ZQkuP/X/UHYH9+KA9LvyFXSXyS+HxuBRFfon+u7bsmKdRBjoZlbDVRkQ==", - "dev": true, - "dependencies": { - "@shikijs/engine-javascript": "1.23.0", - "@shikijs/engine-oniguruma": "1.23.0", - "@shikijs/types": "1.23.0", - "@shikijs/vscode-textmate": "^9.3.0", - "@types/hast": "^3.0.4", - "hast-util-to-html": "^9.0.3" - } - }, - "node_modules/@shikijs/engine-javascript": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-1.23.0.tgz", - "integrity": "sha512-CcrppseWShG+8Efp1iil9divltuXVdCaU4iu+CKvzTGZO5RmXyAiSx668M7VbX8+s/vt1ZKu75Vn/jWi8O3G/Q==", - "dev": true, - "dependencies": { - "@shikijs/types": "1.23.0", - "@shikijs/vscode-textmate": "^9.3.0", - "oniguruma-to-es": "0.1.2" - } - }, - "node_modules/@shikijs/engine-oniguruma": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-1.23.0.tgz", - "integrity": "sha512-gS8bZLqVvmZXX+E5JUMJICsBp+kx6gj79MH/UEpKHKIqnUzppgbmEn6zLa6mB5D+sHse2gFei3YYJxQe1EzZXQ==", - "dev": true, - "dependencies": { - "@shikijs/types": "1.23.0", - "@shikijs/vscode-textmate": "^9.3.0" - } - }, - "node_modules/@shikijs/types": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-1.23.0.tgz", - "integrity": "sha512-HiwzsihRao+IbPk7FER/EQT/D0dEEK3n5LAtHDzL5iRT+JMblA7y9uitUnjEnHeLkKigNM+ZplrP7MuEyyc5kA==", - "dev": true, - "dependencies": { - "@shikijs/vscode-textmate": "^9.3.0", - "@types/hast": "^3.0.4" - } - }, - "node_modules/@shikijs/vscode-textmate": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-9.3.0.tgz", - "integrity": "sha512-jn7/7ky30idSkd/O5yDBfAnVt+JJpepofP/POZ1iMOxK59cOfqIgg/Dj0eFsjOTMw+4ycJN0uhZH/Eb0bs/EUA==", - "dev": true - }, "node_modules/@simonbrunel/vuepress-plugin-versions": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/@simonbrunel/vuepress-plugin-versions/-/vuepress-plugin-versions-0.2.0.tgz", @@ -3189,15 +3134,6 @@ "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.46.tgz", "integrity": "sha512-ynRvcq6wvqexJ9brDMS4BnBLzmr0e14d6ZJTEShTBWKymQiHwlAyGu0ZPEFI2Fh1U53F7tN9ufClWM5KvqkKOw==" }, - "node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "dev": true, - "dependencies": { - "@types/unist": "*" - } - }, "node_modules/@types/highlight.js": { "version": "9.12.4", "resolved": "https://registry.npmjs.org/@types/highlight.js/-/highlight.js-9.12.4.tgz", @@ -3232,9 +3168,9 @@ "dev": true }, "node_modules/@types/jasmine": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-5.1.4.tgz", - "integrity": "sha512-px7OMFO/ncXxixDe1zR13V1iycqWae0MxTaw62RpFlksUi5QuNWgQJFkTQjIOvrmutJbI7Fp2Y2N1F6D2R4G6w==", + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-5.1.5.tgz", + "integrity": "sha512-SaCZ3kM5NjOiJqMRYwHpLbTfUC2Dyk1KS3QanNFsUYPGTk70CWVK/J9ueun6zNhw/UkgV7xl8V4ZLQZNRbfnNw==", "dev": true }, "node_modules/@types/json-schema": { @@ -7532,16 +7468,6 @@ "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", "dev": true }, - "node_modules/ccount": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", - "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -7580,16 +7506,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/character-entities-html4": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", - "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/character-entities-legacy": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", @@ -7991,16 +7907,6 @@ "node": ">= 0.8" } }, - "node_modules/comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -9761,15 +9667,6 @@ "node": ">= 0.8" } }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/des.js": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", @@ -9796,19 +9693,6 @@ "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", "dev": true }, - "node_modules/devlop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", - "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", - "dev": true, - "dependencies": { - "dequal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/di": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", @@ -10092,12 +9976,6 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "node_modules/emoji-regex-xs": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz", - "integrity": "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==", - "dev": true - }, "node_modules/emojis-list": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", @@ -12506,48 +12384,6 @@ "node": ">= 0.4" } }, - "node_modules/hast-util-to-html": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.3.tgz", - "integrity": "sha512-M17uBDzMJ9RPCqLMO92gNNUDuBSq10a25SDBI08iCCxmorf4Yy6sYHK57n9WAbRAAaU+DuR4W6GN9K4DFZesYg==", - "dev": true, - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "ccount": "^2.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-whitespace": "^3.0.0", - "html-void-elements": "^3.0.0", - "mdast-util-to-hast": "^13.0.0", - "property-information": "^6.0.0", - "space-separated-tokens": "^2.0.0", - "stringify-entities": "^4.0.0", - "zwitch": "^2.0.4" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-html/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "dev": true - }, - "node_modules/hast-util-whitespace": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", - "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", - "dev": true, - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -12683,16 +12519,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/html-void-elements": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", - "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/htmlparser2": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", @@ -14413,6 +14239,7 @@ "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", "dev": true, + "peer": true, "dependencies": { "uc.micro": "^2.0.0" } @@ -14590,12 +14417,6 @@ "yallist": "^3.0.2" } }, - "node_modules/lunr": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", - "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", - "dev": true - }, "node_modules/magic-string": { "version": "0.30.12", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz", @@ -14646,6 +14467,7 @@ "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", "dev": true, + "peer": true, "dependencies": { "argparse": "^2.0.1", "entities": "^4.4.0", @@ -14733,7 +14555,8 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "dev": true, + "peer": true }, "node_modules/md5.js": { "version": "1.3.5", @@ -14763,36 +14586,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-to-hast": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", - "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", - "dev": true, - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@ungap/structured-clone": "^1.0.0", - "devlop": "^1.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "trim-lines": "^3.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-hast/node_modules/@types/mdast": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", - "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", - "dev": true, - "dependencies": { - "@types/unist": "*" - } - }, "node_modules/mdast-util-to-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz", @@ -14813,7 +14606,8 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", - "dev": true + "dev": true, + "peer": true }, "node_modules/media-typer": { "version": "0.3.0", @@ -14896,95 +14690,6 @@ "parse-entities": "^2.0.0" } }, - "node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-encode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", - "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-util-sanitize-uri": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", - "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-util-types": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.1.tgz", - "integrity": "sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -15846,17 +15551,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/oniguruma-to-es": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-0.1.2.tgz", - "integrity": "sha512-sBYKVJlIMB0WPO+tSu/NNB1ytSFeHyyJZ3Ayxfx3f/QUuXu0lvZk0VB4K7npmdlHSC0ldqanzh/sUSlAbgCTfw==", - "dev": true, - "dependencies": { - "emoji-regex-xs": "^1.0.0", - "regex": "^4.4.0", - "regex-recursion": "^4.1.0" - } - }, "node_modules/opencollective-postinstall": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", @@ -18054,16 +17748,6 @@ "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", "dev": true }, - "node_modules/property-information": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", - "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -18169,6 +17853,7 @@ "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", "dev": true, + "peer": true, "engines": { "node": ">=6" } @@ -18459,12 +18144,6 @@ "@babel/runtime": "^7.8.4" } }, - "node_modules/regex": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/regex/-/regex-4.4.0.tgz", - "integrity": "sha512-uCUSuobNVeqUupowbdZub6ggI5/JZkYyJdDogddJr60L764oxC2pMZov1fQ3wM9bdyzUILDG+Sqx6NAKAz9rKQ==", - "dev": true - }, "node_modules/regex-not": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", @@ -18503,21 +18182,6 @@ "node": ">=0.10.0" } }, - "node_modules/regex-recursion": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-4.2.1.tgz", - "integrity": "sha512-QHNZyZAeKdndD1G3bKAbBEKOSSK4KOHQrAJ01N1LJeb0SoH4DJIeFhp0uUpETgONifS4+P3sOgoA1dhzgrQvhA==", - "dev": true, - "dependencies": { - "regex-utilities": "^2.3.0" - } - }, - "node_modules/regex-utilities": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", - "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", - "dev": true - }, "node_modules/regexp.prototype.flags": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz", @@ -18955,9 +18619,9 @@ } }, "node_modules/rollup": { - "version": "4.27.3", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.27.3.tgz", - "integrity": "sha512-SLsCOnlmGt9VoZ9Ek8yBK8tAdmPHeppkw+Xa7yDlCEhDTvwYei03JlWo1fdc7YTfLZ4tD8riJCUyAgTbszk1fQ==", + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.27.4.tgz", + "integrity": "sha512-RLKxqHEMjh/RGLsDxAEsaLO3mWgyoU6x9w6n1ikAzet4B3gI2/3yP6PWY2p9QzRTh6MfEIXB3MwsOY0Iv3vNrw==", "dev": true, "dependencies": { "@types/estree": "1.0.6" @@ -18970,24 +18634,24 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.27.3", - "@rollup/rollup-android-arm64": "4.27.3", - "@rollup/rollup-darwin-arm64": "4.27.3", - "@rollup/rollup-darwin-x64": "4.27.3", - "@rollup/rollup-freebsd-arm64": "4.27.3", - "@rollup/rollup-freebsd-x64": "4.27.3", - "@rollup/rollup-linux-arm-gnueabihf": "4.27.3", - "@rollup/rollup-linux-arm-musleabihf": "4.27.3", - "@rollup/rollup-linux-arm64-gnu": "4.27.3", - "@rollup/rollup-linux-arm64-musl": "4.27.3", - "@rollup/rollup-linux-powerpc64le-gnu": "4.27.3", - "@rollup/rollup-linux-riscv64-gnu": "4.27.3", - "@rollup/rollup-linux-s390x-gnu": "4.27.3", - "@rollup/rollup-linux-x64-gnu": "4.27.3", - "@rollup/rollup-linux-x64-musl": "4.27.3", - "@rollup/rollup-win32-arm64-msvc": "4.27.3", - "@rollup/rollup-win32-ia32-msvc": "4.27.3", - "@rollup/rollup-win32-x64-msvc": "4.27.3", + "@rollup/rollup-android-arm-eabi": "4.27.4", + "@rollup/rollup-android-arm64": "4.27.4", + "@rollup/rollup-darwin-arm64": "4.27.4", + "@rollup/rollup-darwin-x64": "4.27.4", + "@rollup/rollup-freebsd-arm64": "4.27.4", + "@rollup/rollup-freebsd-x64": "4.27.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.27.4", + "@rollup/rollup-linux-arm-musleabihf": "4.27.4", + "@rollup/rollup-linux-arm64-gnu": "4.27.4", + "@rollup/rollup-linux-arm64-musl": "4.27.4", + "@rollup/rollup-linux-powerpc64le-gnu": "4.27.4", + "@rollup/rollup-linux-riscv64-gnu": "4.27.4", + "@rollup/rollup-linux-s390x-gnu": "4.27.4", + "@rollup/rollup-linux-x64-gnu": "4.27.4", + "@rollup/rollup-linux-x64-musl": "4.27.4", + "@rollup/rollup-win32-arm64-msvc": "4.27.4", + "@rollup/rollup-win32-ia32-msvc": "4.27.4", + "@rollup/rollup-win32-x64-msvc": "4.27.4", "fsevents": "~2.3.2" } }, @@ -19598,20 +19262,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/shiki": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.23.0.tgz", - "integrity": "sha512-xfdu9DqPkIpExH29cmiTlgo0/jBki5la1Tkfhsv+Wu5TT3APLNHslR1acxuKJOCWqVdSc+pIbs/2ozjVRGppdg==", - "dev": true, - "dependencies": { - "@shikijs/core": "1.23.0", - "@shikijs/engine-javascript": "1.23.0", - "@shikijs/engine-oniguruma": "1.23.0", - "@shikijs/types": "1.23.0", - "@shikijs/vscode-textmate": "^9.3.0", - "@types/hast": "^3.0.4" - } - }, "node_modules/side-channel": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", @@ -19992,16 +19642,6 @@ "deprecated": "Please use @jridgewell/sourcemap-codec instead", "dev": true }, - "node_modules/space-separated-tokens": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/spdy": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", @@ -20366,30 +20006,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/stringify-entities": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", - "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", - "dev": true, - "dependencies": { - "character-entities-html4": "^2.0.0", - "character-entities-legacy": "^3.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/stringify-entities/node_modules/character-entities-legacy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", - "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/stringify-object": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", @@ -21327,16 +20943,6 @@ "tree-kill": "cli.js" } }, - "node_modules/trim-lines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", - "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", @@ -21504,64 +21110,6 @@ "is-typedarray": "^1.0.0" } }, - "node_modules/typedoc": { - "version": "0.26.11", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.26.11.tgz", - "integrity": "sha512-sFEgRRtrcDl2FxVP58Ze++ZK2UQAEvtvvH8rRlig1Ja3o7dDaMHmaBfvJmdGnNEFaLTpQsN8dpvZaTqJSu/Ugw==", - "dev": true, - "dependencies": { - "lunr": "^2.3.9", - "markdown-it": "^14.1.0", - "minimatch": "^9.0.5", - "shiki": "^1.16.2", - "yaml": "^2.5.1" - }, - "bin": { - "typedoc": "bin/typedoc" - }, - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x" - } - }, - "node_modules/typedoc-plugin-markdown": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-4.2.10.tgz", - "integrity": "sha512-PLX3pc1/7z13UJm4TDE9vo9jWGcClFUErXXtd5LdnoLjV6mynPpqZLU992DwMGFSRqJFZeKbVyqlNNeNHnk2tQ==", - "dev": true, - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "typedoc": "0.26.x" - } - }, - "node_modules/typedoc/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/typedoc/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/typescript": { "version": "5.6.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", @@ -21605,7 +21153,8 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", - "dev": true + "dev": true, + "peer": true }, "node_modules/uglify-js": { "version": "3.4.10", @@ -21793,44 +21342,6 @@ "node": ">=8" } }, - "node_modules/unist-util-is": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", - "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", - "dev": true, - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-is/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "dev": true - }, - "node_modules/unist-util-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", - "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", - "dev": true, - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-position/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "dev": true - }, "node_modules/unist-util-stringify-position": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz", @@ -21844,47 +21355,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/unist-util-visit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", - "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", - "dev": true, - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-parents": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", - "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", - "dev": true, - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-parents/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "dev": true - }, - "node_modules/unist-util-visit/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "dev": true - }, "node_modules/universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", @@ -22295,59 +21765,6 @@ "extsprintf": "^1.2.0" } }, - "node_modules/vfile": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", - "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", - "dev": true, - "dependencies": { - "@types/unist": "^3.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-message": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", - "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", - "dev": true, - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-message/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "dev": true - }, - "node_modules/vfile-message/node_modules/unist-util-stringify-position": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", - "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", - "dev": true, - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "dev": true - }, "node_modules/vm-browserify": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", @@ -23324,16 +22741,6 @@ "smoothscroll-polyfill": "^0.4.3" } }, - "node_modules/vuepress-plugin-typedoc": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/vuepress-plugin-typedoc/-/vuepress-plugin-typedoc-0.9.2.tgz", - "integrity": "sha512-daac5HgmoevqcifsT8PG05gXASi2zbJl/YvX+HSdY5rNCaTiriJVZ361V23RkADpfEuFjS4Q6sCkoTW+sxgVxQ==", - "dev": true, - "peerDependencies": { - "typedoc": ">=0.22.0", - "typedoc-plugin-markdown": ">=3.11.0" - } - }, "node_modules/vuepress-theme-chartjs": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/vuepress-theme-chartjs/-/vuepress-theme-chartjs-0.2.0.tgz", @@ -24988,18 +24395,6 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, - "node_modules/yaml": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.0.tgz", - "integrity": "sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==", - "dev": true, - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -25066,16 +24461,6 @@ "resolved": "https://registry.npmjs.org/zepto/-/zepto-1.2.0.tgz", "integrity": "sha512-C1x6lfvBICFTQIMgbt3JqMOno3VOtkWat/xEakLTOurskYIHPmzJrzd1e8BnmtdDVJlGuk5D+FxyCA8MPmkIyA==", "dev": true - }, - "node_modules/zwitch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", - "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } } } } diff --git a/package.json b/package.json index 06d019b91..67c5a5239 100644 --- a/package.json +++ b/package.json @@ -53,8 +53,7 @@ "@simonbrunel/vuepress-plugin-versions": "^0.2.0", "@swc/cli": "^0.5.1", "@swc/core": "^1.9.3", - "@types/jasmine": "^5.1.4", - "@types/linkify-it": "^3.0.5", + "@types/jasmine": "^5.1.5", "@typescript-eslint/eslint-plugin": "^5.4.0", "@typescript-eslint/parser": "^5.62.0", "babel-loader": "^8.3.0", @@ -83,16 +82,13 @@ "karma-spec-reporter": "0.0.36", "ng-hammerjs": "^2.0.8", "pixelmatch": "^6.0.0", - "rollup": "^4.27.3", + "rollup": "^4.27.4", "rollup-plugin-cleanup": "^3.2.1", "rollup-plugin-istanbul": "^5.0.0", - "typedoc": "^0.26.11", - "typedoc-plugin-markdown": "^4.2.10", "typescript": "5.6", "vuepress": "^1.8.2", "vuepress-plugin-flexsearch": "^0.3.0", "vuepress-plugin-redirect": "^1.2.5", - "vuepress-plugin-typedoc": "^0.9.2", "vuepress-theme-chartjs": "^0.2.0" }, "peerDependencies": { diff --git a/scripts/docs-config.sh b/scripts/docs-config.sh index 25b3589d9..1d0895604 100755 --- a/scripts/docs-config.sh +++ b/scripts/docs-config.sh @@ -27,4 +27,4 @@ MODE=$2 TAG=$(tag_from_version "$VERSION" "$MODE") -sed -i -e "s/VERSION/$TAG/g" "docs/.vuepress/config.js" +sed -i -e "s/VERSION/$TAG/g" "docs/.vuepress/config.ts" diff --git a/tsconfig.json b/tsconfig.json index 20148b6a4..324bbc9d5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -32,8 +32,8 @@ "allowSyntheticDefaultImports": true, /* Language and Environment */ - "target": "ES2022", - "lib": ["ES2022", "DOM"], + "target": "ES2021", + "lib": ["ES2021", "DOM"], }, "typedocOptions": { "name": "chartjs-plugin-zoom", From 6916e5139e415f47723b2fa4cf5d6a220e0b3011 Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Fri, 29 Nov 2024 21:16:37 +0200 Subject: [PATCH 04/13] fix: try to fix windows test --- .swcrc-spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.swcrc-spec b/.swcrc-spec index 853100e8c..2c19ed08f 100644 --- a/.swcrc-spec +++ b/.swcrc-spec @@ -8,7 +8,7 @@ "importMeta": true }, "target": "es2022", - "baseUrl": "." + "baseUrl": "/" }, "module": { "type": "es6", From 026dcdad39e4065d1559f1d063a9ca5b574e3634 Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Fri, 29 Nov 2024 21:22:43 +0200 Subject: [PATCH 05/13] fix: one more fix for windows --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 67c5a5239..f32839b0c 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "typecheck": "tsc -p tsconfig.json --noEmit", "test": "npm run test-types && npm run test-unit && npm run test-karma", "pretest-unit": "swc --config-file .swcrc-spec src -d build", - "test-unit": "JASMINE_CONFIG_PATH=jasmine.json c8 --all --src=src --reporter=text --reporter=lcov -o=coverage/unit jasmine", + "test-unit": "cross-env JASMINE_CONFIG_PATH=jasmine.json c8 --all --src=src --reporter=text --reporter=lcov -o=coverage/unit jasmine", "test-karma": "cross-env NODE_ENV=test karma start ./karma.conf.cjs --auto-watch --single-run --coverage", "test-types": "tsc -p test/types/ --noEmit" }, From 4fe16a4c0288f5e2a360ee9e60d61870be087485 Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Fri, 29 Nov 2024 21:41:54 +0200 Subject: [PATCH 06/13] chore: tuning --- package.json | 2 +- src/utils.ts | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index f32839b0c..7c88ef90e 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "typecheck": "tsc -p tsconfig.json --noEmit", "test": "npm run test-types && npm run test-unit && npm run test-karma", "pretest-unit": "swc --config-file .swcrc-spec src -d build", - "test-unit": "cross-env JASMINE_CONFIG_PATH=jasmine.json c8 --all --src=src --reporter=text --reporter=lcov -o=coverage/unit jasmine", + "test-unit": "cross-env JASMINE_CONFIG_PATH=jasmine.json c8 --src=src --reporter=text --reporter=lcov -o=coverage/unit jasmine", "test-karma": "cross-env NODE_ENV=test karma start ./karma.conf.cjs --auto-watch --single-run --coverage", "test-types": "tsc -p test/types/ --noEmit" }, diff --git a/src/utils.ts b/src/utils.ts index 9a7c576d9..d2d4439ff 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,5 +1,4 @@ import type { Chart, Point, Scale } from 'chart.js' -import { each } from 'chart.js/helpers' import type { DragOptions, ModeOption, ModifierKey, PanOptions } from './options' const eventKey = (key: ModifierKey): 'altKey' | 'ctrlKey' | 'metaKey' | 'shiftKey' => `${key}Key` @@ -84,11 +83,11 @@ export function getEnabledScalesByPoint(options: PanOptions | undefined, point: const enabledScales: Scale[] = [] - each(chart.scales, function (scaleItem) { + for (const scaleItem of Object.values(chart.scales)) { if (enabled[scaleItem.axis as 'x' | 'y']) { enabledScales.push(scaleItem) } - }) + } return enabledScales || Object.values(chart.scales) } From fc12a68efa09ce3c80498f5f01c32eff801ecbee Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Fri, 29 Nov 2024 22:15:13 +0200 Subject: [PATCH 07/13] fix: removing comments --- rollup.config.js | 56 ++++++++++++++++++++++----------------------- types/.eslintrc.yml | 23 ------------------- 2 files changed, 28 insertions(+), 51 deletions(-) delete mode 100644 types/.eslintrc.yml diff --git a/rollup.config.js b/rollup.config.js index 89f70c322..eea7fe3b2 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,52 +1,52 @@ -import commonjs from '@rollup/plugin-commonjs'; -import cleanup from 'rollup-plugin-cleanup'; -import json from '@rollup/plugin-json'; -import resolve from '@rollup/plugin-node-resolve'; -import swc from '@rollup/plugin-swc'; -import terser from '@rollup/plugin-terser'; -import {readFileSync} from 'fs'; +import commonjs from '@rollup/plugin-commonjs' +import cleanup from 'rollup-plugin-cleanup' +import json from '@rollup/plugin-json' +import resolve from '@rollup/plugin-node-resolve' +import swc from '@rollup/plugin-swc' +import terser from '@rollup/plugin-terser' +import { readFileSync } from 'fs' -const pkg = JSON.parse(readFileSync('./package.json')); -const dependencies = Object.keys(pkg.dependencies); -const peerDependencies = Object.keys(pkg.peerDependencies); -const allDependencies = dependencies.concat(peerDependencies); +const pkg = JSON.parse(readFileSync('./package.json')) +const dependencies = Object.keys(pkg.dependencies) +const peerDependencies = Object.keys(pkg.peerDependencies) +const allDependencies = dependencies.concat(peerDependencies) const banner = `/*! * ${pkg.name} v${pkg.version} * ${pkg.homepage}${pkg.version}/ - * (c) 2016-${(new Date(process.env.SOURCE_DATE_EPOCH ? (process.env.SOURCE_DATE_EPOCH * 1000) : new Date().getTime())).getFullYear()} chartjs-plugin-zoom Contributors + * (c) 2016-${new Date(process.env.SOURCE_DATE_EPOCH ? process.env.SOURCE_DATE_EPOCH * 1000 : new Date().getTime()).getFullYear()} chartjs-plugin-zoom Contributors * Released under the MIT License - */`; + */` -const name = 'ChartZoom'; +const name = 'ChartZoom' const globals = { 'chart.js': 'Chart', 'chart.js/helpers': 'Chart.helpers', - hammerjs: 'Hammer' -}; -allDependencies.push('chart.js/helpers'); + hammerjs: 'Hammer', +} +allDependencies.push('chart.js/helpers') const plugins = (minify) => [ commonjs({ include: 'node_modules/**', }), json(), - resolve({extensions: ['.ts']}), + resolve({ extensions: ['.js', '.ts'] }), swc({ jsc: { parser: { syntax: 'typescript', tsx: false, }, - target: 'es2022' + target: 'es2022', }, module: { type: 'es6', }, - sourceMaps: true + sourceMaps: true, }), - minify ? terser({output: {comments: 'some'}}) : cleanup({comments: ['some']}), -]; + minify ? terser({ output: { comments: 'some' } }) : cleanup({ comments: ['some'], extensions: ['js', 'ts'] }), +] export default [ { @@ -60,8 +60,8 @@ export default [ globals, sourcemap: true, }, - plugins: plugins(false), - external: allDependencies + plugins: plugins(), + external: allDependencies, }, { input: 'src/index.umd.ts', @@ -75,11 +75,11 @@ export default [ sourcemap: true, }, plugins: plugins(true), - external: allDependencies + external: allDependencies, }, { input: 'src/index.ts', - plugins: plugins(false), + plugins: plugins(), output: { name, file: `dist/${pkg.name}.esm.js`, @@ -88,6 +88,6 @@ export default [ indent: false, sourcemap: true, }, - external: allDependencies + external: allDependencies, }, -]; +] diff --git a/types/.eslintrc.yml b/types/.eslintrc.yml deleted file mode 100644 index ea2c5e417..000000000 --- a/types/.eslintrc.yml +++ /dev/null @@ -1,23 +0,0 @@ -parser: '@typescript-eslint/parser' - -plugins: - - '@typescript-eslint' - -extends: - - chartjs - - plugin:@typescript-eslint/recommended - -rules: - # Replace stock eslint rules with typescript-eslint equivalents for proper - # TypeScript support. - no-use-before-define: "off" - '@typescript-eslint/no-use-before-define': "error" - no-shadow: "off" - '@typescript-eslint/no-shadow': "error" - - # These rules were set to warning to make the linting pass initially, - # without making any major changes to types. - object-curly-spacing: ["warn", "always"] - '@typescript-eslint/no-empty-interface': "warn" - '@typescript-eslint/ban-types': "warn" - '@typescript-eslint/adjacent-overload-signatures': "warn" From f0e6fb77e36be9edecda206ae0389fefca1949d5 Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Fri, 29 Nov 2024 22:26:18 +0200 Subject: [PATCH 08/13] chore: use -Infinity/Infinity --- src/scale.types.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/scale.types.ts b/src/scale.types.ts index 0625c99a0..0674df5b1 100644 --- a/src/scale.types.ts +++ b/src/scale.types.ts @@ -106,8 +106,8 @@ function fixRange( // In case the values are really close to the original values, use the original values. const origLimits: ScaleLimits = { min: 'original', max: 'original' } - const origMin = getLimit(state, scale, origLimits, 'min', Number.NEGATIVE_INFINITY) - const origMax = getLimit(state, scale, origLimits, 'max', Number.POSITIVE_INFINITY) + const origMin = getLimit(state, scale, origLimits, 'min', -Infinity) + const origMax = getLimit(state, scale, origLimits, 'max', Infinity) const epsilon = range / 1e6 if (almostEquals(min, origMin, epsilon)) { From 45aa04d537ca12845d44ce1584183c446202a566 Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Fri, 29 Nov 2024 22:42:49 +0200 Subject: [PATCH 09/13] chore: fix issues --- src/hammer.ts | 6 +++--- src/options.ts | 18 +++++++++++------- src/plugin.ts | 30 +++++++++++++++++------------- src/utils.ts | 27 ++++++++++++++++++--------- 4 files changed, 49 insertions(+), 32 deletions(-) diff --git a/src/hammer.ts b/src/hammer.ts index 681a2536a..e8038b2e3 100644 --- a/src/hammer.ts +++ b/src/hammer.ts @@ -22,7 +22,7 @@ function createEnabler(chart: Chart, state: State) { event.pointerType === 'mouse' && (keyNotPressed(getModifierKey(panOptions), srcEvent) || keyPressed(getModifierKey(zoomOptions.drag), srcEvent)) ) { - panOptions.onPanRejected?.({ chart, event }) + panOptions.onPanRejected?.({ chart, event: event.srcEvent }) return false } return true @@ -111,8 +111,8 @@ function startPan(chart: Chart, state: State, event: HammerInput) { y: event.center.y - rect.top, } - if (onPanStart?.({ chart, event, point }) === false) { - return onPanRejected?.({ chart, event }) + if (onPanStart?.({ chart, event: event.srcEvent, point }) === false) { + return onPanRejected?.({ chart, event: event.srcEvent }) } state.panScales = getEnabledScalesByPoint(state.options.pan, point, chart) diff --git a/src/options.ts b/src/options.ts index fd2ae6983..4f5e122eb 100644 --- a/src/options.ts +++ b/src/options.ts @@ -7,6 +7,10 @@ export type ModifierKey = 'ctrl' | 'alt' | 'shift' | 'meta' export type DrawTime = 'afterDraw' | 'afterDatasetsDraw' | 'beforeDraw' | 'beforeDatasetsDraw' export type ZoomTrigger = 'api' | 'drag' | 'wheel' | 'pinch' +type RejectableStartEvent = (context: { chart: Chart; event: Event; point: Point }) => boolean | undefined +type RejectEvent = (context: { chart: Chart; event: Event }) => void +type GenericEvent = (context: { chart: Chart }) => void + export interface WheelOptions { /** * Enable the zoom via mouse wheel @@ -116,14 +120,14 @@ export interface ZoomOptions { /** * Function called once zooming is completed */ - onZoomComplete?: (context: { chart: Chart }) => void + onZoomComplete?: GenericEvent /** * Function called when wheel input occurs without modifier key */ - onZoomRejected?: (context: { chart: Chart; event: Event }) => void + onZoomRejected?: RejectEvent - onZoomStart?: (context: { chart: Chart; event: Event; point: Point }) => boolean | undefined + onZoomStart?: RejectableStartEvent } /** @@ -163,20 +167,20 @@ export interface PanOptions { /** * Function called while the user is panning */ - onPan?: (context: { chart: Chart }) => void + onPan?: GenericEvent /** * Function called once panning is completed */ - onPanComplete?: (context: { chart: Chart }) => void + onPanComplete?: GenericEvent /** * Function called when pan fails because modifier key was not detected. * event is the Hammer event that failed - see https://hammerjs.github.io/api#event-object */ - onPanRejected?: (context: { chart: Chart; event: HammerInput }) => void + onPanRejected?: RejectEvent - onPanStart?: (context: { chart: Chart; event: HammerInput; point: Point }) => boolean | undefined + onPanStart?: RejectableStartEvent } export interface ScaleLimits { diff --git a/src/plugin.ts b/src/plugin.ts index 48185e776..7b2ea4478 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -48,6 +48,19 @@ function draw(chart: Chart, caller: string, options: ZoomPluginOptions) { ctx.restore() } +const bindApi = (chart: Chart) => { + chart.pan = (delta, panScales, transition) => pan(chart, delta, panScales, transition) + chart.zoom = (args, transition) => zoom(chart, args, transition) + chart.zoomRect = (p0, p1, transition) => zoomRect(chart, p0, p1, transition) + chart.zoomScale = (id, range, transition) => zoomScale(chart, id, range, transition) + chart.resetZoom = (transition) => resetZoom(chart, transition) + chart.getZoomLevel = () => getZoomLevel(chart) + chart.getInitialScaleBounds = () => getInitialScaleBounds(chart) + chart.getZoomedScaleBounds = () => getZoomedScaleBounds(chart) + chart.isZoomedOrPanned = () => isZoomedOrPanned(chart) + chart.isZoomingOrPanning = () => isZoomingOrPanning(chart) +} + export default { id: 'zoom', @@ -55,7 +68,7 @@ export default { defaults, - start: function (chart: Chart, _args: unknown, options: ZoomPluginOptions) { + start(chart: Chart, _args: unknown, options: ZoomPluginOptions) { const state = getState(chart) state.options = options @@ -77,16 +90,7 @@ export default { startHammer(chart, options) } - chart.pan = (delta, panScales, transition) => pan(chart, delta, panScales, transition) - chart.zoom = (args, transition) => zoom(chart, args, transition) - chart.zoomRect = (p0, p1, transition) => zoomRect(chart, p0, p1, transition) - chart.zoomScale = (id, range, transition) => zoomScale(chart, id, range, transition) - chart.resetZoom = (transition) => resetZoom(chart, transition) - chart.getZoomLevel = () => getZoomLevel(chart) - chart.getInitialScaleBounds = () => getInitialScaleBounds(chart) - chart.getZoomedScaleBounds = () => getZoomedScaleBounds(chart) - chart.isZoomedOrPanned = () => isZoomedOrPanned(chart) - chart.isZoomingOrPanning = () => isZoomingOrPanning(chart) + bindApi(chart) }, beforeEvent( @@ -107,7 +111,7 @@ export default { } }, - beforeUpdate: function (chart: Chart, _args: unknown, options: ZoomPluginOptions) { + beforeUpdate(chart: Chart, _args: unknown, options: ZoomPluginOptions) { const state = getState(chart) const previousOptions = state.options state.options = options @@ -137,7 +141,7 @@ export default { draw(chart, 'afterDraw', options) }, - stop: function (chart: Chart) { + stop(chart: Chart) { removeListeners(chart) if (Hammer) { diff --git a/src/utils.ts b/src/utils.ts index d2d4439ff..8a03061d0 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -54,6 +54,23 @@ function getScaleUnderPoint({ x, y }: Point, chart: Chart): Scale | null { return null } +const convertOverScaleMode = ( + chart: Chart, + overScaleMode: ModeOption | undefined, + scaleEnabled: { x: boolean; y: boolean }, + enabled: { x: boolean; y: boolean } +) => { + if (overScaleMode) { + const overScaleEnabled = directionsEnabled(overScaleMode, chart) + for (const axis of ['x', 'y'] as const) { + if (overScaleEnabled[axis]) { + scaleEnabled[axis] = enabled[axis] + enabled[axis] = false + } + } + } +} + /** * Evaluate the chart's mode, scaleMode, and overScaleMode properties to * determine which axes are eligible for scaling. @@ -67,15 +84,7 @@ export function getEnabledScalesByPoint(options: PanOptions | undefined, point: const scaleEnabled = directionsEnabled(scaleMode, chart) // Convert deprecated overScaleEnabled to new scaleEnabled. - if (overScaleMode) { - const overScaleEnabled = directionsEnabled(overScaleMode, chart) - for (const axis of ['x', 'y'] as const) { - if (overScaleEnabled[axis]) { - scaleEnabled[axis] = enabled[axis] - enabled[axis] = false - } - } - } + convertOverScaleMode(chart, overScaleMode, scaleEnabled, enabled) if (scale && scaleEnabled[scale.axis as 'x' | 'y']) { return [scale] From abae87c22a5fe87bc3bf869f01eec2157fadc31c Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Fri, 29 Nov 2024 22:50:12 +0200 Subject: [PATCH 10/13] fix: tests --- src/hammer.ts | 6 ++--- src/options.ts | 17 ++++++++----- src/utils.ts | 16 +++++++------ test/types/exports.ts | 56 ++++++++++++++++++++++--------------------- 4 files changed, 52 insertions(+), 43 deletions(-) diff --git a/src/hammer.ts b/src/hammer.ts index e8038b2e3..681a2536a 100644 --- a/src/hammer.ts +++ b/src/hammer.ts @@ -22,7 +22,7 @@ function createEnabler(chart: Chart, state: State) { event.pointerType === 'mouse' && (keyNotPressed(getModifierKey(panOptions), srcEvent) || keyPressed(getModifierKey(zoomOptions.drag), srcEvent)) ) { - panOptions.onPanRejected?.({ chart, event: event.srcEvent }) + panOptions.onPanRejected?.({ chart, event }) return false } return true @@ -111,8 +111,8 @@ function startPan(chart: Chart, state: State, event: HammerInput) { y: event.center.y - rect.top, } - if (onPanStart?.({ chart, event: event.srcEvent, point }) === false) { - return onPanRejected?.({ chart, event: event.srcEvent }) + if (onPanStart?.({ chart, event, point }) === false) { + return onPanRejected?.({ chart, event }) } state.panScales = getEnabledScalesByPoint(state.options.pan, point, chart) diff --git a/src/options.ts b/src/options.ts index 4f5e122eb..a52a79d16 100644 --- a/src/options.ts +++ b/src/options.ts @@ -7,8 +7,13 @@ export type ModifierKey = 'ctrl' | 'alt' | 'shift' | 'meta' export type DrawTime = 'afterDraw' | 'afterDatasetsDraw' | 'beforeDraw' | 'beforeDatasetsDraw' export type ZoomTrigger = 'api' | 'drag' | 'wheel' | 'pinch' -type RejectableStartEvent = (context: { chart: Chart; event: Event; point: Point }) => boolean | undefined -type RejectEvent = (context: { chart: Chart; event: Event }) => void +type RejectableStartEvent = (context: { + chart: Chart + event: T + point: Point +}) => boolean | undefined +type RejectEvent = (context: { chart: Chart; event: T }) => void + type GenericEvent = (context: { chart: Chart }) => void export interface WheelOptions { @@ -125,9 +130,9 @@ export interface ZoomOptions { /** * Function called when wheel input occurs without modifier key */ - onZoomRejected?: RejectEvent + onZoomRejected?: RejectEvent - onZoomStart?: RejectableStartEvent + onZoomStart?: RejectableStartEvent } /** @@ -178,9 +183,9 @@ export interface PanOptions { * Function called when pan fails because modifier key was not detected. * event is the Hammer event that failed - see https://hammerjs.github.io/api#event-object */ - onPanRejected?: RejectEvent + onPanRejected?: RejectEvent - onPanStart?: RejectableStartEvent + onPanStart?: RejectableStartEvent } export interface ScaleLimits { diff --git a/src/utils.ts b/src/utils.ts index 8a03061d0..dbc6d6810 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -60,13 +60,15 @@ const convertOverScaleMode = ( scaleEnabled: { x: boolean; y: boolean }, enabled: { x: boolean; y: boolean } ) => { - if (overScaleMode) { - const overScaleEnabled = directionsEnabled(overScaleMode, chart) - for (const axis of ['x', 'y'] as const) { - if (overScaleEnabled[axis]) { - scaleEnabled[axis] = enabled[axis] - enabled[axis] = false - } + if (!overScaleMode) { + return + } + + const overScaleEnabled = directionsEnabled(overScaleMode, chart) + for (const axis of ['x', 'y'] as const) { + if (overScaleEnabled[axis]) { + scaleEnabled[axis] = enabled[axis] + enabled[axis] = false } } } diff --git a/test/types/exports.ts b/test/types/exports.ts index bfdb16ae1..f04210869 100644 --- a/test/types/exports.ts +++ b/test/types/exports.ts @@ -1,16 +1,18 @@ -import {Chart} from 'chart.js'; -import Zoom, {pan, zoom, resetZoom} from '../../src'; +import { Chart } from 'chart.js' +import Zoom, { pan, zoom, resetZoom } from '../../src' -Chart.register(Zoom); -Chart.unregister(Zoom); +Chart.register(Zoom) +Chart.unregister(Zoom) const chart = new Chart('id', { type: 'bar', data: { labels: [], - datasets: [{ - data: [] - }] + datasets: [ + { + data: [], + }, + ], }, options: { plugins: { @@ -19,45 +21,45 @@ const chart = new Chart('id', { x: { min: 1, max: 2, - minRange: 1 + minRange: 1, }, y: { min: 1, max: 2, - minRange: 1 + minRange: 1, }, y2: { min: 10, max: 20, - minRange: 5 - } + minRange: 5, + }, }, pan: { enabled: true, mode: 'x', - onPanStart({event}) { - return event.distance > 2; - } + onPanStart({ event }) { + return event.distance > 2 + }, }, zoom: { wheel: { - enabled: true + enabled: true, }, mode: 'x', - } + }, }, - } + }, }, - plugins: [Zoom] -}); + plugins: [Zoom], +}) -chart.resetZoom(); -chart.zoom(1.1); -chart.zoom({x: 1, y: 1.1, focalPoint: {x: 10, y: 10}}, 'zoom'); +chart.resetZoom() +chart.zoom(1.1) +chart.zoom({ x: 1, y: 1.1, focalPoint: { x: 10, y: 10 } }, 'zoom') -chart.pan(10); -chart.pan({x: 10, y: 20}, [chart.scales.x]); +chart.pan(10) +chart.pan({ x: 10, y: 20 }, [chart.scales.x]) -pan(chart, -42, undefined, 'zoom'); -zoom(chart, {x: 1, y: 1.1, focalPoint: {x: 10, y: 10}}, 'zoom'); -resetZoom(chart, 'none'); +pan(chart, -42, undefined, 'zoom') +zoom(chart, { x: 1, y: 1.1, focalPoint: { x: 10, y: 10 } }, 'zoom') +resetZoom(chart, 'none') From 7c957315583f8a24cfd3d93b56174a35466134ab Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Fri, 29 Nov 2024 22:53:52 +0200 Subject: [PATCH 11/13] fix: reduce complexity --- src/utils.ts | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/utils.ts b/src/utils.ts index dbc6d6810..0bb4d6ec0 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -54,11 +54,13 @@ function getScaleUnderPoint({ x, y }: Point, chart: Chart): Scale | null { return null } +type EnabledDirections = { x: boolean; y: boolean } + const convertOverScaleMode = ( chart: Chart, overScaleMode: ModeOption | undefined, - scaleEnabled: { x: boolean; y: boolean }, - enabled: { x: boolean; y: boolean } + scaleEnabled: EnabledDirections, + enabled: EnabledDirections ) => { if (!overScaleMode) { return @@ -73,6 +75,18 @@ const convertOverScaleMode = ( } } +const getEnabledScales = (chart: Chart, enabled: EnabledDirections): Scale[] => { + const enabledScales: Scale[] = [] + + for (const scaleItem of Object.values(chart.scales)) { + if (enabled[scaleItem.axis as 'x' | 'y']) { + enabledScales.push(scaleItem) + } + } + + return enabledScales || Object.values(chart.scales) +} + /** * Evaluate the chart's mode, scaleMode, and overScaleMode properties to * determine which axes are eligible for scaling. @@ -92,13 +106,5 @@ export function getEnabledScalesByPoint(options: PanOptions | undefined, point: return [scale] } - const enabledScales: Scale[] = [] - - for (const scaleItem of Object.values(chart.scales)) { - if (enabled[scaleItem.axis as 'x' | 'y']) { - enabledScales.push(scaleItem) - } - } - - return enabledScales || Object.values(chart.scales) + return getEnabledScales(chart, enabled) } From 21ef77beb8a9d1c10b04a7973ea4fffc5c77ea99 Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Fri, 29 Nov 2024 23:17:42 +0200 Subject: [PATCH 12/13] fix: api documentation --- docs/.vuepress/config.ts | 4 +- package-lock.json | 161 +++++++++++++++++++++++++++++++++++++-- package.json | 5 +- src/handlers.ts | 4 +- src/index.ts | 2 +- tsconfig.json | 5 +- 6 files changed, 166 insertions(+), 15 deletions(-) diff --git a/docs/.vuepress/config.ts b/docs/.vuepress/config.ts index 516080743..b2ca381eb 100755 --- a/docs/.vuepress/config.ts +++ b/docs/.vuepress/config.ts @@ -21,7 +21,6 @@ export default defineConfig({ {base: '/samples', alternative: ['basic']}, ], }], - /* [ 'vuepress-plugin-typedoc', { @@ -34,7 +33,6 @@ export default defineConfig({ }, }, ], - */ ['@simonbrunel/vuepress-plugin-versions', { filters: { suffix: (tag) => tag ? ` (${tag})` : '', @@ -113,7 +111,7 @@ export default defineConfig({ nav: [ {text: 'Home', link: '/'}, {text: 'Guide', link: '/guide/'}, - // {text: 'API', link: '/api/'}, + {text: 'API', link: '/api/'}, {text: 'Samples', link: `/samples/`}, { text: 'Ecosystem', diff --git a/package-lock.json b/package-lock.json index 3094b4082..ed1e54f4b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -55,10 +55,13 @@ "rollup": "^4.27.4", "rollup-plugin-cleanup": "^3.2.1", "rollup-plugin-istanbul": "^5.0.0", - "typescript": "5.6", + "typedoc": "^0.23.28", + "typedoc-plugin-markdown": "3.13", + "typescript": "5.0", "vuepress": "^1.8.2", "vuepress-plugin-flexsearch": "^0.3.0", "vuepress-plugin-redirect": "^1.2.5", + "vuepress-plugin-typedoc": "^0.10.3", "vuepress-theme-chartjs": "^0.2.0" }, "peerDependencies": { @@ -5834,6 +5837,12 @@ "node": ">=8" } }, + "node_modules/ansi-sequence-parser": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz", + "integrity": "sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==", + "dev": true + }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -12170,6 +12179,27 @@ "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", "dev": true }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, "node_modules/har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -13950,6 +13980,12 @@ "node": ">=6" } }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true + }, "node_modules/jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", @@ -14417,6 +14453,12 @@ "yallist": "^3.0.2" } }, + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true + }, "node_modules/magic-string": { "version": "0.30.12", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz", @@ -14558,6 +14600,18 @@ "dev": true, "peer": true }, + "node_modules/marked": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", + "dev": true, + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 12" + } + }, "node_modules/md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -19262,6 +19316,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/shiki": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.7.tgz", + "integrity": "sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==", + "dev": true, + "dependencies": { + "ansi-sequence-parser": "^1.1.0", + "jsonc-parser": "^3.2.0", + "vscode-oniguruma": "^1.7.0", + "vscode-textmate": "^8.0.0" + } + }, "node_modules/side-channel": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", @@ -21110,17 +21176,74 @@ "is-typedarray": "^1.0.0" } }, + "node_modules/typedoc": { + "version": "0.23.28", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.28.tgz", + "integrity": "sha512-9x1+hZWTHEQcGoP7qFmlo4unUoVJLB0H/8vfO/7wqTnZxg4kPuji9y3uRzEu0ZKez63OJAUmiGhUrtukC6Uj3w==", + "dev": true, + "dependencies": { + "lunr": "^2.3.9", + "marked": "^4.2.12", + "minimatch": "^7.1.3", + "shiki": "^0.14.1" + }, + "bin": { + "typedoc": "bin/typedoc" + }, + "engines": { + "node": ">= 14.14" + }, + "peerDependencies": { + "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x" + } + }, + "node_modules/typedoc-plugin-markdown": { + "version": "3.13.6", + "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.13.6.tgz", + "integrity": "sha512-ISSc9v3BK7HkokxSBuJPttXox4tJ6hP0N9wfSIk0fmLN67+eqtAxbk97gs2nDiuha+RTO5eW9gdeAb+RPP0mgg==", + "dev": true, + "dependencies": { + "handlebars": "^4.7.7" + }, + "peerDependencies": { + "typedoc": ">=0.23.0" + } + }, + "node_modules/typedoc/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/typedoc/node_modules/minimatch": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz", + "integrity": "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/typescript": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", - "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", + "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=14.17" + "node": ">=12.20" } }, "node_modules/ua-parser-js": { @@ -21780,6 +21903,18 @@ "node": ">=0.10.0" } }, + "node_modules/vscode-oniguruma": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", + "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", + "dev": true + }, + "node_modules/vscode-textmate": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", + "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", + "dev": true + }, "node_modules/vue": { "version": "2.7.16", "resolved": "https://registry.npmjs.org/vue/-/vue-2.7.16.tgz", @@ -22741,6 +22876,16 @@ "smoothscroll-polyfill": "^0.4.3" } }, + "node_modules/vuepress-plugin-typedoc": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/vuepress-plugin-typedoc/-/vuepress-plugin-typedoc-0.10.3.tgz", + "integrity": "sha512-ll9sC/lX2iPji9DALT1V0uEgln7K/Za0hMzrAIkyGWD8VaCJ27pXx6db3G340Ni/Xv7qem9dgSqcHSYMYTZtEQ==", + "dev": true, + "peerDependencies": { + "typedoc": ">=0.22.0", + "typedoc-plugin-markdown": ">=3.11.10" + } + }, "node_modules/vuepress-theme-chartjs": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/vuepress-theme-chartjs/-/vuepress-theme-chartjs-0.2.0.tgz", @@ -24208,6 +24353,12 @@ "node": ">=0.10.0" } }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true + }, "node_modules/worker-farm": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", diff --git a/package.json b/package.json index 7c88ef90e..0d5d7f942 100644 --- a/package.json +++ b/package.json @@ -85,10 +85,13 @@ "rollup": "^4.27.4", "rollup-plugin-cleanup": "^3.2.1", "rollup-plugin-istanbul": "^5.0.0", - "typescript": "5.6", + "typedoc": "^0.23.28", + "typedoc-plugin-markdown": "3.13", + "typescript": "5.0", "vuepress": "^1.8.2", "vuepress-plugin-flexsearch": "^0.3.0", "vuepress-plugin-redirect": "^1.2.5", + "vuepress-plugin-typedoc": "^0.10.3", "vuepress-theme-chartjs": "^0.2.0" }, "peerDependencies": { diff --git a/src/handlers.ts b/src/handlers.ts index 7814ee1ec..6405fa5e8 100644 --- a/src/handlers.ts +++ b/src/handlers.ts @@ -32,12 +32,12 @@ function addHandler( return } removeHandler(chart, type) - handlers[type] = (event) => handler(chart, event as T, options) + const listener = (handlers[type] = (event) => handler(chart, event as T, options)) targets[type] = target // `passive: false` for wheel events, to prevent chrome warnings. Use default value for other events. const passive = type === 'wheel' ? false : undefined - target.addEventListener(type, handlers[type], { passive }) + target.addEventListener(type, listener, { passive }) } export function mouseMove(chart: Chart, event: MouseEvent) { diff --git a/src/index.ts b/src/index.ts index e4aebcdc4..374922390 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,7 +3,7 @@ import plugin from './plugin' import type { ZoomPluginOptions } from './options' import type { ScaleRange } from './state' -import type { DistributiveArray, PanAmount, ZoomAmount } from './types' +import type { DistributiveArray, PanAmount, ZoomAmount } from './types.js' import type { ChartType, ChartTypeRegistry, Point, Scale, UpdateMode } from 'chart.js' declare module 'chart.js' { diff --git a/tsconfig.json b/tsconfig.json index 324bbc9d5..7f676aa67 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,7 +15,7 @@ "module": "ES2022", "moduleResolution": "bundler", "resolveJsonModule": true, - "rootDir": "src", + "rootDir": ".", /* Emit */ "declaration": true, @@ -37,7 +37,7 @@ }, "typedocOptions": { "name": "chartjs-plugin-zoom", - "entryPoints": ["types/index.d.ts"], + "entryPoints": ["src/index.ts"], "readme": "none", "excludeExternals": true, "includeVersion": true, @@ -45,7 +45,6 @@ }, "include": [ "./src/**/*.ts", - "./types" ], "exclude": [ "./src/**/*.spec.ts", From 771d524507c148e75e1d7badf54c30be192db4b5 Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Wed, 4 Dec 2024 16:59:15 +0200 Subject: [PATCH 13/13] fix: linting issues, license year --- LICENSE.md | 2 +- docs/scripts/defaults.js | 12 ++--- docs/scripts/register.js | 32 ++++++----- docs/scripts/utils.js | 112 +++++++++++++++++++-------------------- karma.conf.cjs | 101 ++++++++++++++++------------------- package.json | 2 +- 6 files changed, 127 insertions(+), 134 deletions(-) diff --git a/LICENSE.md b/LICENSE.md index 5d0f4288b..d074c063a 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2013-2021 chartjs-plugin-zoom contributors +Copyright (c) 2013-2024 chartjs-plugin-zoom contributors 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: diff --git a/docs/scripts/defaults.js b/docs/scripts/defaults.js index b65bbb0b1..3067f74de 100644 --- a/docs/scripts/defaults.js +++ b/docs/scripts/defaults.js @@ -1,17 +1,17 @@ -import {defaults} from 'chart.js'; +import { defaults } from 'chart.js' defaults.set({ datasets: { line: { - tension: 0.4 - } + tension: 0.4, + }, }, interaction: { mode: 'nearest', axis: 'x', - intersect: false + intersect: false, }, plugins: { - legend: false + legend: false, }, -}); +}) diff --git a/docs/scripts/register.js b/docs/scripts/register.js index 49b4d5fa8..ae09ef23b 100644 --- a/docs/scripts/register.js +++ b/docs/scripts/register.js @@ -1,19 +1,23 @@ -import Chart from 'chart.js/auto'; -import 'chartjs-adapter-date-fns'; -import zoomPlugin from '../../dist/chartjs-plugin-zoom.esm.js'; +import Chart from 'chart.js/auto' +import 'chartjs-adapter-date-fns' +import zoomPlugin from '../../dist/chartjs-plugin-zoom.esm.js' -Chart.register(zoomPlugin); +Chart.register(zoomPlugin) Chart.register({ id: 'version', afterDraw(chart) { - const ctx = chart.ctx; - ctx.save(); - ctx.font = '9px monospace'; - ctx.fillStyle = 'rgba(0, 0, 0, 0.5)'; - ctx.textAlign = 'right'; - ctx.textBaseline = 'top'; - ctx.fillText('Chart.js v' + Chart.version + ' + chartjs-plugin-zoom v' + zoomPlugin.version, chart.chartArea.right, 0); - ctx.restore(); - } -}); + const ctx = chart.ctx + ctx.save() + ctx.font = '9px monospace' + ctx.fillStyle = 'rgba(0, 0, 0, 0.5)' + ctx.textAlign = 'right' + ctx.textBaseline = 'top' + ctx.fillText( + 'Chart.js v' + Chart.version + ' + chartjs-plugin-zoom v' + zoomPlugin.version, + chart.chartArea.right, + 0 + ) + ctx.restore() + }, +}) diff --git a/docs/scripts/utils.js b/docs/scripts/utils.js index 0059b59f1..6d350bd1f 100644 --- a/docs/scripts/utils.js +++ b/docs/scripts/utils.js @@ -1,55 +1,55 @@ -import {valueOrDefault} from 'chart.js/helpers'; -import {addHours, startOfWeek, endOfWeek, isWeekend, nextMonday, getHours, setHours} from 'date-fns'; -import addDays from 'date-fns/addDays'; +import { valueOrDefault } from 'chart.js/helpers' +import { addHours, startOfWeek, endOfWeek, isWeekend, nextMonday, getHours, setHours } from 'date-fns' +import addDays from 'date-fns/addDays' // Adapted from http://indiegamr.com/generate-repeatable-random-numbers-in-js/ -let _seed = Date.now(); +let _seed = Date.now() export function srand(seed) { - _seed = seed; + _seed = seed } export function rand(min, max) { - min = valueOrDefault(min, 0); - max = valueOrDefault(max, 0); - _seed = (_seed * 9301 + 49297) % 233280; - return min + (_seed / 233280) * (max - min); + min = valueOrDefault(min, 0) + max = valueOrDefault(max, 0) + _seed = (_seed * 9301 + 49297) % 233280 + return min + (_seed / 233280) * (max - min) } export function numbers(config) { - const cfg = config || {}; - const min = valueOrDefault(cfg.min, 0); - const max = valueOrDefault(cfg.max, 100); - const from = valueOrDefault(cfg.from, []); - const count = valueOrDefault(cfg.count, 8); - const decimals = valueOrDefault(cfg.decimals, 8); - const continuity = valueOrDefault(cfg.continuity, 1); - const dfactor = Math.pow(10, decimals) || 0; - const data = []; - let i, value; + const cfg = config || {} + const min = valueOrDefault(cfg.min, 0) + const max = valueOrDefault(cfg.max, 100) + const from = valueOrDefault(cfg.from, []) + const count = valueOrDefault(cfg.count, 8) + const decimals = valueOrDefault(cfg.decimals, 8) + const continuity = valueOrDefault(cfg.continuity, 1) + const dfactor = Math.pow(10, decimals) || 0 + const data = [] + let i, value for (i = 0; i < count; ++i) { - value = (from[i] || 0) + this.rand(min, max); + value = (from[i] || 0) + this.rand(min, max) if (this.rand() <= continuity) { - data.push(Math.round(dfactor * value) / dfactor); + data.push(Math.round(dfactor * value) / dfactor) } else { - data.push(null); + data.push(null) } } - return data; + return data } export function points(config) { - const xs = this.numbers(config); - const ys = this.numbers(config); - return xs.map((x, i) => ({x, y: ys[i]})); + const xs = this.numbers(config) + const ys = this.numbers(config) + return xs.map((x, i) => ({ x, y: ys[i] })) } -const rand255 = () => Math.round(Math.random() * 255); +const rand255 = () => Math.round(Math.random() * 255) export function randomColor(alpha) { - return 'rgba(' + rand255() + ',' + rand255() + ',' + rand255() + ',' + (alpha || '.3') + ')'; + return 'rgba(' + rand255() + ',' + rand255() + ',' + rand255() + ',' + (alpha || '.3') + ')' } const MONTHS = [ @@ -64,59 +64,59 @@ const MONTHS = [ 'September', 'October', 'November', - 'December' -]; + 'December', +] export function months(config) { - const cfg = config || {}; - const count = cfg.count || 12; - const section = cfg.section; - const values = []; - let i, value; + const cfg = config || {} + const count = cfg.count || 12 + const section = cfg.section + const values = [] + let i, value for (i = 0; i < count; ++i) { - value = MONTHS[Math.ceil(i) % 12]; - values.push(value.substring(0, section)); + value = MONTHS[Math.ceil(i) % 12] + values.push(value.substring(0, section)) } - return values; + return values } export function hourlyPoints(config) { - const ys = this.numbers(config); - const start = new Date().valueOf(); - return ys.map((y, i) => ({x: addHours(start, i), y})); + const ys = this.numbers(config) + const start = new Date().valueOf() + return ys.map((y, i) => ({ x: addHours(start, i), y })) } function nextOfficeHour(time) { if (getHours(time) > 17) { - time = setHours(addDays(time, 1), 8); + time = setHours(addDays(time, 1), 8) } if (getHours(time) < 9) { - time = setHours(time, 9); + time = setHours(time, 9) } else { - time = addHours(time, 1); + time = addHours(time, 1) } if (isWeekend(time)) { - time = setHours(nextMonday(time), 9); + time = setHours(nextMonday(time), 9) } - return time; + return time } export function officeHourPoints(config) { - const ys = this.numbers(config); - let time = new Date().valueOf(); - return ys.map(y => { - time = nextOfficeHour(time); - return {x: +time, y}; - }); + const ys = this.numbers(config) + let time = new Date().valueOf() + return ys.map((y) => { + time = nextOfficeHour(time) + return { x: +time, y } + }) } export function nextWeek() { - const now = new Date().valueOf(); - const min = startOfWeek(addHours(endOfWeek(now), 24)); + const now = new Date().valueOf() + const min = startOfWeek(addHours(endOfWeek(now), 24)) return { min: +min, - max: +endOfWeek(min) - }; + max: +endOfWeek(min), + } } diff --git a/karma.conf.cjs b/karma.conf.cjs index 417758870..185780be0 100644 --- a/karma.conf.cjs +++ b/karma.conf.cjs @@ -1,27 +1,24 @@ -const istanbul = require('rollup-plugin-istanbul'); -const json = require('@rollup/plugin-json'); -const resolve = require('@rollup/plugin-node-resolve'); -const yargs = require('yargs'); -const env = process.env.NODE_ENV; +/* eslint-disable @typescript-eslint/no-var-requires */ +const istanbul = require('rollup-plugin-istanbul') +const json = require('@rollup/plugin-json') +const resolve = require('@rollup/plugin-node-resolve') +const yargs = require('yargs') +const env = process.env.NODE_ENV -module.exports = async function(karma) { - const builds = (await import('./rollup.config.js')).default; +module.exports = async function (karma) { + const builds = (await import('./rollup.config.js')).default - const args = yargs - .option('verbose', {default: false}) - .argv; + const args = yargs.option('verbose', { default: false }).argv // Use the same rollup config as our dist files: when debugging (--watch), // we will prefer the unminified build which is easier to browse and works // better with source mapping. In other cases, pick the minified build to // make sure that the minification process (terser) doesn't break anything. - const regex = karma.autoWatch ? /\.js$/ : /\.min\.js$/; - const build = builds.filter(v => v.output.format === 'umd' && v.output.file.match(regex))[0]; + const regex = karma.autoWatch ? /\.js$/ : /\.min\.js$/ + const build = builds.filter((v) => v.output.format === 'umd' && v.output.file.match(regex))[0] if (env === 'test') { - build.plugins.push( - istanbul({exclude: ['node_modules/**/*.js', 'package.json']}) - ); + build.plugins.push(istanbul({ exclude: ['node_modules/**/*.js', 'package.json'] })) } karma.set({ @@ -34,7 +31,7 @@ module.exports = async function(karma) { jasmine: { stopOnSpecFailure: !!karma.autoWatch, timeoutInterval: 10000, - } + }, }, // Explicitly disable hardware acceleration to make image @@ -44,51 +41,44 @@ module.exports = async function(karma) { customLaunchers: { chrome: { base: 'Chrome', - flags: [ - '--disable-accelerated-2d-canvas' - ] + flags: ['--disable-accelerated-2d-canvas'], }, firefox: { base: 'Firefox', prefs: { 'layers.acceleration.disabled': true, - 'gfx.canvas.accelerated': false - } - } + 'gfx.canvas.accelerated': false, + }, + }, }, files: [ - {pattern: 'test/fixtures/**/*.js', included: false}, - {pattern: 'test/fixtures/**/*.png', included: false}, - {pattern: 'node_modules/chart.js/dist/chart.umd.js'}, - {pattern: 'node_modules/hammer-simulator/index.js'}, - {pattern: 'node_modules/hammerjs/hammer.js'}, - {pattern: 'node_modules/chartjs-adapter-date-fns/dist/chartjs-adapter-date-fns.bundle.js'}, - {pattern: 'test/index.js'}, - {pattern: 'src/index.umd.ts'}, - {pattern: 'test/specs/**/*.js'} + { pattern: 'test/fixtures/**/*.js', included: false }, + { pattern: 'test/fixtures/**/*.png', included: false }, + { pattern: 'node_modules/chart.js/dist/chart.umd.js' }, + { pattern: 'node_modules/hammer-simulator/index.js' }, + { pattern: 'node_modules/hammerjs/hammer.js' }, + { pattern: 'node_modules/chartjs-adapter-date-fns/dist/chartjs-adapter-date-fns.bundle.js' }, + { pattern: 'test/index.js' }, + { pattern: 'src/index.umd.ts' }, + { pattern: 'test/specs/**/*.js' }, ], preprocessors: { 'test/index.js': ['rollup'], - 'src/index.umd.ts': ['sources'] + 'src/index.umd.ts': ['sources'], }, rollupPreprocessor: { - plugins: [ - json(), - resolve({extensions: ['.js', '.ts']}), - ], - external: [ - 'chart.js' - ], + plugins: [json(), resolve({ extensions: ['.js', '.ts'] })], + external: ['chart.js'], output: { name: 'test', format: 'umd', globals: { - 'chart.js': 'Chart' - } - } + 'chart.js': 'Chart', + }, + }, }, customPreprocessors: { @@ -97,31 +87,30 @@ module.exports = async function(karma) { options: { output: { format: 'iife', - name: 'fixture' - } - } + name: 'fixture', + }, + }, }, sources: { base: 'rollup', - options: build - } + options: build, + }, }, // These settings deal with browser disconnects. We had seen test flakiness from Firefox // [Firefox 56.0.0 (Linux 0.0.0)]: Disconnected (1 times), because no message in 10000 ms. // https://github.com/jasmine/jasmine/issues/1327#issuecomment-332939551 - browserDisconnectTolerance: 3 - }); - + browserDisconnectTolerance: 3, + }) if (env === 'test') { - karma.reporters.push('coverage'); + karma.reporters.push('coverage') karma.coverageReporter = { dir: 'coverage/', reporters: [ - {type: 'html', subdir: 'html'}, - {type: 'lcovonly', subdir: (browser) => browser.toLowerCase().split(/[ /-]/)[0]} - ] - }; + { type: 'html', subdir: 'html' }, + { type: 'lcovonly', subdir: (browser) => browser.toLowerCase().split(/[ /-]/)[0] }, + ], + } } -}; +} diff --git a/package.json b/package.json index 0d5d7f942..16edac86c 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "dev:ff": "karma start ./karma.conf.cjs --auto-watch --no-single-run --browsers firefox", "docs": "npm run build && vuepress build docs --no-cache", "docs:dev": "concurrently \"npm run autobuild\" \"vuepress dev docs --no-cache\"", - "lint": "eslint \"src/**/*.ts\" \"test/**/*.js\" \"**/*.md\" \"samples/**/*.html\"", + "lint": "eslint \"src/**/*.ts\" \"test/**/*.js\" \"docs/**/*.js\" \"**/*.md\" \"samples/**/*.html\"", "typecheck": "tsc -p tsconfig.json --noEmit", "test": "npm run test-types && npm run test-unit && npm run test-karma", "pretest-unit": "swc --config-file .swcrc-spec src -d build",