@bensitu/image-editor 2.0.0 → 2.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/README.md +346 -90
- package/dist/cjs/index.cjs +4883 -1191
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/animation/animation-queue.js +16 -9
- package/dist/esm/animation/animation-queue.js.map +1 -1
- package/dist/esm/annotation/annotation-lock.js +7 -0
- package/dist/esm/annotation/annotation-lock.js.map +1 -0
- package/dist/esm/annotation/annotation-manager.js +217 -0
- package/dist/esm/annotation/annotation-manager.js.map +1 -0
- package/dist/esm/annotation/annotation-style.js +50 -0
- package/dist/esm/annotation/annotation-style.js.map +1 -0
- package/dist/esm/annotation/draw-controller.js +114 -0
- package/dist/esm/annotation/draw-controller.js.map +1 -0
- package/dist/esm/annotation/text-controller.js +234 -0
- package/dist/esm/annotation/text-controller.js.map +1 -0
- package/dist/esm/core/default-options.js +447 -11
- package/dist/esm/core/default-options.js.map +1 -1
- package/dist/esm/core/editor-object-kind.js +37 -0
- package/dist/esm/core/editor-object-kind.js.map +1 -0
- package/dist/esm/core/errors.js +19 -0
- package/dist/esm/core/errors.js.map +1 -1
- package/dist/esm/core/layer-order.js +100 -0
- package/dist/esm/core/layer-order.js.map +1 -0
- package/dist/esm/core/operation-guard.js +28 -0
- package/dist/esm/core/operation-guard.js.map +1 -1
- package/dist/esm/core/public-types.js +34 -1
- package/dist/esm/core/public-types.js.map +1 -1
- package/dist/esm/core/state-serializer.js +108 -27
- package/dist/esm/core/state-serializer.js.map +1 -1
- package/dist/esm/crop/crop-controller.js +6 -2
- package/dist/esm/crop/crop-controller.js.map +1 -1
- package/dist/esm/export/export-format.js.map +1 -1
- package/dist/esm/export/export-service.js +140 -141
- package/dist/esm/export/export-service.js.map +1 -1
- package/dist/esm/export/overlay-merge-service.js +75 -0
- package/dist/esm/export/overlay-merge-service.js.map +1 -0
- package/dist/esm/fabric/fabric-animation.js +56 -4
- package/dist/esm/fabric/fabric-animation.js.map +1 -1
- package/dist/esm/history/history-manager.js +2 -2
- package/dist/esm/history/history-manager.js.map +1 -1
- package/dist/esm/image/image-loader.js +27 -65
- package/dist/esm/image/image-loader.js.map +1 -1
- package/dist/esm/image/image-resampler.js +7 -2
- package/dist/esm/image/image-resampler.js.map +1 -1
- package/dist/esm/image/layout-manager.js +2 -20
- package/dist/esm/image/layout-manager.js.map +1 -1
- package/dist/esm/image/transform-controller.js.map +1 -1
- package/dist/esm/image-editor.js +1474 -135
- package/dist/esm/image-editor.js.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/mask/mask-factory.js +92 -43
- package/dist/esm/mask/mask-factory.js.map +1 -1
- package/dist/esm/mask/mask-label-manager.js +2 -0
- package/dist/esm/mask/mask-label-manager.js.map +1 -1
- package/dist/esm/mask/mask-list.js +9 -3
- package/dist/esm/mask/mask-list.js.map +1 -1
- package/dist/esm/mask/mask-style.js.map +1 -1
- package/dist/esm/mosaic/mosaic-controller.js +666 -0
- package/dist/esm/mosaic/mosaic-controller.js.map +1 -0
- package/dist/esm/mosaic/mosaic-geometry.js +81 -0
- package/dist/esm/mosaic/mosaic-geometry.js.map +1 -0
- package/dist/esm/mosaic/mosaic-pixelate.js +71 -0
- package/dist/esm/mosaic/mosaic-pixelate.js.map +1 -0
- package/dist/esm/ui/dom-bindings.js +10 -3
- package/dist/esm/ui/dom-bindings.js.map +1 -1
- package/dist/esm/utils/image-element-loader.js +55 -0
- package/dist/esm/utils/image-element-loader.js.map +1 -0
- package/dist/esm/utils/number.js.map +1 -1
- package/dist/esm/utils/pointer.js +28 -0
- package/dist/esm/utils/pointer.js.map +1 -0
- package/dist/types/animation/animation-queue.d.ts.map +1 -1
- package/dist/types/annotation/annotation-lock.d.ts +12 -0
- package/dist/types/annotation/annotation-lock.d.ts.map +1 -0
- package/dist/types/annotation/annotation-manager.d.ts +33 -0
- package/dist/types/annotation/annotation-manager.d.ts.map +1 -0
- package/dist/types/annotation/annotation-style.d.ts +13 -0
- package/dist/types/annotation/annotation-style.d.ts.map +1 -0
- package/dist/types/annotation/draw-controller.d.ts +43 -0
- package/dist/types/annotation/draw-controller.d.ts.map +1 -0
- package/dist/types/annotation/text-controller.d.ts +47 -0
- package/dist/types/annotation/text-controller.d.ts.map +1 -0
- package/dist/types/core/default-options.d.ts +46 -6
- package/dist/types/core/default-options.d.ts.map +1 -1
- package/dist/types/core/editor-object-kind.d.ts +29 -0
- package/dist/types/core/editor-object-kind.d.ts.map +1 -0
- package/dist/types/core/errors.d.ts +12 -2
- package/dist/types/core/errors.d.ts.map +1 -1
- package/dist/types/core/layer-order.d.ts +21 -0
- package/dist/types/core/layer-order.d.ts.map +1 -0
- package/dist/types/core/operation-guard.d.ts +2 -0
- package/dist/types/core/operation-guard.d.ts.map +1 -1
- package/dist/types/core/public-types.d.ts +341 -33
- package/dist/types/core/public-types.d.ts.map +1 -1
- package/dist/types/core/state-serializer.d.ts +32 -5
- package/dist/types/core/state-serializer.d.ts.map +1 -1
- package/dist/types/crop/crop-controller.d.ts +6 -7
- package/dist/types/crop/crop-controller.d.ts.map +1 -1
- package/dist/types/export/export-format.d.ts +5 -33
- package/dist/types/export/export-format.d.ts.map +1 -1
- package/dist/types/export/export-service.d.ts +24 -15
- package/dist/types/export/export-service.d.ts.map +1 -1
- package/dist/types/export/overlay-merge-service.d.ts +38 -0
- package/dist/types/export/overlay-merge-service.d.ts.map +1 -0
- package/dist/types/fabric/fabric-animation.d.ts.map +1 -1
- package/dist/types/history/history-manager.d.ts +11 -14
- package/dist/types/history/history-manager.d.ts.map +1 -1
- package/dist/types/image/image-loader.d.ts +24 -21
- package/dist/types/image/image-loader.d.ts.map +1 -1
- package/dist/types/image/image-resampler.d.ts +2 -2
- package/dist/types/image/image-resampler.d.ts.map +1 -1
- package/dist/types/image/layout-manager.d.ts +5 -49
- package/dist/types/image/layout-manager.d.ts.map +1 -1
- package/dist/types/image/transform-controller.d.ts +6 -9
- package/dist/types/image/transform-controller.d.ts.map +1 -1
- package/dist/types/image-editor.d.ts +93 -14
- package/dist/types/image-editor.d.ts.map +1 -1
- package/dist/types/index.d.cts +3 -3
- package/dist/types/index.d.cts.map +1 -1
- package/dist/types/index.d.ts +3 -3
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/mask/mask-factory.d.ts +24 -21
- package/dist/types/mask/mask-factory.d.ts.map +1 -1
- package/dist/types/mask/mask-label-manager.d.ts +10 -9
- package/dist/types/mask/mask-label-manager.d.ts.map +1 -1
- package/dist/types/mask/mask-list.d.ts +11 -12
- package/dist/types/mask/mask-list.d.ts.map +1 -1
- package/dist/types/mask/mask-style.d.ts +19 -20
- package/dist/types/mask/mask-style.d.ts.map +1 -1
- package/dist/types/mosaic/mosaic-controller.d.ts +82 -0
- package/dist/types/mosaic/mosaic-controller.d.ts.map +1 -0
- package/dist/types/mosaic/mosaic-geometry.d.ts +29 -0
- package/dist/types/mosaic/mosaic-geometry.d.ts.map +1 -0
- package/dist/types/mosaic/mosaic-pixelate.d.ts +23 -0
- package/dist/types/mosaic/mosaic-pixelate.d.ts.map +1 -0
- package/dist/types/ui/dom-bindings.d.ts +3 -1
- package/dist/types/ui/dom-bindings.d.ts.map +1 -1
- package/dist/types/ui/visibility-state.d.ts +2 -2
- package/dist/types/utils/image-element-loader.d.ts +19 -0
- package/dist/types/utils/image-element-loader.d.ts.map +1 -0
- package/dist/types/utils/number.d.ts +1 -2
- package/dist/types/utils/number.d.ts.map +1 -1
- package/dist/types/utils/pointer.d.ts +16 -0
- package/dist/types/utils/pointer.d.ts.map +1 -0
- package/dist/umd/image-editor.umd.js +1 -1
- package/dist/umd/image-editor.umd.js.map +1 -1
- package/package.json +1 -1
package/dist/esm/image-editor.js
CHANGED
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
import { AnimationQueue } from './animation/animation-queue.js';
|
|
2
2
|
import { reportError, reportWarning } from './core/callback-reporter.js';
|
|
3
|
-
import { resolveOptions } from './core/default-options.js';
|
|
3
|
+
import { areResolvedMosaicConfigsEqual, areResolvedDrawConfigsEqual, areResolvedTextAnnotationConfigsEqual, cloneResolvedMosaicConfig, cloneResolvedDrawConfig, cloneResolvedTextAnnotationConfig, getInvalidDrawConfigFields, getInvalidMosaicConfigFields, getInvalidTextAnnotationConfigFields, isLayoutMode, mergeDrawConfigPatch, mergeMosaicConfigPatch, mergeTextAnnotationConfigPatch, resolveOptions, } from './core/default-options.js';
|
|
4
4
|
import { OperationGuard } from './core/operation-guard.js';
|
|
5
5
|
import { loadFromState as loadFromStateImpl, saveState as saveStateImpl, } from './core/state-serializer.js';
|
|
6
6
|
import { Command, HistoryManager } from './history/history-manager.js';
|
|
7
7
|
import { detectFabric } from './fabric/fabric-adapter.js';
|
|
8
|
-
import { isMaskObject } from './core/public-types.js';
|
|
8
|
+
import { isAnnotationObject, isDrawAnnotationObject, isEditableOverlayObject, isMaskObject, isTextAnnotationObject, } from './core/public-types.js';
|
|
9
|
+
import { getAnnotations as getAnnotationsImpl, removeAllAnnotations as removeAllAnnotationsImpl, removeAnnotationObjects, removeSelectedAnnotation as removeSelectedAnnotationImpl, renderAnnotationList, updateAnnotation as updateAnnotationImpl, updateAnnotationListSelection, updateSelectedAnnotation as updateSelectedAnnotationImpl, } from './annotation/annotation-manager.js';
|
|
10
|
+
import { attachTextEditingHandlersToAnnotations, createTextAnnotation as createTextAnnotationImpl, enterTextMode as enterTextModeImpl, exitTextMode as exitTextModeImpl, finalizeActiveTextEditing, } from './annotation/text-controller.js';
|
|
11
|
+
import { enterDrawMode as enterDrawModeImpl, exitDrawMode as exitDrawModeImpl, updateDrawBrush, } from './annotation/draw-controller.js';
|
|
12
|
+
import { isAnnotationLocked, isAnnotationUnlocked } from './annotation/annotation-lock.js';
|
|
13
|
+
import { syncAnnotationRuntimeStates } from './annotation/annotation-style.js';
|
|
14
|
+
import { normalizeLayerOrder, getEditableOverlayRange } from './core/layer-order.js';
|
|
9
15
|
import { applyCrop as applyCropImpl, cancelCrop as cancelCropImpl, enterCropMode as enterCropModeImpl, } from './crop/crop-controller.js';
|
|
10
|
-
import {
|
|
16
|
+
import { enterMosaicMode as enterMosaicModeImpl, exitMosaicMode as exitMosaicModeImpl, updateMosaicPreview, } from './mosaic/mosaic-controller.js';
|
|
17
|
+
import { downloadImage as downloadImageImpl, exportImageBase64 as exportImageBase64Impl, exportImageFile as exportImageFileImpl, mergeAnnotations as mergeAnnotationsImpl, mergeMasks as mergeMasksImpl, } from './export/export-service.js';
|
|
11
18
|
import { loadImage as loadImageImpl } from './image/image-loader.js';
|
|
12
|
-
import { ViewportCache, applyCanvasDimensions, computeScrollableCanvasSize,
|
|
19
|
+
import { ViewportCache, applyCanvasDimensions, computeScrollableCanvasSize, measureScrollbarSize, } from './image/layout-manager.js';
|
|
13
20
|
import { TransformController } from './image/transform-controller.js';
|
|
14
21
|
import { createMask as createMaskImpl, removeAllMasks as removeAllMasksImpl, removeSelectedMask as removeSelectedMaskImpl, } from './mask/mask-factory.js';
|
|
15
22
|
import { createLabelForMask, hideAllMaskLabels, removeLabelForMask, showLabelForMask, syncMaskLabel, } from './mask/mask-label-manager.js';
|
|
@@ -20,8 +27,8 @@ import { setPlaceholderVisible as setPlaceholderVisibleImpl } from './ui/visibil
|
|
|
20
27
|
import { inferImageMimeType, readFileAsDataUrl, resetFileInput } from './utils/file.js';
|
|
21
28
|
import { detectSourceMimeType } from './image/image-resampler.js';
|
|
22
29
|
const LAYOUT_EPSILON = 0.5;
|
|
23
|
-
const INTERNAL_OPERATION_TOKEN = Symbol
|
|
24
|
-
const INTERNAL_ALLOW_DURING_ANIMATION_QUEUE = Symbol
|
|
30
|
+
const INTERNAL_OPERATION_TOKEN = Symbol('ImageEditorInternalOperation');
|
|
31
|
+
const INTERNAL_ALLOW_DURING_ANIMATION_QUEUE = Symbol('ImageEditorAllowDuringAnimationQueue');
|
|
25
32
|
const CROP_MODE_CONTROL_KEYS = [
|
|
26
33
|
'scalePercentageInput',
|
|
27
34
|
'rotateLeftDegreesInput',
|
|
@@ -32,6 +39,22 @@ const CROP_MODE_CONTROL_KEYS = [
|
|
|
32
39
|
'removeSelectedMaskButton',
|
|
33
40
|
'removeAllMasksButton',
|
|
34
41
|
'mergeMasksButton',
|
|
42
|
+
'mergeAnnotationsButton',
|
|
43
|
+
'enterTextModeButton',
|
|
44
|
+
'exitTextModeButton',
|
|
45
|
+
'textColorInput',
|
|
46
|
+
'textFontSizeInput',
|
|
47
|
+
'enterDrawModeButton',
|
|
48
|
+
'exitDrawModeButton',
|
|
49
|
+
'drawColorInput',
|
|
50
|
+
'drawBrushSizeInput',
|
|
51
|
+
'removeSelectedAnnotationButton',
|
|
52
|
+
'removeAllAnnotationsButton',
|
|
53
|
+
'deleteSelectedObjectButton',
|
|
54
|
+
'bringSelectedObjectForwardButton',
|
|
55
|
+
'sendSelectedObjectBackwardButton',
|
|
56
|
+
'bringSelectedObjectToFrontButton',
|
|
57
|
+
'sendSelectedObjectToBackButton',
|
|
35
58
|
'downloadImageButton',
|
|
36
59
|
'zoomInButton',
|
|
37
60
|
'zoomOutButton',
|
|
@@ -42,9 +65,155 @@ const CROP_MODE_CONTROL_KEYS = [
|
|
|
42
65
|
'enterCropModeButton',
|
|
43
66
|
'applyCropButton',
|
|
44
67
|
'cancelCropButton',
|
|
68
|
+
'enterMosaicModeButton',
|
|
69
|
+
'exitMosaicModeButton',
|
|
70
|
+
'mosaicBrushSizeInput',
|
|
71
|
+
'mosaicBlockSizeInput',
|
|
45
72
|
];
|
|
46
73
|
const CROP_MODE_ENABLED_KEYS = ['applyCropButton', 'cancelCropButton'];
|
|
47
74
|
const CROP_SESSION_ALLOWED_OPERATIONS = new Set(['applyCrop', 'cancelCrop']);
|
|
75
|
+
const TEXT_MODE_ENABLED_KEYS = [
|
|
76
|
+
'exitTextModeButton',
|
|
77
|
+
'textColorInput',
|
|
78
|
+
'textFontSizeInput',
|
|
79
|
+
];
|
|
80
|
+
const DRAW_MODE_ENABLED_KEYS = [
|
|
81
|
+
'exitDrawModeButton',
|
|
82
|
+
'drawColorInput',
|
|
83
|
+
'drawBrushSizeInput',
|
|
84
|
+
];
|
|
85
|
+
const MOSAIC_MODE_CONTROL_KEYS = [
|
|
86
|
+
'scalePercentageInput',
|
|
87
|
+
'rotateLeftDegreesInput',
|
|
88
|
+
'rotateRightDegreesInput',
|
|
89
|
+
'rotateLeftButton',
|
|
90
|
+
'rotateRightButton',
|
|
91
|
+
'createMaskButton',
|
|
92
|
+
'removeSelectedMaskButton',
|
|
93
|
+
'removeAllMasksButton',
|
|
94
|
+
'mergeMasksButton',
|
|
95
|
+
'mergeAnnotationsButton',
|
|
96
|
+
'enterTextModeButton',
|
|
97
|
+
'exitTextModeButton',
|
|
98
|
+
'textColorInput',
|
|
99
|
+
'textFontSizeInput',
|
|
100
|
+
'enterDrawModeButton',
|
|
101
|
+
'exitDrawModeButton',
|
|
102
|
+
'drawColorInput',
|
|
103
|
+
'drawBrushSizeInput',
|
|
104
|
+
'removeSelectedAnnotationButton',
|
|
105
|
+
'removeAllAnnotationsButton',
|
|
106
|
+
'deleteSelectedObjectButton',
|
|
107
|
+
'bringSelectedObjectForwardButton',
|
|
108
|
+
'sendSelectedObjectBackwardButton',
|
|
109
|
+
'bringSelectedObjectToFrontButton',
|
|
110
|
+
'sendSelectedObjectToBackButton',
|
|
111
|
+
'downloadImageButton',
|
|
112
|
+
'zoomInButton',
|
|
113
|
+
'zoomOutButton',
|
|
114
|
+
'resetImageTransformButton',
|
|
115
|
+
'undoButton',
|
|
116
|
+
'redoButton',
|
|
117
|
+
'imageInput',
|
|
118
|
+
'enterCropModeButton',
|
|
119
|
+
'applyCropButton',
|
|
120
|
+
'cancelCropButton',
|
|
121
|
+
'enterMosaicModeButton',
|
|
122
|
+
'exitMosaicModeButton',
|
|
123
|
+
'mosaicBrushSizeInput',
|
|
124
|
+
'mosaicBlockSizeInput',
|
|
125
|
+
];
|
|
126
|
+
const MOSAIC_MODE_ENABLED_KEYS = [
|
|
127
|
+
'exitMosaicModeButton',
|
|
128
|
+
'mosaicBrushSizeInput',
|
|
129
|
+
'mosaicBlockSizeInput',
|
|
130
|
+
];
|
|
131
|
+
const MOSAIC_SESSION_ALLOWED_OPERATIONS = new Set([
|
|
132
|
+
'exitMosaicMode',
|
|
133
|
+
'applyMosaic',
|
|
134
|
+
'setMosaicConfig',
|
|
135
|
+
'resetMosaicConfig',
|
|
136
|
+
'setMosaicBrushSize',
|
|
137
|
+
'setMosaicBlockSize',
|
|
138
|
+
'saveState',
|
|
139
|
+
]);
|
|
140
|
+
const SCROLLBAR_SETTLE_EPSILON = 1;
|
|
141
|
+
const IMAGE_EDITOR_OPERATIONS = new Set([
|
|
142
|
+
'init',
|
|
143
|
+
'loadImage',
|
|
144
|
+
'loadFromState',
|
|
145
|
+
'saveState',
|
|
146
|
+
'scaleImage',
|
|
147
|
+
'rotateImage',
|
|
148
|
+
'resetImageTransform',
|
|
149
|
+
'createMask',
|
|
150
|
+
'removeSelectedMask',
|
|
151
|
+
'removeAllMasks',
|
|
152
|
+
'mergeMasks',
|
|
153
|
+
'createTextAnnotation',
|
|
154
|
+
'enterTextMode',
|
|
155
|
+
'exitTextMode',
|
|
156
|
+
'setTextConfig',
|
|
157
|
+
'resetTextConfig',
|
|
158
|
+
'setTextColor',
|
|
159
|
+
'setTextFontSize',
|
|
160
|
+
'enterDrawMode',
|
|
161
|
+
'exitDrawMode',
|
|
162
|
+
'setDrawConfig',
|
|
163
|
+
'resetDrawConfig',
|
|
164
|
+
'setDrawColor',
|
|
165
|
+
'setDrawBrushSize',
|
|
166
|
+
'updateSelectedAnnotation',
|
|
167
|
+
'updateAnnotation',
|
|
168
|
+
'removeSelectedAnnotation',
|
|
169
|
+
'removeAllAnnotations',
|
|
170
|
+
'deleteSelectedObject',
|
|
171
|
+
'mergeAnnotations',
|
|
172
|
+
'bringSelectedObjectForward',
|
|
173
|
+
'sendSelectedObjectBackward',
|
|
174
|
+
'bringSelectedObjectToFront',
|
|
175
|
+
'sendSelectedObjectToBack',
|
|
176
|
+
'enterCropMode',
|
|
177
|
+
'applyCrop',
|
|
178
|
+
'cancelCrop',
|
|
179
|
+
'enterMosaicMode',
|
|
180
|
+
'exitMosaicMode',
|
|
181
|
+
'applyMosaic',
|
|
182
|
+
'setMosaicConfig',
|
|
183
|
+
'resetMosaicConfig',
|
|
184
|
+
'setMosaicBrushSize',
|
|
185
|
+
'setMosaicBlockSize',
|
|
186
|
+
'undo',
|
|
187
|
+
'redo',
|
|
188
|
+
'exportImageBase64',
|
|
189
|
+
'exportImageFile',
|
|
190
|
+
'downloadImage',
|
|
191
|
+
'dispose',
|
|
192
|
+
]);
|
|
193
|
+
const TOOL_MODE_ALLOWED_OPERATIONS = {
|
|
194
|
+
crop: CROP_SESSION_ALLOWED_OPERATIONS,
|
|
195
|
+
mosaic: MOSAIC_SESSION_ALLOWED_OPERATIONS,
|
|
196
|
+
text: new Set([
|
|
197
|
+
'exitTextMode',
|
|
198
|
+
'createTextAnnotation',
|
|
199
|
+
'setTextConfig',
|
|
200
|
+
'resetTextConfig',
|
|
201
|
+
'setTextColor',
|
|
202
|
+
'setTextFontSize',
|
|
203
|
+
'saveState',
|
|
204
|
+
]),
|
|
205
|
+
draw: new Set([
|
|
206
|
+
'exitDrawMode',
|
|
207
|
+
'setDrawConfig',
|
|
208
|
+
'resetDrawConfig',
|
|
209
|
+
'setDrawColor',
|
|
210
|
+
'setDrawBrushSize',
|
|
211
|
+
'saveState',
|
|
212
|
+
]),
|
|
213
|
+
};
|
|
214
|
+
function isImageEditorOperation(value) {
|
|
215
|
+
return value !== null && IMAGE_EDITOR_OPERATIONS.has(value);
|
|
216
|
+
}
|
|
48
217
|
export class ImageEditor {
|
|
49
218
|
constructor(fabricModuleOrOptions = {}, options = {}) {
|
|
50
219
|
var _a;
|
|
@@ -66,6 +235,48 @@ export class ImageEditor {
|
|
|
66
235
|
writable: true,
|
|
67
236
|
value: void 0
|
|
68
237
|
});
|
|
238
|
+
Object.defineProperty(this, "currentLayoutMode", {
|
|
239
|
+
enumerable: true,
|
|
240
|
+
configurable: true,
|
|
241
|
+
writable: true,
|
|
242
|
+
value: 'expand'
|
|
243
|
+
});
|
|
244
|
+
Object.defineProperty(this, "defaultMosaicConfig", {
|
|
245
|
+
enumerable: true,
|
|
246
|
+
configurable: true,
|
|
247
|
+
writable: true,
|
|
248
|
+
value: void 0
|
|
249
|
+
});
|
|
250
|
+
Object.defineProperty(this, "currentMosaicConfig", {
|
|
251
|
+
enumerable: true,
|
|
252
|
+
configurable: true,
|
|
253
|
+
writable: true,
|
|
254
|
+
value: void 0
|
|
255
|
+
});
|
|
256
|
+
Object.defineProperty(this, "defaultTextConfig", {
|
|
257
|
+
enumerable: true,
|
|
258
|
+
configurable: true,
|
|
259
|
+
writable: true,
|
|
260
|
+
value: void 0
|
|
261
|
+
});
|
|
262
|
+
Object.defineProperty(this, "currentTextConfig", {
|
|
263
|
+
enumerable: true,
|
|
264
|
+
configurable: true,
|
|
265
|
+
writable: true,
|
|
266
|
+
value: void 0
|
|
267
|
+
});
|
|
268
|
+
Object.defineProperty(this, "defaultDrawConfig", {
|
|
269
|
+
enumerable: true,
|
|
270
|
+
configurable: true,
|
|
271
|
+
writable: true,
|
|
272
|
+
value: void 0
|
|
273
|
+
});
|
|
274
|
+
Object.defineProperty(this, "currentDrawConfig", {
|
|
275
|
+
enumerable: true,
|
|
276
|
+
configurable: true,
|
|
277
|
+
writable: true,
|
|
278
|
+
value: void 0
|
|
279
|
+
});
|
|
69
280
|
Object.defineProperty(this, "canvas", {
|
|
70
281
|
enumerable: true,
|
|
71
282
|
configurable: true,
|
|
@@ -162,6 +373,12 @@ export class ImageEditor {
|
|
|
162
373
|
writable: true,
|
|
163
374
|
value: null
|
|
164
375
|
});
|
|
376
|
+
Object.defineProperty(this, "annotationCounter", {
|
|
377
|
+
enumerable: true,
|
|
378
|
+
configurable: true,
|
|
379
|
+
writable: true,
|
|
380
|
+
value: 0
|
|
381
|
+
});
|
|
165
382
|
Object.defineProperty(this, "lastSnapshot", {
|
|
166
383
|
enumerable: true,
|
|
167
384
|
configurable: true,
|
|
@@ -204,12 +421,42 @@ export class ImageEditor {
|
|
|
204
421
|
writable: true,
|
|
205
422
|
value: null
|
|
206
423
|
});
|
|
424
|
+
Object.defineProperty(this, "mosaicSession", {
|
|
425
|
+
enumerable: true,
|
|
426
|
+
configurable: true,
|
|
427
|
+
writable: true,
|
|
428
|
+
value: null
|
|
429
|
+
});
|
|
430
|
+
Object.defineProperty(this, "textSession", {
|
|
431
|
+
enumerable: true,
|
|
432
|
+
configurable: true,
|
|
433
|
+
writable: true,
|
|
434
|
+
value: null
|
|
435
|
+
});
|
|
436
|
+
Object.defineProperty(this, "drawSession", {
|
|
437
|
+
enumerable: true,
|
|
438
|
+
configurable: true,
|
|
439
|
+
writable: true,
|
|
440
|
+
value: null
|
|
441
|
+
});
|
|
207
442
|
Object.defineProperty(this, "domBindings", {
|
|
208
443
|
enumerable: true,
|
|
209
444
|
configurable: true,
|
|
210
445
|
writable: true,
|
|
211
446
|
value: null
|
|
212
447
|
});
|
|
448
|
+
Object.defineProperty(this, "keyboardDocument", {
|
|
449
|
+
enumerable: true,
|
|
450
|
+
configurable: true,
|
|
451
|
+
writable: true,
|
|
452
|
+
value: null
|
|
453
|
+
});
|
|
454
|
+
Object.defineProperty(this, "keyboardHandler", {
|
|
455
|
+
enumerable: true,
|
|
456
|
+
configurable: true,
|
|
457
|
+
writable: true,
|
|
458
|
+
value: null
|
|
459
|
+
});
|
|
213
460
|
Object.defineProperty(this, "isDisposed", {
|
|
214
461
|
enumerable: true,
|
|
215
462
|
configurable: true,
|
|
@@ -244,9 +491,19 @@ export class ImageEditor {
|
|
|
244
491
|
this.fabricModule = (_a = detected.fabric) !== null && _a !== void 0 ? _a : {};
|
|
245
492
|
this.isFabricLoaded = detected.isFabricLoaded;
|
|
246
493
|
this.options = resolveOptions(detected.options);
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
494
|
+
this.currentLayoutMode = this.options.layoutMode;
|
|
495
|
+
this.defaultMosaicConfig = this.options.defaultMosaicConfig;
|
|
496
|
+
this.currentMosaicConfig = cloneResolvedMosaicConfig(this.defaultMosaicConfig);
|
|
497
|
+
this.defaultTextConfig = this.options.defaultTextConfig;
|
|
498
|
+
this.currentTextConfig = cloneResolvedTextAnnotationConfig(this.defaultTextConfig);
|
|
499
|
+
this.defaultDrawConfig = this.options.defaultDrawConfig;
|
|
500
|
+
this.currentDrawConfig = cloneResolvedDrawConfig(this.defaultDrawConfig);
|
|
501
|
+
const rawDefaultLayoutMode = detected.options
|
|
502
|
+
.defaultLayoutMode;
|
|
503
|
+
if (rawDefaultLayoutMode !== undefined && !isLayoutMode(rawDefaultLayoutMode)) {
|
|
504
|
+
reportWarning(this.options, new TypeError(`[ImageEditor] Unsupported defaultLayoutMode ` +
|
|
505
|
+
`${JSON.stringify(rawDefaultLayoutMode)}. ` +
|
|
506
|
+
'Expected "fit", "cover", or "expand".'), 'Invalid defaultLayoutMode fell back to "expand".');
|
|
250
507
|
}
|
|
251
508
|
this.operationGuard = new OperationGuard();
|
|
252
509
|
this.animQueue = new AnimationQueue();
|
|
@@ -277,6 +534,23 @@ export class ImageEditor {
|
|
|
277
534
|
removeSelectedMaskButton: 'removeSelectedMaskButton',
|
|
278
535
|
removeAllMasksButton: 'removeAllMasksButton',
|
|
279
536
|
mergeMasksButton: 'mergeMasksButton',
|
|
537
|
+
annotationList: 'annotationList',
|
|
538
|
+
enterTextModeButton: 'enterTextModeButton',
|
|
539
|
+
exitTextModeButton: 'exitTextModeButton',
|
|
540
|
+
textColorInput: 'textColorInput',
|
|
541
|
+
textFontSizeInput: 'textFontSizeInput',
|
|
542
|
+
enterDrawModeButton: 'enterDrawModeButton',
|
|
543
|
+
exitDrawModeButton: 'exitDrawModeButton',
|
|
544
|
+
drawColorInput: 'drawColorInput',
|
|
545
|
+
drawBrushSizeInput: 'drawBrushSizeInput',
|
|
546
|
+
removeSelectedAnnotationButton: 'removeSelectedAnnotationButton',
|
|
547
|
+
removeAllAnnotationsButton: 'removeAllAnnotationsButton',
|
|
548
|
+
deleteSelectedObjectButton: 'deleteSelectedObjectButton',
|
|
549
|
+
mergeAnnotationsButton: 'mergeAnnotationsButton',
|
|
550
|
+
bringSelectedObjectForwardButton: 'bringSelectedObjectForwardButton',
|
|
551
|
+
sendSelectedObjectBackwardButton: 'sendSelectedObjectBackwardButton',
|
|
552
|
+
bringSelectedObjectToFrontButton: 'bringSelectedObjectToFrontButton',
|
|
553
|
+
sendSelectedObjectToBackButton: 'sendSelectedObjectToBackButton',
|
|
280
554
|
downloadImageButton: 'downloadImageButton',
|
|
281
555
|
maskList: 'maskList',
|
|
282
556
|
zoomInButton: 'zoomInButton',
|
|
@@ -288,15 +562,20 @@ export class ImageEditor {
|
|
|
288
562
|
enterCropModeButton: 'enterCropModeButton',
|
|
289
563
|
applyCropButton: 'applyCropButton',
|
|
290
564
|
cancelCropButton: 'cancelCropButton',
|
|
565
|
+
enterMosaicModeButton: 'enterMosaicModeButton',
|
|
566
|
+
exitMosaicModeButton: 'exitMosaicModeButton',
|
|
567
|
+
mosaicBrushSizeInput: 'mosaicBrushSizeInput',
|
|
568
|
+
mosaicBlockSizeInput: 'mosaicBlockSizeInput',
|
|
291
569
|
uploadArea: 'uploadArea',
|
|
292
570
|
};
|
|
293
571
|
this.elements = { ...defaults, ...idMap };
|
|
294
|
-
this.domBindings = new DomBindings((key) => this.elements[key], () => this.isDisposed);
|
|
295
572
|
this.initCanvas();
|
|
573
|
+
this.domBindings = new DomBindings((key) => this.elements[key], () => this.isDisposed, () => { var _a, _b; return (_b = (_a = this.canvasElement) === null || _a === void 0 ? void 0 : _a.ownerDocument) !== null && _b !== void 0 ? _b : document; });
|
|
296
574
|
this.transformController = new TransformController(this.buildTransformContext());
|
|
297
575
|
this.bindDomEvents();
|
|
298
576
|
this.updateInputs();
|
|
299
577
|
this.updateMaskList();
|
|
578
|
+
this.updateAnnotationList();
|
|
300
579
|
this.updateUi();
|
|
301
580
|
if (this.options.initialImageBase64) {
|
|
302
581
|
void this.loadImage(this.options.initialImageBase64).catch(() => {
|
|
@@ -348,20 +627,24 @@ export class ImageEditor {
|
|
|
348
627
|
});
|
|
349
628
|
this.canvas.on('selection:cleared', () => this.handleSelectionChanged([]));
|
|
350
629
|
const onObjectEvent = (e) => {
|
|
351
|
-
if (e.target
|
|
352
|
-
this.
|
|
630
|
+
if (e.target)
|
|
631
|
+
this.handleObjectMovingScalingRotating(e.target);
|
|
353
632
|
};
|
|
354
633
|
const onObjectModified = (e) => {
|
|
355
|
-
if (
|
|
356
|
-
|
|
357
|
-
this.syncMaskLabel(e.target);
|
|
358
|
-
this.saveState();
|
|
634
|
+
if (e.target)
|
|
635
|
+
this.handleObjectModified(e.target);
|
|
359
636
|
};
|
|
360
637
|
this.canvas.on('object:moving', onObjectEvent);
|
|
361
638
|
this.canvas.on('object:scaling', onObjectEvent);
|
|
362
639
|
this.canvas.on('object:rotating', onObjectEvent);
|
|
363
640
|
this.canvas.on('object:modified', onObjectModified);
|
|
364
641
|
}
|
|
642
|
+
getLiveCanvasOrThrow(operationName) {
|
|
643
|
+
if (this.isDisposed || !this.canvas) {
|
|
644
|
+
throw new Error(`[ImageEditor] Cannot run "${operationName}" after dispose.`);
|
|
645
|
+
}
|
|
646
|
+
return this.canvas;
|
|
647
|
+
}
|
|
365
648
|
bindDomEvents() {
|
|
366
649
|
this.bindElementIfExists('uploadArea', 'click', () => {
|
|
367
650
|
var _a;
|
|
@@ -396,6 +679,42 @@ export class ImageEditor {
|
|
|
396
679
|
this.bindElementIfExists('mergeMasksButton', 'click', () => {
|
|
397
680
|
void this.mergeMasks();
|
|
398
681
|
});
|
|
682
|
+
this.bindElementIfExists('mergeAnnotationsButton', 'click', () => {
|
|
683
|
+
void this.mergeAnnotations();
|
|
684
|
+
});
|
|
685
|
+
this.bindElementIfExists('enterTextModeButton', 'click', () => {
|
|
686
|
+
this.enterTextMode();
|
|
687
|
+
});
|
|
688
|
+
this.bindElementIfExists('exitTextModeButton', 'click', () => {
|
|
689
|
+
this.exitTextMode();
|
|
690
|
+
});
|
|
691
|
+
this.bindElementIfExists('enterDrawModeButton', 'click', () => {
|
|
692
|
+
this.enterDrawMode();
|
|
693
|
+
});
|
|
694
|
+
this.bindElementIfExists('exitDrawModeButton', 'click', () => {
|
|
695
|
+
this.exitDrawMode();
|
|
696
|
+
});
|
|
697
|
+
this.bindElementIfExists('removeSelectedAnnotationButton', 'click', () => {
|
|
698
|
+
this.removeSelectedAnnotation();
|
|
699
|
+
});
|
|
700
|
+
this.bindElementIfExists('removeAllAnnotationsButton', 'click', () => {
|
|
701
|
+
this.removeAllAnnotations();
|
|
702
|
+
});
|
|
703
|
+
this.bindElementIfExists('deleteSelectedObjectButton', 'click', () => {
|
|
704
|
+
this.deleteSelectedObject();
|
|
705
|
+
});
|
|
706
|
+
this.bindElementIfExists('bringSelectedObjectForwardButton', 'click', () => {
|
|
707
|
+
this.bringSelectedObjectForward();
|
|
708
|
+
});
|
|
709
|
+
this.bindElementIfExists('sendSelectedObjectBackwardButton', 'click', () => {
|
|
710
|
+
this.sendSelectedObjectBackward();
|
|
711
|
+
});
|
|
712
|
+
this.bindElementIfExists('bringSelectedObjectToFrontButton', 'click', () => {
|
|
713
|
+
this.bringSelectedObjectToFront();
|
|
714
|
+
});
|
|
715
|
+
this.bindElementIfExists('sendSelectedObjectToBackButton', 'click', () => {
|
|
716
|
+
this.sendSelectedObjectToBack();
|
|
717
|
+
});
|
|
399
718
|
this.bindElementIfExists('downloadImageButton', 'click', () => {
|
|
400
719
|
this.downloadImage();
|
|
401
720
|
});
|
|
@@ -442,11 +761,112 @@ export class ImageEditor {
|
|
|
442
761
|
this.bindElementIfExists('cancelCropButton', 'click', () => {
|
|
443
762
|
this.cancelCrop();
|
|
444
763
|
});
|
|
764
|
+
this.bindElementIfExists('enterMosaicModeButton', 'click', () => {
|
|
765
|
+
this.enterMosaicMode();
|
|
766
|
+
});
|
|
767
|
+
this.bindElementIfExists('exitMosaicModeButton', 'click', () => {
|
|
768
|
+
this.exitMosaicMode();
|
|
769
|
+
});
|
|
770
|
+
const bindMosaicSizeInput = (key, applyValue) => {
|
|
771
|
+
const handler = (event) => {
|
|
772
|
+
const parsed = parseFloat(event.target.value);
|
|
773
|
+
applyValue(parsed);
|
|
774
|
+
};
|
|
775
|
+
this.bindElementIfExists(key, 'input', handler);
|
|
776
|
+
this.bindElementIfExists(key, 'change', handler);
|
|
777
|
+
};
|
|
778
|
+
bindMosaicSizeInput('mosaicBrushSizeInput', (value) => {
|
|
779
|
+
this.setMosaicBrushSize(value);
|
|
780
|
+
});
|
|
781
|
+
bindMosaicSizeInput('mosaicBlockSizeInput', (value) => {
|
|
782
|
+
this.setMosaicBlockSize(value);
|
|
783
|
+
});
|
|
784
|
+
const bindStringInput = (key, applyValue) => {
|
|
785
|
+
const handler = (event) => {
|
|
786
|
+
applyValue(event.target.value);
|
|
787
|
+
};
|
|
788
|
+
this.bindElementIfExists(key, 'input', handler);
|
|
789
|
+
this.bindElementIfExists(key, 'change', handler);
|
|
790
|
+
};
|
|
791
|
+
const bindNumberInput = (key, applyValue) => {
|
|
792
|
+
const handler = (event) => {
|
|
793
|
+
applyValue(parseFloat(event.target.value));
|
|
794
|
+
};
|
|
795
|
+
this.bindElementIfExists(key, 'input', handler);
|
|
796
|
+
this.bindElementIfExists(key, 'change', handler);
|
|
797
|
+
};
|
|
798
|
+
bindStringInput('textColorInput', (value) => this.applyTextColorInput(value));
|
|
799
|
+
bindNumberInput('textFontSizeInput', (value) => this.applyTextFontSizeInput(value));
|
|
800
|
+
bindStringInput('drawColorInput', (value) => this.applyDrawColorInput(value));
|
|
801
|
+
bindNumberInput('drawBrushSizeInput', (value) => this.applyDrawBrushSizeInput(value));
|
|
802
|
+
this.bindKeyboardEvents();
|
|
445
803
|
}
|
|
446
804
|
bindElementIfExists(key, event, handler) {
|
|
447
805
|
var _a;
|
|
448
806
|
(_a = this.domBindings) === null || _a === void 0 ? void 0 : _a.bindIfExists(key, event, handler);
|
|
449
807
|
}
|
|
808
|
+
bindKeyboardEvents() {
|
|
809
|
+
var _a, _b;
|
|
810
|
+
const ownerDocument = (_b = (_a = this.canvasElement) === null || _a === void 0 ? void 0 : _a.ownerDocument) !== null && _b !== void 0 ? _b : document;
|
|
811
|
+
if (this.keyboardHandler && this.keyboardDocument) {
|
|
812
|
+
this.keyboardDocument.removeEventListener('keydown', this.keyboardHandler);
|
|
813
|
+
}
|
|
814
|
+
this.keyboardDocument = ownerDocument;
|
|
815
|
+
this.keyboardHandler = (event) => this.handleKeyboardEvent(event);
|
|
816
|
+
ownerDocument.addEventListener('keydown', this.keyboardHandler);
|
|
817
|
+
}
|
|
818
|
+
isNativeTextInputActive() {
|
|
819
|
+
var _a;
|
|
820
|
+
const activeElement = (_a = this.keyboardDocument) === null || _a === void 0 ? void 0 : _a.activeElement;
|
|
821
|
+
if (!activeElement)
|
|
822
|
+
return false;
|
|
823
|
+
const tagName = activeElement.tagName.toLowerCase();
|
|
824
|
+
return (tagName === 'input' ||
|
|
825
|
+
tagName === 'textarea' ||
|
|
826
|
+
tagName === 'select' ||
|
|
827
|
+
activeElement.isContentEditable === true);
|
|
828
|
+
}
|
|
829
|
+
isFabricTextEditingActive() {
|
|
830
|
+
var _a;
|
|
831
|
+
const activeObject = (_a = this.canvas) === null || _a === void 0 ? void 0 : _a.getActiveObject();
|
|
832
|
+
return !!(activeObject &&
|
|
833
|
+
isTextAnnotationObject(activeObject) &&
|
|
834
|
+
activeObject.isEditing === true);
|
|
835
|
+
}
|
|
836
|
+
handleKeyboardEvent(event) {
|
|
837
|
+
if (this.isDisposed)
|
|
838
|
+
return;
|
|
839
|
+
if (event.key === 'Delete' || event.key === 'Backspace') {
|
|
840
|
+
if (this.isNativeTextInputActive() || this.isFabricTextEditingActive())
|
|
841
|
+
return;
|
|
842
|
+
this.deleteSelectedObject();
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
845
|
+
if (event.key !== 'Escape')
|
|
846
|
+
return;
|
|
847
|
+
if (this.isFabricTextEditingActive() && this.canvas) {
|
|
848
|
+
finalizeActiveTextEditing(this.buildTextControllerContext(), { commit: false });
|
|
849
|
+
event.preventDefault();
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
if (this.textSession) {
|
|
853
|
+
this.exitTextMode();
|
|
854
|
+
}
|
|
855
|
+
else if (this.drawSession) {
|
|
856
|
+
this.exitDrawMode();
|
|
857
|
+
}
|
|
858
|
+
else if (this.mosaicSession) {
|
|
859
|
+
this.exitMosaicMode();
|
|
860
|
+
}
|
|
861
|
+
else if (this.cropSession) {
|
|
862
|
+
this.cancelCrop();
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
finalizeActiveTextEditingIfNeeded() {
|
|
866
|
+
if (!this.canvas || !this.isFabricTextEditingActive())
|
|
867
|
+
return;
|
|
868
|
+
finalizeActiveTextEditing(this.buildTextControllerContext(), { commit: true });
|
|
869
|
+
}
|
|
450
870
|
async loadImageFile(file) {
|
|
451
871
|
const inputId = this.elements.imageInput;
|
|
452
872
|
const inputEl = inputId
|
|
@@ -477,6 +897,9 @@ export class ImageEditor {
|
|
|
477
897
|
}
|
|
478
898
|
}
|
|
479
899
|
async loadImage(base64, options = {}) {
|
|
900
|
+
return this.loadImageInternal(base64, options);
|
|
901
|
+
}
|
|
902
|
+
async loadImageInternal(base64, options = {}) {
|
|
480
903
|
if (!this.isFabricLoaded || !this.canvas)
|
|
481
904
|
return;
|
|
482
905
|
if (this.isDisposed)
|
|
@@ -485,9 +908,11 @@ export class ImageEditor {
|
|
|
485
908
|
return;
|
|
486
909
|
if (!this.canRunIdleOperation('loadImage', options))
|
|
487
910
|
return;
|
|
911
|
+
this.finalizeActiveTextEditingIfNeeded();
|
|
488
912
|
const callbackContext = this.getOperationContext('loadImage', options);
|
|
489
913
|
const previousImage = this.originalImage;
|
|
490
914
|
const hadMasks = this.getMasks().length > 0;
|
|
915
|
+
const hadAnnotations = this.getAnnotations().length > 0;
|
|
491
916
|
this.emitOptionCallback('onImageLoadStart', [callbackContext]);
|
|
492
917
|
this.operationGuard.beginLoading();
|
|
493
918
|
this.emitBusyChangeIfChanged(callbackContext);
|
|
@@ -496,7 +921,7 @@ export class ImageEditor {
|
|
|
496
921
|
const loadImageContext = {
|
|
497
922
|
fabric: this.fabricModule,
|
|
498
923
|
canvas: this.canvas,
|
|
499
|
-
options: this.
|
|
924
|
+
options: this.getRuntimeOptions(),
|
|
500
925
|
containerElement: this.containerElement,
|
|
501
926
|
placeholderElement: this.placeholderElement,
|
|
502
927
|
viewportCache: this.viewportCache,
|
|
@@ -516,6 +941,10 @@ export class ImageEditor {
|
|
|
516
941
|
setMaskCounter: (v) => {
|
|
517
942
|
this.maskCounter = v;
|
|
518
943
|
},
|
|
944
|
+
getAnnotationCounter: () => this.annotationCounter,
|
|
945
|
+
setAnnotationCounter: (v) => {
|
|
946
|
+
this.annotationCounter = v;
|
|
947
|
+
},
|
|
519
948
|
getCurrentScale: () => this.currentScale,
|
|
520
949
|
setCurrentScale: (v) => {
|
|
521
950
|
this.currentScale = v;
|
|
@@ -548,6 +977,7 @@ export class ImageEditor {
|
|
|
548
977
|
this.lastMask = null;
|
|
549
978
|
this.updateInputs();
|
|
550
979
|
this.updateMaskList();
|
|
980
|
+
this.updateAnnotationList();
|
|
551
981
|
this.updateUi();
|
|
552
982
|
if (previousImage && previousImage !== this.originalImage) {
|
|
553
983
|
this.emitOptionCallback('onImageCleared', [previousImage, callbackContext]);
|
|
@@ -559,6 +989,9 @@ export class ImageEditor {
|
|
|
559
989
|
if (hadMasks) {
|
|
560
990
|
this.emitMasksChanged(callbackContext);
|
|
561
991
|
}
|
|
992
|
+
if (hadAnnotations) {
|
|
993
|
+
this.emitAnnotationsChanged(callbackContext);
|
|
994
|
+
}
|
|
562
995
|
this.emitImageChanged(callbackContext);
|
|
563
996
|
}
|
|
564
997
|
getInternalOperationToken(options) {
|
|
@@ -583,10 +1016,11 @@ export class ImageEditor {
|
|
|
583
1016
|
assertIdleForOperation(operationName, options) {
|
|
584
1017
|
const token = this.getInternalOperationToken(options);
|
|
585
1018
|
this.operationGuard.assertIdleForOperation(operationName, token);
|
|
586
|
-
|
|
1019
|
+
const activeToolMode = this.getActiveToolMode();
|
|
1020
|
+
if (activeToolMode &&
|
|
587
1021
|
!this.operationGuard.isOwnOperation(token) &&
|
|
588
|
-
!
|
|
589
|
-
throw new Error(`[ImageEditor] Cannot run "${operationName}" while
|
|
1022
|
+
!TOOL_MODE_ALLOWED_OPERATIONS[activeToolMode].has(operationName)) {
|
|
1023
|
+
throw new Error(`[ImageEditor] Cannot run "${operationName}" while ${activeToolMode} mode is active.`);
|
|
590
1024
|
}
|
|
591
1025
|
if (this.animQueue.isBusy() && !this.canRunDuringAnimationQueue(options)) {
|
|
592
1026
|
throw new Error(`[ImageEditor] Cannot run "${operationName}" while an animation is queued.`);
|
|
@@ -597,10 +1031,17 @@ export class ImageEditor {
|
|
|
597
1031
|
this.assertIdleForOperation(operationName, options);
|
|
598
1032
|
return true;
|
|
599
1033
|
}
|
|
600
|
-
catch {
|
|
1034
|
+
catch (error) {
|
|
1035
|
+
if (!this.isExpectedIdleGuardError(error, operationName)) {
|
|
1036
|
+
throw error;
|
|
1037
|
+
}
|
|
601
1038
|
return false;
|
|
602
1039
|
}
|
|
603
1040
|
}
|
|
1041
|
+
isExpectedIdleGuardError(error, operationName) {
|
|
1042
|
+
return (error instanceof Error &&
|
|
1043
|
+
error.message.startsWith(`[ImageEditor] Cannot run "${operationName}" `));
|
|
1044
|
+
}
|
|
604
1045
|
assertCanQueueAnimation(operationName, options) {
|
|
605
1046
|
this.operationGuard.assertCanQueueAnimation(operationName, this.getInternalOperationToken(options));
|
|
606
1047
|
}
|
|
@@ -612,17 +1053,23 @@ export class ImageEditor {
|
|
|
612
1053
|
((_b = this.originalImage.height) !== null && _b !== void 0 ? _b : 0) > 0);
|
|
613
1054
|
}
|
|
614
1055
|
isBusy() {
|
|
615
|
-
return this.operationGuard.isBusy() || this.animQueue.isBusy() || this.
|
|
1056
|
+
return this.operationGuard.isBusy() || this.animQueue.isBusy() || this.isToolModeActive();
|
|
616
1057
|
}
|
|
617
1058
|
setLayoutMode(mode) {
|
|
618
|
-
if (mode
|
|
1059
|
+
if (!isLayoutMode(mode)) {
|
|
619
1060
|
reportWarning(this.options, new TypeError(`[ImageEditor] Unsupported layout mode ${JSON.stringify(mode)}. ` +
|
|
620
1061
|
'Expected "fit", "cover", or "expand".'), 'Ignored invalid layout mode.');
|
|
621
1062
|
return;
|
|
622
1063
|
}
|
|
623
|
-
this.
|
|
624
|
-
|
|
625
|
-
|
|
1064
|
+
this.currentLayoutMode = mode;
|
|
1065
|
+
}
|
|
1066
|
+
getRuntimeOptions() {
|
|
1067
|
+
if (this.currentLayoutMode === this.options.layoutMode)
|
|
1068
|
+
return this.options;
|
|
1069
|
+
return Object.freeze({
|
|
1070
|
+
...this.options,
|
|
1071
|
+
layoutMode: this.currentLayoutMode,
|
|
1072
|
+
});
|
|
626
1073
|
}
|
|
627
1074
|
buildCallbackContext(operation, isInternalOperation = false) {
|
|
628
1075
|
return { operation, isInternalOperation };
|
|
@@ -631,7 +1078,7 @@ export class ImageEditor {
|
|
|
631
1078
|
const internal = this.getInternalOperationToken(options);
|
|
632
1079
|
const activeOperation = this.operationGuard.activeOperationName();
|
|
633
1080
|
if (internal && activeOperation) {
|
|
634
|
-
return this.buildCallbackContext(activeOperation, true);
|
|
1081
|
+
return this.buildCallbackContext(isImageEditorOperation(activeOperation) ? activeOperation : fallback, true);
|
|
635
1082
|
}
|
|
636
1083
|
return this.buildCallbackContext(fallback, false);
|
|
637
1084
|
}
|
|
@@ -681,11 +1128,35 @@ export class ImageEditor {
|
|
|
681
1128
|
return [];
|
|
682
1129
|
return this.canvas.getObjects().filter(isMaskObject).slice();
|
|
683
1130
|
}
|
|
1131
|
+
getAnnotations() {
|
|
1132
|
+
if (!this.canvas)
|
|
1133
|
+
return [];
|
|
1134
|
+
return getAnnotationsImpl(this.canvas);
|
|
1135
|
+
}
|
|
684
1136
|
getMaskCollectionSignature() {
|
|
685
1137
|
return this.getMasks()
|
|
686
1138
|
.map((mask) => `${mask.maskId}:${mask.maskName}`)
|
|
687
1139
|
.join('|');
|
|
688
1140
|
}
|
|
1141
|
+
getAnnotationCollectionSignature() {
|
|
1142
|
+
return this.getAnnotations()
|
|
1143
|
+
.map((annotation) => `${annotation.annotationId}:${annotation.annotationName}`)
|
|
1144
|
+
.join('|');
|
|
1145
|
+
}
|
|
1146
|
+
getActiveToolMode() {
|
|
1147
|
+
if (this.cropSession)
|
|
1148
|
+
return 'crop';
|
|
1149
|
+
if (this.mosaicSession)
|
|
1150
|
+
return 'mosaic';
|
|
1151
|
+
if (this.textSession)
|
|
1152
|
+
return 'text';
|
|
1153
|
+
if (this.drawSession)
|
|
1154
|
+
return 'draw';
|
|
1155
|
+
return null;
|
|
1156
|
+
}
|
|
1157
|
+
isToolModeActive() {
|
|
1158
|
+
return this.getActiveToolMode() !== null;
|
|
1159
|
+
}
|
|
689
1160
|
getEditorState() {
|
|
690
1161
|
const canvasWidth = this.canvas ? this.canvas.getWidth() : 0;
|
|
691
1162
|
const canvasHeight = this.canvas ? this.canvas.getHeight() : 0;
|
|
@@ -694,10 +1165,15 @@ export class ImageEditor {
|
|
|
694
1165
|
hasImage: image !== null,
|
|
695
1166
|
image,
|
|
696
1167
|
maskCount: this.getMasks().length,
|
|
1168
|
+
annotationCount: this.getAnnotations().length,
|
|
697
1169
|
currentScale: this.currentScale,
|
|
698
1170
|
currentRotation: this.currentRotation,
|
|
699
1171
|
isBusy: this.isBusy(),
|
|
1172
|
+
activeToolMode: this.getActiveToolMode(),
|
|
700
1173
|
isCropMode: this.cropSession !== null,
|
|
1174
|
+
isMosaicMode: this.mosaicSession !== null,
|
|
1175
|
+
isTextMode: this.textSession !== null,
|
|
1176
|
+
isDrawMode: this.drawSession !== null,
|
|
701
1177
|
canUndo: this.historyManager.canUndo(),
|
|
702
1178
|
canRedo: this.historyManager.canRedo(),
|
|
703
1179
|
canvasWidth,
|
|
@@ -710,6 +1186,9 @@ export class ImageEditor {
|
|
|
710
1186
|
emitMasksChanged(context) {
|
|
711
1187
|
this.emitOptionCallback('onMasksChanged', [this.getMasks(), context]);
|
|
712
1188
|
}
|
|
1189
|
+
emitAnnotationsChanged(context) {
|
|
1190
|
+
this.emitOptionCallback('onAnnotationsChanged', [this.getAnnotations(), context]);
|
|
1191
|
+
}
|
|
713
1192
|
emitBusyChangeIfChanged(context) {
|
|
714
1193
|
const isBusy = this.isBusy();
|
|
715
1194
|
if (this.lastEmittedIsBusy === isBusy)
|
|
@@ -718,11 +1197,20 @@ export class ImageEditor {
|
|
|
718
1197
|
this.emitOptionCallback('onBusyChange', [isBusy, context]);
|
|
719
1198
|
}
|
|
720
1199
|
buildSelection(selected) {
|
|
721
|
-
var _a;
|
|
1200
|
+
var _a, _b;
|
|
722
1201
|
const selectedMasks = selected.filter(isMaskObject);
|
|
1202
|
+
const selectedAnnotations = selected.filter(isAnnotationObject);
|
|
1203
|
+
const selectedObjectKind = selectedMasks.length === 1 && selectedAnnotations.length === 0
|
|
1204
|
+
? 'mask'
|
|
1205
|
+
: selectedAnnotations.length === 1 && selectedMasks.length === 0
|
|
1206
|
+
? 'annotation'
|
|
1207
|
+
: null;
|
|
723
1208
|
return {
|
|
724
1209
|
selectedMask: (_a = selectedMasks[0]) !== null && _a !== void 0 ? _a : null,
|
|
725
1210
|
selectedMasks,
|
|
1211
|
+
selectedAnnotation: (_b = selectedAnnotations[0]) !== null && _b !== void 0 ? _b : null,
|
|
1212
|
+
selectedAnnotations,
|
|
1213
|
+
selectedObjectKind,
|
|
726
1214
|
};
|
|
727
1215
|
}
|
|
728
1216
|
withSelectionChangeContext(context, callback) {
|
|
@@ -761,7 +1249,7 @@ export class ImageEditor {
|
|
|
761
1249
|
applyCanvasDimensions(this.canvas, widthPx, heightPx, this.containerElement);
|
|
762
1250
|
}
|
|
763
1251
|
alignObjectBoundingBoxToCanvasTopLeft(object) {
|
|
764
|
-
var _a, _b;
|
|
1252
|
+
var _a, _b, _c;
|
|
765
1253
|
object.setCoords();
|
|
766
1254
|
const boundingRect = object.getBoundingRect();
|
|
767
1255
|
object.set({
|
|
@@ -769,7 +1257,7 @@ export class ImageEditor {
|
|
|
769
1257
|
top: ((_b = object.top) !== null && _b !== void 0 ? _b : 0) - boundingRect.top,
|
|
770
1258
|
});
|
|
771
1259
|
object.setCoords();
|
|
772
|
-
this.canvas.renderAll();
|
|
1260
|
+
(_c = this.canvas) === null || _c === void 0 ? void 0 : _c.renderAll();
|
|
773
1261
|
}
|
|
774
1262
|
measureLayoutViewport(scrollbarSize) {
|
|
775
1263
|
return this.viewportCache.measure(this.containerElement, {
|
|
@@ -777,7 +1265,13 @@ export class ImageEditor {
|
|
|
777
1265
|
height: this.options.canvasHeight,
|
|
778
1266
|
}, scrollbarSize);
|
|
779
1267
|
}
|
|
780
|
-
|
|
1268
|
+
getScrollbarStableViewportCanvasSize(viewport) {
|
|
1269
|
+
return {
|
|
1270
|
+
width: Math.max(1, viewport.width - 1),
|
|
1271
|
+
height: Math.max(1, viewport.height - 1),
|
|
1272
|
+
};
|
|
1273
|
+
}
|
|
1274
|
+
updateCanvasSizeToImageBounds(options = {}) {
|
|
781
1275
|
var _a, _b;
|
|
782
1276
|
if (!this.originalImage)
|
|
783
1277
|
return;
|
|
@@ -785,13 +1279,26 @@ export class ImageEditor {
|
|
|
785
1279
|
const boundingRect = this.originalImage.getBoundingRect();
|
|
786
1280
|
const scrollbarSize = measureScrollbarSize((_b = (_a = this.containerElement) === null || _a === void 0 ? void 0 : _a.ownerDocument) !== null && _b !== void 0 ? _b : null);
|
|
787
1281
|
const viewport = this.measureLayoutViewport(scrollbarSize);
|
|
788
|
-
|
|
1282
|
+
const shouldStabilizeContainedViewport = options.stabilizeContainedViewport !== false;
|
|
1283
|
+
const imageFitsViewport = boundingRect.width <= viewport.width + LAYOUT_EPSILON &&
|
|
1284
|
+
boundingRect.height <= viewport.height + LAYOUT_EPSILON;
|
|
1285
|
+
if (this.currentLayoutMode === 'fit' || this.currentLayoutMode === 'cover') {
|
|
1286
|
+
if (imageFitsViewport) {
|
|
1287
|
+
const canvasSize = shouldStabilizeContainedViewport
|
|
1288
|
+
? this.getScrollbarStableViewportCanvasSize(viewport)
|
|
1289
|
+
: viewport;
|
|
1290
|
+
this.setCanvasSizePx(canvasSize.width, canvasSize.height);
|
|
1291
|
+
return;
|
|
1292
|
+
}
|
|
789
1293
|
const canvasSize = computeScrollableCanvasSize(boundingRect.width, boundingRect.height, viewport, scrollbarSize);
|
|
790
1294
|
this.setCanvasSizePx(canvasSize.width, canvasSize.height);
|
|
791
1295
|
return;
|
|
792
1296
|
}
|
|
793
|
-
if (
|
|
794
|
-
|
|
1297
|
+
if (imageFitsViewport) {
|
|
1298
|
+
const canvasSize = shouldStabilizeContainedViewport
|
|
1299
|
+
? this.getScrollbarStableViewportCanvasSize(viewport)
|
|
1300
|
+
: viewport;
|
|
1301
|
+
this.setCanvasSizePx(canvasSize.width, canvasSize.height);
|
|
795
1302
|
return;
|
|
796
1303
|
}
|
|
797
1304
|
this.setCanvasSizePx(Math.max(viewport.width, Math.ceil(boundingRect.width)), Math.max(viewport.height, Math.ceil(boundingRect.height)));
|
|
@@ -807,14 +1314,14 @@ export class ImageEditor {
|
|
|
807
1314
|
const canvasH = Math.ceil(this.canvas.getHeight());
|
|
808
1315
|
const clipsImage = boundingRect.width > canvasW + LAYOUT_EPSILON ||
|
|
809
1316
|
boundingRect.height > canvasH + LAYOUT_EPSILON;
|
|
810
|
-
if (this.
|
|
1317
|
+
if (this.currentLayoutMode === 'fit' || this.currentLayoutMode === 'cover') {
|
|
811
1318
|
const staleOverflowWidth = canvasW > viewport.width + LAYOUT_EPSILON &&
|
|
812
1319
|
boundingRect.width <= viewport.width + LAYOUT_EPSILON;
|
|
813
1320
|
const staleOverflowHeight = canvasH > viewport.height + LAYOUT_EPSILON &&
|
|
814
1321
|
boundingRect.height <= viewport.height + LAYOUT_EPSILON;
|
|
815
1322
|
return clipsImage || staleOverflowWidth || staleOverflowHeight;
|
|
816
1323
|
}
|
|
817
|
-
if (this.
|
|
1324
|
+
if (this.currentLayoutMode === 'expand') {
|
|
818
1325
|
const expectedW = Math.max(viewport.width, Math.ceil(boundingRect.width));
|
|
819
1326
|
const expectedH = Math.max(viewport.height, Math.ceil(boundingRect.height));
|
|
820
1327
|
return (Math.abs(canvasW - expectedW) > LAYOUT_EPSILON ||
|
|
@@ -822,6 +1329,33 @@ export class ImageEditor {
|
|
|
822
1329
|
}
|
|
823
1330
|
return clipsImage;
|
|
824
1331
|
}
|
|
1332
|
+
settleFitCoverScrollbarsAfterStateRestore() {
|
|
1333
|
+
if (!this.canvas ||
|
|
1334
|
+
!this.containerElement ||
|
|
1335
|
+
(this.currentLayoutMode !== 'fit' && this.currentLayoutMode !== 'cover')) {
|
|
1336
|
+
return;
|
|
1337
|
+
}
|
|
1338
|
+
const canvasW = Math.ceil(this.canvas.getWidth());
|
|
1339
|
+
const canvasH = Math.ceil(this.canvas.getHeight());
|
|
1340
|
+
if (canvasW <= 1 || canvasH <= 1)
|
|
1341
|
+
return;
|
|
1342
|
+
const clientW = Math.floor(this.containerElement.clientWidth || 0);
|
|
1343
|
+
const clientH = Math.floor(this.containerElement.clientHeight || 0);
|
|
1344
|
+
if (clientW <= 0 || clientH <= 0)
|
|
1345
|
+
return;
|
|
1346
|
+
const scrollW = Math.ceil(this.containerElement.scrollWidth || 0);
|
|
1347
|
+
const scrollH = Math.ceil(this.containerElement.scrollHeight || 0);
|
|
1348
|
+
const hasHorizontalScrollbar = scrollW > clientW + LAYOUT_EPSILON;
|
|
1349
|
+
const hasVerticalScrollbar = scrollH > clientH + LAYOUT_EPSILON;
|
|
1350
|
+
if (!hasHorizontalScrollbar && !hasVerticalScrollbar)
|
|
1351
|
+
return;
|
|
1352
|
+
const nudgeWidth = hasVerticalScrollbar && Math.abs(canvasW - clientW) <= SCROLLBAR_SETTLE_EPSILON;
|
|
1353
|
+
const nudgeHeight = hasHorizontalScrollbar && Math.abs(canvasH - clientH) <= SCROLLBAR_SETTLE_EPSILON;
|
|
1354
|
+
if (!nudgeWidth && !nudgeHeight)
|
|
1355
|
+
return;
|
|
1356
|
+
this.setCanvasSizePx(nudgeWidth ? canvasW - 1 : canvasW, nudgeHeight ? canvasH - 1 : canvasH);
|
|
1357
|
+
this.setCanvasSizePx(canvasW, canvasH);
|
|
1358
|
+
}
|
|
825
1359
|
captureImageDisplayGeometry() {
|
|
826
1360
|
if (!this.canvas || !this.originalImage)
|
|
827
1361
|
return null;
|
|
@@ -864,7 +1398,7 @@ export class ImageEditor {
|
|
|
864
1398
|
}
|
|
865
1399
|
buildTransformContext() {
|
|
866
1400
|
return {
|
|
867
|
-
canvas: this.
|
|
1401
|
+
canvas: this.getLiveCanvasOrThrow('buildTransformContext'),
|
|
868
1402
|
options: this.options,
|
|
869
1403
|
guard: this.operationGuard,
|
|
870
1404
|
getOriginalImage: () => this.originalImage,
|
|
@@ -886,11 +1420,7 @@ export class ImageEditor {
|
|
|
886
1420
|
afterTransformSnap: () => {
|
|
887
1421
|
if (this.isDisposed || !this.canvas || !this.originalImage)
|
|
888
1422
|
return;
|
|
889
|
-
|
|
890
|
-
this.options.coverImageToCanvas ||
|
|
891
|
-
this.options.fitImageToCanvas) {
|
|
892
|
-
this.updateCanvasSizeToImageBounds();
|
|
893
|
-
}
|
|
1423
|
+
this.updateCanvasSizeToImageBounds();
|
|
894
1424
|
this.alignObjectBoundingBoxToCanvasTopLeft(this.originalImage);
|
|
895
1425
|
this.canvas
|
|
896
1426
|
.getObjects()
|
|
@@ -1020,6 +1550,7 @@ export class ImageEditor {
|
|
|
1020
1550
|
const context = this.buildCallbackContext(activeRestoreOperation !== null && activeRestoreOperation !== void 0 ? activeRestoreOperation : 'loadFromState', activeRestoreOperation === 'undo' || activeRestoreOperation === 'redo');
|
|
1021
1551
|
const previousImage = this.originalImage;
|
|
1022
1552
|
const previousMaskSignature = this.getMaskCollectionSignature();
|
|
1553
|
+
const previousAnnotationSignature = this.getAnnotationCollectionSignature();
|
|
1023
1554
|
try {
|
|
1024
1555
|
const restoredState = await loadFromStateImpl({
|
|
1025
1556
|
canvas: this.canvas,
|
|
@@ -1042,6 +1573,7 @@ export class ImageEditor {
|
|
|
1042
1573
|
this.canvas.sendObjectToBack(this.originalImage);
|
|
1043
1574
|
}
|
|
1044
1575
|
this.maskCounter = restoredState.maxMaskId;
|
|
1576
|
+
this.annotationCounter = restoredState.maxAnnotationId;
|
|
1045
1577
|
const editorState = restoredState.editorState;
|
|
1046
1578
|
if (editorState) {
|
|
1047
1579
|
this.currentScale = editorState.currentScale;
|
|
@@ -1058,24 +1590,26 @@ export class ImageEditor {
|
|
|
1058
1590
|
this.currentImageMimeType = null;
|
|
1059
1591
|
}
|
|
1060
1592
|
this.isImageLoadedToCanvas = !!this.originalImage;
|
|
1061
|
-
if (this.originalImage &&
|
|
1062
|
-
|
|
1063
|
-
this.options.coverImageToCanvas ||
|
|
1064
|
-
this.options.fitImageToCanvas) &&
|
|
1065
|
-
this.shouldNormalizeCanvasSizeAfterStateRestore()) {
|
|
1066
|
-
this.updateCanvasSizeToImageBounds();
|
|
1593
|
+
if (this.originalImage && this.shouldNormalizeCanvasSizeAfterStateRestore()) {
|
|
1594
|
+
this.updateCanvasSizeToImageBounds({ stabilizeContainedViewport: false });
|
|
1067
1595
|
this.alignObjectBoundingBoxToCanvasTopLeft(this.originalImage);
|
|
1068
1596
|
}
|
|
1069
|
-
|
|
1597
|
+
if (this.originalImage) {
|
|
1598
|
+
this.settleFitCoverScrollbarsAfterStateRestore();
|
|
1599
|
+
}
|
|
1600
|
+
const restoredMasks = restoredState.masks;
|
|
1070
1601
|
this.lastMask = restoredMasks.reduce((lastMask, maskObject) => !lastMask || maskObject.maskId > lastMask.maskId ? maskObject : lastMask, null);
|
|
1071
1602
|
restoredMasks.forEach((maskObject) => {
|
|
1072
1603
|
applyMaskUnselectedStyle(maskObject);
|
|
1073
1604
|
reattachMaskHoverHandlers(maskObject);
|
|
1074
1605
|
});
|
|
1606
|
+
syncAnnotationRuntimeStates(restoredState.annotations);
|
|
1607
|
+
attachTextEditingHandlersToAnnotations(this.buildTextControllerContext(), restoredState.annotations);
|
|
1075
1608
|
this.lastSnapshot = this.captureSnapshotInternal();
|
|
1076
1609
|
this.canvas.renderAll();
|
|
1077
1610
|
this.updateInputs();
|
|
1078
1611
|
this.updateMaskList();
|
|
1612
|
+
this.updateAnnotationList();
|
|
1079
1613
|
this.updateUi();
|
|
1080
1614
|
if (previousImage && previousImage !== this.originalImage) {
|
|
1081
1615
|
this.emitOptionCallback('onImageCleared', [previousImage, context]);
|
|
@@ -1083,17 +1617,32 @@ export class ImageEditor {
|
|
|
1083
1617
|
if (previousMaskSignature !== this.getMaskCollectionSignature()) {
|
|
1084
1618
|
this.emitMasksChanged(context);
|
|
1085
1619
|
}
|
|
1620
|
+
if (previousAnnotationSignature !== this.getAnnotationCollectionSignature()) {
|
|
1621
|
+
this.emitAnnotationsChanged(context);
|
|
1622
|
+
}
|
|
1086
1623
|
this.emitImageChanged(context);
|
|
1624
|
+
const canvas = this.getLiveCanvasOrThrow('loadFromState');
|
|
1087
1625
|
const activeMaskId = editorState === null || editorState === void 0 ? void 0 : editorState.activeMaskId;
|
|
1088
|
-
|
|
1626
|
+
const activeAnnotationId = editorState === null || editorState === void 0 ? void 0 : editorState.activeAnnotationId;
|
|
1627
|
+
if ((editorState === null || editorState === void 0 ? void 0 : editorState.activeObjectKind) === 'mask' && typeof activeMaskId === 'number') {
|
|
1089
1628
|
const activeMask = restoredMasks.find((maskObject) => maskObject.maskId === activeMaskId);
|
|
1090
1629
|
if (activeMask) {
|
|
1091
1630
|
this.withSelectionChangeContext(context, () => {
|
|
1092
|
-
|
|
1631
|
+
canvas.setActiveObject(activeMask);
|
|
1093
1632
|
this.handleSelectionChanged([activeMask]);
|
|
1094
1633
|
});
|
|
1095
1634
|
}
|
|
1096
1635
|
}
|
|
1636
|
+
else if ((editorState === null || editorState === void 0 ? void 0 : editorState.activeObjectKind) === 'annotation' &&
|
|
1637
|
+
typeof activeAnnotationId === 'number') {
|
|
1638
|
+
const activeAnnotation = restoredState.annotations.find((annotation) => annotation.annotationId === activeAnnotationId);
|
|
1639
|
+
if (activeAnnotation) {
|
|
1640
|
+
this.withSelectionChangeContext(context, () => {
|
|
1641
|
+
canvas.setActiveObject(activeAnnotation);
|
|
1642
|
+
this.handleSelectionChanged([activeAnnotation]);
|
|
1643
|
+
});
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1097
1646
|
}
|
|
1098
1647
|
catch (error) {
|
|
1099
1648
|
reportError(this.options, error, 'Failed to restore canvas state.');
|
|
@@ -1104,62 +1653,67 @@ export class ImageEditor {
|
|
|
1104
1653
|
this.saveStateInternal();
|
|
1105
1654
|
}
|
|
1106
1655
|
saveStateInternal(options) {
|
|
1107
|
-
var _a, _b;
|
|
1656
|
+
var _a, _b, _c;
|
|
1108
1657
|
if (!this.canvas || this.shouldSuppressSaveState)
|
|
1109
1658
|
return;
|
|
1110
1659
|
if (!this.canRunIdleOperation('saveState', options))
|
|
1111
1660
|
return;
|
|
1112
1661
|
const activeObj = this.canvas.getActiveObject();
|
|
1113
1662
|
const activeMask = this.getActiveMaskForSnapshot();
|
|
1663
|
+
const activeAnnotation = this.getActiveAnnotationForSnapshot();
|
|
1114
1664
|
this.hideAllMaskLabels();
|
|
1115
1665
|
try {
|
|
1116
1666
|
const after = saveStateImpl({
|
|
1117
1667
|
canvas: this.canvas,
|
|
1118
1668
|
activeMaskId: (_a = activeMask === null || activeMask === void 0 ? void 0 : activeMask.maskId) !== null && _a !== void 0 ? _a : null,
|
|
1669
|
+
activeAnnotationId: (_b = activeAnnotation === null || activeAnnotation === void 0 ? void 0 : activeAnnotation.annotationId) !== null && _b !== void 0 ? _b : null,
|
|
1119
1670
|
currentScale: this.currentScale,
|
|
1120
1671
|
currentRotation: this.currentRotation,
|
|
1121
1672
|
baseImageScale: this.baseImageScale,
|
|
1122
1673
|
currentImageMimeType: this.currentImageMimeType,
|
|
1123
1674
|
});
|
|
1124
|
-
const before = (
|
|
1675
|
+
const before = (_c = this.lastSnapshot) !== null && _c !== void 0 ? _c : after;
|
|
1125
1676
|
if (after === before) {
|
|
1126
1677
|
return;
|
|
1127
1678
|
}
|
|
1128
|
-
let executedOnce = false;
|
|
1129
1679
|
const cmd = new Command(async () => {
|
|
1130
|
-
|
|
1131
|
-
await this.loadFromStateInternal(after, this.withAnimationQueueBypass());
|
|
1132
|
-
}
|
|
1133
|
-
executedOnce = true;
|
|
1680
|
+
await this.loadFromStateInternal(after, this.withAnimationQueueBypass());
|
|
1134
1681
|
}, async () => {
|
|
1135
1682
|
await this.loadFromStateInternal(before, this.withAnimationQueueBypass());
|
|
1136
1683
|
});
|
|
1137
|
-
this.historyManager.
|
|
1684
|
+
this.historyManager.push(cmd);
|
|
1138
1685
|
this.lastSnapshot = after;
|
|
1139
1686
|
}
|
|
1140
1687
|
catch (error) {
|
|
1141
1688
|
reportWarning(this.options, error, 'Failed to capture canvas snapshot.');
|
|
1142
1689
|
}
|
|
1143
1690
|
finally {
|
|
1144
|
-
this.
|
|
1691
|
+
this.restoreActiveObjectAfterSnapshot(activeObj, activeMask, activeAnnotation);
|
|
1145
1692
|
this.updateUi();
|
|
1146
1693
|
}
|
|
1147
1694
|
}
|
|
1148
|
-
|
|
1695
|
+
restoreActiveObjectAfterSnapshot(activeObj, activeMask, activeAnnotation) {
|
|
1149
1696
|
if (!this.canvas)
|
|
1150
1697
|
return;
|
|
1151
1698
|
const maskToRestore = activeObj && isMaskObject(activeObj) ? activeObj : activeMask;
|
|
1152
|
-
|
|
1699
|
+
const annotationToRestore = activeObj && isAnnotationObject(activeObj) ? activeObj : activeAnnotation;
|
|
1700
|
+
if (maskToRestore && this.canvas.getObjects().includes(maskToRestore)) {
|
|
1701
|
+
this.canvas.setActiveObject(maskToRestore);
|
|
1702
|
+
this.showLabelForMask(maskToRestore);
|
|
1703
|
+
this.updateMaskListSelection(maskToRestore);
|
|
1153
1704
|
return;
|
|
1154
|
-
|
|
1155
|
-
this.
|
|
1156
|
-
|
|
1705
|
+
}
|
|
1706
|
+
if (annotationToRestore && this.canvas.getObjects().includes(annotationToRestore)) {
|
|
1707
|
+
this.canvas.setActiveObject(annotationToRestore);
|
|
1708
|
+
this.updateAnnotationListSelection(annotationToRestore);
|
|
1709
|
+
}
|
|
1157
1710
|
}
|
|
1158
1711
|
undo() {
|
|
1159
1712
|
if (this.isDisposed)
|
|
1160
1713
|
return Promise.resolve();
|
|
1161
1714
|
if (!this.canRunIdleOperation('undo'))
|
|
1162
1715
|
return Promise.resolve();
|
|
1716
|
+
this.finalizeActiveTextEditingIfNeeded();
|
|
1163
1717
|
const context = this.buildCallbackContext('undo', true);
|
|
1164
1718
|
const job = this.animQueue.add(async () => {
|
|
1165
1719
|
if (this.isDisposed)
|
|
@@ -1183,6 +1737,7 @@ export class ImageEditor {
|
|
|
1183
1737
|
return Promise.resolve();
|
|
1184
1738
|
if (!this.canRunIdleOperation('redo'))
|
|
1185
1739
|
return Promise.resolve();
|
|
1740
|
+
this.finalizeActiveTextEditingIfNeeded();
|
|
1186
1741
|
const context = this.buildCallbackContext('redo', true);
|
|
1187
1742
|
const job = this.animQueue.add(async () => {
|
|
1188
1743
|
if (this.isDisposed)
|
|
@@ -1248,8 +1803,8 @@ export class ImageEditor {
|
|
|
1248
1803
|
buildCreateMaskContext() {
|
|
1249
1804
|
return {
|
|
1250
1805
|
fabric: this.fabricModule,
|
|
1251
|
-
canvas: this.
|
|
1252
|
-
options: this.
|
|
1806
|
+
canvas: this.getLiveCanvasOrThrow('createMask'),
|
|
1807
|
+
options: this.getRuntimeOptions(),
|
|
1253
1808
|
getLastMask: () => this.lastMask,
|
|
1254
1809
|
setLastMask: (maskObject) => {
|
|
1255
1810
|
this.lastMask = maskObject;
|
|
@@ -1271,7 +1826,7 @@ export class ImageEditor {
|
|
|
1271
1826
|
}
|
|
1272
1827
|
buildRemoveMaskContext() {
|
|
1273
1828
|
return {
|
|
1274
|
-
canvas: this.
|
|
1829
|
+
canvas: this.getLiveCanvasOrThrow('removeMask'),
|
|
1275
1830
|
removeLabelForMask: (mask) => {
|
|
1276
1831
|
this.removeLabelForMask(mask);
|
|
1277
1832
|
},
|
|
@@ -1321,11 +1876,35 @@ export class ImageEditor {
|
|
|
1321
1876
|
return;
|
|
1322
1877
|
showLabelForMask(context, mask);
|
|
1323
1878
|
}
|
|
1879
|
+
handleObjectMovingScalingRotating(target) {
|
|
1880
|
+
if (isMaskObject(target)) {
|
|
1881
|
+
this.syncMaskLabel(target);
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
handleObjectModified(target) {
|
|
1885
|
+
if (isMaskObject(target)) {
|
|
1886
|
+
this.syncMaskLabel(target);
|
|
1887
|
+
const context = this.buildCallbackContext('saveState', false);
|
|
1888
|
+
this.saveState();
|
|
1889
|
+
this.emitMasksChanged(context);
|
|
1890
|
+
this.emitImageChanged(context);
|
|
1891
|
+
return;
|
|
1892
|
+
}
|
|
1893
|
+
if (isAnnotationObject(target)) {
|
|
1894
|
+
if (isAnnotationLocked(target))
|
|
1895
|
+
return;
|
|
1896
|
+
const context = this.buildCallbackContext('updateAnnotation', false);
|
|
1897
|
+
this.saveState();
|
|
1898
|
+
this.emitAnnotationsChanged(context);
|
|
1899
|
+
this.emitImageChanged(context);
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1324
1902
|
handleSelectionChanged(selected) {
|
|
1325
|
-
var _a, _b, _c;
|
|
1903
|
+
var _a, _b, _c, _d;
|
|
1326
1904
|
if (!this.canvas)
|
|
1327
1905
|
return;
|
|
1328
1906
|
const selectedMask = (_a = selected.find(isMaskObject)) !== null && _a !== void 0 ? _a : null;
|
|
1907
|
+
const selectedAnnotation = (_b = selected.find(isAnnotationObject)) !== null && _b !== void 0 ? _b : null;
|
|
1329
1908
|
const masks = this.canvas.getObjects().filter(isMaskObject);
|
|
1330
1909
|
masks.forEach((maskObject) => {
|
|
1331
1910
|
if (maskObject !== selectedMask) {
|
|
@@ -1341,9 +1920,10 @@ export class ImageEditor {
|
|
|
1341
1920
|
if (selectedMask)
|
|
1342
1921
|
this.showLabelForMask(selectedMask);
|
|
1343
1922
|
this.updateMaskListSelection(selectedMask);
|
|
1923
|
+
this.updateAnnotationListSelection(selectedAnnotation);
|
|
1344
1924
|
this.canvas.requestRenderAll();
|
|
1345
1925
|
this.updateUi();
|
|
1346
|
-
const context = (
|
|
1926
|
+
const context = (_c = this.nextSelectionChangeContext) !== null && _c !== void 0 ? _c : this.buildCallbackContext((_d = this.activeStateRestoreOperation) !== null && _d !== void 0 ? _d : 'createMask', this.activeStateRestoreOperation === 'undo' ||
|
|
1347
1927
|
this.activeStateRestoreOperation === 'redo');
|
|
1348
1928
|
this.emitOptionCallback('onSelectionChange', [this.buildSelection(selected), context]);
|
|
1349
1929
|
}
|
|
@@ -1360,68 +1940,490 @@ export class ImageEditor {
|
|
|
1360
1940
|
updateMaskListSelection(selectedMask) {
|
|
1361
1941
|
updateMaskListSelection(this.buildMaskListContext(), selectedMask);
|
|
1362
1942
|
}
|
|
1363
|
-
|
|
1943
|
+
enterTextMode() {
|
|
1364
1944
|
if (!this.canvas)
|
|
1365
1945
|
return;
|
|
1366
|
-
if (!this.canRunIdleOperation('
|
|
1946
|
+
if (!this.canRunIdleOperation('enterTextMode'))
|
|
1367
1947
|
return;
|
|
1368
|
-
|
|
1369
|
-
if (!hasMasks)
|
|
1948
|
+
if (this.isToolModeActive())
|
|
1370
1949
|
return;
|
|
1371
|
-
|
|
1372
|
-
const
|
|
1950
|
+
enterTextModeImpl(this.buildTextControllerContext());
|
|
1951
|
+
const callbackContext = this.buildCallbackContext('enterTextMode', false);
|
|
1373
1952
|
this.emitBusyChangeIfChanged(callbackContext);
|
|
1374
|
-
this.
|
|
1375
|
-
try {
|
|
1376
|
-
const mergeMasksContext = this.buildMergeMasksContext(operationToken);
|
|
1377
|
-
await mergeMasksImpl(mergeMasksContext);
|
|
1378
|
-
this.updateInputs();
|
|
1379
|
-
this.updateMaskList();
|
|
1380
|
-
this.emitMasksChanged(callbackContext);
|
|
1381
|
-
this.emitImageChanged(callbackContext);
|
|
1382
|
-
}
|
|
1383
|
-
finally {
|
|
1384
|
-
this.operationGuard.endBusyOperation(operationToken);
|
|
1385
|
-
this.emitBusyChangeIfChanged(callbackContext);
|
|
1386
|
-
this.updateUi();
|
|
1387
|
-
}
|
|
1953
|
+
this.emitImageChanged(callbackContext);
|
|
1388
1954
|
}
|
|
1389
|
-
|
|
1390
|
-
if (!this.canvas)
|
|
1955
|
+
exitTextMode() {
|
|
1956
|
+
if (!this.canvas || !this.textSession)
|
|
1391
1957
|
return;
|
|
1392
|
-
if (!this.canRunIdleOperation('
|
|
1958
|
+
if (!this.canRunIdleOperation('exitTextMode'))
|
|
1393
1959
|
return;
|
|
1394
|
-
|
|
1395
|
-
const
|
|
1960
|
+
exitTextModeImpl(this.buildTextControllerContext());
|
|
1961
|
+
const callbackContext = this.buildCallbackContext('exitTextMode', false);
|
|
1396
1962
|
this.emitBusyChangeIfChanged(callbackContext);
|
|
1397
|
-
|
|
1398
|
-
try {
|
|
1399
|
-
downloadImageImpl(exportContext, fileName);
|
|
1400
|
-
}
|
|
1401
|
-
finally {
|
|
1402
|
-
this.operationGuard.endBusyOperation(operationToken);
|
|
1403
|
-
this.emitBusyChangeIfChanged(callbackContext);
|
|
1404
|
-
}
|
|
1963
|
+
this.emitImageChanged(callbackContext);
|
|
1405
1964
|
}
|
|
1406
|
-
|
|
1965
|
+
isTextMode() {
|
|
1966
|
+
return this.textSession !== null;
|
|
1967
|
+
}
|
|
1968
|
+
createTextAnnotation(config = {}) {
|
|
1407
1969
|
if (!this.canvas)
|
|
1408
|
-
return
|
|
1409
|
-
if (!this.canRunIdleOperation('
|
|
1410
|
-
return
|
|
1411
|
-
|
|
1412
|
-
const operationToken = this.operationGuard.beginBusyOperation('exportImageBase64');
|
|
1413
|
-
this.emitBusyChangeIfChanged(callbackContext);
|
|
1414
|
-
const exportContext = this.buildExportServiceContext();
|
|
1415
|
-
try {
|
|
1416
|
-
return await exportImageBase64Impl(exportContext, options);
|
|
1417
|
-
}
|
|
1418
|
-
finally {
|
|
1419
|
-
this.operationGuard.endBusyOperation(operationToken);
|
|
1420
|
-
this.emitBusyChangeIfChanged(callbackContext);
|
|
1421
|
-
}
|
|
1970
|
+
return null;
|
|
1971
|
+
if (!this.canRunIdleOperation('createTextAnnotation'))
|
|
1972
|
+
return null;
|
|
1973
|
+
return createTextAnnotationImpl(this.buildTextControllerContext(), config);
|
|
1422
1974
|
}
|
|
1423
|
-
|
|
1424
|
-
this.
|
|
1975
|
+
enterDrawMode() {
|
|
1976
|
+
if (!this.canvas)
|
|
1977
|
+
return;
|
|
1978
|
+
if (!this.canRunIdleOperation('enterDrawMode'))
|
|
1979
|
+
return;
|
|
1980
|
+
if (this.isToolModeActive())
|
|
1981
|
+
return;
|
|
1982
|
+
enterDrawModeImpl(this.buildDrawControllerContext());
|
|
1983
|
+
const callbackContext = this.buildCallbackContext('enterDrawMode', false);
|
|
1984
|
+
this.emitBusyChangeIfChanged(callbackContext);
|
|
1985
|
+
this.emitImageChanged(callbackContext);
|
|
1986
|
+
}
|
|
1987
|
+
exitDrawMode() {
|
|
1988
|
+
if (!this.canvas || !this.drawSession)
|
|
1989
|
+
return;
|
|
1990
|
+
if (!this.canRunIdleOperation('exitDrawMode'))
|
|
1991
|
+
return;
|
|
1992
|
+
exitDrawModeImpl(this.buildDrawControllerContext());
|
|
1993
|
+
const callbackContext = this.buildCallbackContext('exitDrawMode', false);
|
|
1994
|
+
this.emitBusyChangeIfChanged(callbackContext);
|
|
1995
|
+
this.emitImageChanged(callbackContext);
|
|
1996
|
+
}
|
|
1997
|
+
isDrawMode() {
|
|
1998
|
+
return this.drawSession !== null;
|
|
1999
|
+
}
|
|
2000
|
+
getTextConfig() {
|
|
2001
|
+
return cloneResolvedTextAnnotationConfig(this.currentTextConfig);
|
|
2002
|
+
}
|
|
2003
|
+
setTextConfig(config) {
|
|
2004
|
+
this.applyTextConfigPatch(config, 'setTextConfig');
|
|
2005
|
+
}
|
|
2006
|
+
resetTextConfig() {
|
|
2007
|
+
this.applyTextConfigPatch(this.defaultTextConfig, 'resetTextConfig');
|
|
2008
|
+
}
|
|
2009
|
+
setTextColor(color) {
|
|
2010
|
+
this.applyTextConfigPatch({ fill: color }, 'setTextColor');
|
|
2011
|
+
}
|
|
2012
|
+
setTextFontSize(size) {
|
|
2013
|
+
this.applyTextConfigPatch({ fontSize: size }, 'setTextFontSize');
|
|
2014
|
+
}
|
|
2015
|
+
getDrawConfig() {
|
|
2016
|
+
return cloneResolvedDrawConfig(this.currentDrawConfig);
|
|
2017
|
+
}
|
|
2018
|
+
setDrawConfig(config) {
|
|
2019
|
+
this.applyDrawConfigPatch(config, 'setDrawConfig');
|
|
2020
|
+
}
|
|
2021
|
+
resetDrawConfig() {
|
|
2022
|
+
this.applyDrawConfigPatch(this.defaultDrawConfig, 'resetDrawConfig');
|
|
2023
|
+
}
|
|
2024
|
+
setDrawColor(color) {
|
|
2025
|
+
this.applyDrawConfigPatch({ color }, 'setDrawColor');
|
|
2026
|
+
}
|
|
2027
|
+
setDrawBrushSize(size) {
|
|
2028
|
+
this.applyDrawConfigPatch({ brushSize: size }, 'setDrawBrushSize');
|
|
2029
|
+
}
|
|
2030
|
+
removeSelectedAnnotation() {
|
|
2031
|
+
if (!this.canvas)
|
|
2032
|
+
return;
|
|
2033
|
+
if (!this.canRunIdleOperation('removeSelectedAnnotation'))
|
|
2034
|
+
return;
|
|
2035
|
+
const before = this.getAnnotations().length;
|
|
2036
|
+
const callbackContext = this.buildCallbackContext('removeSelectedAnnotation', false);
|
|
2037
|
+
this.withSelectionChangeContext(callbackContext, () => {
|
|
2038
|
+
removeSelectedAnnotationImpl(this.buildAnnotationManagerContext());
|
|
2039
|
+
});
|
|
2040
|
+
this.updateAnnotationList();
|
|
2041
|
+
this.updateUi();
|
|
2042
|
+
if (this.getAnnotations().length !== before) {
|
|
2043
|
+
this.emitAnnotationsChanged(callbackContext);
|
|
2044
|
+
this.emitImageChanged(callbackContext);
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
removeAllAnnotations(options = {}) {
|
|
2048
|
+
if (!this.canvas)
|
|
2049
|
+
return;
|
|
2050
|
+
if (!this.canRunIdleOperation('removeAllAnnotations', options))
|
|
2051
|
+
return;
|
|
2052
|
+
const before = this.getAnnotations().length;
|
|
2053
|
+
const callbackContext = this.buildCallbackContext('removeAllAnnotations', false);
|
|
2054
|
+
this.withSelectionChangeContext(callbackContext, () => {
|
|
2055
|
+
removeAllAnnotationsImpl(this.buildAnnotationManagerContext(), options);
|
|
2056
|
+
});
|
|
2057
|
+
this.updateAnnotationList();
|
|
2058
|
+
this.updateUi();
|
|
2059
|
+
if (this.getAnnotations().length !== before) {
|
|
2060
|
+
this.emitAnnotationsChanged(callbackContext);
|
|
2061
|
+
this.emitImageChanged(callbackContext);
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
updateAnnotation(annotationId, config) {
|
|
2065
|
+
if (!this.canvas)
|
|
2066
|
+
return;
|
|
2067
|
+
if (!this.canRunIdleOperation('updateAnnotation'))
|
|
2068
|
+
return;
|
|
2069
|
+
const callbackContext = this.buildCallbackContext('updateAnnotation', false);
|
|
2070
|
+
const changed = updateAnnotationImpl(this.buildAnnotationManagerContext(), annotationId, config);
|
|
2071
|
+
if (changed) {
|
|
2072
|
+
this.updateAnnotationList();
|
|
2073
|
+
this.emitAnnotationsChanged(callbackContext);
|
|
2074
|
+
this.emitImageChanged(callbackContext);
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
updateSelectedAnnotation(config) {
|
|
2078
|
+
if (!this.canvas)
|
|
2079
|
+
return;
|
|
2080
|
+
if (!this.canRunIdleOperation('updateSelectedAnnotation'))
|
|
2081
|
+
return;
|
|
2082
|
+
const callbackContext = this.buildCallbackContext('updateSelectedAnnotation', false);
|
|
2083
|
+
const changed = updateSelectedAnnotationImpl(this.buildAnnotationManagerContext(), config);
|
|
2084
|
+
if (changed) {
|
|
2085
|
+
this.updateAnnotationList();
|
|
2086
|
+
this.emitAnnotationsChanged(callbackContext);
|
|
2087
|
+
this.emitImageChanged(callbackContext);
|
|
2088
|
+
}
|
|
2089
|
+
}
|
|
2090
|
+
deleteSelectedObject() {
|
|
2091
|
+
if (!this.canvas)
|
|
2092
|
+
return;
|
|
2093
|
+
if (!this.canRunIdleOperation('deleteSelectedObject'))
|
|
2094
|
+
return;
|
|
2095
|
+
this.finalizeActiveTextEditingIfNeeded();
|
|
2096
|
+
const selectedObjects = this.getSelectedCanvasObjects();
|
|
2097
|
+
const selectedMasks = selectedObjects.filter(isMaskObject);
|
|
2098
|
+
const selectedAnnotations = selectedObjects.filter((object) => isAnnotationObject(object) && isAnnotationUnlocked(object));
|
|
2099
|
+
if (selectedMasks.length === 0 && selectedAnnotations.length === 0)
|
|
2100
|
+
return;
|
|
2101
|
+
const canvas = this.getLiveCanvasOrThrow('deleteSelectedObject');
|
|
2102
|
+
const callbackContext = this.buildCallbackContext('deleteSelectedObject', false);
|
|
2103
|
+
this.withSelectionChangeContext(callbackContext, () => {
|
|
2104
|
+
for (const mask of selectedMasks) {
|
|
2105
|
+
this.removeLabelForMask(mask);
|
|
2106
|
+
canvas.remove(mask);
|
|
2107
|
+
}
|
|
2108
|
+
removeAnnotationObjects(this.buildAnnotationManagerContext(), selectedAnnotations, {
|
|
2109
|
+
saveHistory: false,
|
|
2110
|
+
force: true,
|
|
2111
|
+
});
|
|
2112
|
+
canvas.discardActiveObject();
|
|
2113
|
+
canvas.renderAll();
|
|
2114
|
+
this.saveState();
|
|
2115
|
+
});
|
|
2116
|
+
this.updateMaskList();
|
|
2117
|
+
this.updateAnnotationList();
|
|
2118
|
+
this.updateUi();
|
|
2119
|
+
if (selectedMasks.length > 0)
|
|
2120
|
+
this.emitMasksChanged(callbackContext);
|
|
2121
|
+
if (selectedAnnotations.length > 0)
|
|
2122
|
+
this.emitAnnotationsChanged(callbackContext);
|
|
2123
|
+
this.emitImageChanged(callbackContext);
|
|
2124
|
+
}
|
|
2125
|
+
bringSelectedObjectForward() {
|
|
2126
|
+
this.moveSelectedEditableObject('bringSelectedObjectForward');
|
|
2127
|
+
}
|
|
2128
|
+
sendSelectedObjectBackward() {
|
|
2129
|
+
this.moveSelectedEditableObject('sendSelectedObjectBackward');
|
|
2130
|
+
}
|
|
2131
|
+
bringSelectedObjectToFront() {
|
|
2132
|
+
this.moveSelectedEditableObject('bringSelectedObjectToFront');
|
|
2133
|
+
}
|
|
2134
|
+
sendSelectedObjectToBack() {
|
|
2135
|
+
this.moveSelectedEditableObject('sendSelectedObjectToBack');
|
|
2136
|
+
}
|
|
2137
|
+
buildAnnotationManagerContext() {
|
|
2138
|
+
return {
|
|
2139
|
+
canvas: this.getLiveCanvasOrThrow('annotationManager'),
|
|
2140
|
+
saveCanvasState: () => this.saveState(),
|
|
2141
|
+
updateUi: () => this.updateUi(),
|
|
2142
|
+
};
|
|
2143
|
+
}
|
|
2144
|
+
buildAnnotationListContext() {
|
|
2145
|
+
return {
|
|
2146
|
+
canvas: this.canvas,
|
|
2147
|
+
getListElementId: () => this.elements.annotationList,
|
|
2148
|
+
onAnnotationSelected: (annotation) => this.handleSelectionChanged([annotation]),
|
|
2149
|
+
};
|
|
2150
|
+
}
|
|
2151
|
+
updateAnnotationList() {
|
|
2152
|
+
renderAnnotationList(this.buildAnnotationListContext());
|
|
2153
|
+
}
|
|
2154
|
+
updateAnnotationListSelection(selectedAnnotation) {
|
|
2155
|
+
updateAnnotationListSelection(this.buildAnnotationListContext(), selectedAnnotation);
|
|
2156
|
+
}
|
|
2157
|
+
buildTextControllerContext() {
|
|
2158
|
+
return {
|
|
2159
|
+
fabric: this.fabricModule,
|
|
2160
|
+
canvas: this.getLiveCanvasOrThrow('textController'),
|
|
2161
|
+
options: this.options,
|
|
2162
|
+
getOriginalImage: () => this.originalImage,
|
|
2163
|
+
getTextConfig: () => this.currentTextConfig,
|
|
2164
|
+
isImageLoaded: () => this.isImageLoaded(),
|
|
2165
|
+
getAnnotationCounter: () => this.annotationCounter,
|
|
2166
|
+
setAnnotationCounter: (value) => {
|
|
2167
|
+
this.annotationCounter = value;
|
|
2168
|
+
},
|
|
2169
|
+
getTextSession: () => this.textSession,
|
|
2170
|
+
setTextSession: (session) => {
|
|
2171
|
+
this.textSession = session;
|
|
2172
|
+
},
|
|
2173
|
+
saveCanvasState: () => this.saveState(),
|
|
2174
|
+
updateAnnotationList: () => this.updateAnnotationList(),
|
|
2175
|
+
updateUi: () => this.updateUi(),
|
|
2176
|
+
emitAnnotationsChanged: (context) => this.emitAnnotationsChanged(context),
|
|
2177
|
+
emitImageChanged: (context) => this.emitImageChanged(context),
|
|
2178
|
+
buildCallbackContext: (operation) => this.buildCallbackContext(operation, false),
|
|
2179
|
+
};
|
|
2180
|
+
}
|
|
2181
|
+
buildDrawControllerContext() {
|
|
2182
|
+
return {
|
|
2183
|
+
fabric: this.fabricModule,
|
|
2184
|
+
canvas: this.getLiveCanvasOrThrow('drawController'),
|
|
2185
|
+
options: this.options,
|
|
2186
|
+
getDrawConfig: () => this.currentDrawConfig,
|
|
2187
|
+
isImageLoaded: () => this.isImageLoaded(),
|
|
2188
|
+
getAnnotationCounter: () => this.annotationCounter,
|
|
2189
|
+
setAnnotationCounter: (value) => {
|
|
2190
|
+
this.annotationCounter = value;
|
|
2191
|
+
},
|
|
2192
|
+
getDrawSession: () => this.drawSession,
|
|
2193
|
+
setDrawSession: (session) => {
|
|
2194
|
+
this.drawSession = session;
|
|
2195
|
+
},
|
|
2196
|
+
saveCanvasState: () => this.saveState(),
|
|
2197
|
+
updateAnnotationList: () => this.updateAnnotationList(),
|
|
2198
|
+
updateUi: () => this.updateUi(),
|
|
2199
|
+
emitAnnotationsChanged: (context) => this.emitAnnotationsChanged(context),
|
|
2200
|
+
emitImageChanged: (context) => this.emitImageChanged(context),
|
|
2201
|
+
buildCallbackContext: (operation) => this.buildCallbackContext(operation, false),
|
|
2202
|
+
};
|
|
2203
|
+
}
|
|
2204
|
+
applyTextConfigPatch(config, operation) {
|
|
2205
|
+
if (!this.canRunIdleOperation(operation))
|
|
2206
|
+
return;
|
|
2207
|
+
const invalidFields = getInvalidTextAnnotationConfigFields(config);
|
|
2208
|
+
if (invalidFields.length > 0) {
|
|
2209
|
+
reportWarning(this.options, null, `${operation} ignored invalid Text config fields: ${invalidFields.join(', ')}.`);
|
|
2210
|
+
}
|
|
2211
|
+
const next = mergeTextAnnotationConfigPatch(this.currentTextConfig, config, this.defaultTextConfig);
|
|
2212
|
+
if (areResolvedTextAnnotationConfigsEqual(this.currentTextConfig, next))
|
|
2213
|
+
return;
|
|
2214
|
+
this.currentTextConfig = next;
|
|
2215
|
+
this.updateInputs();
|
|
2216
|
+
this.updateUi();
|
|
2217
|
+
this.emitImageChanged(this.buildCallbackContext(operation, false));
|
|
2218
|
+
}
|
|
2219
|
+
applyDrawConfigPatch(config, operation) {
|
|
2220
|
+
if (!this.canRunIdleOperation(operation))
|
|
2221
|
+
return;
|
|
2222
|
+
const invalidFields = getInvalidDrawConfigFields(config);
|
|
2223
|
+
if (invalidFields.length > 0) {
|
|
2224
|
+
reportWarning(this.options, null, `${operation} ignored invalid Draw config fields: ${invalidFields.join(', ')}.`);
|
|
2225
|
+
}
|
|
2226
|
+
const next = mergeDrawConfigPatch(this.currentDrawConfig, config, this.defaultDrawConfig);
|
|
2227
|
+
if (areResolvedDrawConfigsEqual(this.currentDrawConfig, next))
|
|
2228
|
+
return;
|
|
2229
|
+
this.currentDrawConfig = next;
|
|
2230
|
+
updateDrawBrush(this.buildDrawControllerContext());
|
|
2231
|
+
this.updateInputs();
|
|
2232
|
+
this.updateUi();
|
|
2233
|
+
this.emitImageChanged(this.buildCallbackContext(operation, false));
|
|
2234
|
+
}
|
|
2235
|
+
applyTextColorInput(color) {
|
|
2236
|
+
var _a;
|
|
2237
|
+
if (this.isTextMode()) {
|
|
2238
|
+
this.setTextColor(color);
|
|
2239
|
+
return;
|
|
2240
|
+
}
|
|
2241
|
+
const selected = (_a = this.canvas) === null || _a === void 0 ? void 0 : _a.getActiveObject();
|
|
2242
|
+
if (selected && isTextAnnotationObject(selected)) {
|
|
2243
|
+
this.updateSelectedAnnotation({ fill: color });
|
|
2244
|
+
return;
|
|
2245
|
+
}
|
|
2246
|
+
this.setTextColor(color);
|
|
2247
|
+
}
|
|
2248
|
+
applyTextFontSizeInput(size) {
|
|
2249
|
+
var _a;
|
|
2250
|
+
if (this.isTextMode()) {
|
|
2251
|
+
this.setTextFontSize(size);
|
|
2252
|
+
return;
|
|
2253
|
+
}
|
|
2254
|
+
const selected = (_a = this.canvas) === null || _a === void 0 ? void 0 : _a.getActiveObject();
|
|
2255
|
+
if (selected && isTextAnnotationObject(selected)) {
|
|
2256
|
+
this.updateSelectedAnnotation({ fontSize: size });
|
|
2257
|
+
return;
|
|
2258
|
+
}
|
|
2259
|
+
this.setTextFontSize(size);
|
|
2260
|
+
}
|
|
2261
|
+
applyDrawColorInput(color) {
|
|
2262
|
+
var _a;
|
|
2263
|
+
if (this.isDrawMode()) {
|
|
2264
|
+
this.setDrawColor(color);
|
|
2265
|
+
return;
|
|
2266
|
+
}
|
|
2267
|
+
const selected = (_a = this.canvas) === null || _a === void 0 ? void 0 : _a.getActiveObject();
|
|
2268
|
+
if (selected && isDrawAnnotationObject(selected)) {
|
|
2269
|
+
this.updateSelectedAnnotation({ stroke: color });
|
|
2270
|
+
return;
|
|
2271
|
+
}
|
|
2272
|
+
this.setDrawColor(color);
|
|
2273
|
+
}
|
|
2274
|
+
applyDrawBrushSizeInput(size) {
|
|
2275
|
+
var _a;
|
|
2276
|
+
if (this.isDrawMode()) {
|
|
2277
|
+
this.setDrawBrushSize(size);
|
|
2278
|
+
return;
|
|
2279
|
+
}
|
|
2280
|
+
const selected = (_a = this.canvas) === null || _a === void 0 ? void 0 : _a.getActiveObject();
|
|
2281
|
+
if (selected && isDrawAnnotationObject(selected)) {
|
|
2282
|
+
this.updateSelectedAnnotation({ strokeWidth: size });
|
|
2283
|
+
return;
|
|
2284
|
+
}
|
|
2285
|
+
this.setDrawBrushSize(size);
|
|
2286
|
+
}
|
|
2287
|
+
getSelectedCanvasObjects() {
|
|
2288
|
+
var _a, _b, _c;
|
|
2289
|
+
if (!this.canvas)
|
|
2290
|
+
return [];
|
|
2291
|
+
const activeObject = this.canvas.getActiveObject();
|
|
2292
|
+
if (!activeObject)
|
|
2293
|
+
return [];
|
|
2294
|
+
const type = typeof activeObject.type === 'string' ? activeObject.type.toLowerCase() : '';
|
|
2295
|
+
const isActiveSelection = type === 'activeselection' ||
|
|
2296
|
+
((_c = (_b = (_a = activeObject).isType) === null || _b === void 0 ? void 0 : _b.call(_a, 'ActiveSelection')) !== null && _c !== void 0 ? _c : false);
|
|
2297
|
+
if (!isActiveSelection)
|
|
2298
|
+
return [activeObject];
|
|
2299
|
+
const getObjects = activeObject
|
|
2300
|
+
.getObjects;
|
|
2301
|
+
return typeof getObjects === 'function' ? getObjects.call(activeObject) : [];
|
|
2302
|
+
}
|
|
2303
|
+
moveSelectedEditableObject(operation) {
|
|
2304
|
+
if (!this.canvas)
|
|
2305
|
+
return;
|
|
2306
|
+
if (!this.canRunIdleOperation(operation))
|
|
2307
|
+
return;
|
|
2308
|
+
const selected = this.getSelectedCanvasObjects().filter(isEditableOverlayObject);
|
|
2309
|
+
if (selected.length !== 1) {
|
|
2310
|
+
if (selected.length > 1) {
|
|
2311
|
+
reportWarning(this.options, null, `${operation} skipped: ActiveSelection layer moves are not supported.`);
|
|
2312
|
+
}
|
|
2313
|
+
return;
|
|
2314
|
+
}
|
|
2315
|
+
const object = selected[0];
|
|
2316
|
+
const range = getEditableOverlayRange(this.canvas);
|
|
2317
|
+
const overlays = range.overlays;
|
|
2318
|
+
const currentOverlayIndex = overlays.indexOf(object);
|
|
2319
|
+
if (currentOverlayIndex < 0)
|
|
2320
|
+
return;
|
|
2321
|
+
let nextOverlayIndex = currentOverlayIndex;
|
|
2322
|
+
if (operation === 'bringSelectedObjectForward') {
|
|
2323
|
+
nextOverlayIndex = Math.min(overlays.length - 1, currentOverlayIndex + 1);
|
|
2324
|
+
}
|
|
2325
|
+
else if (operation === 'sendSelectedObjectBackward') {
|
|
2326
|
+
nextOverlayIndex = Math.max(0, currentOverlayIndex - 1);
|
|
2327
|
+
}
|
|
2328
|
+
else if (operation === 'bringSelectedObjectToFront') {
|
|
2329
|
+
nextOverlayIndex = overlays.length - 1;
|
|
2330
|
+
}
|
|
2331
|
+
else if (operation === 'sendSelectedObjectToBack') {
|
|
2332
|
+
nextOverlayIndex = 0;
|
|
2333
|
+
}
|
|
2334
|
+
if (nextOverlayIndex === currentOverlayIndex)
|
|
2335
|
+
return;
|
|
2336
|
+
const reordered = overlays.slice();
|
|
2337
|
+
reordered.splice(currentOverlayIndex, 1);
|
|
2338
|
+
reordered.splice(nextOverlayIndex, 0, object);
|
|
2339
|
+
reordered.forEach((overlay, index) => {
|
|
2340
|
+
var _a, _b;
|
|
2341
|
+
(_b = (_a = this.canvas).moveObjectTo) === null || _b === void 0 ? void 0 : _b.call(_a, overlay, range.start + index);
|
|
2342
|
+
});
|
|
2343
|
+
normalizeLayerOrder(this.canvas);
|
|
2344
|
+
this.canvas.setActiveObject(object);
|
|
2345
|
+
this.canvas.renderAll();
|
|
2346
|
+
this.saveState();
|
|
2347
|
+
this.updateMaskList();
|
|
2348
|
+
this.updateAnnotationList();
|
|
2349
|
+
this.updateUi();
|
|
2350
|
+
const context = this.buildCallbackContext(operation, false);
|
|
2351
|
+
if (isMaskObject(object))
|
|
2352
|
+
this.emitMasksChanged(context);
|
|
2353
|
+
if (isAnnotationObject(object))
|
|
2354
|
+
this.emitAnnotationsChanged(context);
|
|
2355
|
+
this.emitImageChanged(context);
|
|
2356
|
+
}
|
|
2357
|
+
async mergeMasks() {
|
|
2358
|
+
if (!this.canvas)
|
|
2359
|
+
return;
|
|
2360
|
+
if (!this.canRunIdleOperation('mergeMasks'))
|
|
2361
|
+
return;
|
|
2362
|
+
this.finalizeActiveTextEditingIfNeeded();
|
|
2363
|
+
const hasMasks = this.canvas.getObjects().some(isMaskObject);
|
|
2364
|
+
if (!hasMasks)
|
|
2365
|
+
return;
|
|
2366
|
+
const callbackContext = this.buildCallbackContext('mergeMasks', false);
|
|
2367
|
+
const operationToken = this.operationGuard.beginBusyOperation('mergeMasks');
|
|
2368
|
+
this.emitBusyChangeIfChanged(callbackContext);
|
|
2369
|
+
this.updateUi();
|
|
2370
|
+
try {
|
|
2371
|
+
const mergeMasksContext = this.buildMergeMasksContext(operationToken);
|
|
2372
|
+
await mergeMasksImpl(mergeMasksContext);
|
|
2373
|
+
this.updateInputs();
|
|
2374
|
+
this.updateMaskList();
|
|
2375
|
+
this.updateAnnotationList();
|
|
2376
|
+
this.emitMasksChanged(callbackContext);
|
|
2377
|
+
if (this.getAnnotations().length > 0) {
|
|
2378
|
+
this.emitAnnotationsChanged(callbackContext);
|
|
2379
|
+
}
|
|
2380
|
+
this.emitImageChanged(callbackContext);
|
|
2381
|
+
}
|
|
2382
|
+
finally {
|
|
2383
|
+
this.operationGuard.endBusyOperation(operationToken);
|
|
2384
|
+
this.emitBusyChangeIfChanged(callbackContext);
|
|
2385
|
+
this.updateUi();
|
|
2386
|
+
}
|
|
2387
|
+
}
|
|
2388
|
+
downloadImage(options) {
|
|
2389
|
+
if (!this.canvas)
|
|
2390
|
+
return;
|
|
2391
|
+
if (!this.canRunIdleOperation('downloadImage'))
|
|
2392
|
+
return;
|
|
2393
|
+
this.finalizeActiveTextEditingIfNeeded();
|
|
2394
|
+
const callbackContext = this.buildCallbackContext('downloadImage', false);
|
|
2395
|
+
const operationToken = this.operationGuard.beginBusyOperation('downloadImage');
|
|
2396
|
+
this.emitBusyChangeIfChanged(callbackContext);
|
|
2397
|
+
const exportContext = this.buildExportServiceContext();
|
|
2398
|
+
try {
|
|
2399
|
+
downloadImageImpl(exportContext, options);
|
|
2400
|
+
}
|
|
2401
|
+
finally {
|
|
2402
|
+
this.operationGuard.endBusyOperation(operationToken);
|
|
2403
|
+
this.emitBusyChangeIfChanged(callbackContext);
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2406
|
+
async exportImageBase64(options) {
|
|
2407
|
+
if (!this.canvas)
|
|
2408
|
+
return '';
|
|
2409
|
+
if (!this.canRunIdleOperation('exportImageBase64', options))
|
|
2410
|
+
return '';
|
|
2411
|
+
this.finalizeActiveTextEditingIfNeeded();
|
|
2412
|
+
const callbackContext = this.buildCallbackContext('exportImageBase64', false);
|
|
2413
|
+
const operationToken = this.operationGuard.beginBusyOperation('exportImageBase64');
|
|
2414
|
+
this.emitBusyChangeIfChanged(callbackContext);
|
|
2415
|
+
const exportContext = this.buildExportServiceContext();
|
|
2416
|
+
try {
|
|
2417
|
+
return await exportImageBase64Impl(exportContext, options);
|
|
2418
|
+
}
|
|
2419
|
+
finally {
|
|
2420
|
+
this.operationGuard.endBusyOperation(operationToken);
|
|
2421
|
+
this.emitBusyChangeIfChanged(callbackContext);
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
async exportImageFile(options) {
|
|
2425
|
+
this.assertIdleForOperation('exportImageFile', options);
|
|
2426
|
+
this.finalizeActiveTextEditingIfNeeded();
|
|
1425
2427
|
const callbackContext = this.buildCallbackContext('exportImageFile', false);
|
|
1426
2428
|
const operationToken = this.operationGuard.beginBusyOperation('exportImageFile');
|
|
1427
2429
|
this.emitBusyChangeIfChanged(callbackContext);
|
|
@@ -1437,7 +2439,7 @@ export class ImageEditor {
|
|
|
1437
2439
|
buildExportServiceContext() {
|
|
1438
2440
|
return {
|
|
1439
2441
|
fabric: this.fabricModule,
|
|
1440
|
-
canvas: this.
|
|
2442
|
+
canvas: this.getLiveCanvasOrThrow('export'),
|
|
1441
2443
|
options: this.options,
|
|
1442
2444
|
isImageLoaded: () => this.isImageLoaded(),
|
|
1443
2445
|
getOriginalImage: () => this.originalImage,
|
|
@@ -1450,26 +2452,77 @@ export class ImageEditor {
|
|
|
1450
2452
|
containerElement: this.containerElement,
|
|
1451
2453
|
loadImage: async (base64, providedOptions) => {
|
|
1452
2454
|
const geometry = this.captureImageDisplayGeometry();
|
|
1453
|
-
await this.
|
|
2455
|
+
await this.loadImageInternal(base64, this.withInternalOperationOptions(operationToken, providedOptions !== null && providedOptions !== void 0 ? providedOptions : {}));
|
|
1454
2456
|
this.restoreMergedImageDisplayGeometry(geometry);
|
|
1455
2457
|
},
|
|
1456
|
-
|
|
2458
|
+
captureSnapshot: () => this.captureSnapshotInternal(),
|
|
1457
2459
|
loadFromState: (snapshot) => this.loadFromStateInternal(snapshot, this.withInternalOperationOptions(operationToken, this.withAnimationQueueBypass())),
|
|
2460
|
+
exportImageBase64: (options) => exportImageBase64Impl(this.buildExportServiceContext(), options),
|
|
2461
|
+
updateUi: () => this.updateUi(),
|
|
2462
|
+
updateInputs: () => this.updateInputs(),
|
|
1458
2463
|
removeAllMasksNoHistory: () => {
|
|
1459
2464
|
const context = this.buildRemoveMaskContext();
|
|
1460
2465
|
removeAllMasksImpl(context, { saveHistory: false });
|
|
1461
2466
|
},
|
|
2467
|
+
getAnnotations: () => this.getAnnotations(),
|
|
2468
|
+
restoreAnnotations: (objects) => {
|
|
2469
|
+
const canvas = this.getLiveCanvasOrThrow('restoreAnnotations');
|
|
2470
|
+
objects.forEach((annotation) => {
|
|
2471
|
+
canvas.add(annotation);
|
|
2472
|
+
});
|
|
2473
|
+
syncAnnotationRuntimeStates(objects);
|
|
2474
|
+
attachTextEditingHandlersToAnnotations(this.buildTextControllerContext(), objects);
|
|
2475
|
+
this.annotationCounter = Math.max(this.annotationCounter, ...objects.map((annotation) => annotation.annotationId), 0);
|
|
2476
|
+
this.updateAnnotationList();
|
|
2477
|
+
},
|
|
2478
|
+
};
|
|
2479
|
+
}
|
|
2480
|
+
buildMergeAnnotationsContext(operationToken) {
|
|
2481
|
+
return {
|
|
2482
|
+
...this.buildExportServiceContext(),
|
|
2483
|
+
historyManager: this.historyManager,
|
|
2484
|
+
containerElement: this.containerElement,
|
|
2485
|
+
loadImage: async (base64, providedOptions) => {
|
|
2486
|
+
const geometry = this.captureImageDisplayGeometry();
|
|
2487
|
+
await this.loadImageInternal(base64, this.withInternalOperationOptions(operationToken, providedOptions !== null && providedOptions !== void 0 ? providedOptions : {}));
|
|
2488
|
+
this.restoreMergedImageDisplayGeometry(geometry);
|
|
2489
|
+
},
|
|
2490
|
+
captureSnapshot: () => this.captureSnapshotInternal(),
|
|
2491
|
+
loadFromState: (snapshot) => this.loadFromStateInternal(snapshot, this.withInternalOperationOptions(operationToken, this.withAnimationQueueBypass())),
|
|
2492
|
+
exportImageBase64: (options) => exportImageBase64Impl(this.buildExportServiceContext(), options),
|
|
2493
|
+
updateUi: () => this.updateUi(),
|
|
2494
|
+
updateInputs: () => this.updateInputs(),
|
|
2495
|
+
removeAllAnnotationsNoHistory: () => {
|
|
2496
|
+
removeAllAnnotationsImpl(this.buildAnnotationManagerContext(), {
|
|
2497
|
+
saveHistory: false,
|
|
2498
|
+
force: true,
|
|
2499
|
+
});
|
|
2500
|
+
},
|
|
2501
|
+
getMasks: () => this.getMasks(),
|
|
2502
|
+
restoreMasks: (objects) => {
|
|
2503
|
+
const canvas = this.getLiveCanvasOrThrow('restoreMasks');
|
|
2504
|
+
objects.forEach((mask) => {
|
|
2505
|
+
canvas.add(mask);
|
|
2506
|
+
reattachMaskHoverHandlers(mask);
|
|
2507
|
+
});
|
|
2508
|
+
this.lastMask = objects.reduce((lastMask, mask) => !lastMask || mask.maskId > lastMask.maskId ? mask : lastMask, null);
|
|
2509
|
+
this.maskCounter = Math.max(this.maskCounter, ...objects.map((mask) => mask.maskId), 0);
|
|
2510
|
+
this.updateMaskList();
|
|
2511
|
+
},
|
|
1462
2512
|
};
|
|
1463
2513
|
}
|
|
1464
2514
|
captureSnapshotInternal() {
|
|
1465
|
-
var _a;
|
|
1466
|
-
if (!this.canvas)
|
|
1467
|
-
|
|
2515
|
+
var _a, _b;
|
|
2516
|
+
if (!this.canvas) {
|
|
2517
|
+
throw new Error('[ImageEditor] Cannot capture canvas snapshot before init or after dispose.');
|
|
2518
|
+
}
|
|
1468
2519
|
const activeMask = this.getActiveMaskForSnapshot();
|
|
2520
|
+
const activeAnnotation = this.getActiveAnnotationForSnapshot();
|
|
1469
2521
|
this.hideAllMaskLabels();
|
|
1470
2522
|
return saveStateImpl({
|
|
1471
2523
|
canvas: this.canvas,
|
|
1472
2524
|
activeMaskId: (_a = activeMask === null || activeMask === void 0 ? void 0 : activeMask.maskId) !== null && _a !== void 0 ? _a : null,
|
|
2525
|
+
activeAnnotationId: (_b = activeAnnotation === null || activeAnnotation === void 0 ? void 0 : activeAnnotation.annotationId) !== null && _b !== void 0 ? _b : null,
|
|
1473
2526
|
currentScale: this.currentScale,
|
|
1474
2527
|
currentRotation: this.currentRotation,
|
|
1475
2528
|
baseImageScale: this.baseImageScale,
|
|
@@ -1483,9 +2536,140 @@ export class ImageEditor {
|
|
|
1483
2536
|
const activeObject = this.canvas.getActiveObject();
|
|
1484
2537
|
if (activeObject && isMaskObject(activeObject))
|
|
1485
2538
|
return activeObject;
|
|
1486
|
-
|
|
2539
|
+
const labeledMasks = this.canvas
|
|
1487
2540
|
.getObjects()
|
|
1488
|
-
.
|
|
2541
|
+
.filter((object) => isMaskObject(object) && !!object.labelObject);
|
|
2542
|
+
return labeledMasks.length === 1 ? ((_a = labeledMasks[0]) !== null && _a !== void 0 ? _a : null) : null;
|
|
2543
|
+
}
|
|
2544
|
+
getActiveAnnotationForSnapshot() {
|
|
2545
|
+
if (!this.canvas)
|
|
2546
|
+
return null;
|
|
2547
|
+
const activeObject = this.canvas.getActiveObject();
|
|
2548
|
+
return activeObject && isAnnotationObject(activeObject) ? activeObject : null;
|
|
2549
|
+
}
|
|
2550
|
+
enterMosaicMode() {
|
|
2551
|
+
if (!this.canvas || !this.originalImage)
|
|
2552
|
+
return;
|
|
2553
|
+
if (this.mosaicSession)
|
|
2554
|
+
return;
|
|
2555
|
+
if (!this.isImageLoaded())
|
|
2556
|
+
return;
|
|
2557
|
+
if (!this.canRunIdleOperation('enterMosaicMode'))
|
|
2558
|
+
return;
|
|
2559
|
+
enterMosaicModeImpl(this.buildMosaicControllerContext());
|
|
2560
|
+
this.updateInputs();
|
|
2561
|
+
this.updateUi();
|
|
2562
|
+
const callbackContext = this.buildCallbackContext('enterMosaicMode', false);
|
|
2563
|
+
this.emitBusyChangeIfChanged(callbackContext);
|
|
2564
|
+
this.emitImageChanged(callbackContext);
|
|
2565
|
+
}
|
|
2566
|
+
exitMosaicMode() {
|
|
2567
|
+
if (!this.canvas || !this.mosaicSession)
|
|
2568
|
+
return;
|
|
2569
|
+
if (!this.canRunIdleOperation('exitMosaicMode'))
|
|
2570
|
+
return;
|
|
2571
|
+
exitMosaicModeImpl(this.buildMosaicControllerContext());
|
|
2572
|
+
this.updateInputs();
|
|
2573
|
+
this.updateUi();
|
|
2574
|
+
const callbackContext = this.buildCallbackContext('exitMosaicMode', false);
|
|
2575
|
+
this.emitBusyChangeIfChanged(callbackContext);
|
|
2576
|
+
this.emitImageChanged(callbackContext);
|
|
2577
|
+
}
|
|
2578
|
+
isMosaicMode() {
|
|
2579
|
+
return this.mosaicSession !== null;
|
|
2580
|
+
}
|
|
2581
|
+
getMosaicConfig() {
|
|
2582
|
+
return cloneResolvedMosaicConfig(this.currentMosaicConfig);
|
|
2583
|
+
}
|
|
2584
|
+
setMosaicConfig(config) {
|
|
2585
|
+
this.applyMosaicConfigPatch(config, 'setMosaicConfig');
|
|
2586
|
+
}
|
|
2587
|
+
resetMosaicConfig() {
|
|
2588
|
+
if (this.isDisposed)
|
|
2589
|
+
return;
|
|
2590
|
+
const nextConfig = cloneResolvedMosaicConfig(this.defaultMosaicConfig);
|
|
2591
|
+
if (areResolvedMosaicConfigsEqual(this.currentMosaicConfig, nextConfig))
|
|
2592
|
+
return;
|
|
2593
|
+
this.currentMosaicConfig = nextConfig;
|
|
2594
|
+
if (this.mosaicSession && this.canvas) {
|
|
2595
|
+
updateMosaicPreview(this.buildMosaicControllerContext());
|
|
2596
|
+
}
|
|
2597
|
+
this.updateInputs();
|
|
2598
|
+
this.updateUi();
|
|
2599
|
+
this.emitImageChanged(this.buildCallbackContext('resetMosaicConfig', false));
|
|
2600
|
+
}
|
|
2601
|
+
setMosaicBrushSize(size) {
|
|
2602
|
+
this.applyMosaicConfigPatch({ brushSize: size }, 'setMosaicBrushSize');
|
|
2603
|
+
}
|
|
2604
|
+
setMosaicBlockSize(size) {
|
|
2605
|
+
this.applyMosaicConfigPatch({ blockSize: size }, 'setMosaicBlockSize');
|
|
2606
|
+
}
|
|
2607
|
+
applyMosaicConfigPatch(config, operation) {
|
|
2608
|
+
if (this.isDisposed)
|
|
2609
|
+
return;
|
|
2610
|
+
if (config === null || typeof config !== 'object' || Array.isArray(config)) {
|
|
2611
|
+
reportWarning(this.options, new TypeError('[ImageEditor] Invalid Mosaic config object.'), 'Ignored invalid Mosaic config.');
|
|
2612
|
+
return;
|
|
2613
|
+
}
|
|
2614
|
+
const invalidFields = getInvalidMosaicConfigFields(config);
|
|
2615
|
+
if (invalidFields.length > 0) {
|
|
2616
|
+
reportWarning(this.options, new TypeError(`[ImageEditor] Ignored invalid Mosaic config field(s): ` +
|
|
2617
|
+
`${invalidFields.join(', ')}.`), 'Ignored invalid Mosaic config fields.');
|
|
2618
|
+
}
|
|
2619
|
+
const nextConfig = mergeMosaicConfigPatch(this.currentMosaicConfig, config);
|
|
2620
|
+
if (areResolvedMosaicConfigsEqual(this.currentMosaicConfig, nextConfig))
|
|
2621
|
+
return;
|
|
2622
|
+
this.currentMosaicConfig = nextConfig;
|
|
2623
|
+
if (this.mosaicSession && this.canvas) {
|
|
2624
|
+
updateMosaicPreview(this.buildMosaicControllerContext());
|
|
2625
|
+
}
|
|
2626
|
+
this.updateInputs();
|
|
2627
|
+
this.updateUi();
|
|
2628
|
+
this.emitImageChanged(this.buildCallbackContext(operation, false));
|
|
2629
|
+
}
|
|
2630
|
+
buildMosaicControllerContext() {
|
|
2631
|
+
return {
|
|
2632
|
+
fabric: this.fabricModule,
|
|
2633
|
+
canvas: this.getLiveCanvasOrThrow('mosaicController'),
|
|
2634
|
+
options: this.options,
|
|
2635
|
+
historyManager: this.historyManager,
|
|
2636
|
+
getMosaicConfig: () => cloneResolvedMosaicConfig(this.currentMosaicConfig),
|
|
2637
|
+
isImageLoaded: () => this.isImageLoaded(),
|
|
2638
|
+
getOriginalImage: () => this.originalImage,
|
|
2639
|
+
setOriginalImage: (image) => {
|
|
2640
|
+
this.originalImage = image;
|
|
2641
|
+
},
|
|
2642
|
+
getCurrentImageMimeType: () => this.currentImageMimeType,
|
|
2643
|
+
setCurrentImageMimeType: (mimeType) => {
|
|
2644
|
+
this.currentImageMimeType = mimeType;
|
|
2645
|
+
},
|
|
2646
|
+
getLastSnapshot: () => this.lastSnapshot,
|
|
2647
|
+
setLastSnapshot: (snapshot) => {
|
|
2648
|
+
this.lastSnapshot = snapshot;
|
|
2649
|
+
},
|
|
2650
|
+
captureSnapshot: () => this.captureSnapshotInternal(),
|
|
2651
|
+
loadFromState: (snapshot) => this.loadFromStateInternal(snapshot, this.withAnimationQueueBypass()),
|
|
2652
|
+
updateUi: () => {
|
|
2653
|
+
this.updateUi();
|
|
2654
|
+
},
|
|
2655
|
+
updateInputs: () => {
|
|
2656
|
+
this.updateInputs();
|
|
2657
|
+
},
|
|
2658
|
+
hideAllMaskLabels: () => {
|
|
2659
|
+
this.hideAllMaskLabels();
|
|
2660
|
+
},
|
|
2661
|
+
emitImageChanged: (context) => {
|
|
2662
|
+
this.emitImageChanged(context);
|
|
2663
|
+
},
|
|
2664
|
+
emitBusyChangeIfChanged: (context) => {
|
|
2665
|
+
this.emitBusyChangeIfChanged(context);
|
|
2666
|
+
},
|
|
2667
|
+
buildCallbackContext: (operation, isInternal) => this.buildCallbackContext(operation, isInternal),
|
|
2668
|
+
getMosaicSession: () => this.mosaicSession,
|
|
2669
|
+
setMosaicSession: (session) => {
|
|
2670
|
+
this.mosaicSession = session;
|
|
2671
|
+
},
|
|
2672
|
+
};
|
|
1489
2673
|
}
|
|
1490
2674
|
enterCropMode() {
|
|
1491
2675
|
if (!this.canvas || !this.originalImage)
|
|
@@ -1546,7 +2730,7 @@ export class ImageEditor {
|
|
|
1546
2730
|
buildCropControllerContext(operationToken) {
|
|
1547
2731
|
return {
|
|
1548
2732
|
fabric: this.fabricModule,
|
|
1549
|
-
canvas: this.
|
|
2733
|
+
canvas: this.getLiveCanvasOrThrow('cropController'),
|
|
1550
2734
|
options: this.options,
|
|
1551
2735
|
historyManager: this.historyManager,
|
|
1552
2736
|
isImageLoaded: () => this.isImageLoaded(),
|
|
@@ -1558,7 +2742,7 @@ export class ImageEditor {
|
|
|
1558
2742
|
},
|
|
1559
2743
|
saveState: () => this.captureSnapshotInternal(),
|
|
1560
2744
|
loadFromState: (snapshot) => this.loadFromStateInternal(snapshot, this.withInternalOperationOptions(operationToken, this.withAnimationQueueBypass())),
|
|
1561
|
-
loadImage: (base64, providedOptions) => this.
|
|
2745
|
+
loadImage: (base64, providedOptions) => this.loadImageInternal(base64, this.withInternalOperationOptions(operationToken, providedOptions !== null && providedOptions !== void 0 ? providedOptions : {})),
|
|
1562
2746
|
getMaskCounter: () => this.maskCounter,
|
|
1563
2747
|
setMaskCounter: (n) => {
|
|
1564
2748
|
this.maskCounter = n;
|
|
@@ -1568,33 +2752,131 @@ export class ImageEditor {
|
|
|
1568
2752
|
},
|
|
1569
2753
|
};
|
|
1570
2754
|
}
|
|
2755
|
+
syncInputValue(inputElement, value) {
|
|
2756
|
+
if (!inputElement)
|
|
2757
|
+
return;
|
|
2758
|
+
const ownerDocument = inputElement.ownerDocument;
|
|
2759
|
+
if (ownerDocument.activeElement === inputElement && !inputElement.readOnly)
|
|
2760
|
+
return;
|
|
2761
|
+
if (inputElement.value !== value)
|
|
2762
|
+
inputElement.value = value;
|
|
2763
|
+
}
|
|
1571
2764
|
updateInputs() {
|
|
1572
2765
|
const scaleId = this.elements.scalePercentageInput;
|
|
1573
|
-
if (
|
|
2766
|
+
if (scaleId) {
|
|
2767
|
+
const scaleInputElement = document.getElementById(scaleId);
|
|
2768
|
+
this.syncInputValue(scaleInputElement, String(Math.round(this.currentScale * 100)));
|
|
2769
|
+
}
|
|
2770
|
+
const mosaicConfig = this.getMosaicConfig();
|
|
2771
|
+
const mosaicBrushSizeInputId = this.elements.mosaicBrushSizeInput;
|
|
2772
|
+
if (mosaicBrushSizeInputId) {
|
|
2773
|
+
const brushInput = document.getElementById(mosaicBrushSizeInputId);
|
|
2774
|
+
this.syncInputValue(brushInput, String(mosaicConfig.brushSize));
|
|
2775
|
+
}
|
|
2776
|
+
const mosaicBlockSizeInputId = this.elements.mosaicBlockSizeInput;
|
|
2777
|
+
if (mosaicBlockSizeInputId) {
|
|
2778
|
+
const blockInput = document.getElementById(mosaicBlockSizeInputId);
|
|
2779
|
+
this.syncInputValue(blockInput, String(mosaicConfig.blockSize));
|
|
2780
|
+
}
|
|
2781
|
+
const textConfig = this.getTextConfig();
|
|
2782
|
+
const textColorInputId = this.elements.textColorInput;
|
|
2783
|
+
if (textColorInputId) {
|
|
2784
|
+
const textColorInput = document.getElementById(textColorInputId);
|
|
2785
|
+
this.syncInputValue(textColorInput, textConfig.fill);
|
|
2786
|
+
}
|
|
2787
|
+
const textFontSizeInputId = this.elements.textFontSizeInput;
|
|
2788
|
+
if (textFontSizeInputId) {
|
|
2789
|
+
const fontInput = document.getElementById(textFontSizeInputId);
|
|
2790
|
+
this.syncInputValue(fontInput, String(textConfig.fontSize));
|
|
2791
|
+
}
|
|
2792
|
+
const drawConfig = this.getDrawConfig();
|
|
2793
|
+
const drawColorInputId = this.elements.drawColorInput;
|
|
2794
|
+
if (drawColorInputId) {
|
|
2795
|
+
const drawColorInput = document.getElementById(drawColorInputId);
|
|
2796
|
+
this.syncInputValue(drawColorInput, drawConfig.color);
|
|
2797
|
+
}
|
|
2798
|
+
const drawBrushSizeInputId = this.elements.drawBrushSizeInput;
|
|
2799
|
+
if (drawBrushSizeInputId) {
|
|
2800
|
+
const brushInput = document.getElementById(drawBrushSizeInputId);
|
|
2801
|
+
this.syncInputValue(brushInput, String(drawConfig.brushSize));
|
|
2802
|
+
}
|
|
2803
|
+
}
|
|
2804
|
+
async mergeAnnotations() {
|
|
2805
|
+
if (!this.canvas)
|
|
2806
|
+
return;
|
|
2807
|
+
if (!this.canRunIdleOperation('mergeAnnotations'))
|
|
1574
2808
|
return;
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
2809
|
+
this.finalizeActiveTextEditingIfNeeded();
|
|
2810
|
+
const hasAnnotations = this.canvas.getObjects().some(isAnnotationObject);
|
|
2811
|
+
if (!hasAnnotations)
|
|
2812
|
+
return;
|
|
2813
|
+
const callbackContext = this.buildCallbackContext('mergeAnnotations', false);
|
|
2814
|
+
const operationToken = this.operationGuard.beginBusyOperation('mergeAnnotations');
|
|
2815
|
+
this.emitBusyChangeIfChanged(callbackContext);
|
|
2816
|
+
this.updateUi();
|
|
2817
|
+
try {
|
|
2818
|
+
await mergeAnnotationsImpl(this.buildMergeAnnotationsContext(operationToken));
|
|
2819
|
+
this.updateInputs();
|
|
2820
|
+
this.updateMaskList();
|
|
2821
|
+
this.updateAnnotationList();
|
|
2822
|
+
this.emitAnnotationsChanged(callbackContext);
|
|
2823
|
+
if (this.getMasks().length > 0)
|
|
2824
|
+
this.emitMasksChanged(callbackContext);
|
|
2825
|
+
this.emitImageChanged(callbackContext);
|
|
2826
|
+
}
|
|
2827
|
+
finally {
|
|
2828
|
+
this.operationGuard.endBusyOperation(operationToken);
|
|
2829
|
+
this.emitBusyChangeIfChanged(callbackContext);
|
|
2830
|
+
this.updateUi();
|
|
2831
|
+
}
|
|
1578
2832
|
}
|
|
1579
2833
|
updateUi() {
|
|
2834
|
+
var _a;
|
|
1580
2835
|
if (!this.canvas)
|
|
1581
2836
|
return;
|
|
1582
2837
|
const hasImage = !!this.originalImage;
|
|
1583
2838
|
const masks = hasImage ? this.canvas.getObjects().filter(isMaskObject) : [];
|
|
2839
|
+
const annotations = hasImage ? this.canvas.getObjects().filter(isAnnotationObject) : [];
|
|
1584
2840
|
const hasMasks = masks.length > 0;
|
|
2841
|
+
const hasAnnotations = annotations.length > 0;
|
|
1585
2842
|
const activeObject = this.canvas.getActiveObject();
|
|
1586
2843
|
const hasSelectedMask = !!(activeObject && isMaskObject(activeObject));
|
|
2844
|
+
const hasSelectedAnnotation = !!(activeObject && isAnnotationObject(activeObject));
|
|
2845
|
+
const hasSelectedEditableObject = !!activeObject && isEditableOverlayObject(activeObject);
|
|
1587
2846
|
const isDefaultTransform = this.currentScale === 1 && this.currentRotation === 0;
|
|
1588
2847
|
const canUndo = this.historyManager.canUndo();
|
|
1589
2848
|
const canRedo = this.historyManager.canRedo();
|
|
1590
2849
|
const isInCropMode = this.cropSession !== null;
|
|
2850
|
+
const isInMosaicMode = this.mosaicSession !== null;
|
|
2851
|
+
const isInTextMode = this.textSession !== null;
|
|
2852
|
+
const isInDrawMode = this.drawSession !== null;
|
|
1591
2853
|
const isBusy = this.operationGuard.isBusy() || this.animQueue.isBusy();
|
|
2854
|
+
const isMosaicApplying = ((_a = this.mosaicSession) === null || _a === void 0 ? void 0 : _a.isApplying) === true;
|
|
1592
2855
|
if (isInCropMode) {
|
|
1593
2856
|
CROP_MODE_CONTROL_KEYS.forEach((key) => {
|
|
1594
2857
|
this.setControlEnabled(key, !isBusy && CROP_MODE_ENABLED_KEYS.includes(key));
|
|
1595
2858
|
});
|
|
1596
2859
|
return;
|
|
1597
2860
|
}
|
|
2861
|
+
if (isInTextMode) {
|
|
2862
|
+
CROP_MODE_CONTROL_KEYS.forEach((key) => {
|
|
2863
|
+
this.setControlEnabled(key, !isBusy && TEXT_MODE_ENABLED_KEYS.includes(key));
|
|
2864
|
+
});
|
|
2865
|
+
return;
|
|
2866
|
+
}
|
|
2867
|
+
if (isInDrawMode) {
|
|
2868
|
+
CROP_MODE_CONTROL_KEYS.forEach((key) => {
|
|
2869
|
+
this.setControlEnabled(key, !isBusy && DRAW_MODE_ENABLED_KEYS.includes(key));
|
|
2870
|
+
});
|
|
2871
|
+
return;
|
|
2872
|
+
}
|
|
2873
|
+
if (isInMosaicMode) {
|
|
2874
|
+
MOSAIC_MODE_CONTROL_KEYS.forEach((key) => {
|
|
2875
|
+
this.setControlEnabled(key, !isBusy && !isMosaicApplying && MOSAIC_MODE_ENABLED_KEYS.includes(key));
|
|
2876
|
+
});
|
|
2877
|
+
this.setControlEnabled('imageInput', false);
|
|
2878
|
+
return;
|
|
2879
|
+
}
|
|
1598
2880
|
this.setControlEnabled('scalePercentageInput', hasImage && !isBusy);
|
|
1599
2881
|
this.setControlEnabled('rotateLeftDegreesInput', hasImage && !isBusy);
|
|
1600
2882
|
this.setControlEnabled('rotateRightDegreesInput', hasImage && !isBusy);
|
|
@@ -1606,11 +2888,31 @@ export class ImageEditor {
|
|
|
1606
2888
|
this.setControlEnabled('removeSelectedMaskButton', hasSelectedMask && !isBusy);
|
|
1607
2889
|
this.setControlEnabled('removeAllMasksButton', hasMasks && !isBusy);
|
|
1608
2890
|
this.setControlEnabled('mergeMasksButton', hasImage && hasMasks && !isBusy);
|
|
2891
|
+
this.setControlEnabled('removeSelectedAnnotationButton', hasSelectedAnnotation && !isBusy);
|
|
2892
|
+
this.setControlEnabled('removeAllAnnotationsButton', hasAnnotations && !isBusy);
|
|
2893
|
+
this.setControlEnabled('deleteSelectedObjectButton', hasSelectedEditableObject && !isBusy);
|
|
2894
|
+
this.setControlEnabled('mergeAnnotationsButton', hasImage && hasAnnotations && !isBusy);
|
|
2895
|
+
this.setControlEnabled('bringSelectedObjectForwardButton', hasSelectedEditableObject && !isBusy);
|
|
2896
|
+
this.setControlEnabled('sendSelectedObjectBackwardButton', hasSelectedEditableObject && !isBusy);
|
|
2897
|
+
this.setControlEnabled('bringSelectedObjectToFrontButton', hasSelectedEditableObject && !isBusy);
|
|
2898
|
+
this.setControlEnabled('sendSelectedObjectToBackButton', hasSelectedEditableObject && !isBusy);
|
|
1609
2899
|
this.setControlEnabled('downloadImageButton', hasImage && !isBusy);
|
|
1610
2900
|
this.setControlEnabled('resetImageTransformButton', hasImage && !isDefaultTransform && !isBusy);
|
|
1611
2901
|
this.setControlEnabled('undoButton', hasImage && !isBusy && canUndo);
|
|
1612
2902
|
this.setControlEnabled('redoButton', hasImage && !isBusy && canRedo);
|
|
1613
2903
|
this.setControlEnabled('enterCropModeButton', hasImage && !isBusy);
|
|
2904
|
+
this.setControlEnabled('enterMosaicModeButton', hasImage && !isBusy);
|
|
2905
|
+
this.setControlEnabled('enterTextModeButton', hasImage && !isBusy);
|
|
2906
|
+
this.setControlEnabled('enterDrawModeButton', hasImage && !isBusy);
|
|
2907
|
+
this.setControlEnabled('exitMosaicModeButton', false);
|
|
2908
|
+
this.setControlEnabled('exitTextModeButton', false);
|
|
2909
|
+
this.setControlEnabled('exitDrawModeButton', false);
|
|
2910
|
+
this.setControlEnabled('mosaicBrushSizeInput', !this.isDisposed);
|
|
2911
|
+
this.setControlEnabled('mosaicBlockSizeInput', !this.isDisposed);
|
|
2912
|
+
this.setControlEnabled('textColorInput', !this.isDisposed);
|
|
2913
|
+
this.setControlEnabled('textFontSizeInput', !this.isDisposed);
|
|
2914
|
+
this.setControlEnabled('drawColorInput', !this.isDisposed);
|
|
2915
|
+
this.setControlEnabled('drawBrushSizeInput', !this.isDisposed);
|
|
1614
2916
|
this.setControlEnabled('imageInput', !isBusy);
|
|
1615
2917
|
this.setControlEnabled('applyCropButton', false);
|
|
1616
2918
|
this.setControlEnabled('cancelCropButton', false);
|
|
@@ -1625,7 +2927,10 @@ export class ImageEditor {
|
|
|
1625
2927
|
return;
|
|
1626
2928
|
this.recordElementOriginalState(key, controlElement);
|
|
1627
2929
|
if ('disabled' in controlElement) {
|
|
1628
|
-
|
|
2930
|
+
const formControl = controlElement;
|
|
2931
|
+
const nextDisabled = !isEnabled;
|
|
2932
|
+
if (formControl.disabled !== nextDisabled)
|
|
2933
|
+
formControl.disabled = nextDisabled;
|
|
1629
2934
|
return;
|
|
1630
2935
|
}
|
|
1631
2936
|
if (!isEnabled) {
|
|
@@ -1698,6 +3003,15 @@ export class ImageEditor {
|
|
|
1698
3003
|
this.operationGuard.markDisposed();
|
|
1699
3004
|
this.animQueue.clear();
|
|
1700
3005
|
(_a = this.domBindings) === null || _a === void 0 ? void 0 : _a.removeAll();
|
|
3006
|
+
if (this.keyboardHandler && this.keyboardDocument) {
|
|
3007
|
+
try {
|
|
3008
|
+
this.keyboardDocument.removeEventListener('keydown', this.keyboardHandler);
|
|
3009
|
+
}
|
|
3010
|
+
catch {
|
|
3011
|
+
}
|
|
3012
|
+
}
|
|
3013
|
+
this.keyboardHandler = null;
|
|
3014
|
+
this.keyboardDocument = null;
|
|
1701
3015
|
this.restoreElementOriginalStates();
|
|
1702
3016
|
if (this.cropSession && this.canvas) {
|
|
1703
3017
|
try {
|
|
@@ -1708,6 +3022,30 @@ export class ImageEditor {
|
|
|
1708
3022
|
}
|
|
1709
3023
|
this.cropSession = null;
|
|
1710
3024
|
}
|
|
3025
|
+
if (this.mosaicSession && this.canvas) {
|
|
3026
|
+
try {
|
|
3027
|
+
exitMosaicModeImpl(this.buildMosaicControllerContext());
|
|
3028
|
+
}
|
|
3029
|
+
catch {
|
|
3030
|
+
}
|
|
3031
|
+
this.mosaicSession = null;
|
|
3032
|
+
}
|
|
3033
|
+
if (this.textSession && this.canvas) {
|
|
3034
|
+
try {
|
|
3035
|
+
exitTextModeImpl(this.buildTextControllerContext());
|
|
3036
|
+
}
|
|
3037
|
+
catch {
|
|
3038
|
+
}
|
|
3039
|
+
this.textSession = null;
|
|
3040
|
+
}
|
|
3041
|
+
if (this.drawSession && this.canvas) {
|
|
3042
|
+
try {
|
|
3043
|
+
exitDrawModeImpl(this.buildDrawControllerContext());
|
|
3044
|
+
}
|
|
3045
|
+
catch {
|
|
3046
|
+
}
|
|
3047
|
+
this.drawSession = null;
|
|
3048
|
+
}
|
|
1711
3049
|
if (this.canvas) {
|
|
1712
3050
|
try {
|
|
1713
3051
|
void Promise.resolve(this.canvas.dispose()).catch(() => {
|
|
@@ -1723,6 +3061,7 @@ export class ImageEditor {
|
|
|
1723
3061
|
this.currentImageMimeType = null;
|
|
1724
3062
|
this.lastMask = null;
|
|
1725
3063
|
this.maskCounter = 0;
|
|
3064
|
+
this.annotationCounter = 0;
|
|
1726
3065
|
this.currentScale = 1;
|
|
1727
3066
|
this.currentRotation = 0;
|
|
1728
3067
|
this.baseImageScale = 1;
|