js-draw 0.3.0 → 0.3.2
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/.github/ISSUE_TEMPLATE/translation.md +4 -1
- package/CHANGELOG.md +15 -0
- package/dist/bundle.js +1 -1
- package/dist/src/Editor.d.ts +4 -1
- package/dist/src/Editor.js +117 -2
- package/dist/src/EditorImage.js +4 -1
- package/dist/src/SVGLoader.d.ts +4 -1
- package/dist/src/SVGLoader.js +78 -33
- package/dist/src/UndoRedoHistory.d.ts +1 -0
- package/dist/src/UndoRedoHistory.js +6 -0
- package/dist/src/Viewport.d.ts +1 -0
- package/dist/src/Viewport.js +12 -4
- package/dist/src/commands/lib.d.ts +2 -1
- package/dist/src/commands/lib.js +2 -1
- package/dist/src/commands/localization.d.ts +1 -0
- package/dist/src/commands/localization.js +1 -0
- package/dist/src/commands/uniteCommands.d.ts +4 -0
- package/dist/src/commands/uniteCommands.js +105 -0
- package/dist/src/components/AbstractComponent.d.ts +2 -0
- package/dist/src/components/AbstractComponent.js +41 -5
- package/dist/src/components/ImageComponent.d.ts +27 -0
- package/dist/src/components/ImageComponent.js +129 -0
- package/dist/src/components/Stroke.js +11 -6
- package/dist/src/components/builders/FreehandLineBuilder.js +7 -7
- package/dist/src/components/lib.d.ts +4 -2
- package/dist/src/components/lib.js +4 -2
- package/dist/src/components/localization.d.ts +2 -0
- package/dist/src/components/localization.js +2 -0
- package/dist/src/math/LineSegment2.d.ts +4 -0
- package/dist/src/math/LineSegment2.js +9 -0
- package/dist/src/math/Path.d.ts +5 -1
- package/dist/src/math/Path.js +89 -7
- package/dist/src/math/Rect2.js +1 -1
- package/dist/src/math/Triangle.d.ts +11 -0
- package/dist/src/math/Triangle.js +19 -0
- package/dist/src/rendering/Display.js +2 -2
- package/dist/src/rendering/localization.d.ts +3 -0
- package/dist/src/rendering/localization.js +3 -0
- package/dist/src/rendering/renderers/AbstractRenderer.d.ts +9 -1
- package/dist/src/rendering/renderers/CanvasRenderer.d.ts +2 -1
- package/dist/src/rendering/renderers/CanvasRenderer.js +7 -0
- package/dist/src/rendering/renderers/DummyRenderer.d.ts +3 -1
- package/dist/src/rendering/renderers/DummyRenderer.js +5 -0
- package/dist/src/rendering/renderers/SVGRenderer.d.ts +14 -12
- package/dist/src/rendering/renderers/SVGRenderer.js +71 -87
- package/dist/src/rendering/renderers/TextOnlyRenderer.d.ts +3 -1
- package/dist/src/rendering/renderers/TextOnlyRenderer.js +8 -1
- package/dist/src/toolbar/HTMLToolbar.d.ts +1 -0
- package/dist/src/toolbar/HTMLToolbar.js +1 -0
- package/dist/src/toolbar/widgets/BaseWidget.d.ts +3 -0
- package/dist/src/toolbar/widgets/BaseWidget.js +21 -1
- package/dist/src/tools/BaseTool.d.ts +4 -1
- package/dist/src/tools/BaseTool.js +12 -0
- package/dist/src/tools/PasteHandler.d.ts +16 -0
- package/dist/src/tools/PasteHandler.js +142 -0
- package/dist/src/tools/Pen.d.ts +2 -1
- package/dist/src/tools/Pen.js +16 -0
- package/dist/src/tools/SelectionTool.d.ts +7 -1
- package/dist/src/tools/SelectionTool.js +63 -5
- package/dist/src/tools/ToolController.d.ts +1 -0
- package/dist/src/tools/ToolController.js +45 -29
- package/dist/src/tools/ToolSwitcherShortcut.d.ts +8 -0
- package/dist/src/tools/ToolSwitcherShortcut.js +26 -0
- package/dist/src/tools/lib.d.ts +2 -0
- package/dist/src/tools/lib.js +2 -0
- package/dist/src/tools/localization.d.ts +4 -0
- package/dist/src/tools/localization.js +4 -0
- package/dist/src/types.d.ts +21 -4
- package/dist/src/types.js +3 -0
- package/package.json +2 -2
- package/src/Editor.ts +131 -2
- package/src/EditorImage.ts +7 -1
- package/src/SVGLoader.ts +90 -36
- package/src/UndoRedoHistory.test.ts +33 -0
- package/src/UndoRedoHistory.ts +8 -0
- package/src/Viewport.ts +13 -4
- package/src/commands/lib.ts +2 -0
- package/src/commands/localization.ts +2 -0
- package/src/commands/uniteCommands.test.ts +23 -0
- package/src/commands/uniteCommands.ts +121 -0
- package/src/components/AbstractComponent.ts +55 -9
- package/src/components/ImageComponent.ts +153 -0
- package/src/components/Stroke.test.ts +5 -0
- package/src/components/Stroke.ts +13 -7
- package/src/components/builders/FreehandLineBuilder.ts +7 -7
- package/src/components/lib.ts +7 -2
- package/src/components/localization.ts +4 -0
- package/src/math/LineSegment2.test.ts +9 -0
- package/src/math/LineSegment2.ts +13 -0
- package/src/math/Path.test.ts +53 -0
- package/src/math/Path.toString.test.ts +4 -2
- package/src/math/Path.ts +109 -11
- package/src/math/Rect2.ts +1 -1
- package/src/math/Triangle.ts +29 -0
- package/src/rendering/Display.ts +2 -2
- package/src/rendering/localization.ts +6 -0
- package/src/rendering/renderers/AbstractRenderer.ts +17 -0
- package/src/rendering/renderers/CanvasRenderer.ts +10 -1
- package/src/rendering/renderers/DummyRenderer.ts +6 -1
- package/src/rendering/renderers/SVGRenderer.ts +76 -101
- package/src/rendering/renderers/TextOnlyRenderer.ts +10 -2
- package/src/toolbar/HTMLToolbar.ts +1 -1
- package/src/toolbar/types.ts +1 -1
- package/src/toolbar/widgets/BaseWidget.ts +27 -1
- package/src/tools/BaseTool.ts +17 -1
- package/src/tools/PasteHandler.ts +156 -0
- package/src/tools/Pen.ts +20 -1
- package/src/tools/SelectionTool.ts +80 -8
- package/src/tools/ToolController.ts +60 -46
- package/src/tools/ToolSwitcherShortcut.ts +34 -0
- package/src/tools/lib.ts +2 -0
- package/src/tools/localization.ts +10 -0
- package/src/types.ts +29 -3
@@ -1,7 +1,7 @@
|
|
1
1
|
import { LoadSaveDataTable } from '../../components/AbstractComponent';
|
2
2
|
import { TextStyle } from '../../components/Text';
|
3
3
|
import Mat33 from '../../math/Mat33';
|
4
|
-
import { PathCommand } from '../../math/Path';
|
4
|
+
import Path, { PathCommand } from '../../math/Path';
|
5
5
|
import Rect2 from '../../math/Rect2';
|
6
6
|
import { Point2, Vec2 } from '../../math/Vec2';
|
7
7
|
import Viewport from '../../Viewport';
|
@@ -10,6 +10,13 @@ export interface RenderablePathSpec {
|
|
10
10
|
startPoint: Point2;
|
11
11
|
commands: PathCommand[];
|
12
12
|
style: RenderingStyle;
|
13
|
+
path?: Path;
|
14
|
+
}
|
15
|
+
export interface RenderableImage {
|
16
|
+
transform: Mat33;
|
17
|
+
image: HTMLImageElement | HTMLCanvasElement;
|
18
|
+
base64Url: string;
|
19
|
+
label?: string;
|
13
20
|
}
|
14
21
|
export default abstract class AbstractRenderer {
|
15
22
|
private viewport;
|
@@ -26,6 +33,7 @@ export default abstract class AbstractRenderer {
|
|
26
33
|
protected abstract traceCubicBezierCurve(p1: Point2, p2: Point2, p3: Point2): void;
|
27
34
|
protected abstract traceQuadraticBezierCurve(controlPoint: Point2, endPoint: Point2): void;
|
28
35
|
abstract drawText(text: string, transform: Mat33, style: TextStyle): void;
|
36
|
+
abstract drawImage(image: RenderableImage): void;
|
29
37
|
abstract isTooSmallToRender(rect: Rect2): boolean;
|
30
38
|
setDraftMode(_draftMode: boolean): void;
|
31
39
|
protected objectLevel: number;
|
@@ -5,7 +5,7 @@ import { Point2, Vec2 } from '../../math/Vec2';
|
|
5
5
|
import Vec3 from '../../math/Vec3';
|
6
6
|
import Viewport from '../../Viewport';
|
7
7
|
import RenderingStyle from '../RenderingStyle';
|
8
|
-
import AbstractRenderer, { RenderablePathSpec } from './AbstractRenderer';
|
8
|
+
import AbstractRenderer, { RenderableImage, RenderablePathSpec } from './AbstractRenderer';
|
9
9
|
export default class CanvasRenderer extends AbstractRenderer {
|
10
10
|
private ctx;
|
11
11
|
private ignoreObjectsAboveLevel;
|
@@ -28,6 +28,7 @@ export default class CanvasRenderer extends AbstractRenderer {
|
|
28
28
|
protected traceQuadraticBezierCurve(controlPoint: Vec3, endPoint: Vec3): void;
|
29
29
|
drawPath(path: RenderablePathSpec): void;
|
30
30
|
drawText(text: string, transform: Mat33, style: TextStyle): void;
|
31
|
+
drawImage(image: RenderableImage): void;
|
31
32
|
private clipLevels;
|
32
33
|
startObject(boundingBox: Rect2, clip: boolean): void;
|
33
34
|
endObject(): void;
|
@@ -125,6 +125,13 @@ export default class CanvasRenderer extends AbstractRenderer {
|
|
125
125
|
}
|
126
126
|
this.ctx.restore();
|
127
127
|
}
|
128
|
+
drawImage(image) {
|
129
|
+
this.ctx.save();
|
130
|
+
const transform = this.getCanvasToScreenTransform().rightMul(image.transform);
|
131
|
+
this.transformBy(transform);
|
132
|
+
this.ctx.drawImage(image.image, 0, 0);
|
133
|
+
this.ctx.restore();
|
134
|
+
}
|
128
135
|
startObject(boundingBox, clip) {
|
129
136
|
if (this.isTooSmallToRender(boundingBox)) {
|
130
137
|
this.ignoreObjectsAboveLevel = this.getNestingLevel();
|
@@ -5,7 +5,7 @@ import { Point2, Vec2 } from '../../math/Vec2';
|
|
5
5
|
import Vec3 from '../../math/Vec3';
|
6
6
|
import Viewport from '../../Viewport';
|
7
7
|
import RenderingStyle from '../RenderingStyle';
|
8
|
-
import AbstractRenderer from './AbstractRenderer';
|
8
|
+
import AbstractRenderer, { RenderableImage } from './AbstractRenderer';
|
9
9
|
export default class DummyRenderer extends AbstractRenderer {
|
10
10
|
clearedCount: number;
|
11
11
|
renderedPathCount: number;
|
@@ -13,6 +13,7 @@ export default class DummyRenderer extends AbstractRenderer {
|
|
13
13
|
lastPoint: Point2 | null;
|
14
14
|
objectNestingLevel: number;
|
15
15
|
lastText: string | null;
|
16
|
+
lastImage: RenderableImage | null;
|
16
17
|
pointBuffer: Point2[];
|
17
18
|
constructor(viewport: Viewport);
|
18
19
|
displaySize(): Vec2;
|
@@ -25,6 +26,7 @@ export default class DummyRenderer extends AbstractRenderer {
|
|
25
26
|
protected traceQuadraticBezierCurve(controlPoint: Vec3, endPoint: Vec3): void;
|
26
27
|
drawPoints(..._points: Vec3[]): void;
|
27
28
|
drawText(text: string, _transform: Mat33, _style: TextStyle): void;
|
29
|
+
drawImage(image: RenderableImage): void;
|
28
30
|
startObject(boundingBox: Rect2, _clip: boolean): void;
|
29
31
|
endObject(): void;
|
30
32
|
isTooSmallToRender(_rect: Rect2): boolean;
|
@@ -11,6 +11,7 @@ export default class DummyRenderer extends AbstractRenderer {
|
|
11
11
|
this.lastPoint = null;
|
12
12
|
this.objectNestingLevel = 0;
|
13
13
|
this.lastText = null;
|
14
|
+
this.lastImage = null;
|
14
15
|
// List of points drawn since the last clear.
|
15
16
|
this.pointBuffer = [];
|
16
17
|
}
|
@@ -30,6 +31,7 @@ export default class DummyRenderer extends AbstractRenderer {
|
|
30
31
|
this.renderedPathCount = 0;
|
31
32
|
this.pointBuffer = [];
|
32
33
|
this.lastText = null;
|
34
|
+
this.lastImage = null;
|
33
35
|
// Ensure all objects finished rendering
|
34
36
|
if (this.objectNestingLevel > 0) {
|
35
37
|
throw new Error(`Within an object while clearing! Nesting level: ${this.objectNestingLevel}`);
|
@@ -73,6 +75,9 @@ export default class DummyRenderer extends AbstractRenderer {
|
|
73
75
|
drawText(text, _transform, _style) {
|
74
76
|
this.lastText = text;
|
75
77
|
}
|
78
|
+
drawImage(image) {
|
79
|
+
this.lastImage = image;
|
80
|
+
}
|
76
81
|
startObject(boundingBox, _clip) {
|
77
82
|
super.startObject(boundingBox);
|
78
83
|
this.objectNestingLevel += 1;
|
@@ -5,30 +5,32 @@ import Rect2 from '../../math/Rect2';
|
|
5
5
|
import { Point2, Vec2 } from '../../math/Vec2';
|
6
6
|
import Viewport from '../../Viewport';
|
7
7
|
import RenderingStyle from '../RenderingStyle';
|
8
|
-
import AbstractRenderer from './AbstractRenderer';
|
8
|
+
import AbstractRenderer, { RenderableImage, RenderablePathSpec } from './AbstractRenderer';
|
9
9
|
export default class SVGRenderer extends AbstractRenderer {
|
10
10
|
private elem;
|
11
|
-
private
|
12
|
-
private pathStart;
|
11
|
+
private sanitize;
|
13
12
|
private lastPathStyle;
|
14
|
-
private
|
15
|
-
private lastPathStart;
|
13
|
+
private lastPathString;
|
16
14
|
private objectElems;
|
17
15
|
private overwrittenAttrs;
|
18
|
-
constructor(elem: SVGSVGElement, viewport: Viewport);
|
16
|
+
constructor(elem: SVGSVGElement, viewport: Viewport, sanitize?: boolean);
|
19
17
|
setRootSVGAttribute(name: string, value: string | null): void;
|
20
18
|
displaySize(): Vec2;
|
21
19
|
clear(): void;
|
22
|
-
protected beginPath(startPoint: Point2): void;
|
23
|
-
protected endPath(style: RenderingStyle): void;
|
24
20
|
private addPathToSVG;
|
21
|
+
drawPath(pathSpec: RenderablePathSpec): void;
|
22
|
+
private transformFrom;
|
25
23
|
drawText(text: string, transform: Mat33, style: TextStyle): void;
|
24
|
+
drawImage(image: RenderableImage): void;
|
26
25
|
startObject(boundingBox: Rect2): void;
|
27
26
|
endObject(loaderData?: LoadSaveDataTable): void;
|
28
|
-
|
29
|
-
protected
|
30
|
-
protected
|
31
|
-
protected
|
27
|
+
private unimplementedMessage;
|
28
|
+
protected beginPath(_startPoint: Point2): void;
|
29
|
+
protected endPath(_style: RenderingStyle): void;
|
30
|
+
protected lineTo(_point: Point2): void;
|
31
|
+
protected moveTo(_point: Point2): void;
|
32
|
+
protected traceCubicBezierCurve(_controlPoint1: Point2, _controlPoint2: Point2, _endPoint: Point2): void;
|
33
|
+
protected traceQuadraticBezierCurve(_controlPoint: Point2, _endPoint: Point2): void;
|
32
34
|
drawPoints(...points: Point2[]): void;
|
33
35
|
drawSVGElem(elem: SVGElement): void;
|
34
36
|
isTooSmallToRender(_rect: Rect2): boolean;
|
@@ -1,20 +1,27 @@
|
|
1
1
|
import Mat33 from '../../math/Mat33';
|
2
|
-
import Path
|
2
|
+
import Path from '../../math/Path';
|
3
3
|
import { toRoundedString } from '../../math/rounding';
|
4
4
|
import { Vec2 } from '../../math/Vec2';
|
5
5
|
import { svgAttributesDataKey, svgStyleAttributesDataKey } from '../../SVGLoader';
|
6
6
|
import AbstractRenderer from './AbstractRenderer';
|
7
7
|
const svgNameSpace = 'http://www.w3.org/2000/svg';
|
8
8
|
export default class SVGRenderer extends AbstractRenderer {
|
9
|
-
|
9
|
+
// Renders onto `elem`. If `sanitize`, don't render potentially untrusted data.
|
10
|
+
constructor(elem, viewport, sanitize = false) {
|
10
11
|
super(viewport);
|
11
12
|
this.elem = elem;
|
13
|
+
this.sanitize = sanitize;
|
14
|
+
this.lastPathStyle = null;
|
15
|
+
this.lastPathString = [];
|
12
16
|
this.objectElems = null;
|
13
17
|
this.overwrittenAttrs = {};
|
14
18
|
this.clear();
|
15
19
|
}
|
16
20
|
// Sets an attribute on the root SVG element.
|
17
21
|
setRootSVGAttribute(name, value) {
|
22
|
+
if (this.sanitize) {
|
23
|
+
return;
|
24
|
+
}
|
18
25
|
// Make the original value of the attribute restorable on clear
|
19
26
|
if (!(name in this.overwrittenAttrs)) {
|
20
27
|
this.overwrittenAttrs[name] = this.elem.getAttribute(name);
|
@@ -30,55 +37,29 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
30
37
|
return Vec2.of(this.elem.clientWidth, this.elem.clientHeight);
|
31
38
|
}
|
32
39
|
clear() {
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
this.
|
38
|
-
|
39
|
-
|
40
|
-
|
40
|
+
this.lastPathString = [];
|
41
|
+
if (!this.sanitize) {
|
42
|
+
// Restore all all attributes
|
43
|
+
for (const attrName in this.overwrittenAttrs) {
|
44
|
+
const value = this.overwrittenAttrs[attrName];
|
45
|
+
if (value) {
|
46
|
+
this.elem.setAttribute(attrName, value);
|
47
|
+
}
|
48
|
+
else {
|
49
|
+
this.elem.removeAttribute(attrName);
|
50
|
+
}
|
41
51
|
}
|
42
|
-
|
43
|
-
this.overwrittenAttrs = {};
|
44
|
-
}
|
45
|
-
beginPath(startPoint) {
|
46
|
-
var _a;
|
47
|
-
this.currentPath = [];
|
48
|
-
this.pathStart = this.canvasToScreen(startPoint);
|
49
|
-
(_a = this.lastPathStart) !== null && _a !== void 0 ? _a : (this.lastPathStart = this.pathStart);
|
50
|
-
}
|
51
|
-
endPath(style) {
|
52
|
-
var _a;
|
53
|
-
if (this.currentPath == null) {
|
54
|
-
throw new Error('No path exists to end! Make sure beginPath was called!');
|
55
|
-
}
|
56
|
-
// Try to extend the previous path, if possible
|
57
|
-
if (style.fill.eq((_a = this.lastPathStyle) === null || _a === void 0 ? void 0 : _a.fill) && this.lastPath != null) {
|
58
|
-
this.lastPath.push({
|
59
|
-
kind: PathCommandType.MoveTo,
|
60
|
-
point: this.pathStart,
|
61
|
-
}, ...this.currentPath);
|
62
|
-
this.pathStart = null;
|
63
|
-
this.currentPath = null;
|
64
|
-
}
|
65
|
-
else {
|
66
|
-
this.addPathToSVG();
|
67
|
-
this.lastPathStart = this.pathStart;
|
68
|
-
this.lastPathStyle = style;
|
69
|
-
this.lastPath = this.currentPath;
|
70
|
-
this.pathStart = null;
|
71
|
-
this.currentPath = null;
|
52
|
+
this.overwrittenAttrs = {};
|
72
53
|
}
|
73
54
|
}
|
74
55
|
// Push [this.fullPath] to the SVG
|
75
56
|
addPathToSVG() {
|
76
57
|
var _a;
|
77
|
-
if (!this.lastPathStyle ||
|
58
|
+
if (!this.lastPathStyle || this.lastPathString.length === 0) {
|
78
59
|
return;
|
79
60
|
}
|
80
61
|
const pathElem = document.createElementNS(svgNameSpace, 'path');
|
81
|
-
pathElem.setAttribute('d',
|
62
|
+
pathElem.setAttribute('d', this.lastPathString.join(' '));
|
82
63
|
const style = this.lastPathStyle;
|
83
64
|
pathElem.setAttribute('fill', style.fill.toHexString());
|
84
65
|
if (style.stroke) {
|
@@ -88,25 +69,41 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
88
69
|
this.elem.appendChild(pathElem);
|
89
70
|
(_a = this.objectElems) === null || _a === void 0 ? void 0 : _a.push(pathElem);
|
90
71
|
}
|
91
|
-
|
92
|
-
var _a
|
93
|
-
|
72
|
+
drawPath(pathSpec) {
|
73
|
+
var _a;
|
74
|
+
const style = pathSpec.style;
|
75
|
+
const path = Path.fromRenderable(pathSpec);
|
76
|
+
// Try to extend the previous path, if possible
|
77
|
+
if (!style.fill.eq((_a = this.lastPathStyle) === null || _a === void 0 ? void 0 : _a.fill) || this.lastPathString.length === 0) {
|
78
|
+
this.addPathToSVG();
|
79
|
+
this.lastPathStyle = style;
|
80
|
+
this.lastPathString = [];
|
81
|
+
}
|
82
|
+
this.lastPathString.push(path.toString());
|
83
|
+
}
|
84
|
+
// Apply [elemTransform] to [elem].
|
85
|
+
transformFrom(elemTransform, elem) {
|
86
|
+
let transform = this.getCanvasToScreenTransform().rightMul(elemTransform);
|
94
87
|
const translation = transform.transformVec2(Vec2.zero);
|
95
88
|
transform = transform.rightMul(Mat33.translation(translation.times(-1)));
|
96
|
-
|
97
|
-
textElem.appendChild(document.createTextNode(text));
|
98
|
-
textElem.style.transform = `matrix(
|
89
|
+
elem.style.transform = `matrix(
|
99
90
|
${transform.a1}, ${transform.b1},
|
100
91
|
${transform.a2}, ${transform.b2},
|
101
92
|
${transform.a3}, ${transform.b3}
|
102
93
|
)`;
|
94
|
+
elem.setAttribute('x', `${toRoundedString(translation.x)}`);
|
95
|
+
elem.setAttribute('y', `${toRoundedString(translation.y)}`);
|
96
|
+
}
|
97
|
+
drawText(text, transform, style) {
|
98
|
+
var _a, _b, _c;
|
99
|
+
const textElem = document.createElementNS(svgNameSpace, 'text');
|
100
|
+
textElem.appendChild(document.createTextNode(text));
|
101
|
+
this.transformFrom(transform, textElem);
|
103
102
|
textElem.style.fontFamily = style.fontFamily;
|
104
103
|
textElem.style.fontVariant = (_a = style.fontVariant) !== null && _a !== void 0 ? _a : '';
|
105
104
|
textElem.style.fontWeight = (_b = style.fontWeight) !== null && _b !== void 0 ? _b : '';
|
106
105
|
textElem.style.fontSize = style.size + 'px';
|
107
106
|
textElem.style.fill = style.renderingStyle.fill.toHexString();
|
108
|
-
textElem.setAttribute('x', `${toRoundedString(translation.x)}`);
|
109
|
-
textElem.setAttribute('y', `${toRoundedString(translation.y)}`);
|
110
107
|
if (style.renderingStyle.stroke) {
|
111
108
|
const strokeStyle = style.renderingStyle.stroke;
|
112
109
|
textElem.style.stroke = strokeStyle.color.toHexString();
|
@@ -115,11 +112,21 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
115
112
|
this.elem.appendChild(textElem);
|
116
113
|
(_c = this.objectElems) === null || _c === void 0 ? void 0 : _c.push(textElem);
|
117
114
|
}
|
115
|
+
drawImage(image) {
|
116
|
+
var _a, _b, _c, _d, _e;
|
117
|
+
const svgImgElem = document.createElementNS(svgNameSpace, 'image');
|
118
|
+
svgImgElem.setAttribute('href', image.base64Url);
|
119
|
+
svgImgElem.setAttribute('width', (_a = image.image.getAttribute('width')) !== null && _a !== void 0 ? _a : '');
|
120
|
+
svgImgElem.setAttribute('height', (_b = image.image.getAttribute('height')) !== null && _b !== void 0 ? _b : '');
|
121
|
+
svgImgElem.setAttribute('aria-label', (_d = (_c = image.image.getAttribute('aria-label')) !== null && _c !== void 0 ? _c : image.image.getAttribute('alt')) !== null && _d !== void 0 ? _d : '');
|
122
|
+
this.transformFrom(image.transform, svgImgElem);
|
123
|
+
this.elem.appendChild(svgImgElem);
|
124
|
+
(_e = this.objectElems) === null || _e === void 0 ? void 0 : _e.push(svgImgElem);
|
125
|
+
}
|
118
126
|
startObject(boundingBox) {
|
119
127
|
super.startObject(boundingBox);
|
120
128
|
// Only accumulate a path within an object
|
121
|
-
this.
|
122
|
-
this.lastPathStart = null;
|
129
|
+
this.lastPathString = [];
|
123
130
|
this.lastPathStyle = null;
|
124
131
|
this.objectElems = [];
|
125
132
|
}
|
@@ -128,7 +135,7 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
128
135
|
super.endObject(loaderData);
|
129
136
|
// Don't extend paths across objects
|
130
137
|
this.addPathToSVG();
|
131
|
-
if (loaderData) {
|
138
|
+
if (loaderData && !this.sanitize) {
|
132
139
|
// Restore any attributes unsupported by the app.
|
133
140
|
for (const elem of (_a = this.objectElems) !== null && _a !== void 0 ? _a : []) {
|
134
141
|
const attrs = loaderData[svgAttributesDataKey];
|
@@ -146,40 +153,14 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
146
153
|
}
|
147
154
|
}
|
148
155
|
}
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
}
|
156
|
-
|
157
|
-
point = this.canvasToScreen(point);
|
158
|
-
this.currentPath.push({
|
159
|
-
kind: PathCommandType.MoveTo,
|
160
|
-
point,
|
161
|
-
});
|
162
|
-
}
|
163
|
-
traceCubicBezierCurve(controlPoint1, controlPoint2, endPoint) {
|
164
|
-
controlPoint1 = this.canvasToScreen(controlPoint1);
|
165
|
-
controlPoint2 = this.canvasToScreen(controlPoint2);
|
166
|
-
endPoint = this.canvasToScreen(endPoint);
|
167
|
-
this.currentPath.push({
|
168
|
-
kind: PathCommandType.CubicBezierTo,
|
169
|
-
controlPoint1,
|
170
|
-
controlPoint2,
|
171
|
-
endPoint,
|
172
|
-
});
|
173
|
-
}
|
174
|
-
traceQuadraticBezierCurve(controlPoint, endPoint) {
|
175
|
-
controlPoint = this.canvasToScreen(controlPoint);
|
176
|
-
endPoint = this.canvasToScreen(endPoint);
|
177
|
-
this.currentPath.push({
|
178
|
-
kind: PathCommandType.QuadraticBezierTo,
|
179
|
-
controlPoint,
|
180
|
-
endPoint,
|
181
|
-
});
|
182
|
-
}
|
156
|
+
// Not implemented -- use drawPath instead.
|
157
|
+
unimplementedMessage() { throw new Error('Not implemenented!'); }
|
158
|
+
beginPath(_startPoint) { this.unimplementedMessage(); }
|
159
|
+
endPath(_style) { this.unimplementedMessage(); }
|
160
|
+
lineTo(_point) { this.unimplementedMessage(); }
|
161
|
+
moveTo(_point) { this.unimplementedMessage(); }
|
162
|
+
traceCubicBezierCurve(_controlPoint1, _controlPoint2, _endPoint) { this.unimplementedMessage(); }
|
163
|
+
traceQuadraticBezierCurve(_controlPoint, _endPoint) { this.unimplementedMessage(); }
|
183
164
|
drawPoints(...points) {
|
184
165
|
points.map(point => {
|
185
166
|
const elem = document.createElementNS(svgNameSpace, 'circle');
|
@@ -191,6 +172,9 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
191
172
|
}
|
192
173
|
// Renders a **copy** of the given element.
|
193
174
|
drawSVGElem(elem) {
|
175
|
+
if (this.sanitize) {
|
176
|
+
return;
|
177
|
+
}
|
194
178
|
this.elem.appendChild(elem.cloneNode(true));
|
195
179
|
}
|
196
180
|
isTooSmallToRender(_rect) {
|
@@ -5,12 +5,13 @@ import Vec3 from '../../math/Vec3';
|
|
5
5
|
import Viewport from '../../Viewport';
|
6
6
|
import { TextRendererLocalization } from '../localization';
|
7
7
|
import RenderingStyle from '../RenderingStyle';
|
8
|
-
import AbstractRenderer from './AbstractRenderer';
|
8
|
+
import AbstractRenderer, { RenderableImage } from './AbstractRenderer';
|
9
9
|
export default class TextOnlyRenderer extends AbstractRenderer {
|
10
10
|
private localizationTable;
|
11
11
|
private descriptionBuilder;
|
12
12
|
private pathCount;
|
13
13
|
private textNodeCount;
|
14
|
+
private imageNodeCount;
|
14
15
|
constructor(viewport: Viewport, localizationTable: TextRendererLocalization);
|
15
16
|
displaySize(): Vec3;
|
16
17
|
clear(): void;
|
@@ -22,6 +23,7 @@ export default class TextOnlyRenderer extends AbstractRenderer {
|
|
22
23
|
protected traceCubicBezierCurve(_p1: Vec3, _p2: Vec3, _p3: Vec3): void;
|
23
24
|
protected traceQuadraticBezierCurve(_controlPoint: Vec3, _endPoint: Vec3): void;
|
24
25
|
drawText(text: string, _transform: Mat33, _style: TextStyle): void;
|
26
|
+
drawImage(image: RenderableImage): void;
|
25
27
|
isTooSmallToRender(rect: Rect2): boolean;
|
26
28
|
drawPoints(..._points: Vec3[]): void;
|
27
29
|
}
|
@@ -8,6 +8,7 @@ export default class TextOnlyRenderer extends AbstractRenderer {
|
|
8
8
|
this.descriptionBuilder = [];
|
9
9
|
this.pathCount = 0;
|
10
10
|
this.textNodeCount = 0;
|
11
|
+
this.imageNodeCount = 0;
|
11
12
|
}
|
12
13
|
displaySize() {
|
13
14
|
// We don't have a graphical display, export a reasonable size.
|
@@ -21,7 +22,8 @@ export default class TextOnlyRenderer extends AbstractRenderer {
|
|
21
22
|
getDescription() {
|
22
23
|
return [
|
23
24
|
this.localizationTable.pathNodeCount(this.pathCount),
|
24
|
-
this.localizationTable.textNodeCount(this.textNodeCount),
|
25
|
+
...(this.textNodeCount > 0 ? this.localizationTable.textNodeCount(this.textNodeCount) : []),
|
26
|
+
...(this.imageNodeCount > 0 ? this.localizationTable.imageNodeCount(this.imageNodeCount) : []),
|
25
27
|
...this.descriptionBuilder
|
26
28
|
].join('\n');
|
27
29
|
}
|
@@ -42,6 +44,11 @@ export default class TextOnlyRenderer extends AbstractRenderer {
|
|
42
44
|
this.descriptionBuilder.push(this.localizationTable.textNode(text));
|
43
45
|
this.textNodeCount++;
|
44
46
|
}
|
47
|
+
drawImage(image) {
|
48
|
+
const label = image.label ? this.localizationTable.imageNode(image.label) : this.localizationTable.unlabeledImageNode;
|
49
|
+
this.descriptionBuilder.push(label);
|
50
|
+
this.imageNodeCount++;
|
51
|
+
}
|
45
52
|
isTooSmallToRender(rect) {
|
46
53
|
return rect.maxDimension < 15 / this.getSizeOfCanvasPixelOnScreen();
|
47
54
|
}
|
@@ -9,6 +9,7 @@ export default class HTMLToolbar {
|
|
9
9
|
private container;
|
10
10
|
private static colorisStarted;
|
11
11
|
private updateColoris;
|
12
|
+
/** @internal */
|
12
13
|
constructor(editor: Editor, parent: HTMLElement, localizationTable?: ToolbarLocalization);
|
13
14
|
setupColorPickers(): void;
|
14
15
|
addWidget(widget: BaseWidget): void;
|
@@ -14,6 +14,7 @@ import HandToolWidget from './widgets/HandToolWidget';
|
|
14
14
|
import { EraserTool, PenTool } from '../tools/lib';
|
15
15
|
export const toolbarCSSPrefix = 'toolbar-';
|
16
16
|
export default class HTMLToolbar {
|
17
|
+
/** @internal */
|
17
18
|
constructor(editor, parent, localizationTable = defaultToolbarLocalization) {
|
18
19
|
this.editor = editor;
|
19
20
|
this.localizationTable = localizationTable;
|
@@ -12,6 +12,7 @@ export default abstract class BaseWidget {
|
|
12
12
|
private label;
|
13
13
|
private disabled;
|
14
14
|
private subWidgets;
|
15
|
+
private toplevel;
|
15
16
|
constructor(editor: Editor, localizationTable: ToolbarLocalization);
|
16
17
|
protected abstract getTitle(): string;
|
17
18
|
protected abstract createIcon(): Element;
|
@@ -26,6 +27,8 @@ export default abstract class BaseWidget {
|
|
26
27
|
setSelected(selected: boolean): void;
|
27
28
|
protected setDropdownVisible(visible: boolean): void;
|
28
29
|
protected repositionDropdown(): void;
|
30
|
+
/** Set whether the widget is contained within another. @internal */
|
31
|
+
protected setIsToplevel(toplevel: boolean): void;
|
29
32
|
protected isDropdownVisible(): boolean;
|
30
33
|
protected isSelected(): boolean;
|
31
34
|
private createDropdownIcon;
|
@@ -10,7 +10,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
10
10
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
11
11
|
};
|
12
12
|
var _BaseWidget_hasDropdown;
|
13
|
-
import { InputEvtType } from '../../types';
|
13
|
+
import { EditorEventType, InputEvtType } from '../../types';
|
14
14
|
import { toolbarCSSPrefix } from '../HTMLToolbar';
|
15
15
|
import { makeDropdownIcon } from '../icons';
|
16
16
|
export default class BaseWidget {
|
@@ -20,6 +20,7 @@ export default class BaseWidget {
|
|
20
20
|
_BaseWidget_hasDropdown.set(this, void 0);
|
21
21
|
this.disabled = false;
|
22
22
|
this.subWidgets = [];
|
23
|
+
this.toplevel = true;
|
23
24
|
this.icon = null;
|
24
25
|
this.container = document.createElement('div');
|
25
26
|
this.container.classList.add(`${toolbarCSSPrefix}toolContainer`);
|
@@ -41,6 +42,7 @@ export default class BaseWidget {
|
|
41
42
|
}
|
42
43
|
for (const widget of this.subWidgets) {
|
43
44
|
widget.addTo(dropdown);
|
45
|
+
widget.setIsToplevel(false);
|
44
46
|
}
|
45
47
|
return true;
|
46
48
|
}
|
@@ -87,6 +89,7 @@ export default class BaseWidget {
|
|
87
89
|
this.subWidgets.push(widget);
|
88
90
|
}
|
89
91
|
// Adds this to [parent]. This can only be called once for each ToolbarWidget.
|
92
|
+
// @internal
|
90
93
|
addTo(parent) {
|
91
94
|
this.label.innerText = this.getTitle();
|
92
95
|
this.setupActionBtnClickListener(this.button);
|
@@ -99,6 +102,15 @@ export default class BaseWidget {
|
|
99
102
|
this.dropdownIcon = this.createDropdownIcon();
|
100
103
|
this.button.appendChild(this.dropdownIcon);
|
101
104
|
this.container.appendChild(this.dropdownContainer);
|
105
|
+
this.editor.notifier.on(EditorEventType.ToolbarDropdownShown, (evt) => {
|
106
|
+
if (evt.kind === EditorEventType.ToolbarDropdownShown
|
107
|
+
&& evt.parentWidget !== this
|
108
|
+
// Don't hide if a submenu wash shown (it might be a submenu of
|
109
|
+
// the current menu).
|
110
|
+
&& evt.parentWidget.toplevel) {
|
111
|
+
this.setDropdownVisible(false);
|
112
|
+
}
|
113
|
+
});
|
102
114
|
}
|
103
115
|
this.setDropdownVisible(false);
|
104
116
|
parent.appendChild(this.container);
|
@@ -144,6 +156,10 @@ export default class BaseWidget {
|
|
144
156
|
this.dropdownContainer.classList.remove('hidden');
|
145
157
|
this.container.classList.add('dropdownVisible');
|
146
158
|
this.editor.announceForAccessibility(this.localizationTable.dropdownShown(this.getTitle()));
|
159
|
+
this.editor.notifier.dispatch(EditorEventType.ToolbarDropdownShown, {
|
160
|
+
kind: EditorEventType.ToolbarDropdownShown,
|
161
|
+
parentWidget: this,
|
162
|
+
});
|
147
163
|
}
|
148
164
|
else {
|
149
165
|
this.dropdownContainer.classList.add('hidden');
|
@@ -164,6 +180,10 @@ export default class BaseWidget {
|
|
164
180
|
this.dropdownContainer.style.transform = '';
|
165
181
|
}
|
166
182
|
}
|
183
|
+
/** Set whether the widget is contained within another. @internal */
|
184
|
+
setIsToplevel(toplevel) {
|
185
|
+
this.toplevel = toplevel;
|
186
|
+
}
|
167
187
|
isDropdownVisible() {
|
168
188
|
return !this.dropdownContainer.classList.contains('hidden');
|
169
189
|
}
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { PointerEvtListener, WheelEvt, PointerEvt, EditorNotifier, KeyPressEvent, KeyUpEvent } from '../types';
|
1
|
+
import { PointerEvtListener, WheelEvt, PointerEvt, EditorNotifier, KeyPressEvent, KeyUpEvent, PasteEvent, CopyEvent } from '../types';
|
2
2
|
import ToolEnabledGroup from './ToolEnabledGroup';
|
3
3
|
export default abstract class BaseTool implements PointerEvtListener {
|
4
4
|
private notifier;
|
@@ -11,9 +11,12 @@ export default abstract class BaseTool implements PointerEvtListener {
|
|
11
11
|
onGestureCancel(): void;
|
12
12
|
protected constructor(notifier: EditorNotifier, description: string);
|
13
13
|
onWheel(_event: WheelEvt): boolean;
|
14
|
+
onCopy(_event: CopyEvent): boolean;
|
15
|
+
onPaste(_event: PasteEvent): boolean;
|
14
16
|
onKeyPress(_event: KeyPressEvent): boolean;
|
15
17
|
onKeyUp(_event: KeyUpEvent): boolean;
|
16
18
|
setEnabled(enabled: boolean): void;
|
17
19
|
isEnabled(): boolean;
|
18
20
|
setToolGroup(group: ToolEnabledGroup): void;
|
21
|
+
getToolGroup(): ToolEnabledGroup | null;
|
19
22
|
}
|
@@ -13,6 +13,12 @@ export default class BaseTool {
|
|
13
13
|
onWheel(_event) {
|
14
14
|
return false;
|
15
15
|
}
|
16
|
+
onCopy(_event) {
|
17
|
+
return false;
|
18
|
+
}
|
19
|
+
onPaste(_event) {
|
20
|
+
return false;
|
21
|
+
}
|
16
22
|
onKeyPress(_event) {
|
17
23
|
return false;
|
18
24
|
}
|
@@ -48,4 +54,10 @@ export default class BaseTool {
|
|
48
54
|
}
|
49
55
|
this.group = group;
|
50
56
|
}
|
57
|
+
getToolGroup() {
|
58
|
+
if (this.group) {
|
59
|
+
return this.group;
|
60
|
+
}
|
61
|
+
return null;
|
62
|
+
}
|
51
63
|
}
|
@@ -0,0 +1,16 @@
|
|
1
|
+
/**
|
2
|
+
* A tool that handles paste events.
|
3
|
+
* @packageDocumentation
|
4
|
+
*/
|
5
|
+
import Editor from '../Editor';
|
6
|
+
import { PasteEvent } from '../types';
|
7
|
+
import BaseTool from './BaseTool';
|
8
|
+
export default class PasteHandler extends BaseTool {
|
9
|
+
private editor;
|
10
|
+
constructor(editor: Editor);
|
11
|
+
onPaste(event: PasteEvent): boolean;
|
12
|
+
private addComponentsFromPaste;
|
13
|
+
private doSVGPaste;
|
14
|
+
private doTextPaste;
|
15
|
+
private doImagePaste;
|
16
|
+
}
|