fabric 7.1.0 → 7.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.husky/pre-commit +1 -0
- package/CHANGELOG.md +13 -0
- package/dist/extensions/cropping_controls/croppingControls.d.ts +12 -8
- package/dist/extensions/cropping_controls/croppingControls.d.ts.map +1 -1
- package/dist/extensions/cropping_controls/croppingHandlers.d.ts +19 -1
- package/dist/extensions/cropping_controls/croppingHandlers.d.ts.map +1 -1
- package/dist/extensions/cropping_controls/enterCropMode.d.ts.map +1 -1
- package/dist/index.js +189 -160
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/index.min.mjs +1 -1
- package/dist/index.min.mjs.map +1 -1
- package/dist/index.mjs +189 -160
- package/dist/index.mjs.map +1 -1
- package/dist/index.node.cjs +189 -160
- package/dist/index.node.cjs.map +1 -1
- package/dist/index.node.mjs +189 -160
- package/dist/index.node.mjs.map +1 -1
- package/dist/package.json.min.mjs +1 -1
- package/dist/package.json.mjs +1 -1
- package/dist/src/EventTypeDefs.d.ts +3 -0
- package/dist/src/EventTypeDefs.d.ts.map +1 -1
- package/dist/src/Pattern/Pattern.d.ts.map +1 -1
- package/dist/src/Pattern/Pattern.min.mjs +1 -1
- package/dist/src/Pattern/Pattern.min.mjs.map +1 -1
- package/dist/src/Pattern/Pattern.mjs +2 -1
- package/dist/src/Pattern/Pattern.mjs.map +1 -1
- package/dist/src/Shadow.d.ts +1 -1
- package/dist/src/Shadow.d.ts.map +1 -1
- package/dist/src/Shadow.min.mjs +1 -1
- package/dist/src/Shadow.min.mjs.map +1 -1
- package/dist/src/Shadow.mjs +5 -4
- package/dist/src/Shadow.mjs.map +1 -1
- package/dist/src/canvas/CanvasOptions.d.ts.map +1 -1
- package/dist/src/canvas/CanvasOptions.min.mjs.map +1 -1
- package/dist/src/canvas/CanvasOptions.mjs.map +1 -1
- package/dist/src/canvas/SelectableCanvas.d.ts +2 -0
- package/dist/src/canvas/SelectableCanvas.d.ts.map +1 -1
- package/dist/src/canvas/SelectableCanvas.min.mjs +1 -1
- package/dist/src/canvas/SelectableCanvas.min.mjs.map +1 -1
- package/dist/src/canvas/SelectableCanvas.mjs +6 -1
- package/dist/src/canvas/SelectableCanvas.mjs.map +1 -1
- package/dist/src/canvas/StaticCanvas.d.ts.map +1 -1
- package/dist/src/canvas/StaticCanvas.min.mjs +1 -1
- package/dist/src/canvas/StaticCanvas.min.mjs.map +1 -1
- package/dist/src/canvas/StaticCanvas.mjs +3 -1
- package/dist/src/canvas/StaticCanvas.mjs.map +1 -1
- package/dist/src/canvas/StaticCanvasOptions.d.ts.map +1 -1
- package/dist/src/canvas/StaticCanvasOptions.min.mjs.map +1 -1
- package/dist/src/canvas/StaticCanvasOptions.mjs.map +1 -1
- package/dist/src/controls/Control.d.ts +9 -1
- package/dist/src/controls/Control.d.ts.map +1 -1
- package/dist/src/controls/Control.min.mjs +1 -1
- package/dist/src/controls/Control.min.mjs.map +1 -1
- package/dist/src/controls/Control.mjs +8 -0
- package/dist/src/controls/Control.mjs.map +1 -1
- package/dist/src/gradient/Gradient.d.ts.map +1 -1
- package/dist/src/gradient/Gradient.min.mjs +1 -1
- package/dist/src/gradient/Gradient.min.mjs.map +1 -1
- package/dist/src/gradient/Gradient.mjs +19 -6
- package/dist/src/gradient/Gradient.mjs.map +1 -1
- package/dist/src/shapes/Circle.d.ts.map +1 -1
- package/dist/src/shapes/Circle.min.mjs +1 -1
- package/dist/src/shapes/Circle.min.mjs.map +1 -1
- package/dist/src/shapes/Circle.mjs +10 -7
- package/dist/src/shapes/Circle.mjs.map +1 -1
- package/dist/src/shapes/Ellipse.d.ts.map +1 -1
- package/dist/src/shapes/Ellipse.min.mjs +1 -1
- package/dist/src/shapes/Ellipse.min.mjs.map +1 -1
- package/dist/src/shapes/Ellipse.mjs +2 -1
- package/dist/src/shapes/Ellipse.mjs.map +1 -1
- package/dist/src/shapes/Group.d.ts.map +1 -1
- package/dist/src/shapes/Group.min.mjs +1 -1
- package/dist/src/shapes/Group.min.mjs.map +1 -1
- package/dist/src/shapes/Group.mjs +2 -1
- package/dist/src/shapes/Group.mjs.map +1 -1
- package/dist/src/shapes/IText/IText.d.ts.map +1 -1
- package/dist/src/shapes/IText/IText.min.mjs.map +1 -1
- package/dist/src/shapes/IText/IText.mjs.map +1 -1
- package/dist/src/shapes/Image.d.ts +1 -1
- package/dist/src/shapes/Image.d.ts.map +1 -1
- package/dist/src/shapes/Image.min.mjs +1 -1
- package/dist/src/shapes/Image.min.mjs.map +1 -1
- package/dist/src/shapes/Image.mjs +4 -3
- package/dist/src/shapes/Image.mjs.map +1 -1
- package/dist/src/shapes/Line.d.ts.map +1 -1
- package/dist/src/shapes/Line.min.mjs +1 -1
- package/dist/src/shapes/Line.min.mjs.map +1 -1
- package/dist/src/shapes/Line.mjs +6 -10
- package/dist/src/shapes/Line.mjs.map +1 -1
- package/dist/src/shapes/Object/FabricObjectSVGExportMixin.d.ts.map +1 -1
- package/dist/src/shapes/Object/FabricObjectSVGExportMixin.min.mjs +1 -1
- package/dist/src/shapes/Object/FabricObjectSVGExportMixin.min.mjs.map +1 -1
- package/dist/src/shapes/Object/FabricObjectSVGExportMixin.mjs +5 -4
- package/dist/src/shapes/Object/FabricObjectSVGExportMixin.mjs.map +1 -1
- package/dist/src/shapes/Object/InteractiveObject.d.ts.map +1 -1
- package/dist/src/shapes/Object/InteractiveObject.min.mjs.map +1 -1
- package/dist/src/shapes/Object/InteractiveObject.mjs.map +1 -1
- package/dist/src/shapes/Object/Object.d.ts.map +1 -1
- package/dist/src/shapes/Object/Object.min.mjs +1 -1
- package/dist/src/shapes/Object/Object.min.mjs.map +1 -1
- package/dist/src/shapes/Object/Object.mjs +3 -0
- package/dist/src/shapes/Object/Object.mjs.map +1 -1
- package/dist/src/shapes/Object/types/FabricObjectProps.d.ts.map +1 -1
- package/dist/src/shapes/Object/types/ObjectProps.d.ts.map +1 -1
- package/dist/src/shapes/Path.d.ts.map +1 -1
- package/dist/src/shapes/Path.min.mjs.map +1 -1
- package/dist/src/shapes/Path.mjs +1 -2
- package/dist/src/shapes/Path.mjs.map +1 -1
- package/dist/src/shapes/Polyline.d.ts.map +1 -1
- package/dist/src/shapes/Polyline.min.mjs +1 -1
- package/dist/src/shapes/Polyline.min.mjs.map +1 -1
- package/dist/src/shapes/Polyline.mjs +10 -6
- package/dist/src/shapes/Polyline.mjs.map +1 -1
- package/dist/src/shapes/Rect.d.ts.map +1 -1
- package/dist/src/shapes/Rect.min.mjs +1 -1
- package/dist/src/shapes/Rect.min.mjs.map +1 -1
- package/dist/src/shapes/Rect.mjs +2 -1
- package/dist/src/shapes/Rect.mjs.map +1 -1
- package/dist/src/shapes/Text/Text.d.ts.map +1 -1
- package/dist/src/shapes/Text/Text.min.mjs.map +1 -1
- package/dist/src/shapes/Text/Text.mjs.map +1 -1
- package/dist/src/shapes/Text/TextSVGExportMixin.min.mjs +1 -1
- package/dist/src/shapes/Text/TextSVGExportMixin.min.mjs.map +1 -1
- package/dist/src/shapes/Text/TextSVGExportMixin.mjs +5 -5
- package/dist/src/shapes/Text/TextSVGExportMixin.mjs.map +1 -1
- package/dist/src/shapes/Textbox.d.ts.map +1 -1
- package/dist/src/shapes/Textbox.min.mjs.map +1 -1
- package/dist/src/shapes/Textbox.mjs.map +1 -1
- package/dist/src/shapes/Triangle.d.ts.map +1 -1
- package/dist/src/shapes/Triangle.min.mjs.map +1 -1
- package/dist/src/shapes/Triangle.mjs.map +1 -1
- package/dist/src/util/lang_string.d.ts +1 -1
- package/dist/src/util/lang_string.d.ts.map +1 -1
- package/dist/src/util/lang_string.min.mjs +1 -1
- package/dist/src/util/lang_string.min.mjs.map +1 -1
- package/dist/src/util/lang_string.mjs +1 -1
- package/dist/src/util/lang_string.mjs.map +1 -1
- package/dist/src/util/misc/svgParsing.d.ts.map +1 -1
- package/dist/src/util/misc/svgParsing.min.mjs +1 -1
- package/dist/src/util/misc/svgParsing.min.mjs.map +1 -1
- package/dist/src/util/misc/svgParsing.mjs +2 -1
- package/dist/src/util/misc/svgParsing.mjs.map +1 -1
- package/dist-extensions/cropping_controls/croppingControls.mjs +39 -9
- package/dist-extensions/cropping_controls/croppingControls.mjs.map +1 -1
- package/dist-extensions/cropping_controls/croppingHandlers.mjs +84 -2
- package/dist-extensions/cropping_controls/croppingHandlers.mjs.map +1 -1
- package/dist-extensions/cropping_controls/enterCropMode.mjs +7 -2
- package/dist-extensions/cropping_controls/enterCropMode.mjs.map +1 -1
- package/dist-extensions/extensions/cropping_controls/croppingControls.d.ts +12 -8
- package/dist-extensions/extensions/cropping_controls/croppingControls.d.ts.map +1 -1
- package/dist-extensions/extensions/cropping_controls/croppingHandlers.d.ts +19 -1
- package/dist-extensions/extensions/cropping_controls/croppingHandlers.d.ts.map +1 -1
- package/dist-extensions/extensions/cropping_controls/enterCropMode.d.ts.map +1 -1
- package/dist-extensions/fabric-extensions.min.js +1 -1
- package/dist-extensions/fabric-extensions.min.js.map +1 -1
- package/dist-extensions/src/EventTypeDefs.d.ts +3 -0
- package/dist-extensions/src/EventTypeDefs.d.ts.map +1 -1
- package/dist-extensions/src/Pattern/Pattern.d.ts.map +1 -1
- package/dist-extensions/src/Shadow.d.ts +1 -1
- package/dist-extensions/src/Shadow.d.ts.map +1 -1
- package/dist-extensions/src/canvas/CanvasOptions.d.ts.map +1 -1
- package/dist-extensions/src/canvas/SelectableCanvas.d.ts +2 -0
- package/dist-extensions/src/canvas/SelectableCanvas.d.ts.map +1 -1
- package/dist-extensions/src/canvas/StaticCanvas.d.ts.map +1 -1
- package/dist-extensions/src/canvas/StaticCanvasOptions.d.ts.map +1 -1
- package/dist-extensions/src/controls/Control.d.ts +9 -1
- package/dist-extensions/src/controls/Control.d.ts.map +1 -1
- package/dist-extensions/src/gradient/Gradient.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Circle.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Ellipse.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Group.d.ts.map +1 -1
- package/dist-extensions/src/shapes/IText/IText.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Image.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Line.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Object/FabricObjectSVGExportMixin.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Object/InteractiveObject.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Object/Object.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Object/types/FabricObjectProps.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Object/types/ObjectProps.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Path.d.ts +1 -1
- package/dist-extensions/src/shapes/Path.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Polyline.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Rect.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Text/Text.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Textbox.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Triangle.d.ts.map +1 -1
- package/dist-extensions/src/util/lang_string.d.ts +1 -1
- package/dist-extensions/src/util/lang_string.d.ts.map +1 -1
- package/dist-extensions/src/util/misc/svgParsing.d.ts.map +1 -1
- package/eslint.config.mjs +2 -0
- package/extensions/cropping_controls/croppingControls.spec.ts +65 -27
- package/extensions/cropping_controls/croppingControls.ts +40 -8
- package/extensions/cropping_controls/croppingHandlers.spec.ts +355 -46
- package/extensions/cropping_controls/croppingHandlers.ts +123 -0
- package/extensions/cropping_controls/enterCropMode.ts +6 -2
- package/package.json +17 -8
- package/src/ClassRegistry.spec.ts +18 -19
- package/src/EventTypeDefs.ts +13 -11
- package/src/Pattern/Pattern.spec.ts +12 -0
- package/src/Pattern/Pattern.ts +3 -2
- package/src/Shadow.ts +9 -8
- package/src/brushes/PencilBrush.spec.ts +11 -11
- package/src/canvas/Canvas-dispose.spec.ts +8 -7
- package/src/canvas/Canvas.spec.ts +27 -29
- package/src/canvas/CanvasOptions.ts +2 -1
- package/src/canvas/SelectableCanvas.ts +11 -4
- package/src/canvas/StaticCanvas.spec.ts +20 -0
- package/src/canvas/StaticCanvas.ts +7 -4
- package/src/canvas/StaticCanvasOptions.ts +1 -3
- package/src/controls/Control.ts +24 -1
- package/src/gradient/Gradient.spec.ts +101 -46
- package/src/gradient/Gradient.ts +27 -14
- package/src/shapes/Circle.spec.ts +10 -39
- package/src/shapes/Circle.ts +11 -11
- package/src/shapes/Ellipse.spec.ts +8 -37
- package/src/shapes/Ellipse.ts +7 -7
- package/src/shapes/Group.ts +3 -3
- package/src/shapes/IText/IText-click-behavior.spec.ts +36 -49
- package/src/shapes/IText/IText.ts +5 -6
- package/src/shapes/IText/__snapshots__/ITextBehavior.test.ts.snap +6 -6
- package/src/shapes/Image.spec.ts +17 -33
- package/src/shapes/Image.ts +15 -11
- package/src/shapes/Line.spec.ts +4 -30
- package/src/shapes/Line.ts +11 -16
- package/src/shapes/Object/FabricObjectSVGExportMixin.ts +11 -4
- package/src/shapes/Object/InteractiveObject.ts +4 -4
- package/src/shapes/Object/Object.ts +6 -5
- package/src/shapes/Object/objectSvgExport.spec.ts +112 -0
- package/src/shapes/Object/types/FabricObjectProps.ts +1 -4
- package/src/shapes/Object/types/ObjectProps.ts +1 -3
- package/src/shapes/Path.spec.ts +4 -27
- package/src/shapes/Path.ts +2 -4
- package/src/shapes/Polygon.spec.ts +4 -31
- package/src/shapes/Polyline.spec.ts +4 -31
- package/src/shapes/Polyline.ts +11 -12
- package/src/shapes/Rect.spec.ts +25 -33
- package/src/shapes/Rect.ts +7 -7
- package/src/shapes/Text/Text.spec.ts +3 -32
- package/src/shapes/Text/Text.ts +5 -6
- package/src/shapes/Text/TextSVGExportMixin.ts +14 -14
- package/src/shapes/Text/__snapshots__/Text.spec.ts.snap +1 -1
- package/src/shapes/Textbox.spec.ts +5 -5
- package/src/shapes/Textbox.ts +6 -5
- package/src/shapes/Triangle.ts +4 -4
- package/src/shapes/__snapshots__/Image.spec.ts.snap +4 -4
- package/src/shapes/__snapshots__/Textbox.spec.ts.snap +5 -5
- package/src/util/lang_string.ts +3 -2
- package/src/util/misc/svgParsing.ts +2 -1
- package/tsconfig.spec.json +1 -0
- package/vitest.config.ts +12 -2
- package/vitest.extend.ts +6 -2
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "fabric",
|
|
3
3
|
"description": "Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.",
|
|
4
4
|
"homepage": "http://fabricjs.com/",
|
|
5
|
-
"version": "7.
|
|
5
|
+
"version": "7.2.0",
|
|
6
6
|
"author": "Juriy Zaytsev <kangax@gmail.com>",
|
|
7
7
|
"contributors": [
|
|
8
8
|
{
|
|
@@ -63,7 +63,8 @@
|
|
|
63
63
|
"local-server": "serve ./ -l tcp://localhost:8080",
|
|
64
64
|
"lint": "eslint src extensions",
|
|
65
65
|
"prettier:check": "prettier --check .",
|
|
66
|
-
"prettier:write": "prettier --write ."
|
|
66
|
+
"prettier:write": "prettier --write .",
|
|
67
|
+
"prepare": "husky install"
|
|
67
68
|
},
|
|
68
69
|
"devDependencies": {
|
|
69
70
|
"@babel/cli": "^7.28.3",
|
|
@@ -71,7 +72,7 @@
|
|
|
71
72
|
"@babel/preset-env": "^7.28.3",
|
|
72
73
|
"@babel/preset-typescript": "^7.27.1",
|
|
73
74
|
"@eslint/js": "^9.37.0",
|
|
74
|
-
"@playwright/test": "^1.
|
|
75
|
+
"@playwright/test": "^1.58.1",
|
|
75
76
|
"@rollup/plugin-babel": "^6.0.4",
|
|
76
77
|
"@rollup/plugin-json": "^6.1.0",
|
|
77
78
|
"@rollup/plugin-terser": "^0.4.4",
|
|
@@ -79,26 +80,29 @@
|
|
|
79
80
|
"@types/jsdom": "^21.1.7",
|
|
80
81
|
"@types/micromatch": "^4.0.9",
|
|
81
82
|
"@types/node": "^24.7.0",
|
|
82
|
-
"@vitest/browser-playwright": "^4.0.
|
|
83
|
-
"@vitest/coverage-v8": "^4.0.
|
|
84
|
-
"@vitest/ui": "^4.0.
|
|
83
|
+
"@vitest/browser-playwright": "^4.0.18",
|
|
84
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
85
|
+
"@vitest/ui": "^4.0.18",
|
|
85
86
|
"babel-plugin-transform-imports": "git+https://git@github.com/fabricjs/babel-plugin-transform-imports.git",
|
|
86
87
|
"commander": "^14.0.1",
|
|
87
88
|
"es-toolkit": "1.40.0",
|
|
88
89
|
"eslint-config-prettier": "^10.1.8",
|
|
90
|
+
"husky": "^9.1.7",
|
|
89
91
|
"inquirer": "^12.9.6",
|
|
92
|
+
"lint-staged": "^16.2.7",
|
|
90
93
|
"micromatch": "^4.0.8",
|
|
91
94
|
"nyc": "^17.1.0",
|
|
92
|
-
"prettier": "^3.
|
|
95
|
+
"prettier": "^3.8.1",
|
|
93
96
|
"ps-list": "^9.0.0",
|
|
94
97
|
"rollup": "^4.52.4",
|
|
95
98
|
"semver": "^7.7.3",
|
|
96
99
|
"serve": "^14.2.5",
|
|
100
|
+
"tsc-files": "^1.1.4",
|
|
97
101
|
"tslib": "^2.8.1",
|
|
98
102
|
"typescript": "^5.9.3",
|
|
99
103
|
"typescript-eslint": "^8.46.0",
|
|
100
104
|
"v8-to-istanbul": "^9.3.0",
|
|
101
|
-
"vitest": "^4.0.
|
|
105
|
+
"vitest": "^4.0.18",
|
|
102
106
|
"westures": "^1.1.1"
|
|
103
107
|
},
|
|
104
108
|
"engines": {
|
|
@@ -153,5 +157,10 @@
|
|
|
153
157
|
"optionalDependencies": {
|
|
154
158
|
"canvas": "^3.2.0",
|
|
155
159
|
"jsdom": "^26.1.0"
|
|
160
|
+
},
|
|
161
|
+
"lint-staged": {
|
|
162
|
+
"*.{js,md,css,ts,tsx,jsx,json}": "eslint --fix",
|
|
163
|
+
"*.{js,css,md,ts,tsx,jsx,json}": "prettier --write",
|
|
164
|
+
"**/*.ts !(**/*.spec.ts) !(**/*.test.ts) !(vitest*.ts)": "tsc-files --noEmit"
|
|
156
165
|
}
|
|
157
166
|
}
|
|
@@ -1,17 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ClassRegistry,
|
|
3
|
-
classRegistry as genericClassRegistryInstance,
|
|
4
|
-
JSON,
|
|
5
|
-
} from './ClassRegistry';
|
|
6
|
-
import './shapes/Object/FabricObject';
|
|
1
|
+
import type { ClassRegistry } from './ClassRegistry';
|
|
7
2
|
|
|
8
|
-
import { describe, expect, beforeEach, it } from 'vitest';
|
|
3
|
+
import { describe, expect, beforeEach, afterEach, it, vi } from 'vitest';
|
|
9
4
|
|
|
10
5
|
describe('ClassRegistry', () => {
|
|
11
6
|
let classRegistry: ClassRegistry;
|
|
12
|
-
beforeEach(() => {
|
|
7
|
+
beforeEach(async () => {
|
|
8
|
+
const { ClassRegistry } = await import('./ClassRegistry');
|
|
13
9
|
classRegistry = new ClassRegistry();
|
|
14
10
|
});
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
vi.resetModules();
|
|
13
|
+
});
|
|
15
14
|
it('will error if a class is request that is not registered', () => {
|
|
16
15
|
expect(() => classRegistry.getClass('any')).toThrow(
|
|
17
16
|
'No class registered for any',
|
|
@@ -57,30 +56,30 @@ describe('ClassRegistry', () => {
|
|
|
57
56
|
expect(resolved, 'resolved different classes').not.toBe(resolvedSvg);
|
|
58
57
|
});
|
|
59
58
|
it('legacy resolution preparation', async () => {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
expect(
|
|
65
|
-
expect(
|
|
66
|
-
expect(
|
|
67
|
-
expect(genericClassRegistryInstance.has('object')).toBe(false);
|
|
59
|
+
const { classRegistry: freshRegistry } = await import('./ClassRegistry');
|
|
60
|
+
|
|
61
|
+
// Registry should be empty before any shape classes are imported
|
|
62
|
+
expect(freshRegistry.has('rect')).toBe(false);
|
|
63
|
+
expect(freshRegistry.has('i-text')).toBe(false);
|
|
64
|
+
expect(freshRegistry.has('activeSelection')).toBe(false);
|
|
65
|
+
expect(freshRegistry.has('object')).toBe(false);
|
|
68
66
|
});
|
|
69
67
|
it('legacy resolution', async () => {
|
|
68
|
+
const { classRegistry: freshRegistry } = await import('./ClassRegistry');
|
|
70
69
|
const { Rect } = await import('./shapes/Rect');
|
|
71
70
|
const { IText } = await import('./shapes/IText/IText');
|
|
72
71
|
const { ActiveSelection } = await import('./shapes/ActiveSelection');
|
|
73
72
|
// const { FabricObject } = await import('./shapes/Object/FabricObject');
|
|
74
73
|
expect(
|
|
75
|
-
|
|
74
|
+
freshRegistry.getClass('rect'),
|
|
76
75
|
'resolves Rect class correctly',
|
|
77
76
|
).toBe(Rect);
|
|
78
77
|
expect(
|
|
79
|
-
|
|
78
|
+
freshRegistry.getClass('i-text'),
|
|
80
79
|
'resolves IText class correctly',
|
|
81
80
|
).toBe(IText);
|
|
82
81
|
expect(
|
|
83
|
-
|
|
82
|
+
freshRegistry.getClass('activeSelection'),
|
|
84
83
|
'resolves ActiveSelection class correctly',
|
|
85
84
|
).toBe(ActiveSelection);
|
|
86
85
|
// expect(
|
package/src/EventTypeDefs.ts
CHANGED
|
@@ -108,8 +108,9 @@ interface TEventWithTarget<E extends Event = TPointerEvent> extends TEvent<E> {
|
|
|
108
108
|
target: FabricObject;
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
export interface BasicTransformEvent<
|
|
112
|
-
extends
|
|
111
|
+
export interface BasicTransformEvent<
|
|
112
|
+
E extends Event = TPointerEvent,
|
|
113
|
+
> extends TEvent<E> {
|
|
113
114
|
transform: Transform;
|
|
114
115
|
/* This pointer is usually a scenePoint. It isn't in the case of actions inside groups,
|
|
115
116
|
* where it becomes a point relative to the group center
|
|
@@ -163,8 +164,9 @@ type CanvasModificationEvents = {
|
|
|
163
164
|
'object:modified': ModifiedEvent;
|
|
164
165
|
};
|
|
165
166
|
|
|
166
|
-
export interface TPointerEventInfo<
|
|
167
|
-
extends
|
|
167
|
+
export interface TPointerEventInfo<
|
|
168
|
+
E extends TPointerEvent = TPointerEvent,
|
|
169
|
+
> extends TEvent<E> {
|
|
168
170
|
target?: FabricObject;
|
|
169
171
|
subTargets?: FabricObject[];
|
|
170
172
|
transform?: Transform | null;
|
|
@@ -172,8 +174,9 @@ export interface TPointerEventInfo<E extends TPointerEvent = TPointerEvent>
|
|
|
172
174
|
viewportPoint: Point;
|
|
173
175
|
}
|
|
174
176
|
|
|
175
|
-
interface SimpleEventHandler<
|
|
176
|
-
extends
|
|
177
|
+
interface SimpleEventHandler<
|
|
178
|
+
T extends Event = TPointerEvent,
|
|
179
|
+
> extends TEvent<T> {
|
|
177
180
|
target?: FabricObject;
|
|
178
181
|
subTargets: FabricObject[];
|
|
179
182
|
}
|
|
@@ -288,10 +291,7 @@ export interface MiscEvents {
|
|
|
288
291
|
}
|
|
289
292
|
|
|
290
293
|
export interface ObjectEvents
|
|
291
|
-
extends ObjectPointerEvents,
|
|
292
|
-
DnDEvents,
|
|
293
|
-
MiscEvents,
|
|
294
|
-
ObjectModificationEvents {
|
|
294
|
+
extends ObjectPointerEvents, DnDEvents, MiscEvents, ObjectModificationEvents {
|
|
295
295
|
// selection
|
|
296
296
|
selected: Partial<TEvent> & {
|
|
297
297
|
target: FabricObject;
|
|
@@ -305,6 +305,7 @@ export interface ObjectEvents
|
|
|
305
305
|
|
|
306
306
|
// erasing
|
|
307
307
|
'erasing:end': { path: FabricObject };
|
|
308
|
+
'before:render': { ctx: CanvasRenderingContext2D };
|
|
308
309
|
}
|
|
309
310
|
|
|
310
311
|
export interface StaticCanvasEvents extends CollectionEvents {
|
|
@@ -319,7 +320,8 @@ export interface StaticCanvasEvents extends CollectionEvents {
|
|
|
319
320
|
}
|
|
320
321
|
|
|
321
322
|
export interface CanvasEvents
|
|
322
|
-
extends
|
|
323
|
+
extends
|
|
324
|
+
StaticCanvasEvents,
|
|
323
325
|
CanvasPointerEvents,
|
|
324
326
|
CanvasDnDEvents,
|
|
325
327
|
MiscEvents,
|
|
@@ -187,4 +187,16 @@ describe('Pattern', () => {
|
|
|
187
187
|
const obj = await Rect.fromObject(rectObj);
|
|
188
188
|
expect(obj.fill instanceof Pattern).toBeTruthy();
|
|
189
189
|
});
|
|
190
|
+
|
|
191
|
+
describe('attribute injection', () => {
|
|
192
|
+
it('escapes correctly the src', () => {
|
|
193
|
+
const pattern = new Pattern({
|
|
194
|
+
source: { src: '"><svg onload=alert(1)>', width: 10, height: 10 },
|
|
195
|
+
});
|
|
196
|
+
const svg = pattern.toSVG({ width: 100, height: 100 });
|
|
197
|
+
expect(svg).toContain(
|
|
198
|
+
'xlink:href=""><svg onload=alert(1)>"',
|
|
199
|
+
);
|
|
200
|
+
});
|
|
201
|
+
});
|
|
190
202
|
});
|
package/src/Pattern/Pattern.ts
CHANGED
|
@@ -12,6 +12,7 @@ import type {
|
|
|
12
12
|
SerializedPatternOptions,
|
|
13
13
|
} from './types';
|
|
14
14
|
import { log } from '../util/internals/console';
|
|
15
|
+
import { escapeXml } from '../util/lang_string';
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* @see {@link http://fabric5.fabricjs.com/patterns demo}
|
|
@@ -177,12 +178,12 @@ export class Pattern {
|
|
|
177
178
|
: ifNaN((patternSource as HTMLImageElement).height / height, 0);
|
|
178
179
|
|
|
179
180
|
return [
|
|
180
|
-
`<pattern id="SVGID_${id}" x="${patternOffsetX}" y="${patternOffsetY}" width="${patternWidth}" height="${patternHeight}">`,
|
|
181
|
+
`<pattern id="SVGID_${escapeXml(id)}" x="${patternOffsetX}" y="${patternOffsetY}" width="${patternWidth}" height="${patternHeight}">`,
|
|
181
182
|
`<image x="0" y="0" width="${
|
|
182
183
|
(patternSource as HTMLImageElement).width
|
|
183
184
|
}" height="${
|
|
184
185
|
(patternSource as HTMLImageElement).height
|
|
185
|
-
}" xlink:href="${this.sourceToString()}"></image>`,
|
|
186
|
+
}" xlink:href="${escapeXml(this.sourceToString())}"></image>`,
|
|
186
187
|
`</pattern>`,
|
|
187
188
|
'',
|
|
188
189
|
].join('\n');
|
package/src/Shadow.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { Point } from './Point';
|
|
|
6
6
|
import type { FabricObject } from './shapes/Object/FabricObject';
|
|
7
7
|
import type { TClassProperties } from './typedefs';
|
|
8
8
|
import { uid } from './util/internals/uid';
|
|
9
|
+
import { escapeXml } from './util/lang_string';
|
|
9
10
|
import { pickBy } from './util/misc/pick';
|
|
10
11
|
import { degreesToRadians } from './util/misc/radiansDegreesConversion';
|
|
11
12
|
import { toFixed } from './util/misc/toFixed';
|
|
@@ -27,7 +28,6 @@ import { rotateVector } from './util/misc/vectors';
|
|
|
27
28
|
|
|
28
29
|
(?:$|\s): This captures either the end of the line or a whitespace character. It ensures that the match ends either at the end of the string or with a whitespace character.
|
|
29
30
|
*/
|
|
30
|
-
// eslint-disable-next-line max-len
|
|
31
31
|
|
|
32
32
|
const shadowOffsetRegex = '(-?\\d+(?:\\.\\d*)?(?:px)?(?:\\s?|$))?';
|
|
33
33
|
|
|
@@ -105,7 +105,7 @@ export class Shadow {
|
|
|
105
105
|
*/
|
|
106
106
|
declare nonScaling: boolean;
|
|
107
107
|
|
|
108
|
-
declare id: number;
|
|
108
|
+
declare id: number | string;
|
|
109
109
|
|
|
110
110
|
static ownDefaults = shadowDefaultValues;
|
|
111
111
|
|
|
@@ -163,6 +163,7 @@ export class Shadow {
|
|
|
163
163
|
degreesToRadians(-object.angle),
|
|
164
164
|
),
|
|
165
165
|
BLUR_BOX = 20,
|
|
166
|
+
NUM_FRACTION_DIGITS = config.NUM_FRACTION_DIGITS,
|
|
166
167
|
color = new Color(this.color);
|
|
167
168
|
let fBoxX = 40,
|
|
168
169
|
fBoxY = 40;
|
|
@@ -173,14 +174,14 @@ export class Shadow {
|
|
|
173
174
|
fBoxX =
|
|
174
175
|
toFixed(
|
|
175
176
|
(Math.abs(offset.x) + this.blur) / object.width,
|
|
176
|
-
|
|
177
|
+
NUM_FRACTION_DIGITS,
|
|
177
178
|
) *
|
|
178
179
|
100 +
|
|
179
180
|
BLUR_BOX;
|
|
180
181
|
fBoxY =
|
|
181
182
|
toFixed(
|
|
182
183
|
(Math.abs(offset.y) + this.blur) / object.height,
|
|
183
|
-
|
|
184
|
+
NUM_FRACTION_DIGITS,
|
|
184
185
|
) *
|
|
185
186
|
100 +
|
|
186
187
|
BLUR_BOX;
|
|
@@ -192,19 +193,19 @@ export class Shadow {
|
|
|
192
193
|
offset.y *= -1;
|
|
193
194
|
}
|
|
194
195
|
|
|
195
|
-
return `<filter id="SVGID_${this.id}" y="-${fBoxY}%" height="${
|
|
196
|
+
return `<filter id="SVGID_${escapeXml(this.id)}" y="-${fBoxY}%" height="${
|
|
196
197
|
100 + 2 * fBoxY
|
|
197
198
|
}%" x="-${fBoxX}%" width="${
|
|
198
199
|
100 + 2 * fBoxX
|
|
199
200
|
}%" >\n\t<feGaussianBlur in="SourceAlpha" stdDeviation="${toFixed(
|
|
200
201
|
this.blur ? this.blur / 2 : 0,
|
|
201
|
-
|
|
202
|
+
NUM_FRACTION_DIGITS,
|
|
202
203
|
)}"></feGaussianBlur>\n\t<feOffset dx="${toFixed(
|
|
203
204
|
offset.x,
|
|
204
|
-
|
|
205
|
+
NUM_FRACTION_DIGITS,
|
|
205
206
|
)}" dy="${toFixed(
|
|
206
207
|
offset.y,
|
|
207
|
-
|
|
208
|
+
NUM_FRACTION_DIGITS,
|
|
208
209
|
)}" result="oBlur" ></feOffset>\n\t<feFlood flood-color="${color.toRgb()}" flood-opacity="${color.getAlpha()}"/>\n\t<feComposite in2="oBlur" operator="in" />\n\t<feMerge>\n\t\t<feMergeNode></feMergeNode>\n\t\t<feMergeNode in="SourceGraphic"></feMergeNode>\n\t</feMerge>\n</filter>\n`;
|
|
209
210
|
}
|
|
210
211
|
|
|
@@ -2,9 +2,9 @@ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
|
2
2
|
import { Canvas } from '../canvas/Canvas';
|
|
3
3
|
import { PencilBrush } from './PencilBrush';
|
|
4
4
|
import { parsePath } from '../util';
|
|
5
|
-
import type { TPointerEvent } from '../EventTypeDefs';
|
|
6
5
|
import { Path } from '../shapes/Path';
|
|
7
6
|
import { Point } from '../Point';
|
|
7
|
+
import { createPointerEvent } from '../../test/utils';
|
|
8
8
|
|
|
9
9
|
describe('PencilBrush', () => {
|
|
10
10
|
let canvas: Canvas;
|
|
@@ -55,12 +55,12 @@ describe('PencilBrush', () => {
|
|
|
55
55
|
|
|
56
56
|
it('draws a point correctly', () => {
|
|
57
57
|
const brush = new PencilBrush(canvas);
|
|
58
|
-
const e = {
|
|
59
|
-
|
|
60
|
-
...e,
|
|
58
|
+
const e = createPointerEvent({
|
|
59
|
+
target: canvas.upperCanvasEl,
|
|
61
60
|
clientX: 10,
|
|
62
61
|
clientY: 10,
|
|
63
62
|
});
|
|
63
|
+
const pointer = canvas.getScenePoint(e);
|
|
64
64
|
brush.onMouseDown(pointer, { e });
|
|
65
65
|
// @ts-expect-error -- protected
|
|
66
66
|
const pathData = brush.convertPointsToSVGPath(brush._points);
|
|
@@ -72,12 +72,12 @@ describe('PencilBrush', () => {
|
|
|
72
72
|
|
|
73
73
|
it('handles multiple coincident points', () => {
|
|
74
74
|
const brush = new PencilBrush(canvas);
|
|
75
|
-
const e = {
|
|
76
|
-
|
|
77
|
-
...e,
|
|
75
|
+
const e = createPointerEvent({
|
|
76
|
+
target: canvas.upperCanvasEl,
|
|
78
77
|
clientX: 10,
|
|
79
78
|
clientY: 10,
|
|
80
79
|
});
|
|
80
|
+
const pointer = canvas.getScenePoint(e);
|
|
81
81
|
brush.onMouseDown(pointer, { e });
|
|
82
82
|
brush.onMouseMove(pointer, { e });
|
|
83
83
|
brush.onMouseMove(pointer, { e });
|
|
@@ -95,7 +95,7 @@ describe('PencilBrush', () => {
|
|
|
95
95
|
|
|
96
96
|
it('handles multiple non-coincident points', () => {
|
|
97
97
|
const brush = new PencilBrush(canvas);
|
|
98
|
-
const e = { target: canvas.upperCanvasEl }
|
|
98
|
+
const e = createPointerEvent({ target: canvas.upperCanvasEl });
|
|
99
99
|
const pointer = canvas.getScenePoint({
|
|
100
100
|
...e,
|
|
101
101
|
clientX: 10,
|
|
@@ -129,7 +129,7 @@ describe('PencilBrush', () => {
|
|
|
129
129
|
|
|
130
130
|
it('handles points outside canvas', () => {
|
|
131
131
|
const brush = new PencilBrush(canvas);
|
|
132
|
-
const e = { target: canvas.upperCanvasEl }
|
|
132
|
+
const e = createPointerEvent({ target: canvas.upperCanvasEl });
|
|
133
133
|
const pointer = canvas.getScenePoint({
|
|
134
134
|
...e,
|
|
135
135
|
clientX: 10,
|
|
@@ -177,7 +177,7 @@ describe('PencilBrush', () => {
|
|
|
177
177
|
it('limits points to canvas size when limitedToCanvasSize is true', () => {
|
|
178
178
|
const brush = new PencilBrush(canvas);
|
|
179
179
|
brush.limitedToCanvasSize = true;
|
|
180
|
-
const e = { target: canvas.upperCanvasEl }
|
|
180
|
+
const e = createPointerEvent({ target: canvas.upperCanvasEl });
|
|
181
181
|
const pointer = canvas.getScenePoint({
|
|
182
182
|
...e,
|
|
183
183
|
clientX: 10,
|
|
@@ -237,7 +237,7 @@ describe('PencilBrush', () => {
|
|
|
237
237
|
});
|
|
238
238
|
|
|
239
239
|
const brush = new PencilBrush(canvas);
|
|
240
|
-
const e = { target: canvas.upperCanvasEl }
|
|
240
|
+
const e = createPointerEvent({ target: canvas.upperCanvasEl });
|
|
241
241
|
const pointer = canvas.getScenePoint({
|
|
242
242
|
...e,
|
|
243
243
|
clientX: 10,
|
|
@@ -195,23 +195,24 @@ describe('Canvas dispose', () => {
|
|
|
195
195
|
|
|
196
196
|
it('dispose edge case: `toCanvasElement` after dispose', async () => {
|
|
197
197
|
const canvas = new CanvasClass(undefined, { renderOnAddRemove: false });
|
|
198
|
-
const
|
|
198
|
+
const getAlphaValues = () => {
|
|
199
199
|
return canvas
|
|
200
200
|
.toCanvasElement()
|
|
201
201
|
.getContext('2d')!
|
|
202
|
-
.getImageData(
|
|
203
|
-
.data.filter((_, i) => i % 4 ===
|
|
204
|
-
.every((x) => x === colorByteVal);
|
|
202
|
+
.getImageData(10, 10, 20, 20)
|
|
203
|
+
.data.filter((_, i) => i % 4 === 3);
|
|
205
204
|
};
|
|
205
|
+
const hasOpaquePixels = () => getAlphaValues().some((x) => x === 255);
|
|
206
|
+
const isFullyTransparent = () => getAlphaValues().every((x) => x === 0);
|
|
206
207
|
canvas.add(
|
|
207
208
|
makeRect({ fill: 'red', width: 20, height: 20, top: 10, left: 10 }),
|
|
208
209
|
);
|
|
209
|
-
expect(
|
|
210
|
+
expect(hasOpaquePixels(), 'control').toBeTruthy();
|
|
210
211
|
canvas.disposed = true;
|
|
211
|
-
expect(
|
|
212
|
+
expect(hasOpaquePixels(), 'should render canvas').toBeTruthy();
|
|
212
213
|
canvas.destroyed = true;
|
|
213
214
|
expect(
|
|
214
|
-
|
|
215
|
+
isFullyTransparent(),
|
|
215
216
|
'should have disabled canvas rendering',
|
|
216
217
|
).toBeTruthy();
|
|
217
218
|
canvas.destroyed = false;
|
|
@@ -10,6 +10,7 @@ import type {
|
|
|
10
10
|
MultiSelectionStacking,
|
|
11
11
|
TPointerEvent,
|
|
12
12
|
} from '../../fabric';
|
|
13
|
+
import { createPointerEvent } from '../../test/utils';
|
|
13
14
|
import {
|
|
14
15
|
ActiveSelection,
|
|
15
16
|
Circle,
|
|
@@ -862,9 +863,7 @@ describe('Canvas', () => {
|
|
|
862
863
|
deltaX: 5,
|
|
863
864
|
deltaY: 5,
|
|
864
865
|
});
|
|
865
|
-
canvas._onMouseUp({
|
|
866
|
-
target: canvas.upperCanvasEl,
|
|
867
|
-
} as unknown as TPointerEvent);
|
|
866
|
+
canvas._onMouseUp(createPointerEvent({ target: canvas.upperCanvasEl }));
|
|
868
867
|
|
|
869
868
|
expect(fired, 'event fired for each of 3 rects').toBe(3);
|
|
870
869
|
});
|
|
@@ -885,9 +884,7 @@ describe('Canvas', () => {
|
|
|
885
884
|
deltaX: 5,
|
|
886
885
|
deltaY: 5,
|
|
887
886
|
});
|
|
888
|
-
canvas._onMouseUp({
|
|
889
|
-
target: canvas.upperCanvasEl,
|
|
890
|
-
} as unknown as TPointerEvent);
|
|
887
|
+
canvas._onMouseUp(createPointerEvent({ target: canvas.upperCanvasEl }));
|
|
891
888
|
|
|
892
889
|
expect(isFired, 'selection created fired').toBe(true);
|
|
893
890
|
expect(
|
|
@@ -917,9 +914,7 @@ describe('Canvas', () => {
|
|
|
917
914
|
deltaX: 5,
|
|
918
915
|
deltaY: 5,
|
|
919
916
|
});
|
|
920
|
-
canvas._onMouseUp({
|
|
921
|
-
target: canvas.upperCanvasEl,
|
|
922
|
-
} as unknown as TPointerEvent);
|
|
917
|
+
canvas._onMouseUp(createPointerEvent({ target: canvas.upperCanvasEl }));
|
|
923
918
|
|
|
924
919
|
expect(isFired, 'selection:created fired').toBe(true);
|
|
925
920
|
expect(canvas.getActiveObject(), 'rect1 is set as activeObject').toBe(
|
|
@@ -1115,18 +1110,22 @@ describe('Canvas', () => {
|
|
|
1115
1110
|
const rect = makeRect({ left: 0, top: 0 });
|
|
1116
1111
|
canvas.add(rect);
|
|
1117
1112
|
|
|
1118
|
-
const { target } = canvas.findTarget(
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1113
|
+
const { target } = canvas.findTarget(
|
|
1114
|
+
createPointerEvent({
|
|
1115
|
+
clientX: 5,
|
|
1116
|
+
clientY: 5,
|
|
1117
|
+
target: canvas.upperCanvasEl,
|
|
1118
|
+
}),
|
|
1119
|
+
);
|
|
1123
1120
|
expect(target, 'Should return the rect').toBe(rect);
|
|
1124
1121
|
|
|
1125
|
-
const { target: target2 } = canvas.findTarget(
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1122
|
+
const { target: target2 } = canvas.findTarget(
|
|
1123
|
+
createPointerEvent({
|
|
1124
|
+
clientX: 30,
|
|
1125
|
+
clientY: 30,
|
|
1126
|
+
target: canvas.upperCanvasEl,
|
|
1127
|
+
}),
|
|
1128
|
+
);
|
|
1130
1129
|
expect(target2, 'Should not find target').toBeUndefined();
|
|
1131
1130
|
|
|
1132
1131
|
canvas.remove(rect);
|
|
@@ -1897,16 +1896,15 @@ describe('Canvas', () => {
|
|
|
1897
1896
|
});
|
|
1898
1897
|
|
|
1899
1898
|
it('cleans up transform when discarding active object', () => {
|
|
1900
|
-
const e = {
|
|
1899
|
+
const e = createPointerEvent({
|
|
1901
1900
|
clientX: 5,
|
|
1902
1901
|
clientY: 5,
|
|
1903
|
-
which: 1,
|
|
1904
1902
|
target: canvas.upperCanvasEl,
|
|
1905
|
-
};
|
|
1903
|
+
});
|
|
1906
1904
|
const target = makeRect();
|
|
1907
1905
|
canvas.add(target);
|
|
1908
1906
|
canvas.setActiveObject(target);
|
|
1909
|
-
canvas._setupCurrentTransform(e
|
|
1907
|
+
canvas._setupCurrentTransform(e, target, true);
|
|
1910
1908
|
expect(canvas._currentTransform, 'transform should be set').toBeTruthy();
|
|
1911
1909
|
|
|
1912
1910
|
target.isMoving = true;
|
|
@@ -2221,11 +2219,11 @@ describe('Canvas', () => {
|
|
|
2221
2219
|
const rect = new Rect({ left: 100, top: 100, width: 50, height: 50 });
|
|
2222
2220
|
canvas.add(rect);
|
|
2223
2221
|
const canvasOffset = canvas.calcOffset();
|
|
2224
|
-
let eventStub = {
|
|
2222
|
+
let eventStub = createPointerEvent({
|
|
2225
2223
|
clientX: canvasOffset.left + 100,
|
|
2226
2224
|
clientY: canvasOffset.top + 100,
|
|
2227
2225
|
target: canvas.upperCanvasEl,
|
|
2228
|
-
}
|
|
2226
|
+
});
|
|
2229
2227
|
canvas.setActiveObject(rect);
|
|
2230
2228
|
const targetCorner = rect.findControl(canvas.getViewportPoint(eventStub));
|
|
2231
2229
|
rect.__corner = targetCorner ? targetCorner.key : undefined;
|
|
@@ -2237,11 +2235,11 @@ describe('Canvas', () => {
|
|
|
2237
2235
|
expect(t.originX, 'no origin change for drag').toBe(rect.originX);
|
|
2238
2236
|
expect(t.originY, 'no origin change for drag').toBe(rect.originY);
|
|
2239
2237
|
|
|
2240
|
-
eventStub = {
|
|
2238
|
+
eventStub = createPointerEvent({
|
|
2241
2239
|
clientX: canvasOffset.left + rect.oCoords.tl.corner.tl.x + 1,
|
|
2242
2240
|
clientY: canvasOffset.top + rect.oCoords.tl.corner.tl.y + 1,
|
|
2243
2241
|
target: canvas.upperCanvasEl,
|
|
2244
|
-
}
|
|
2242
|
+
});
|
|
2245
2243
|
rect.__corner = rect.findControl(canvas.getViewportPoint(eventStub))!.key;
|
|
2246
2244
|
canvas._setupCurrentTransform(eventStub, rect, false);
|
|
2247
2245
|
t = canvas._currentTransform!;
|
|
@@ -2264,12 +2262,12 @@ describe('Canvas', () => {
|
|
|
2264
2262
|
expect(t.originY, 'origin in opposite direction').toBe('bottom');
|
|
2265
2263
|
expect(t.shiftKey, 'shift was not pressed').toBe(undefined);
|
|
2266
2264
|
|
|
2267
|
-
eventStub = {
|
|
2265
|
+
eventStub = createPointerEvent({
|
|
2268
2266
|
clientX: canvasOffset.left + rect.left - 2 - rect.width / 2,
|
|
2269
2267
|
clientY: canvasOffset.top + rect.top,
|
|
2270
2268
|
target: canvas.upperCanvasEl,
|
|
2271
2269
|
shiftKey: true,
|
|
2272
|
-
}
|
|
2270
|
+
});
|
|
2273
2271
|
rect.__corner = rect.findControl(canvas.getViewportPoint(eventStub))!.key;
|
|
2274
2272
|
canvas._setupCurrentTransform(eventStub, rect, alreadySelected);
|
|
2275
2273
|
t = canvas._currentTransform!;
|
|
@@ -565,6 +565,8 @@ export class SelectableCanvas<EventSpec extends CanvasEvents = CanvasEvents>
|
|
|
565
565
|
* Given the control clicked, determine the origin of the transform.
|
|
566
566
|
* This is bad because controls can totally have custom names
|
|
567
567
|
* should disappear before release 4.0
|
|
568
|
+
* Fabric 7.1, jan 2026 we are still using this.
|
|
569
|
+
* Needs to go.
|
|
568
570
|
* @private
|
|
569
571
|
* @deprecated
|
|
570
572
|
*/
|
|
@@ -572,15 +574,20 @@ export class SelectableCanvas<EventSpec extends CanvasEvents = CanvasEvents>
|
|
|
572
574
|
target: FabricObject,
|
|
573
575
|
controlName: string,
|
|
574
576
|
): { x: TOriginX; y: TOriginY } {
|
|
575
|
-
const origin =
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
577
|
+
const origin = controlName
|
|
578
|
+
? target.controls[controlName].getTransformAnchorPoint()
|
|
579
|
+
: {
|
|
580
|
+
x: target.originX,
|
|
581
|
+
y: target.originY,
|
|
582
|
+
};
|
|
579
583
|
|
|
580
584
|
if (!controlName) {
|
|
581
585
|
return origin;
|
|
582
586
|
}
|
|
583
587
|
|
|
588
|
+
// this part down here is deprecated.
|
|
589
|
+
// It is left to do not change the standard behavior in the middle of a major version
|
|
590
|
+
// but when possible `getTransformAnchorPoint` will be the only source of truth
|
|
584
591
|
// is a left control ?
|
|
585
592
|
if (['ml', 'tl', 'bl'].includes(controlName)) {
|
|
586
593
|
origin.x = RIGHT;
|
|
@@ -2241,6 +2241,26 @@ describe('StaticCanvas', () => {
|
|
|
2241
2241
|
// });
|
|
2242
2242
|
});
|
|
2243
2243
|
|
|
2244
|
+
describe('malicious tests', () => {
|
|
2245
|
+
it('from JSON to svg', async () => {
|
|
2246
|
+
const canvas = new StaticCanvas();
|
|
2247
|
+
const maliciousJSON = {
|
|
2248
|
+
objects: [
|
|
2249
|
+
{
|
|
2250
|
+
type: 'rect',
|
|
2251
|
+
id: '"><set onbegin="alert(1)"/>',
|
|
2252
|
+
width: 100,
|
|
2253
|
+
height: 100,
|
|
2254
|
+
fill: 'red',
|
|
2255
|
+
},
|
|
2256
|
+
],
|
|
2257
|
+
};
|
|
2258
|
+
await canvas.loadFromJSON(maliciousJSON);
|
|
2259
|
+
const svg = canvas.toSVG();
|
|
2260
|
+
expect(svg).not.toContain('onbegin="alert(1)"');
|
|
2261
|
+
});
|
|
2262
|
+
});
|
|
2263
|
+
|
|
2244
2264
|
function makeRect(options = {}) {
|
|
2245
2265
|
const defaultOptions = { width: 10, height: 10 };
|
|
2246
2266
|
return new Rect({ ...defaultOptions, ...options });
|
|
@@ -44,6 +44,7 @@ import type { StaticCanvasOptions } from './StaticCanvasOptions';
|
|
|
44
44
|
import { staticCanvasDefaults } from './StaticCanvasOptions';
|
|
45
45
|
import { log, FabricError } from '../util/internals/console';
|
|
46
46
|
import { getDevicePixelRatio } from '../env';
|
|
47
|
+
import { escapeXml } from '../util/lang_string';
|
|
47
48
|
|
|
48
49
|
/**
|
|
49
50
|
* Having both options in TCanvasSizeOptions set to true transform the call in a calcOffset
|
|
@@ -86,9 +87,9 @@ export type PatternQuality = 'fast' | 'good' | 'best' | 'nearest' | 'bilinear';
|
|
|
86
87
|
*/
|
|
87
88
|
// TODO: fix `EventSpec` inheritance https://github.com/microsoft/TypeScript/issues/26154#issuecomment-1366616260
|
|
88
89
|
export class StaticCanvas<
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
91
|
+
EventSpec extends StaticCanvasEvents = StaticCanvasEvents,
|
|
92
|
+
>
|
|
92
93
|
extends createCollectionMixin(CommonMethods<CanvasEvents>)
|
|
93
94
|
implements StaticCanvasOptions
|
|
94
95
|
{
|
|
@@ -950,7 +951,9 @@ export class StaticCanvas<
|
|
|
950
951
|
this._setSVGPreamble(markup, options);
|
|
951
952
|
this._setSVGHeader(markup, options);
|
|
952
953
|
if (this.clipPath) {
|
|
953
|
-
markup.push(
|
|
954
|
+
markup.push(
|
|
955
|
+
`<g clip-path="url(#${escapeXml(this.clipPath.clipPathId ?? '')})" >\n`,
|
|
956
|
+
);
|
|
954
957
|
}
|
|
955
958
|
this._setSVGBgOverlayColor(markup, 'background');
|
|
956
959
|
this._setSVGBgOverlayImage(markup, 'backgroundImage', reviver);
|