js-draw 1.0.1 → 1.0.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/LICENSE +21 -0
- package/dist/bundle.js +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/mjs/version.mjs +1 -1
- package/docs/img/readme-images/js-draw.jpg +0 -0
- package/docs/img/readme-images/unsupported-elements--in-editor.png +0 -0
- package/package.json +5 -4
- package/dist-test/test_imports/package-lock.json +0 -13
- package/dist-test/test_imports/package.json +0 -12
- package/dist-test/test_imports/test-imports.js +0 -11
- package/dist-test/test_imports/test-require.cjs +0 -14
- package/src/Editor.loadFrom.test.ts +0 -24
- package/src/Editor.test.ts +0 -107
- package/src/Editor.toSVG.test.ts +0 -294
- package/src/Editor.ts +0 -1443
- package/src/EditorImage.test.ts +0 -117
- package/src/EditorImage.ts +0 -609
- package/src/EventDispatcher.test.ts +0 -123
- package/src/EventDispatcher.ts +0 -72
- package/src/Pointer.ts +0 -183
- package/src/SVGLoader.test.ts +0 -114
- package/src/SVGLoader.ts +0 -672
- package/src/UndoRedoHistory.test.ts +0 -34
- package/src/UndoRedoHistory.ts +0 -102
- package/src/Viewport.ts +0 -322
- package/src/bundle/bundled.ts +0 -7
- package/src/commands/Command.ts +0 -45
- package/src/commands/Duplicate.ts +0 -75
- package/src/commands/Erase.ts +0 -95
- package/src/commands/SerializableCommand.ts +0 -49
- package/src/commands/UnresolvedCommand.ts +0 -37
- package/src/commands/invertCommand.ts +0 -58
- package/src/commands/lib.ts +0 -16
- package/src/commands/localization.ts +0 -47
- package/src/commands/uniteCommands.test.ts +0 -23
- package/src/commands/uniteCommands.ts +0 -140
- package/src/components/AbstractComponent.transformBy.test.ts +0 -23
- package/src/components/AbstractComponent.ts +0 -383
- package/src/components/BackgroundComponent.test.ts +0 -44
- package/src/components/BackgroundComponent.ts +0 -348
- package/src/components/ImageComponent.ts +0 -176
- package/src/components/RestylableComponent.ts +0 -161
- package/src/components/SVGGlobalAttributesObject.ts +0 -79
- package/src/components/Stroke.test.ts +0 -137
- package/src/components/Stroke.ts +0 -294
- package/src/components/TextComponent.test.ts +0 -202
- package/src/components/TextComponent.ts +0 -429
- package/src/components/UnknownSVGObject.test.ts +0 -10
- package/src/components/UnknownSVGObject.ts +0 -60
- package/src/components/builders/ArrowBuilder.ts +0 -106
- package/src/components/builders/CircleBuilder.ts +0 -100
- package/src/components/builders/FreehandLineBuilder.test.ts +0 -24
- package/src/components/builders/FreehandLineBuilder.ts +0 -210
- package/src/components/builders/LineBuilder.ts +0 -77
- package/src/components/builders/PressureSensitiveFreehandLineBuilder.ts +0 -453
- package/src/components/builders/RectangleBuilder.ts +0 -73
- package/src/components/builders/types.ts +0 -15
- package/src/components/lib.ts +0 -31
- package/src/components/localization.ts +0 -24
- package/src/components/util/StrokeSmoother.ts +0 -302
- package/src/components/util/describeComponentList.ts +0 -18
- package/src/dialogs/makeAboutDialog.ts +0 -82
- package/src/inputEvents.ts +0 -143
- package/src/lib.ts +0 -91
- package/src/localization.ts +0 -34
- package/src/localizations/de.ts +0 -146
- package/src/localizations/en.ts +0 -8
- package/src/localizations/es.ts +0 -74
- package/src/localizations/getLocalizationTable.test.ts +0 -27
- package/src/localizations/getLocalizationTable.ts +0 -74
- package/src/rendering/Display.ts +0 -247
- package/src/rendering/RenderablePathSpec.ts +0 -88
- package/src/rendering/RenderingStyle.test.ts +0 -68
- package/src/rendering/RenderingStyle.ts +0 -55
- package/src/rendering/TextRenderingStyle.ts +0 -55
- package/src/rendering/caching/CacheRecord.test.ts +0 -48
- package/src/rendering/caching/CacheRecord.ts +0 -76
- package/src/rendering/caching/CacheRecordManager.ts +0 -71
- package/src/rendering/caching/RenderingCache.test.ts +0 -43
- package/src/rendering/caching/RenderingCache.ts +0 -66
- package/src/rendering/caching/RenderingCacheNode.ts +0 -404
- package/src/rendering/caching/testUtils.ts +0 -35
- package/src/rendering/caching/types.ts +0 -34
- package/src/rendering/lib.ts +0 -8
- package/src/rendering/localization.ts +0 -20
- package/src/rendering/renderers/AbstractRenderer.ts +0 -232
- package/src/rendering/renderers/CanvasRenderer.ts +0 -312
- package/src/rendering/renderers/DummyRenderer.test.ts +0 -41
- package/src/rendering/renderers/DummyRenderer.ts +0 -142
- package/src/rendering/renderers/SVGRenderer.ts +0 -434
- package/src/rendering/renderers/TextOnlyRenderer.test.ts +0 -34
- package/src/rendering/renderers/TextOnlyRenderer.ts +0 -68
- package/src/shortcuts/KeyBinding.test.ts +0 -61
- package/src/shortcuts/KeyBinding.ts +0 -257
- package/src/shortcuts/KeyboardShortcutManager.test.ts +0 -95
- package/src/shortcuts/KeyboardShortcutManager.ts +0 -163
- package/src/shortcuts/lib.ts +0 -3
- package/src/testing/createEditor.ts +0 -11
- package/src/testing/getUniquePointerId.ts +0 -18
- package/src/testing/lib.ts +0 -3
- package/src/testing/sendPenEvent.ts +0 -36
- package/src/testing/sendTouchEvent.ts +0 -71
- package/src/toolbar/AbstractToolbar.ts +0 -542
- package/src/toolbar/DropdownToolbar.ts +0 -220
- package/src/toolbar/EdgeToolbar.test.ts +0 -54
- package/src/toolbar/EdgeToolbar.ts +0 -543
- package/src/toolbar/IconProvider.ts +0 -861
- package/src/toolbar/constants.ts +0 -1
- package/src/toolbar/lib.ts +0 -6
- package/src/toolbar/localization.ts +0 -136
- package/src/toolbar/types.ts +0 -13
- package/src/toolbar/widgets/ActionButtonWidget.ts +0 -39
- package/src/toolbar/widgets/BaseToolWidget.ts +0 -81
- package/src/toolbar/widgets/BaseWidget.ts +0 -495
- package/src/toolbar/widgets/DocumentPropertiesWidget.ts +0 -250
- package/src/toolbar/widgets/EraserToolWidget.ts +0 -84
- package/src/toolbar/widgets/HandToolWidget.ts +0 -239
- package/src/toolbar/widgets/InsertImageWidget.ts +0 -248
- package/src/toolbar/widgets/OverflowWidget.ts +0 -92
- package/src/toolbar/widgets/PenToolWidget.ts +0 -369
- package/src/toolbar/widgets/SelectionToolWidget.ts +0 -195
- package/src/toolbar/widgets/TextToolWidget.ts +0 -149
- package/src/toolbar/widgets/components/makeColorInput.ts +0 -184
- package/src/toolbar/widgets/components/makeFileInput.ts +0 -128
- package/src/toolbar/widgets/components/makeGridSelector.ts +0 -179
- package/src/toolbar/widgets/components/makeSeparator.ts +0 -17
- package/src/toolbar/widgets/components/makeThicknessSlider.ts +0 -62
- package/src/toolbar/widgets/keybindings.ts +0 -19
- package/src/toolbar/widgets/layout/DropdownLayoutManager.ts +0 -262
- package/src/toolbar/widgets/layout/EdgeToolbarLayoutManager.ts +0 -71
- package/src/toolbar/widgets/layout/types.ts +0 -74
- package/src/toolbar/widgets/lib.ts +0 -13
- package/src/tools/BaseTool.ts +0 -169
- package/src/tools/Eraser.test.ts +0 -103
- package/src/tools/Eraser.ts +0 -173
- package/src/tools/FindTool.test.ts +0 -67
- package/src/tools/FindTool.ts +0 -153
- package/src/tools/InputFilter/FunctionMapper.ts +0 -17
- package/src/tools/InputFilter/InputMapper.ts +0 -41
- package/src/tools/InputFilter/InputPipeline.test.ts +0 -41
- package/src/tools/InputFilter/InputPipeline.ts +0 -34
- package/src/tools/InputFilter/InputStabilizer.ts +0 -254
- package/src/tools/InputFilter/StrokeKeyboardControl.ts +0 -104
- package/src/tools/PanZoom.test.ts +0 -339
- package/src/tools/PanZoom.ts +0 -525
- package/src/tools/PasteHandler.ts +0 -94
- package/src/tools/Pen.test.ts +0 -260
- package/src/tools/Pen.ts +0 -284
- package/src/tools/PipetteTool.ts +0 -84
- package/src/tools/SelectionTool/SelectAllShortcutHandler.ts +0 -29
- package/src/tools/SelectionTool/Selection.ts +0 -647
- package/src/tools/SelectionTool/SelectionHandle.ts +0 -142
- package/src/tools/SelectionTool/SelectionTool.test.ts +0 -370
- package/src/tools/SelectionTool/SelectionTool.ts +0 -510
- package/src/tools/SelectionTool/TransformMode.ts +0 -112
- package/src/tools/SelectionTool/types.ts +0 -11
- package/src/tools/SoundUITool.ts +0 -221
- package/src/tools/TextTool.ts +0 -339
- package/src/tools/ToolController.ts +0 -224
- package/src/tools/ToolEnabledGroup.ts +0 -14
- package/src/tools/ToolSwitcherShortcut.ts +0 -39
- package/src/tools/ToolbarShortcutHandler.ts +0 -39
- package/src/tools/UndoRedoShortcut.test.ts +0 -62
- package/src/tools/UndoRedoShortcut.ts +0 -24
- package/src/tools/keybindings.ts +0 -85
- package/src/tools/lib.ts +0 -22
- package/src/tools/localization.ts +0 -76
- package/src/types.ts +0 -151
- package/src/util/ReactiveValue.test.ts +0 -168
- package/src/util/ReactiveValue.ts +0 -241
- package/src/util/assertions.ts +0 -55
- package/src/util/fileToBase64.ts +0 -18
- package/src/util/guessKeyCodeFromKey.ts +0 -36
- package/src/util/listPrefixMatch.ts +0 -19
- package/src/util/stopPropagationOfScrollingWheelEvents.ts +0 -20
- package/src/util/untilNextAnimationFrame.ts +0 -9
- package/src/util/waitForAll.ts +0 -18
- package/src/util/waitForTimeout.ts +0 -9
- package/src/version.test.ts +0 -12
- package/src/version.ts +0 -3
- package/tools/allLocales.js +0 -4
- package/tools/copyREADME.ts +0 -62
package/dist/cjs/version.js
CHANGED
package/dist/mjs/version.mjs
CHANGED
Binary file
|
Binary file
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "js-draw",
|
3
|
-
"version": "1.0.
|
3
|
+
"version": "1.0.2",
|
4
4
|
"description": "Draw pictures using a pen, touchscreen, or mouse! JS-draw is a drawing library for JavaScript and TypeScript. ",
|
5
5
|
"types": "./dist/mjs/lib.d.ts",
|
6
6
|
"main": "./dist/cjs/lib.js",
|
@@ -64,11 +64,11 @@
|
|
64
64
|
"postpack": "ts-node tools/copyREADME.ts revert"
|
65
65
|
},
|
66
66
|
"dependencies": {
|
67
|
-
"@js-draw/math": "^1.0.
|
67
|
+
"@js-draw/math": "^1.0.2",
|
68
68
|
"@melloware/coloris": "0.21.0"
|
69
69
|
},
|
70
70
|
"devDependencies": {
|
71
|
-
"@js-draw/build-tool": "^1.0.
|
71
|
+
"@js-draw/build-tool": "^1.0.2",
|
72
72
|
"@types/bezier-js": "4.1.0",
|
73
73
|
"@types/jest": "29.5.3",
|
74
74
|
"@types/jsdom": "21.1.1"
|
@@ -86,5 +86,6 @@
|
|
86
86
|
"pen",
|
87
87
|
"freehand",
|
88
88
|
"svg"
|
89
|
-
]
|
89
|
+
],
|
90
|
+
"gitHead": "f5a92284625b49b2ef6541bdafce1bd926c10441"
|
90
91
|
}
|
@@ -1,12 +0,0 @@
|
|
1
|
-
{
|
2
|
-
"name": "js-draw-test-imports",
|
3
|
-
"version": "0.0.1",
|
4
|
-
"description": "Test module and CommonJS imports",
|
5
|
-
"author": "Henry Heino",
|
6
|
-
"license": "MIT",
|
7
|
-
"private": true,
|
8
|
-
"type": "module",
|
9
|
-
"scripts": {
|
10
|
-
"test": "node test-imports.js && node test-require.cjs"
|
11
|
-
}
|
12
|
-
}
|
@@ -1,11 +0,0 @@
|
|
1
|
-
console.log('Testing imports...');
|
2
|
-
|
3
|
-
import { TextComponent, StrokeComponent } from 'js-draw/components';
|
4
|
-
|
5
|
-
if (!TextComponent.fromLines) {
|
6
|
-
throw new Error('Failed to import module TextComponent');
|
7
|
-
}
|
8
|
-
|
9
|
-
if (!StrokeComponent.deserializeFromJSON) {
|
10
|
-
throw new Error('Failed to import StrokeComponent');
|
11
|
-
}
|
@@ -1,14 +0,0 @@
|
|
1
|
-
console.log('Testing require()...');
|
2
|
-
|
3
|
-
// TODO: Test require('js-draw') (requires removing Coloris because
|
4
|
-
// Coloris depends on the DOM when first loaded).
|
5
|
-
|
6
|
-
const { TextComponent, StrokeComponent } = require('js-draw/components');
|
7
|
-
|
8
|
-
if (!TextComponent.fromLines) {
|
9
|
-
throw new Error('Failed to import module TextComponent');
|
10
|
-
}
|
11
|
-
|
12
|
-
if (!StrokeComponent.deserializeFromJSON) {
|
13
|
-
throw new Error('Failed to import StrokeComponent');
|
14
|
-
}
|
@@ -1,24 +0,0 @@
|
|
1
|
-
import { Color4 } from '@js-draw/math';
|
2
|
-
import { imageBackgroundCSSClassName } from './components/BackgroundComponent';
|
3
|
-
import { RestyleableComponent } from './lib';
|
4
|
-
import SVGLoader from './SVGLoader';
|
5
|
-
import createEditor from './testing/createEditor';
|
6
|
-
|
7
|
-
describe('Editor.loadFrom', () => {
|
8
|
-
it('should remove existing BackgroundComponents when loading new BackgroundComponents', async () => {
|
9
|
-
const editor = createEditor();
|
10
|
-
await editor.dispatch(editor.setBackgroundColor(Color4.red));
|
11
|
-
|
12
|
-
let backgroundComponents = editor.image.getBackgroundComponents();
|
13
|
-
expect(backgroundComponents).toHaveLength(1);
|
14
|
-
expect((backgroundComponents[0] as RestyleableComponent).getStyle().color).objEq(Color4.red);
|
15
|
-
|
16
|
-
await editor.loadFrom(SVGLoader.fromString(`<svg viewBox='0 0 100 100'>
|
17
|
-
<path class='${imageBackgroundCSSClassName}' d='m0,0 L100,0 L100,100 L0,100 z' fill='#000'/>
|
18
|
-
</svg>`, true));
|
19
|
-
|
20
|
-
backgroundComponents = editor.image.getBackgroundComponents();
|
21
|
-
expect(backgroundComponents).toHaveLength(1);
|
22
|
-
expect((backgroundComponents[0] as RestyleableComponent).getStyle().color).objEq(Color4.black);
|
23
|
-
});
|
24
|
-
});
|
package/src/Editor.test.ts
DELETED
@@ -1,107 +0,0 @@
|
|
1
|
-
import { BaseTool, InputEvtType } from './lib';
|
2
|
-
import createEditor from './testing/createEditor';
|
3
|
-
|
4
|
-
describe('Editor', () => {
|
5
|
-
it('should fire keyup events when the editor loses focus', () => {
|
6
|
-
const editor = createEditor();
|
7
|
-
const rootElem = editor.getRootElement();
|
8
|
-
|
9
|
-
const inputArea = rootElem.querySelector('textarea')! as HTMLTextAreaElement;
|
10
|
-
|
11
|
-
// Set the only tool to a tool that reports which keys are pressed.
|
12
|
-
const keyPressMock = jest.fn(() => true);
|
13
|
-
const keyReleaseMock = jest.fn();
|
14
|
-
editor.toolController.setTools([
|
15
|
-
new (class extends BaseTool {
|
16
|
-
public constructor() {
|
17
|
-
super(editor.notifier, 'test');
|
18
|
-
}
|
19
|
-
|
20
|
-
public override onKeyPress = keyPressMock;
|
21
|
-
public override onKeyUp = keyReleaseMock;
|
22
|
-
})()
|
23
|
-
]);
|
24
|
-
|
25
|
-
inputArea.focus();
|
26
|
-
|
27
|
-
// Sends a keyboard event to the editor
|
28
|
-
const dispatchKeyEvent = (kind: 'keydown'|'keyup', code: string, key: string) => {
|
29
|
-
const event = new KeyboardEvent(kind, {
|
30
|
-
bubbles: true,
|
31
|
-
key,
|
32
|
-
code,
|
33
|
-
shiftKey: false,
|
34
|
-
ctrlKey: false,
|
35
|
-
metaKey: false,
|
36
|
-
});
|
37
|
-
inputArea.dispatchEvent(event);
|
38
|
-
};
|
39
|
-
|
40
|
-
// Press the A key
|
41
|
-
dispatchKeyEvent('keydown', 'KeyA', 'a');
|
42
|
-
|
43
|
-
const keyAEvent = {
|
44
|
-
kind: InputEvtType.KeyPressEvent,
|
45
|
-
key: 'a',
|
46
|
-
code: 'KeyA',
|
47
|
-
ctrlKey: false,
|
48
|
-
altKey: false,
|
49
|
-
shiftKey: false,
|
50
|
-
};
|
51
|
-
expect(keyPressMock).toHaveBeenLastCalledWith(keyAEvent);
|
52
|
-
expect(keyPressMock).toHaveBeenCalledTimes(1);
|
53
|
-
|
54
|
-
// Press it again
|
55
|
-
dispatchKeyEvent('keydown', 'KeyA', 'a');
|
56
|
-
|
57
|
-
expect(keyPressMock).toHaveBeenLastCalledWith(keyAEvent);
|
58
|
-
expect(keyPressMock).toHaveBeenCalledTimes(2);
|
59
|
-
|
60
|
-
// Pressing a different key should send a different keydownb event to the toolbar
|
61
|
-
dispatchKeyEvent('keydown', 'KeyB', 'b');
|
62
|
-
|
63
|
-
expect(keyPressMock).not.toHaveBeenLastCalledWith(keyAEvent);
|
64
|
-
expect(keyPressMock).toHaveBeenCalledTimes(3);
|
65
|
-
|
66
|
-
// Press yet another key (and multiple times) -- if this key is still down when the
|
67
|
-
// editor is blured, a keyup event should only be fired once.
|
68
|
-
dispatchKeyEvent('keydown', 'KeyF', 'f');
|
69
|
-
expect(keyPressMock).toHaveBeenCalledTimes(4);
|
70
|
-
|
71
|
-
dispatchKeyEvent('keydown', 'KeyF', 'f');
|
72
|
-
expect(keyPressMock).toHaveBeenCalledTimes(5);
|
73
|
-
|
74
|
-
dispatchKeyEvent('keyup', 'KeyA', 'a');
|
75
|
-
expect(keyPressMock).toHaveBeenCalledTimes(5);
|
76
|
-
expect(keyReleaseMock).toHaveBeenCalledTimes(1);
|
77
|
-
expect(keyReleaseMock).toHaveBeenLastCalledWith({
|
78
|
-
...keyAEvent,
|
79
|
-
kind: InputEvtType.KeyUpEvent
|
80
|
-
});
|
81
|
-
|
82
|
-
// Defocus the input --- this should fire a key up event for the keys still down
|
83
|
-
inputArea.blur();
|
84
|
-
inputArea.dispatchEvent(new Event('blur', { bubbles: true }));
|
85
|
-
|
86
|
-
const focusable = document.createElement('button');
|
87
|
-
document.body.appendChild(focusable);
|
88
|
-
focusable.focus();
|
89
|
-
|
90
|
-
expect(keyReleaseMock).toHaveBeenCalledTimes(3);
|
91
|
-
|
92
|
-
// Events for both keys that were still down should have been fired:
|
93
|
-
const secondToLastCall = keyReleaseMock.mock.calls[keyReleaseMock.mock.calls.length - 2];
|
94
|
-
const lastCall = keyReleaseMock.mock.lastCall;
|
95
|
-
|
96
|
-
expect(secondToLastCall).toMatchObject([{
|
97
|
-
kind: InputEvtType.KeyUpEvent,
|
98
|
-
key: 'b',
|
99
|
-
code: 'KeyB',
|
100
|
-
}]);
|
101
|
-
expect(lastCall).toMatchObject([{
|
102
|
-
kind: InputEvtType.KeyUpEvent,
|
103
|
-
key: 'f',
|
104
|
-
code: 'KeyF',
|
105
|
-
}]);
|
106
|
-
});
|
107
|
-
});
|
package/src/Editor.toSVG.test.ts
DELETED
@@ -1,294 +0,0 @@
|
|
1
|
-
import { Color4, Mat33, Rect2, TextComponent, EditorImage, Vec2, StrokeComponent, SelectionTool, sendPenEvent, InputEvtType } from './lib';
|
2
|
-
import TextRenderingStyle from './rendering/TextRenderingStyle';
|
3
|
-
import SVGLoader from './SVGLoader';
|
4
|
-
import createEditor from './testing/createEditor';
|
5
|
-
|
6
|
-
describe('Editor.toSVG', () => {
|
7
|
-
it('should correctly nest text objects', async () => {
|
8
|
-
const editor = createEditor();
|
9
|
-
const textStyle: TextRenderingStyle = {
|
10
|
-
fontFamily: 'sans', size: 12, renderingStyle: { fill: Color4.black }
|
11
|
-
};
|
12
|
-
const text = new TextComponent([
|
13
|
-
'Testing...',
|
14
|
-
new TextComponent([ 'Test 2' ], Mat33.translation(Vec2.of(0, 100)), textStyle),
|
15
|
-
], Mat33.identity, textStyle);
|
16
|
-
editor.dispatch(EditorImage.addElement(text));
|
17
|
-
|
18
|
-
const matches = editor.image.getElementsIntersectingRegion(new Rect2(4, -100, 100, 100));
|
19
|
-
expect(matches).toHaveLength(1);
|
20
|
-
expect(text).not.toBeNull();
|
21
|
-
|
22
|
-
const asSVG = editor.toSVG();
|
23
|
-
const allTSpans = [ ...asSVG.querySelectorAll('tspan') ];
|
24
|
-
expect(allTSpans).toHaveLength(1);
|
25
|
-
expect(allTSpans[0].getAttribute('x')).toBe('0');
|
26
|
-
expect(allTSpans[0].getAttribute('y')).toBe('100');
|
27
|
-
expect(allTSpans[0].style.transform).toBe('');
|
28
|
-
});
|
29
|
-
|
30
|
-
it('should preserve empty tspans', async () => {
|
31
|
-
const editor = createEditor();
|
32
|
-
await editor.loadFrom(SVGLoader.fromString(`
|
33
|
-
<svg viewBox="0 0 500 500" width="500" height="500" version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
|
34
|
-
<style id="js-draw-style-sheet">
|
35
|
-
path {
|
36
|
-
stroke-linecap:round;
|
37
|
-
stroke-linejoin:round;
|
38
|
-
}
|
39
|
-
</style>
|
40
|
-
<text style="transform: matrix(1, 0, 0, 1, 12, 35); font-family: sans-serif; font-size: 32px; fill: rgb(128, 51, 128);">Testing...<tspan x="3" y="40" style="font-family: sans-serif; font-size: 33px; fill: rgb(128, 51, 128);"></tspan><tspan x="3" y="70">Test 2. ☺</tspan></text>
|
41
|
-
</svg>
|
42
|
-
`, true));
|
43
|
-
|
44
|
-
const textNodesInImage = editor.image.getAllElements().filter(elem => elem instanceof TextComponent);
|
45
|
-
expect(
|
46
|
-
textNodesInImage
|
47
|
-
).toHaveLength(1);
|
48
|
-
|
49
|
-
const asSVG = editor.toSVG();
|
50
|
-
const textObject = asSVG.querySelector('text');
|
51
|
-
|
52
|
-
if (!textObject) {
|
53
|
-
throw new Error('No text object found');
|
54
|
-
}
|
55
|
-
|
56
|
-
const childTextNodes = textObject.querySelectorAll('tspan');
|
57
|
-
expect(childTextNodes).toHaveLength(2);
|
58
|
-
});
|
59
|
-
|
60
|
-
it('should preserve text child size/placement while not saving additional properties', async () => {
|
61
|
-
const secondLineText = 'This is a test of a thing that has been known to break. Will this test catch the issue?';
|
62
|
-
const thirdLineText = 'This is a test of saving/loading multi-line text...';
|
63
|
-
|
64
|
-
const editor = createEditor();
|
65
|
-
await editor.loadFrom(SVGLoader.fromString(`
|
66
|
-
<svg viewBox="0 0 500 500" width="500" height="500" version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
|
67
|
-
<style id="js-draw-style-sheet">
|
68
|
-
path {
|
69
|
-
stroke-linecap:round;
|
70
|
-
stroke-linejoin:round;
|
71
|
-
}
|
72
|
-
</style>
|
73
|
-
<text style="transform: matrix(1, 0, 0, 1, 12, 35); font-family: sans-serif; font-size: 32px; fill: rgb(128, 51, 128);">Testing...<tspan x="3" y="40" style="font-family: sans-serif; font-size: 33px; fill: rgb(128, 51, 128);">${secondLineText}</tspan><tspan x="0" y="72" style="font-family: sans-serif; font-size: 32px; fill: rgb(128, 51, 128);">${thirdLineText}</tspan><tspan x="0" y="112" style="font-family: sans-serif; font-size: 32px; fill: rgb(128, 51, 128);">Will it pass or fail?</tspan></text>
|
74
|
-
</svg>
|
75
|
-
`, true));
|
76
|
-
|
77
|
-
expect(
|
78
|
-
editor.image.getAllElements().filter(elem => elem instanceof TextComponent)
|
79
|
-
).toHaveLength(1);
|
80
|
-
|
81
|
-
const asSVG = editor.toSVG();
|
82
|
-
const textObject = asSVG.querySelector('text');
|
83
|
-
|
84
|
-
if (!textObject) {
|
85
|
-
throw new Error('No text object found');
|
86
|
-
}
|
87
|
-
|
88
|
-
expect(textObject.style.transform.replace(/\s+/g, '')).toBe('matrix(1,0,0,1,12,35)');
|
89
|
-
expect(textObject.style.fontFamily).toBe('sans-serif');
|
90
|
-
expect(textObject.style.fontSize).toBe('32px');
|
91
|
-
|
92
|
-
const childTextNodes = textObject.querySelectorAll('tspan');
|
93
|
-
expect(childTextNodes).toHaveLength(3);
|
94
|
-
const firstChild = childTextNodes[0];
|
95
|
-
|
96
|
-
expect(firstChild.textContent).toBe(secondLineText);
|
97
|
-
expect(firstChild.style.transform).toBe('');
|
98
|
-
expect(firstChild.style.fontSize).toBe('33px');
|
99
|
-
expect(firstChild.getAttribute('x')).toBe('3');
|
100
|
-
expect(firstChild.getAttribute('y')).toBe('40');
|
101
|
-
|
102
|
-
// Should not save a fontSize when not necessary (same fill as parent text node)
|
103
|
-
const secondChild = childTextNodes[1];
|
104
|
-
expect(secondChild.style.fontSize ?? '').toBe('');
|
105
|
-
|
106
|
-
// Should not save additional "style" attributes when not necessary
|
107
|
-
// TODO: Uncomment before some future major version release. Currently a "fill" is set for every
|
108
|
-
// tspan to work around a loading bug.
|
109
|
-
//expect(secondChild.outerHTML).toBe(`<tspan x="0" y="72">${thirdLineText}</tspan>`);
|
110
|
-
});
|
111
|
-
|
112
|
-
it('should preserve group elements', async () => {
|
113
|
-
const editor = createEditor();
|
114
|
-
await editor.loadFrom(SVGLoader.fromString(`
|
115
|
-
<svg viewBox="0 0 500 500" width="500" height="500" version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
|
116
|
-
<style id="js-draw-style-sheet">
|
117
|
-
path {
|
118
|
-
stroke-linecap:round;
|
119
|
-
stroke-linejoin:round;
|
120
|
-
}
|
121
|
-
</style>
|
122
|
-
<g id='main-group'>
|
123
|
-
<g id='sub-group-1'>
|
124
|
-
<path d='M0,0 L10,10 0,10' fill='#f00'/>
|
125
|
-
<path d='M20,0 L10,10 0,10'/>
|
126
|
-
</g>
|
127
|
-
<g id='empty-group-2'></g>
|
128
|
-
</g>
|
129
|
-
<g id='empty-group-3'></g>
|
130
|
-
|
131
|
-
<!-- Groups without IDs should also be preserved -->
|
132
|
-
<g><g><g id='marker-1'></g></g></g>
|
133
|
-
<g class='test'><g id='marker-2'/></g>
|
134
|
-
|
135
|
-
<!-- Groups with duplicate IDs should preserved (though IDs)
|
136
|
-
may be changed -->
|
137
|
-
<g id='empty-group-2'/>
|
138
|
-
<g id='empty-group-2'><g id='empty-group-2'/></g>
|
139
|
-
</svg>
|
140
|
-
`));
|
141
|
-
|
142
|
-
// Both paths should exist.
|
143
|
-
expect(
|
144
|
-
editor.image
|
145
|
-
.getElementsIntersectingRegion(new Rect2(-10, -10, 100, 100))
|
146
|
-
.filter(elem => elem instanceof StrokeComponent)
|
147
|
-
).toHaveLength(2);
|
148
|
-
|
149
|
-
const outputSVG = editor.toSVG();
|
150
|
-
|
151
|
-
// Should still have the expected number of groups
|
152
|
-
expect(outputSVG.querySelectorAll('g')).toHaveLength(12);
|
153
|
-
|
154
|
-
// Should preserve the empty group.
|
155
|
-
expect(outputSVG.querySelectorAll('g#empty-group-2')).toHaveLength(1);
|
156
|
-
|
157
|
-
// The empty group should still have the correct parent
|
158
|
-
expect(outputSVG.querySelectorAll('g#main-group > g#empty-group-2')).toHaveLength(1);
|
159
|
-
|
160
|
-
// Paths should still be children of sub-group-1
|
161
|
-
expect(outputSVG.querySelectorAll('g#sub-group-1 > path')).toHaveLength(2);
|
162
|
-
|
163
|
-
// sub-group-1 should have the correct parent
|
164
|
-
expect(outputSVG.querySelectorAll('g#main-group > g#sub-group-1')).toHaveLength(1);
|
165
|
-
|
166
|
-
// And these should be the only paths.
|
167
|
-
expect(outputSVG.querySelectorAll('path')).toHaveLength(2);
|
168
|
-
|
169
|
-
// Should also preserve groups without IDs
|
170
|
-
// Selector ref: https://stackoverflow.com/a/18607777
|
171
|
-
expect(outputSVG.querySelectorAll('svg > g > g > g#marker-1')).toHaveLength(1);
|
172
|
-
expect(outputSVG.querySelectorAll('svg > g > g#marker-2')).toHaveLength(1);
|
173
|
-
|
174
|
-
// Should preserve class names on `g` objects:
|
175
|
-
expect(outputSVG.querySelectorAll('svg > g.test > g#marker-2')).toHaveLength(1);
|
176
|
-
|
177
|
-
// Should preserve groups that had duplicate IDs
|
178
|
-
expect(outputSVG.querySelectorAll('svg > g#empty-group-2--1')).toHaveLength(1);
|
179
|
-
expect(outputSVG.querySelectorAll('svg > g#empty-group-2--2')).toHaveLength(1);
|
180
|
-
expect(outputSVG.querySelectorAll('svg > g#empty-group-2--2 > g#empty-group-2--3')).toHaveLength(1);
|
181
|
-
});
|
182
|
-
|
183
|
-
describe('should not preserve group elements when doing so would change the z order', () => {
|
184
|
-
it('in an image with few items', async () => {
|
185
|
-
const editor = createEditor();
|
186
|
-
await editor.loadFrom(SVGLoader.fromString(`
|
187
|
-
<svg viewBox="0 0 500 500" width="500" height="500" version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
|
188
|
-
<g id='main-group-1'>
|
189
|
-
<path d='M0,0 L-10,10 0,10' fill='#f00'/>
|
190
|
-
<path d='M20,0 L10,10 0,10'/>
|
191
|
-
</g>
|
192
|
-
<path d='M40,40 l10,10 0,10'/>
|
193
|
-
</svg>
|
194
|
-
`));
|
195
|
-
|
196
|
-
// All paths should exist.
|
197
|
-
expect(
|
198
|
-
editor.image
|
199
|
-
.getElementsIntersectingRegion(new Rect2(-10, -10, 100, 100))
|
200
|
-
.filter(elem => elem instanceof StrokeComponent)
|
201
|
-
).toHaveLength(3);
|
202
|
-
|
203
|
-
// Before modifying, both paths should be within the main-group-1 group
|
204
|
-
expect(editor.toSVG().querySelectorAll('svg > g#main-group-1 > path')).toHaveLength(2);
|
205
|
-
|
206
|
-
const selectionTool = editor.toolController.getMatchingTools(SelectionTool)[0];
|
207
|
-
selectionTool.setEnabled(true);
|
208
|
-
|
209
|
-
// Select the bottommost stroke
|
210
|
-
sendPenEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(-11, 9));
|
211
|
-
sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(-9, 10));
|
212
|
-
sendPenEvent(editor, InputEvtType.PointerUpEvt, Vec2.of(-9, 10));
|
213
|
-
|
214
|
-
// The stroke should be selected
|
215
|
-
expect(selectionTool.getSelectedObjects()).toHaveLength(1);
|
216
|
-
expect(selectionTool.getSelectedObjects()[0].getBBox())
|
217
|
-
.objEq(new Rect2(-10, 0, 10, 10));
|
218
|
-
|
219
|
-
// Drag the selection (moves the selected item to the top)
|
220
|
-
sendPenEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(-11, 9));
|
221
|
-
sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(0, 0));
|
222
|
-
sendPenEvent(editor, InputEvtType.PointerUpEvt, Vec2.of(0, 0));
|
223
|
-
|
224
|
-
expect(selectionTool.getSelectedObjects()[0].getBBox())
|
225
|
-
.not.objEq(new Rect2(-10, 0, 10, 10));
|
226
|
-
selectionTool.setEnabled(false);
|
227
|
-
|
228
|
-
// One of the items should have been moved out of the main group
|
229
|
-
const outputSVG = editor.toSVG();
|
230
|
-
expect(outputSVG.querySelectorAll('svg > path')).toHaveLength(2);
|
231
|
-
expect(outputSVG.querySelectorAll('svg > g#main-group-1 > path')).toHaveLength(1);
|
232
|
-
});
|
233
|
-
|
234
|
-
it('in an image with many items in nested groups', async () => {
|
235
|
-
const editor = createEditor();
|
236
|
-
await editor.loadFrom(SVGLoader.fromString(`
|
237
|
-
<svg viewBox="0 0 500 500" width="500" height="500" version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
|
238
|
-
<path d='M-100,-100 l 2,2 0,2'/>
|
239
|
-
<g id='group-1'>
|
240
|
-
<path d='M0,0 L-10,10 0,10' fill='#f00'/>
|
241
|
-
<path d='M20,0 L10,10 0,10'/>
|
242
|
-
|
243
|
-
<g id='group-2'>
|
244
|
-
<path d='M100,100 l 2,2 0,2'/>
|
245
|
-
</g>
|
246
|
-
</g>
|
247
|
-
<path d='M40,40 l10,10 0,10'/>
|
248
|
-
</svg>
|
249
|
-
`));
|
250
|
-
|
251
|
-
// .expects that all paths have their original parent groups.
|
252
|
-
const expectGroupParentsToBeOriginal = () => {
|
253
|
-
expect(
|
254
|
-
editor.image
|
255
|
-
.getAllElements()
|
256
|
-
.filter(elem => elem instanceof StrokeComponent)
|
257
|
-
).toHaveLength(5);
|
258
|
-
|
259
|
-
const output = editor.toSVG();
|
260
|
-
expect(
|
261
|
-
output.querySelectorAll('svg > g#group-1 path')
|
262
|
-
).toHaveLength(3);
|
263
|
-
expect(
|
264
|
-
output.querySelectorAll('svg > g#group-1 > g > path')
|
265
|
-
).toHaveLength(1);
|
266
|
-
};
|
267
|
-
|
268
|
-
expectGroupParentsToBeOriginal();
|
269
|
-
|
270
|
-
const nudgePathNear = async (pos: Vec2) => {
|
271
|
-
const targetElems = editor.image.getElementsIntersectingRegion(Rect2.bboxOf([ pos ], 5));
|
272
|
-
|
273
|
-
expect(targetElems).toHaveLength(1);
|
274
|
-
|
275
|
-
// Move the path to the top
|
276
|
-
const target = targetElems[0];
|
277
|
-
await editor.dispatch(target.transformBy(Mat33.scaling2D(1.01)));
|
278
|
-
};
|
279
|
-
|
280
|
-
// Moving a path that's below all groups should not change group parentings.
|
281
|
-
nudgePathNear(Vec2.of(-100, -100));
|
282
|
-
expectGroupParentsToBeOriginal();
|
283
|
-
|
284
|
-
// Moving a path that's within nested groups should move the path out of that group.
|
285
|
-
nudgePathNear(Vec2.of(100, 100));
|
286
|
-
|
287
|
-
const outputSVG = editor.toSVG();
|
288
|
-
expect(outputSVG.querySelectorAll('svg > path')).toHaveLength(3);
|
289
|
-
expect(outputSVG.querySelectorAll('svg > g#group-1 > path')).toHaveLength(2);
|
290
|
-
expect(outputSVG.querySelectorAll('svg > g#group-1 > g')).toHaveLength(1);
|
291
|
-
expect(outputSVG.querySelectorAll('svg > g#group-1 > g > *')).toHaveLength(0);
|
292
|
-
});
|
293
|
-
});
|
294
|
-
});
|