@bensitu/image-editor 1.4.2 → 1.5.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 +445 -131
- package/dist/image-editor.esm.js +304 -130
- package/dist/image-editor.esm.js.map +2 -2
- package/dist/image-editor.esm.min.js +2 -2
- package/dist/image-editor.esm.min.js.map +3 -3
- package/dist/image-editor.esm.min.mjs +2 -2
- package/dist/image-editor.esm.min.mjs.map +3 -3
- package/dist/image-editor.esm.mjs +304 -130
- package/dist/image-editor.esm.mjs.map +2 -2
- package/dist/image-editor.js +304 -130
- package/dist/image-editor.js.map +2 -2
- package/dist/image-editor.min.js +2 -2
- package/dist/image-editor.min.js.map +3 -3
- package/image-editor.d.ts +60 -19
- package/package.json +1 -1
- package/src/image-editor.js +322 -127
package/dist/image-editor.esm.js
CHANGED
|
@@ -5,7 +5,7 @@ import fabricModule from "fabric";
|
|
|
5
5
|
/**
|
|
6
6
|
* @file image-editor.js
|
|
7
7
|
* @module image-editor
|
|
8
|
-
* @version 1.
|
|
8
|
+
* @version 1.5.0
|
|
9
9
|
* @author Ben Situ
|
|
10
10
|
* @license MIT
|
|
11
11
|
* @description Lightweight canvas-based image editor with masking/transform/export support.
|
|
@@ -146,7 +146,7 @@ var ImageEditor = class {
|
|
|
146
146
|
this._activeAnimationRejectors = /* @__PURE__ */ new Set();
|
|
147
147
|
this._disposed = false;
|
|
148
148
|
this._initialized = false;
|
|
149
|
-
this.onImageLoaded = typeof options.onImageLoaded === "function" ? options.onImageLoaded : null;
|
|
149
|
+
this.onImageLoaded = typeof this.options.onImageLoaded === "function" ? this.options.onImageLoaded : null;
|
|
150
150
|
this.animationQueue = new AnimationQueue();
|
|
151
151
|
this.historyManager = new HistoryManager(this.maxHistorySize);
|
|
152
152
|
}
|
|
@@ -192,10 +192,12 @@ var ImageEditor = class {
|
|
|
192
192
|
* Use this method to set up the editor UI before interacting with it.
|
|
193
193
|
*
|
|
194
194
|
* @param {Object} [idMap={}] - Optional mapping from logical element names to actual DOM element IDs.
|
|
195
|
-
* Supported keys include: canvas, canvasContainer,
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
*
|
|
195
|
+
* Supported keys include: canvas, canvasContainer, imagePlaceholder, scalePercentageInput,
|
|
196
|
+
* rotateLeftDegreesInput, rotateRightDegreesInput, rotateLeftButton, rotateRightButton,
|
|
197
|
+
* createMaskButton, removeSelectedMaskButton, removeAllMasksButton, mergeMasksButton,
|
|
198
|
+
* downloadImageButton, maskList, zoomInButton, zoomOutButton, resetImageTransformButton,
|
|
199
|
+
* undoButton, redoButton, imageInput, uploadArea, enterCropModeButton, applyCropButton,
|
|
200
|
+
* and cancelCropButton. Deprecated 1.x names remain supported as aliases.
|
|
199
201
|
*
|
|
200
202
|
* @returns {void}
|
|
201
203
|
*
|
|
@@ -204,7 +206,7 @@ var ImageEditor = class {
|
|
|
204
206
|
* @example
|
|
205
207
|
* editor.init({
|
|
206
208
|
* canvas: 'myFabricCanvasId',
|
|
207
|
-
*
|
|
209
|
+
* downloadImageButton: 'myDownloadButtonId'
|
|
208
210
|
* });
|
|
209
211
|
*/
|
|
210
212
|
init(idMap = {}) {
|
|
@@ -223,33 +225,53 @@ var ImageEditor = class {
|
|
|
223
225
|
this._containerOriginalOverflow = null;
|
|
224
226
|
this._lastContainerViewportSize = null;
|
|
225
227
|
this._canvasElementOriginalStyle = null;
|
|
228
|
+
this._deprecatedElementKeyWarnings = /* @__PURE__ */ new Set();
|
|
226
229
|
const defaults = {
|
|
227
230
|
canvas: "fabricCanvas",
|
|
228
231
|
canvasContainer: null,
|
|
229
232
|
// Pass an ID here if you have a scrollable viewport container
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
233
|
+
imagePlaceholder: "imagePlaceholder",
|
|
234
|
+
imgPlaceholder: null,
|
|
235
|
+
scalePercentageInput: "scalePercentageInput",
|
|
236
|
+
scaleRate: null,
|
|
237
|
+
rotateLeftDegreesInput: "rotateLeftDegreesInput",
|
|
238
|
+
rotationLeftInput: null,
|
|
239
|
+
rotateRightDegreesInput: "rotateRightDegreesInput",
|
|
240
|
+
rotationRightInput: null,
|
|
241
|
+
rotateLeftButton: "rotateLeftButton",
|
|
242
|
+
rotateLeftBtn: null,
|
|
243
|
+
rotateRightButton: "rotateRightButton",
|
|
244
|
+
rotateRightBtn: null,
|
|
245
|
+
createMaskButton: "createMaskButton",
|
|
246
|
+
addMaskBtn: null,
|
|
247
|
+
removeSelectedMaskButton: "removeSelectedMaskButton",
|
|
248
|
+
removeMaskBtn: null,
|
|
249
|
+
removeAllMasksButton: "removeAllMasksButton",
|
|
250
|
+
removeAllMasksBtn: null,
|
|
251
|
+
mergeMasksButton: "mergeMasksButton",
|
|
252
|
+
mergeBtn: null,
|
|
253
|
+
downloadImageButton: "downloadImageButton",
|
|
254
|
+
downloadBtn: null,
|
|
241
255
|
maskList: "maskList",
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
256
|
+
zoomInButton: "zoomInButton",
|
|
257
|
+
zoomInBtn: null,
|
|
258
|
+
zoomOutButton: "zoomOutButton",
|
|
259
|
+
zoomOutBtn: null,
|
|
260
|
+
resetImageTransformButton: "resetImageTransformButton",
|
|
261
|
+
resetBtn: null,
|
|
262
|
+
undoButton: "undoButton",
|
|
263
|
+
undoBtn: null,
|
|
264
|
+
redoButton: "redoButton",
|
|
265
|
+
redoBtn: null,
|
|
247
266
|
imageInput: "imageInput",
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
267
|
+
enterCropModeButton: "enterCropModeButton",
|
|
268
|
+
cropBtn: null,
|
|
269
|
+
applyCropButton: "applyCropButton",
|
|
270
|
+
applyCropBtn: null,
|
|
271
|
+
cancelCropButton: "cancelCropButton",
|
|
272
|
+
cancelCropBtn: null
|
|
251
273
|
};
|
|
252
|
-
this.elements = {
|
|
274
|
+
this.elements = this._resolveElementIdMap(idMap || {}, defaults);
|
|
253
275
|
this._elementCache = {};
|
|
254
276
|
this._initCanvas();
|
|
255
277
|
this._bindEvents();
|
|
@@ -262,6 +284,63 @@ var ImageEditor = class {
|
|
|
262
284
|
this._updatePlaceholderStatus();
|
|
263
285
|
}
|
|
264
286
|
}
|
|
287
|
+
_resolveElementIdMap(idMap, defaults) {
|
|
288
|
+
const resolved = { ...defaults, ...idMap };
|
|
289
|
+
this._resolveElementAliases(resolved, idMap, defaults, "imagePlaceholder", ["imgPlaceholder"]);
|
|
290
|
+
this._resolveElementAliases(resolved, idMap, defaults, "scalePercentageInput", ["scaleRate"]);
|
|
291
|
+
this._resolveElementAliases(resolved, idMap, defaults, "rotateLeftDegreesInput", ["rotationLeftInput"]);
|
|
292
|
+
this._resolveElementAliases(resolved, idMap, defaults, "rotateRightDegreesInput", ["rotationRightInput"]);
|
|
293
|
+
this._resolveElementAlias(resolved, idMap, defaults, "rotateLeftButton", "rotateLeftBtn");
|
|
294
|
+
this._resolveElementAlias(resolved, idMap, defaults, "rotateRightButton", "rotateRightBtn");
|
|
295
|
+
this._resolveElementAlias(resolved, idMap, defaults, "createMaskButton", "addMaskBtn");
|
|
296
|
+
this._resolveElementAliases(resolved, idMap, defaults, "removeSelectedMaskButton", ["removeMaskBtn"]);
|
|
297
|
+
this._resolveElementAlias(resolved, idMap, defaults, "removeAllMasksButton", "removeAllMasksBtn");
|
|
298
|
+
this._resolveElementAlias(resolved, idMap, defaults, "mergeMasksButton", "mergeBtn");
|
|
299
|
+
this._resolveElementAliases(resolved, idMap, defaults, "downloadImageButton", ["downloadBtn"]);
|
|
300
|
+
this._resolveElementAlias(resolved, idMap, defaults, "zoomInButton", "zoomInBtn");
|
|
301
|
+
this._resolveElementAlias(resolved, idMap, defaults, "zoomOutButton", "zoomOutBtn");
|
|
302
|
+
this._resolveElementAlias(resolved, idMap, defaults, "resetImageTransformButton", "resetBtn");
|
|
303
|
+
this._resolveElementAlias(resolved, idMap, defaults, "undoButton", "undoBtn");
|
|
304
|
+
this._resolveElementAlias(resolved, idMap, defaults, "redoButton", "redoBtn");
|
|
305
|
+
this._resolveElementAliases(resolved, idMap, defaults, "enterCropModeButton", ["cropBtn"]);
|
|
306
|
+
this._resolveElementAlias(resolved, idMap, defaults, "applyCropButton", "applyCropBtn");
|
|
307
|
+
this._resolveElementAlias(resolved, idMap, defaults, "cancelCropButton", "cancelCropBtn");
|
|
308
|
+
return resolved;
|
|
309
|
+
}
|
|
310
|
+
_resolveElementAlias(resolved, idMap, defaults, canonicalKey, deprecatedKey) {
|
|
311
|
+
this._resolveElementAliases(resolved, idMap, defaults, canonicalKey, [deprecatedKey]);
|
|
312
|
+
}
|
|
313
|
+
_resolveElementAliases(resolved, idMap, defaults, canonicalKey, deprecatedKeys) {
|
|
314
|
+
const hasCanonicalKey = Object.prototype.hasOwnProperty.call(idMap, canonicalKey);
|
|
315
|
+
if (hasCanonicalKey) {
|
|
316
|
+
resolved[canonicalKey] = idMap[canonicalKey];
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
let deprecatedValue;
|
|
320
|
+
let hasDeprecatedValue = false;
|
|
321
|
+
for (const deprecatedKey of deprecatedKeys) {
|
|
322
|
+
if (Object.prototype.hasOwnProperty.call(idMap, deprecatedKey)) {
|
|
323
|
+
if (!hasDeprecatedValue) {
|
|
324
|
+
deprecatedValue = idMap[deprecatedKey];
|
|
325
|
+
hasDeprecatedValue = true;
|
|
326
|
+
}
|
|
327
|
+
this._warnDeprecatedElementIdKey(deprecatedKey, canonicalKey);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
if (hasDeprecatedValue) {
|
|
331
|
+
resolved[canonicalKey] = deprecatedValue;
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
resolved[canonicalKey] = defaults[canonicalKey];
|
|
335
|
+
}
|
|
336
|
+
_warnDeprecatedElementIdKey(deprecatedKey, canonicalKey) {
|
|
337
|
+
if (!this._deprecatedElementKeyWarnings) this._deprecatedElementKeyWarnings = /* @__PURE__ */ new Set();
|
|
338
|
+
if (this._deprecatedElementKeyWarnings.has(deprecatedKey)) return;
|
|
339
|
+
this._deprecatedElementKeyWarnings.add(deprecatedKey);
|
|
340
|
+
this._reportWarning(
|
|
341
|
+
`ElementIdMap.${deprecatedKey} is deprecated. Use ${canonicalKey} instead. This alias will be removed in v2.0.0.`
|
|
342
|
+
);
|
|
343
|
+
}
|
|
265
344
|
_reportError(message, error = null) {
|
|
266
345
|
const handler = this.options && this.options.onError;
|
|
267
346
|
if (typeof handler !== "function") return;
|
|
@@ -278,6 +357,11 @@ var ImageEditor = class {
|
|
|
278
357
|
} catch {
|
|
279
358
|
}
|
|
280
359
|
}
|
|
360
|
+
_notifyImageLoaded() {
|
|
361
|
+
const optionsCallback = this.options && this.options.onImageLoaded;
|
|
362
|
+
const callback = typeof optionsCallback === "function" ? optionsCallback : this.onImageLoaded;
|
|
363
|
+
if (typeof callback === "function") callback();
|
|
364
|
+
}
|
|
281
365
|
/**
|
|
282
366
|
* Initializes the Fabric canvas, viewport elements, and selection event handlers.
|
|
283
367
|
*
|
|
@@ -300,7 +384,7 @@ var ImageEditor = class {
|
|
|
300
384
|
} else {
|
|
301
385
|
this.containerElement = canvasElement.parentElement;
|
|
302
386
|
}
|
|
303
|
-
this.placeholderElement = this._getElement("
|
|
387
|
+
this.placeholderElement = this._getElement("imagePlaceholder") || null;
|
|
304
388
|
let initialWidth = this.options.canvasWidth;
|
|
305
389
|
let initialHeight = this.options.canvasHeight;
|
|
306
390
|
if (this.containerElement) {
|
|
@@ -450,20 +534,20 @@ var ImageEditor = class {
|
|
|
450
534
|
});
|
|
451
535
|
}
|
|
452
536
|
});
|
|
453
|
-
this._bindIfExists("
|
|
454
|
-
this._bindIfExists("
|
|
455
|
-
this._bindIfExists("
|
|
537
|
+
this._bindIfExists("zoomInButton", "click", () => this.scaleImage(this.currentScale + this.options.scaleStep).catch((error) => this._reportError("scaleImage failed", error)));
|
|
538
|
+
this._bindIfExists("zoomOutButton", "click", () => this.scaleImage(this.currentScale - this.options.scaleStep).catch((error) => this._reportError("scaleImage failed", error)));
|
|
539
|
+
this._bindIfExists("resetImageTransformButton", "click", () => {
|
|
456
540
|
this.resetImageTransform().catch((error) => this._reportError("resetImageTransform failed", error));
|
|
457
541
|
});
|
|
458
|
-
this._bindIfExists("
|
|
459
|
-
this._bindIfExists("
|
|
460
|
-
this._bindIfExists("
|
|
461
|
-
this._bindIfExists("
|
|
462
|
-
this._bindIfExists("
|
|
463
|
-
this._bindIfExists("
|
|
464
|
-
this._bindIfExists("
|
|
465
|
-
this._bindIfExists("
|
|
466
|
-
const rotationInputElement = this._getElement("
|
|
542
|
+
this._bindIfExists("createMaskButton", "click", () => this.createMask());
|
|
543
|
+
this._bindIfExists("removeSelectedMaskButton", "click", () => this.removeSelectedMask());
|
|
544
|
+
this._bindIfExists("removeAllMasksButton", "click", () => this.removeAllMasks());
|
|
545
|
+
this._bindIfExists("mergeMasksButton", "click", () => this.mergeMasks().catch((error) => this._reportError("merge error", error)));
|
|
546
|
+
this._bindIfExists("downloadImageButton", "click", () => this.downloadImage());
|
|
547
|
+
this._bindIfExists("undoButton", "click", () => this.undo().catch((error) => this._reportError("undo failed", error)));
|
|
548
|
+
this._bindIfExists("redoButton", "click", () => this.redo().catch((error) => this._reportError("redo failed", error)));
|
|
549
|
+
this._bindIfExists("rotateLeftButton", "click", () => {
|
|
550
|
+
const rotationInputElement = this._getElement("rotateLeftDegreesInput");
|
|
467
551
|
let step = this.options.rotationStep;
|
|
468
552
|
if (rotationInputElement) {
|
|
469
553
|
const parsedStep = parseFloat(rotationInputElement.value);
|
|
@@ -471,8 +555,8 @@ var ImageEditor = class {
|
|
|
471
555
|
}
|
|
472
556
|
this.rotateImage(this.currentRotation - step).catch((error) => this._reportError("rotateImage failed", error));
|
|
473
557
|
});
|
|
474
|
-
this._bindIfExists("
|
|
475
|
-
const rotationInputElement = this._getElement("
|
|
558
|
+
this._bindIfExists("rotateRightButton", "click", () => {
|
|
559
|
+
const rotationInputElement = this._getElement("rotateRightDegreesInput");
|
|
476
560
|
let step = this.options.rotationStep;
|
|
477
561
|
if (rotationInputElement) {
|
|
478
562
|
const parsedStep = parseFloat(rotationInputElement.value);
|
|
@@ -480,11 +564,11 @@ var ImageEditor = class {
|
|
|
480
564
|
}
|
|
481
565
|
this.rotateImage(this.currentRotation + step).catch((error) => this._reportError("rotateImage failed", error));
|
|
482
566
|
});
|
|
483
|
-
this._bindIfExists("
|
|
484
|
-
this._bindIfExists("
|
|
567
|
+
this._bindIfExists("enterCropModeButton", "click", () => this.enterCropMode());
|
|
568
|
+
this._bindIfExists("applyCropButton", "click", () => {
|
|
485
569
|
this.applyCrop().catch((error) => this._reportError("applyCrop failed", error));
|
|
486
570
|
});
|
|
487
|
-
this._bindIfExists("
|
|
571
|
+
this._bindIfExists("cancelCropButton", "click", () => this.cancelCrop());
|
|
488
572
|
this._bindIfExists("maskList", "click", (event) => this._handleMaskListClick(event));
|
|
489
573
|
}
|
|
490
574
|
/**
|
|
@@ -654,9 +738,7 @@ var ImageEditor = class {
|
|
|
654
738
|
this._updateUI();
|
|
655
739
|
this.canvas.renderAll();
|
|
656
740
|
this._lastSnapshot = this._captureCanvasStateOrThrow("loadImage");
|
|
657
|
-
|
|
658
|
-
this.onImageLoaded();
|
|
659
|
-
}
|
|
741
|
+
this._notifyImageLoaded();
|
|
660
742
|
} catch (error) {
|
|
661
743
|
await this._rollbackLoadImageTransaction(transaction);
|
|
662
744
|
throw error;
|
|
@@ -709,7 +791,7 @@ var ImageEditor = class {
|
|
|
709
791
|
try {
|
|
710
792
|
imageElement.src = "";
|
|
711
793
|
} catch (error) {
|
|
712
|
-
|
|
794
|
+
this._reportWarning("Image timeout cleanup failed", error);
|
|
713
795
|
}
|
|
714
796
|
}, safeTimeoutMs);
|
|
715
797
|
imageElement.onload = () => settle(() => resolve(imageElement));
|
|
@@ -777,6 +859,7 @@ var ImageEditor = class {
|
|
|
777
859
|
async _rollbackLoadImageTransaction(transaction) {
|
|
778
860
|
if (!transaction || !this.canvas || this._disposed) return;
|
|
779
861
|
let didRestoreCanvasState = false;
|
|
862
|
+
let didFailCanvasRestore = false;
|
|
780
863
|
try {
|
|
781
864
|
if (transaction.canvasState) {
|
|
782
865
|
await this.loadFromState(transaction.canvasState);
|
|
@@ -784,22 +867,27 @@ var ImageEditor = class {
|
|
|
784
867
|
}
|
|
785
868
|
} catch (error) {
|
|
786
869
|
this._lastMask = null;
|
|
870
|
+
didFailCanvasRestore = true;
|
|
787
871
|
this._reportError("loadImage rollback failed", error);
|
|
788
872
|
}
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
this.currentRotation = transaction.currentRotation;
|
|
792
|
-
this.maskCounter = transaction.maskCounter;
|
|
793
|
-
this.isImageLoadedToCanvas = transaction.isImageLoadedToCanvas;
|
|
794
|
-
this._lastSnapshot = transaction.lastSnapshot;
|
|
795
|
-
if (didRestoreCanvasState) {
|
|
796
|
-
this._restoreLastMaskReference(transaction.lastMask);
|
|
873
|
+
if (didFailCanvasRestore) {
|
|
874
|
+
this._reconcileEditorStateFromCanvas();
|
|
797
875
|
} else {
|
|
798
|
-
this.
|
|
876
|
+
this.baseImageScale = transaction.baseImageScale;
|
|
877
|
+
this.currentScale = transaction.currentScale;
|
|
878
|
+
this.currentRotation = transaction.currentRotation;
|
|
879
|
+
this.maskCounter = transaction.maskCounter;
|
|
880
|
+
this.isImageLoadedToCanvas = transaction.isImageLoadedToCanvas;
|
|
881
|
+
this._lastSnapshot = transaction.lastSnapshot;
|
|
882
|
+
if (didRestoreCanvasState) {
|
|
883
|
+
this._restoreLastMaskReference(transaction.lastMask);
|
|
884
|
+
} else {
|
|
885
|
+
this._lastMask = null;
|
|
886
|
+
}
|
|
887
|
+
this._lastMaskInitialLeft = transaction.lastMaskInitialLeft;
|
|
888
|
+
this._lastMaskInitialTop = transaction.lastMaskInitialTop;
|
|
889
|
+
this._lastMaskInitialWidth = transaction.lastMaskInitialWidth;
|
|
799
890
|
}
|
|
800
|
-
this._lastMaskInitialLeft = transaction.lastMaskInitialLeft;
|
|
801
|
-
this._lastMaskInitialTop = transaction.lastMaskInitialTop;
|
|
802
|
-
this._lastMaskInitialWidth = transaction.lastMaskInitialWidth;
|
|
803
891
|
this._restoreElementVisibility(this.placeholderElement, transaction.placeholderVisibility);
|
|
804
892
|
this._restoreElementVisibility(this._getCanvasVisibilityElement(), transaction.canvasVisibility);
|
|
805
893
|
if (this.containerElement) {
|
|
@@ -812,6 +900,46 @@ var ImageEditor = class {
|
|
|
812
900
|
this._updateUI();
|
|
813
901
|
if (this.canvas) this.canvas.renderAll();
|
|
814
902
|
}
|
|
903
|
+
_reconcileEditorStateFromCanvas() {
|
|
904
|
+
if (!this.canvas) {
|
|
905
|
+
this.originalImage = null;
|
|
906
|
+
this.baseImageScale = 1;
|
|
907
|
+
this.currentScale = 1;
|
|
908
|
+
this.currentRotation = 0;
|
|
909
|
+
this.maskCounter = 0;
|
|
910
|
+
this.isImageLoadedToCanvas = false;
|
|
911
|
+
this._lastSnapshot = null;
|
|
912
|
+
this._clearMaskPlacementMemory();
|
|
913
|
+
return;
|
|
914
|
+
}
|
|
915
|
+
const canvasObjects = this.canvas.getObjects();
|
|
916
|
+
this.originalImage = canvasObjects.find((object) => object.type === "image" && !object.maskId) || null;
|
|
917
|
+
if (this.originalImage) {
|
|
918
|
+
const imageScale = Number(this.originalImage.scaleX) || 1;
|
|
919
|
+
this.baseImageScale = imageScale;
|
|
920
|
+
this.currentScale = 1;
|
|
921
|
+
this.currentRotation = Number(this.originalImage.angle) || 0;
|
|
922
|
+
} else {
|
|
923
|
+
this.baseImageScale = 1;
|
|
924
|
+
this.currentScale = 1;
|
|
925
|
+
this.currentRotation = 0;
|
|
926
|
+
}
|
|
927
|
+
const masks = canvasObjects.filter((object) => object.maskId);
|
|
928
|
+
this.maskCounter = masks.reduce((max, mask) => Math.max(max, Number(mask.maskId) || 0), 0);
|
|
929
|
+
this._lastMask = masks[masks.length - 1] || null;
|
|
930
|
+
if (!this._lastMask) {
|
|
931
|
+
this._lastMaskInitialLeft = null;
|
|
932
|
+
this._lastMaskInitialTop = null;
|
|
933
|
+
this._lastMaskInitialWidth = null;
|
|
934
|
+
}
|
|
935
|
+
this.isImageLoadedToCanvas = !!this.originalImage;
|
|
936
|
+
try {
|
|
937
|
+
this._lastSnapshot = this._serializeCanvasState();
|
|
938
|
+
} catch (error) {
|
|
939
|
+
this._lastSnapshot = null;
|
|
940
|
+
this._reportWarning("loadImage rollback: failed to reconcile canvas snapshot", error);
|
|
941
|
+
}
|
|
942
|
+
}
|
|
815
943
|
_restoreLastMaskReference(previousLastMask) {
|
|
816
944
|
if (!this.canvas) {
|
|
817
945
|
this._lastMask = null;
|
|
@@ -882,6 +1010,7 @@ var ImageEditor = class {
|
|
|
882
1010
|
* @private
|
|
883
1011
|
*/
|
|
884
1012
|
_setCanvasSizeInt(width, height) {
|
|
1013
|
+
if (!this.canvas) return;
|
|
885
1014
|
const integerWidth = Math.max(1, Math.round(Number(width) || 1));
|
|
886
1015
|
const integerHeight = Math.max(1, Math.round(Number(height) || 1));
|
|
887
1016
|
this.canvas.setWidth(integerWidth);
|
|
@@ -1154,7 +1283,7 @@ var ImageEditor = class {
|
|
|
1154
1283
|
/**
|
|
1155
1284
|
* Captures editor-owned runtime state that Fabric does not include in canvas JSON.
|
|
1156
1285
|
*
|
|
1157
|
-
* @returns {{version:number, baseImageScale:number, currentScale:number, currentRotation:number, maskCounter:number}} Serializable editor metadata.
|
|
1286
|
+
* @returns {{version:number, baseImageScale:number, currentScale:number, currentRotation:number, maskCounter:number, canvasWidth:number, canvasHeight:number}} Serializable editor metadata.
|
|
1158
1287
|
* @private
|
|
1159
1288
|
*/
|
|
1160
1289
|
_serializeEditorMetadata() {
|
|
@@ -1162,12 +1291,16 @@ var ImageEditor = class {
|
|
|
1162
1291
|
const currentScale = Number(this.currentScale);
|
|
1163
1292
|
const currentRotation = Number(this.currentRotation);
|
|
1164
1293
|
const maskCounter = Number(this.maskCounter);
|
|
1294
|
+
const canvasWidth = this.canvas ? Number(this.canvas.getWidth()) : NaN;
|
|
1295
|
+
const canvasHeight = this.canvas ? Number(this.canvas.getHeight()) : NaN;
|
|
1165
1296
|
return {
|
|
1166
1297
|
version: 1,
|
|
1167
1298
|
baseImageScale: Number.isFinite(baseImageScale) && baseImageScale > 0 ? baseImageScale : 1,
|
|
1168
1299
|
currentScale: Number.isFinite(currentScale) && currentScale > 0 ? currentScale : 1,
|
|
1169
1300
|
currentRotation: Number.isFinite(currentRotation) ? currentRotation : 0,
|
|
1170
|
-
maskCounter: Number.isFinite(maskCounter) && maskCounter > 0 ? Math.floor(maskCounter) : 0
|
|
1301
|
+
maskCounter: Number.isFinite(maskCounter) && maskCounter > 0 ? Math.floor(maskCounter) : 0,
|
|
1302
|
+
canvasWidth: Number.isFinite(canvasWidth) && canvasWidth > 0 ? Math.round(canvasWidth) : 1,
|
|
1303
|
+
canvasHeight: Number.isFinite(canvasHeight) && canvasHeight > 0 ? Math.round(canvasHeight) : 1
|
|
1171
1304
|
};
|
|
1172
1305
|
}
|
|
1173
1306
|
_serializeCanvasState() {
|
|
@@ -1503,17 +1636,13 @@ var ImageEditor = class {
|
|
|
1503
1636
|
requiredWidth = Math.max(requiredWidth, Math.ceil(boundingRect.left + boundingRect.width + padding));
|
|
1504
1637
|
requiredHeight = Math.max(requiredHeight, Math.ceil(boundingRect.top + boundingRect.height + padding));
|
|
1505
1638
|
});
|
|
1506
|
-
const shouldUseScrollSafeViewport = this.options.fitImageToCanvas || this.options.coverImageToCanvas;
|
|
1507
1639
|
let minWidth = 0;
|
|
1508
1640
|
let minHeight = 0;
|
|
1509
|
-
if (
|
|
1641
|
+
if (this.containerElement) {
|
|
1510
1642
|
const viewport = this._getContainerViewportSize();
|
|
1511
1643
|
const safetyMargin = this._getScrollSafetyMargin();
|
|
1512
1644
|
minWidth = Math.max(1, viewport.width - safetyMargin);
|
|
1513
1645
|
minHeight = Math.max(1, viewport.height - safetyMargin);
|
|
1514
|
-
} else if (this.containerElement) {
|
|
1515
|
-
minWidth = Math.floor(this.containerElement.clientWidth || 0);
|
|
1516
|
-
minHeight = Math.floor(this.containerElement.clientHeight || 0);
|
|
1517
1646
|
}
|
|
1518
1647
|
const newWidth = Math.max(currentWidth, minWidth, requiredWidth);
|
|
1519
1648
|
const newHeight = Math.max(currentHeight, minHeight, requiredHeight);
|
|
@@ -1582,9 +1711,15 @@ var ImageEditor = class {
|
|
|
1582
1711
|
_assertEditorAvailable(operationName) {
|
|
1583
1712
|
if (this._disposed || !this.canvas) throw new Error(`${operationName} cannot run after the editor has been disposed`);
|
|
1584
1713
|
}
|
|
1714
|
+
_isCropModeAllowedOperation(operationName) {
|
|
1715
|
+
return operationName === "applyCrop" || operationName === "cancelCrop";
|
|
1716
|
+
}
|
|
1585
1717
|
_assertIdleForOperation(operationName, options = {}) {
|
|
1586
1718
|
this._assertEditorAvailable(operationName);
|
|
1587
1719
|
const isOwnInternalOperation = this._isOwnInternalOperation(options);
|
|
1720
|
+
if (this._cropMode && !this._isCropModeAllowedOperation(operationName) && !isOwnInternalOperation) {
|
|
1721
|
+
throw new Error(`${operationName} cannot run while crop mode is active`);
|
|
1722
|
+
}
|
|
1588
1723
|
if (this.isAnimating || this.animationQueue && this.animationQueue.isBusy()) {
|
|
1589
1724
|
throw new Error(`${operationName} cannot run while an animation is running`);
|
|
1590
1725
|
}
|
|
@@ -1597,10 +1732,14 @@ var ImageEditor = class {
|
|
|
1597
1732
|
}
|
|
1598
1733
|
_assertCanQueueAnimation(operationName, options = {}) {
|
|
1599
1734
|
this._assertEditorAvailable(operationName);
|
|
1600
|
-
|
|
1735
|
+
const isOwnInternalOperation = this._isOwnInternalOperation(options);
|
|
1736
|
+
if (this._cropMode && !this._isCropModeAllowedOperation(operationName) && !isOwnInternalOperation) {
|
|
1737
|
+
throw new Error(`${operationName} cannot run while crop mode is active`);
|
|
1738
|
+
}
|
|
1739
|
+
if (this._isLoading && !isOwnInternalOperation) {
|
|
1601
1740
|
throw new Error(`${operationName} cannot run while an image is loading`);
|
|
1602
1741
|
}
|
|
1603
|
-
if (this._activeOperationToken && !
|
|
1742
|
+
if (this._activeOperationToken && !isOwnInternalOperation) {
|
|
1604
1743
|
throw new Error(`${operationName} cannot run while ${this._activeOperationName || "another operation"} is running`);
|
|
1605
1744
|
}
|
|
1606
1745
|
}
|
|
@@ -1787,10 +1926,19 @@ var ImageEditor = class {
|
|
|
1787
1926
|
}
|
|
1788
1927
|
return this.animationQueue.add(async () => {
|
|
1789
1928
|
const before = this._lastSnapshot || this._captureCanvasStateOrThrow("resetImageTransform");
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1929
|
+
try {
|
|
1930
|
+
await this._scaleImageImpl(1, { saveHistory: false });
|
|
1931
|
+
await this._rotateImageImpl(0, { saveHistory: false });
|
|
1932
|
+
const after = this._captureCanvasStateOrThrow("resetImageTransform");
|
|
1933
|
+
this._pushStateTransition(before, after);
|
|
1934
|
+
} catch (error) {
|
|
1935
|
+
try {
|
|
1936
|
+
await this.loadFromState(before);
|
|
1937
|
+
} catch (restoreError) {
|
|
1938
|
+
this._reportError("resetImageTransform rollback failed", restoreError);
|
|
1939
|
+
}
|
|
1940
|
+
throw error;
|
|
1941
|
+
}
|
|
1794
1942
|
}).finally(() => {
|
|
1795
1943
|
if (!this._disposed && this.canvas) this._updateUI();
|
|
1796
1944
|
}).catch((error) => {
|
|
@@ -1829,10 +1977,13 @@ var ImageEditor = class {
|
|
|
1829
1977
|
try {
|
|
1830
1978
|
const state = typeof serializedState === "string" ? JSON.parse(serializedState) : serializedState;
|
|
1831
1979
|
const editorMetadata = state && state.imageEditorMetadata ? state.imageEditorMetadata : null;
|
|
1980
|
+
const restoredCanvasWidth = Number(editorMetadata && editorMetadata.canvasWidth);
|
|
1981
|
+
const restoredCanvasHeight = Number(editorMetadata && editorMetadata.canvasHeight);
|
|
1982
|
+
const hasRestoredCanvasSize = Number.isFinite(restoredCanvasWidth) && restoredCanvasWidth > 0 && Number.isFinite(restoredCanvasHeight) && restoredCanvasHeight > 0;
|
|
1832
1983
|
if (editorMetadata && Object.prototype.hasOwnProperty.call(editorMetadata, "version") && Number(editorMetadata.version) !== 1) {
|
|
1833
1984
|
this._reportWarning(`loadFromState: unsupported editor metadata version ${editorMetadata.version}`);
|
|
1834
1985
|
}
|
|
1835
|
-
|
|
1986
|
+
const finishLoad = async () => {
|
|
1836
1987
|
try {
|
|
1837
1988
|
if (this._disposed || !this.canvas) {
|
|
1838
1989
|
reject(new Error("Editor was disposed while loading state"));
|
|
@@ -1868,6 +2019,11 @@ var ImageEditor = class {
|
|
|
1868
2019
|
this.currentScale = 1;
|
|
1869
2020
|
this.currentRotation = 0;
|
|
1870
2021
|
}
|
|
2022
|
+
if (hasRestoredCanvasSize) {
|
|
2023
|
+
this._setCanvasSizeInt(restoredCanvasWidth, restoredCanvasHeight);
|
|
2024
|
+
} else if (this.originalImage && this._shouldResizeCanvasToContentBounds()) {
|
|
2025
|
+
this._updateCanvasSizeToImageBounds();
|
|
2026
|
+
}
|
|
1871
2027
|
const masks = canvasObjects.filter((object) => object.maskId);
|
|
1872
2028
|
masks.forEach((mask) => {
|
|
1873
2029
|
this._restoreMaskControls(mask);
|
|
@@ -1895,6 +2051,9 @@ var ImageEditor = class {
|
|
|
1895
2051
|
this._reportError("loadFromState() failed", callbackError);
|
|
1896
2052
|
reject(callbackError);
|
|
1897
2053
|
}
|
|
2054
|
+
};
|
|
2055
|
+
this.canvas.loadFromJSON(state, () => {
|
|
2056
|
+
void finishLoad();
|
|
1898
2057
|
});
|
|
1899
2058
|
} catch (error) {
|
|
1900
2059
|
this._reportError("loadFromState() failed", error);
|
|
@@ -2042,14 +2201,7 @@ var ImageEditor = class {
|
|
|
2042
2201
|
}
|
|
2043
2202
|
_rebindMaskEvents(mask) {
|
|
2044
2203
|
if (!mask) return;
|
|
2045
|
-
|
|
2046
|
-
try {
|
|
2047
|
-
mask.off("mouseover", mask.__imageEditorMaskHandlers.mouseover);
|
|
2048
|
-
mask.off("mouseout", mask.__imageEditorMaskHandlers.mouseout);
|
|
2049
|
-
} catch (error) {
|
|
2050
|
-
void error;
|
|
2051
|
-
}
|
|
2052
|
-
}
|
|
2204
|
+
this._cleanupMaskEvents(mask);
|
|
2053
2205
|
const metadata = {};
|
|
2054
2206
|
if (!Number.isFinite(Number(mask.originalAlpha))) {
|
|
2055
2207
|
metadata.originalAlpha = Number.isFinite(Number(mask.opacity)) ? Number(mask.opacity) : 0.5;
|
|
@@ -2076,6 +2228,22 @@ var ImageEditor = class {
|
|
|
2076
2228
|
mask.on("mouseout", mouseout);
|
|
2077
2229
|
mask.__imageEditorMaskHandlers = { mouseover, mouseout };
|
|
2078
2230
|
}
|
|
2231
|
+
_cleanupMaskEvents(mask) {
|
|
2232
|
+
if (!mask || !mask.__imageEditorMaskHandlers) return;
|
|
2233
|
+
try {
|
|
2234
|
+
if (typeof mask.off === "function") {
|
|
2235
|
+
mask.off("mouseover", mask.__imageEditorMaskHandlers.mouseover);
|
|
2236
|
+
mask.off("mouseout", mask.__imageEditorMaskHandlers.mouseout);
|
|
2237
|
+
}
|
|
2238
|
+
} catch (error) {
|
|
2239
|
+
this._reportWarning("Mask event cleanup failed", error);
|
|
2240
|
+
}
|
|
2241
|
+
try {
|
|
2242
|
+
delete mask.__imageEditorMaskHandlers;
|
|
2243
|
+
} catch (error) {
|
|
2244
|
+
this._reportWarning("Mask event metadata cleanup failed", error);
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
2079
2247
|
/**
|
|
2080
2248
|
* Creates a mask and adds it to the canvas.
|
|
2081
2249
|
*
|
|
@@ -2286,6 +2454,7 @@ var ImageEditor = class {
|
|
|
2286
2454
|
this.canvas.discardActiveObject();
|
|
2287
2455
|
selectedMasks.forEach((mask) => {
|
|
2288
2456
|
this._removeLabelForMask(mask);
|
|
2457
|
+
this._cleanupMaskEvents(mask);
|
|
2289
2458
|
this.canvas.remove(mask);
|
|
2290
2459
|
});
|
|
2291
2460
|
const masks = this.canvas.getObjects().filter((object) => object.maskId);
|
|
@@ -2310,7 +2479,10 @@ var ImageEditor = class {
|
|
|
2310
2479
|
const saveHistory = options.saveHistory !== false;
|
|
2311
2480
|
const masks = this.canvas.getObjects().filter((object) => object.maskId);
|
|
2312
2481
|
masks.forEach((mask) => this._removeLabelForMask(mask));
|
|
2313
|
-
masks.forEach((mask) =>
|
|
2482
|
+
masks.forEach((mask) => {
|
|
2483
|
+
this._cleanupMaskEvents(mask);
|
|
2484
|
+
this.canvas.remove(mask);
|
|
2485
|
+
});
|
|
2314
2486
|
this.canvas.discardActiveObject();
|
|
2315
2487
|
this._lastMask = null;
|
|
2316
2488
|
this._lastMaskInitialLeft = null;
|
|
@@ -2379,7 +2551,7 @@ var ImageEditor = class {
|
|
|
2379
2551
|
if (backup.labelInCanvas) this.canvas.bringToFront(backup.label);
|
|
2380
2552
|
this._syncMaskLabel(backup.mask);
|
|
2381
2553
|
} catch (error) {
|
|
2382
|
-
|
|
2554
|
+
this._reportWarning("restoreMaskLabelBackups: failed to restore mask label", error);
|
|
2383
2555
|
}
|
|
2384
2556
|
});
|
|
2385
2557
|
}
|
|
@@ -2510,7 +2682,6 @@ var ImageEditor = class {
|
|
|
2510
2682
|
try {
|
|
2511
2683
|
if (canvasObjectSet.has(label)) {
|
|
2512
2684
|
this.canvas.remove(label);
|
|
2513
|
-
canvasObjectSet.delete(label);
|
|
2514
2685
|
}
|
|
2515
2686
|
} catch (error) {
|
|
2516
2687
|
void error;
|
|
@@ -2796,7 +2967,6 @@ var ImageEditor = class {
|
|
|
2796
2967
|
const maskStyleBackups = this._captureMaskExportBackups(masks);
|
|
2797
2968
|
const labelBackups = this._captureMaskLabelBackups(masks);
|
|
2798
2969
|
const activeObjectBackup = this._captureActiveObjectBackup();
|
|
2799
|
-
let finalBase64;
|
|
2800
2970
|
try {
|
|
2801
2971
|
masks.forEach((mask) => this._removeLabelForMask(mask));
|
|
2802
2972
|
this.canvas.discardActiveObject();
|
|
@@ -2809,7 +2979,7 @@ var ImageEditor = class {
|
|
|
2809
2979
|
this.originalImage.setCoords();
|
|
2810
2980
|
const imageBounds = this.originalImage.getBoundingRect(true, true);
|
|
2811
2981
|
const exportRegion = this._getClampedCanvasRegion(imageBounds);
|
|
2812
|
-
|
|
2982
|
+
return await this._exportCanvasRegionToDataURL({
|
|
2813
2983
|
...exportRegion,
|
|
2814
2984
|
multiplier,
|
|
2815
2985
|
quality,
|
|
@@ -2822,7 +2992,6 @@ var ImageEditor = class {
|
|
|
2822
2992
|
this._restoreActiveObjectBackup(activeObjectBackup);
|
|
2823
2993
|
this.canvas.renderAll();
|
|
2824
2994
|
}
|
|
2825
|
-
return finalBase64;
|
|
2826
2995
|
}
|
|
2827
2996
|
/**
|
|
2828
2997
|
* Backward-compatible alias for {@link ImageEditor#exportImageBase64}.
|
|
@@ -2949,22 +3118,21 @@ var ImageEditor = class {
|
|
|
2949
3118
|
this._cropPrevEvented = null;
|
|
2950
3119
|
}
|
|
2951
3120
|
_removeCropRect() {
|
|
2952
|
-
if (
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
targetHandlers.handlers.forEach((handlerRecord) => {
|
|
3121
|
+
if (this._cropHandlers && this._cropHandlers.length) {
|
|
3122
|
+
this._cropHandlers.forEach((targetHandlers) => {
|
|
3123
|
+
(targetHandlers.handlers || []).forEach((handlerRecord) => {
|
|
3124
|
+
try {
|
|
2957
3125
|
if (targetHandlers.target && typeof targetHandlers.target.off === "function") {
|
|
2958
3126
|
targetHandlers.target.off(handlerRecord.eventName, handlerRecord.handler);
|
|
2959
3127
|
}
|
|
2960
|
-
})
|
|
3128
|
+
} catch (error) {
|
|
3129
|
+
this._reportWarning("Crop handler cleanup failed", error);
|
|
3130
|
+
}
|
|
2961
3131
|
});
|
|
2962
|
-
}
|
|
2963
|
-
} catch (error) {
|
|
2964
|
-
void error;
|
|
3132
|
+
});
|
|
2965
3133
|
}
|
|
2966
3134
|
try {
|
|
2967
|
-
if (this.canvas) this.canvas.remove(this._cropRect);
|
|
3135
|
+
if (this.canvas && this._cropRect) this.canvas.remove(this._cropRect);
|
|
2968
3136
|
} catch (error) {
|
|
2969
3137
|
void error;
|
|
2970
3138
|
}
|
|
@@ -3134,9 +3302,13 @@ var ImageEditor = class {
|
|
|
3134
3302
|
try {
|
|
3135
3303
|
beforeJson = this._serializeCanvasState();
|
|
3136
3304
|
} catch (error) {
|
|
3137
|
-
this.
|
|
3305
|
+
this._reportError("applyCrop: failed to capture rollback state", error);
|
|
3138
3306
|
beforeJson = null;
|
|
3139
3307
|
}
|
|
3308
|
+
if (!beforeJson) {
|
|
3309
|
+
this.cancelCrop();
|
|
3310
|
+
return;
|
|
3311
|
+
}
|
|
3140
3312
|
const preservedMasks = [];
|
|
3141
3313
|
try {
|
|
3142
3314
|
const masks = this.canvas.getObjects().filter((object) => object.maskId);
|
|
@@ -3146,6 +3318,7 @@ var ImageEditor = class {
|
|
|
3146
3318
|
const maskBounds = mask.getBoundingRect(true, true);
|
|
3147
3319
|
const intersectsCrop = maskBounds.left < cropRegion.sourceX + cropRegion.sourceWidth && maskBounds.left + maskBounds.width > cropRegion.sourceX && maskBounds.top < cropRegion.sourceY + cropRegion.sourceHeight && maskBounds.top + maskBounds.height > cropRegion.sourceY;
|
|
3148
3320
|
this._removeLabelForMask(mask);
|
|
3321
|
+
this._cleanupMaskEvents(mask);
|
|
3149
3322
|
this.canvas.remove(mask);
|
|
3150
3323
|
if (shouldPreserveMasks && intersectsCrop) {
|
|
3151
3324
|
this._translateObjectByCanvasOffset(mask, -cropRegion.sourceX, -cropRegion.sourceY);
|
|
@@ -3216,7 +3389,7 @@ var ImageEditor = class {
|
|
|
3216
3389
|
* @private
|
|
3217
3390
|
*/
|
|
3218
3391
|
_updateInputs() {
|
|
3219
|
-
const scaleInputElement = this._getElement("
|
|
3392
|
+
const scaleInputElement = this._getElement("scalePercentageInput");
|
|
3220
3393
|
if (scaleInputElement) scaleInputElement.value = Math.round(this.currentScale * 100);
|
|
3221
3394
|
}
|
|
3222
3395
|
/**
|
|
@@ -3240,7 +3413,7 @@ var ImageEditor = class {
|
|
|
3240
3413
|
for (const key of Object.keys(this.elements || {})) {
|
|
3241
3414
|
const element = this._getElement(key);
|
|
3242
3415
|
if (!element) continue;
|
|
3243
|
-
if (key === "applyCropBtn" || key === "cancelCropBtn") {
|
|
3416
|
+
if (key === "applyCropButton" || key === "cancelCropButton" || key === "applyCropBtn" || key === "cancelCropBtn") {
|
|
3244
3417
|
this._setDisabled(key, false);
|
|
3245
3418
|
} else {
|
|
3246
3419
|
this._setDisabled(key, true);
|
|
@@ -3248,24 +3421,24 @@ var ImageEditor = class {
|
|
|
3248
3421
|
}
|
|
3249
3422
|
return;
|
|
3250
3423
|
}
|
|
3251
|
-
this._setDisabled("
|
|
3252
|
-
this._setDisabled("
|
|
3253
|
-
this._setDisabled("
|
|
3254
|
-
this._setDisabled("
|
|
3255
|
-
this._setDisabled("
|
|
3256
|
-
this._setDisabled("
|
|
3257
|
-
this._setDisabled("
|
|
3258
|
-
this._setDisabled("
|
|
3259
|
-
this._setDisabled("
|
|
3260
|
-
this._setDisabled("
|
|
3261
|
-
this._setDisabled("
|
|
3262
|
-
this._setDisabled("
|
|
3263
|
-
this._setDisabled("
|
|
3264
|
-
this._setDisabled("
|
|
3265
|
-
this._setDisabled("
|
|
3266
|
-
this._setDisabled("
|
|
3267
|
-
this._setDisabled("
|
|
3268
|
-
this._setDisabled("
|
|
3424
|
+
this._setDisabled("zoomInButton", !hasImage || isBusy || this.currentScale >= this.options.maxScale);
|
|
3425
|
+
this._setDisabled("zoomOutButton", !hasImage || isBusy || this.currentScale <= this.options.minScale);
|
|
3426
|
+
this._setDisabled("rotateLeftButton", !hasImage || isBusy);
|
|
3427
|
+
this._setDisabled("rotateRightButton", !hasImage || isBusy);
|
|
3428
|
+
this._setDisabled("createMaskButton", !hasImage || isBusy);
|
|
3429
|
+
this._setDisabled("removeSelectedMaskButton", !hasSelectedMask || isBusy);
|
|
3430
|
+
this._setDisabled("removeAllMasksButton", !hasMasks || isBusy);
|
|
3431
|
+
this._setDisabled("mergeMasksButton", !hasImage || !hasMasks || isBusy);
|
|
3432
|
+
this._setDisabled("downloadImageButton", !hasImage || isBusy);
|
|
3433
|
+
this._setDisabled("resetImageTransformButton", !hasImage || isDefaultTransform || isBusy);
|
|
3434
|
+
this._setDisabled("undoButton", !hasImage || isBusy || !canUndo);
|
|
3435
|
+
this._setDisabled("redoButton", !hasImage || isBusy || !canRedo);
|
|
3436
|
+
this._setDisabled("enterCropModeButton", !hasImage || isBusy);
|
|
3437
|
+
this._setDisabled("applyCropButton", true);
|
|
3438
|
+
this._setDisabled("cancelCropButton", true);
|
|
3439
|
+
this._setDisabled("scalePercentageInput", !hasImage || isBusy);
|
|
3440
|
+
this._setDisabled("rotateLeftDegreesInput", !hasImage || isBusy);
|
|
3441
|
+
this._setDisabled("rotateRightDegreesInput", !hasImage || isBusy);
|
|
3269
3442
|
this._setDisabled("maskList", !hasImage || isBusy);
|
|
3270
3443
|
this._setDisabled("imageInput", isBusy);
|
|
3271
3444
|
this._setDisabled("uploadArea", isBusy);
|
|
@@ -3273,7 +3446,7 @@ var ImageEditor = class {
|
|
|
3273
3446
|
/**
|
|
3274
3447
|
* Enables or disables a specific UI element (typically a button) by its key.
|
|
3275
3448
|
*
|
|
3276
|
-
* @param {string} key - Key of the element in this.elements (e.g. '
|
|
3449
|
+
* @param {string} key - Key of the element in this.elements (e.g. 'zoomInButton').
|
|
3277
3450
|
* @param {boolean} disabled - If true, disables the element; otherwise enables.
|
|
3278
3451
|
* @private
|
|
3279
3452
|
*/
|
|
@@ -3397,14 +3570,7 @@ var ImageEditor = class {
|
|
|
3397
3570
|
} catch (error) {
|
|
3398
3571
|
void error;
|
|
3399
3572
|
}
|
|
3400
|
-
if (this._cropRect)
|
|
3401
|
-
try {
|
|
3402
|
-
this.canvas.remove(this._cropRect);
|
|
3403
|
-
} catch (error) {
|
|
3404
|
-
void error;
|
|
3405
|
-
}
|
|
3406
|
-
this._cropRect = null;
|
|
3407
|
-
}
|
|
3573
|
+
if (this._cropRect) this._removeCropRect();
|
|
3408
3574
|
if (this.containerElement && this._containerOriginalOverflow) {
|
|
3409
3575
|
try {
|
|
3410
3576
|
this._restoreContainerOverflowState();
|
|
@@ -3427,11 +3593,19 @@ var ImageEditor = class {
|
|
|
3427
3593
|
this.canvasElement.style.display = this._canvasElementOriginalStyle.display;
|
|
3428
3594
|
this.canvasElement.style.width = this._canvasElementOriginalStyle.width;
|
|
3429
3595
|
this.canvasElement.style.height = this._canvasElementOriginalStyle.height;
|
|
3596
|
+
this.canvasElement.style.maxWidth = this._canvasElementOriginalStyle.maxWidth;
|
|
3430
3597
|
} catch (error) {
|
|
3431
3598
|
void error;
|
|
3432
3599
|
}
|
|
3433
3600
|
}
|
|
3434
3601
|
if (this.canvas) {
|
|
3602
|
+
try {
|
|
3603
|
+
this.canvas.getObjects().forEach((object) => {
|
|
3604
|
+
if (object && object.maskId) this._cleanupMaskEvents(object);
|
|
3605
|
+
});
|
|
3606
|
+
} catch (error) {
|
|
3607
|
+
void error;
|
|
3608
|
+
}
|
|
3435
3609
|
try {
|
|
3436
3610
|
this.canvas.dispose();
|
|
3437
3611
|
} catch (error) {
|
|
@@ -3528,7 +3702,7 @@ var AnimationQueue = class {
|
|
|
3528
3702
|
task.reject(error);
|
|
3529
3703
|
}
|
|
3530
3704
|
} finally {
|
|
3531
|
-
if (
|
|
3705
|
+
if (this.currentTask === task) this.currentTask = null;
|
|
3532
3706
|
}
|
|
3533
3707
|
}
|
|
3534
3708
|
} finally {
|