@bensitu/image-editor 1.3.0 → 1.3.1

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.
@@ -5,19 +5,16 @@ import fabricModule from "fabric";
5
5
  /**
6
6
  * @file image-editor.js
7
7
  * @module image-editor
8
- * @version 1.3.0
8
+ * @version 1.3.1
9
9
  * @author Ben Situ
10
10
  * @license MIT
11
11
  * @description Lightweight canvas-based image editor with masking/transform/export support.
12
12
  */
13
13
  var fabric = null;
14
14
  function getGlobalScope() {
15
- if (typeof globalThis !== "undefined")
16
- return globalThis;
17
- if (typeof self !== "undefined")
18
- return self;
19
- if (typeof window !== "undefined")
20
- return window;
15
+ if (typeof globalThis !== "undefined") return globalThis;
16
+ if (typeof self !== "undefined") return self;
17
+ if (typeof window !== "undefined") return window;
21
18
  return null;
22
19
  }
23
20
  function getGlobalFabric() {
@@ -29,8 +26,7 @@ function setFabric(fabricInstance2) {
29
26
  return fabric;
30
27
  }
31
28
  function ensureFabric() {
32
- if (!fabric)
33
- setFabric();
29
+ if (!fabric) setFabric();
34
30
  return fabric;
35
31
  }
36
32
  var ImageEditor = class {
@@ -198,8 +194,7 @@ var ImageEditor = class {
198
194
  * });
199
195
  */
200
196
  init(idMap = {}) {
201
- if (!this._fabricLoaded)
202
- return;
197
+ if (!this._fabricLoaded) return;
203
198
  const defaults = {
204
199
  canvas: "fabricCanvas",
205
200
  canvasContainer: null,
@@ -240,8 +235,7 @@ var ImageEditor = class {
240
235
  }
241
236
  _reportError(message, error = null) {
242
237
  const handler = this.options && this.options.onError;
243
- if (typeof handler !== "function")
244
- return;
238
+ if (typeof handler !== "function") return;
245
239
  try {
246
240
  handler(error, message);
247
241
  } catch {
@@ -249,8 +243,7 @@ var ImageEditor = class {
249
243
  }
250
244
  _reportWarning(message, error = null) {
251
245
  const handler = this.options && this.options.onWarning;
252
- if (typeof handler !== "function")
253
- return;
246
+ if (typeof handler !== "function") return;
254
247
  try {
255
248
  handler(error, message);
256
249
  } catch {
@@ -264,8 +257,7 @@ var ImageEditor = class {
264
257
  */
265
258
  _initCanvas() {
266
259
  const canvasElement = document.getElementById(this.elements.canvas);
267
- if (!canvasElement)
268
- throw new Error("Canvas is not found: " + this.elements.canvas);
260
+ if (!canvasElement) throw new Error("Canvas is not found: " + this.elements.canvas);
269
261
  this.canvasElement = canvasElement;
270
262
  if (this.elements.canvasContainer) {
271
263
  const containerElement = document.getElementById(this.elements.canvasContainer);
@@ -295,16 +287,13 @@ var ImageEditor = class {
295
287
  this.canvas.on("selection:updated", (event) => this._handleSelectionChanged(event.selected));
296
288
  this.canvas.on("selection:cleared", () => this._handleSelectionChanged([]));
297
289
  this.canvas.on("object:moving", (event) => {
298
- if (event.target && event.target.maskId)
299
- this._syncMaskLabel(event.target);
290
+ if (event.target && event.target.maskId) this._syncMaskLabel(event.target);
300
291
  });
301
292
  this.canvas.on("object:scaling", (event) => {
302
- if (event.target && event.target.maskId)
303
- this._syncMaskLabel(event.target);
293
+ if (event.target && event.target.maskId) this._syncMaskLabel(event.target);
304
294
  });
305
295
  this.canvas.on("object:rotating", (event) => {
306
- if (event.target && event.target.maskId)
307
- this._syncMaskLabel(event.target);
296
+ if (event.target && event.target.maskId) this._syncMaskLabel(event.target);
308
297
  });
309
298
  this.canvas.on("object:modified", (event) => this._handleObjectModified(event.target));
310
299
  this.canvasElement.style.display = "block";
@@ -318,11 +307,9 @@ var ImageEditor = class {
318
307
  */
319
308
  _handleObjectModified(target) {
320
309
  const masks = this._getModifiedMasks(target);
321
- if (!masks.length)
322
- return;
310
+ if (!masks.length) return;
323
311
  masks.forEach((mask) => {
324
- if (typeof mask.setCoords === "function")
325
- mask.setCoords();
312
+ if (typeof mask.setCoords === "function") mask.setCoords();
326
313
  this._syncMaskLabel(mask);
327
314
  });
328
315
  this._expandCanvasToFitObjects(masks);
@@ -336,10 +323,8 @@ var ImageEditor = class {
336
323
  * @private
337
324
  */
338
325
  _getModifiedMasks(target) {
339
- if (!target)
340
- return [];
341
- if (target.maskId)
342
- return [target];
326
+ if (!target) return [];
327
+ if (target.maskId) return [target];
343
328
  const objects = typeof target.getObjects === "function" ? target.getObjects() : [];
344
329
  return Array.isArray(objects) ? objects.filter((object) => object && object.maskId) : [];
345
330
  }
@@ -352,8 +337,7 @@ var ImageEditor = class {
352
337
  * @private
353
338
  */
354
339
  _syncContainerOverflow(options = {}) {
355
- if (!this.containerElement || !this.containerElement.style)
356
- return;
340
+ if (!this.containerElement || !this.containerElement.style) return;
357
341
  if (this._containerOriginalOverflow === void 0) {
358
342
  this._containerOriginalOverflow = this.containerElement.style.overflow || "";
359
343
  }
@@ -381,14 +365,12 @@ var ImageEditor = class {
381
365
  _bindEvents() {
382
366
  this._bindIfExists("uploadArea", "click", () => {
383
367
  const uploadAreaElement = document.getElementById(this.elements.uploadArea);
384
- if (this._isElementDisabled(uploadAreaElement))
385
- return;
368
+ if (this._isElementDisabled(uploadAreaElement)) return;
386
369
  document.getElementById(this.elements.imageInput)?.click();
387
370
  });
388
371
  this._bindIfExists("imageInput", "change", (event) => {
389
372
  const file = event.target.files && event.target.files[0];
390
- if (file)
391
- this._loadImageFile(file);
373
+ if (file) this._loadImageFile(file);
392
374
  });
393
375
  this._bindIfExists("zoomInBtn", "click", () => this.scaleImage(this.currentScale + this.options.scaleStep));
394
376
  this._bindIfExists("zoomOutBtn", "click", () => this.scaleImage(this.currentScale - this.options.scaleStep));
@@ -407,8 +389,7 @@ var ImageEditor = class {
407
389
  let step = this.options.rotationStep;
408
390
  if (rotationInputElement) {
409
391
  const parsedStep = parseFloat(rotationInputElement.value);
410
- if (!isNaN(parsedStep))
411
- step = parsedStep;
392
+ if (!isNaN(parsedStep)) step = parsedStep;
412
393
  }
413
394
  this.rotateImage(this.currentRotation - step);
414
395
  });
@@ -417,8 +398,7 @@ var ImageEditor = class {
417
398
  let step = this.options.rotationStep;
418
399
  if (rotationInputElement) {
419
400
  const parsedStep = parseFloat(rotationInputElement.value);
420
- if (!isNaN(parsedStep))
421
- step = parsedStep;
401
+ if (!isNaN(parsedStep)) step = parsedStep;
422
402
  }
423
403
  this.rotateImage(this.currentRotation + step);
424
404
  });
@@ -441,8 +421,7 @@ var ImageEditor = class {
441
421
  if (element) {
442
422
  element.addEventListener(eventName, handler);
443
423
  this._handlersByElementKey = this._handlersByElementKey || {};
444
- if (!this._handlersByElementKey[key])
445
- this._handlersByElementKey[key] = [];
424
+ if (!this._handlersByElementKey[key]) this._handlersByElementKey[key] = [];
446
425
  this._handlersByElementKey[key].push({ eventName, handler });
447
426
  }
448
427
  }
@@ -453,8 +432,7 @@ var ImageEditor = class {
453
432
  * @private
454
433
  */
455
434
  _loadImageFile(file) {
456
- if (!file || !file.type.startsWith("image/"))
457
- return;
435
+ if (!file || !file.type.startsWith("image/")) return;
458
436
  const reader = new FileReader();
459
437
  reader.onload = (event) => this.loadImage(event.target.result);
460
438
  reader.onerror = (event) => {
@@ -474,8 +452,7 @@ var ImageEditor = class {
474
452
  ["coverImageToCanvas", this.options.coverImageToCanvas],
475
453
  ["expandCanvasToImage", this.options.expandCanvasToImage]
476
454
  ].filter(([, isEnabled]) => !!isEnabled).map(([name]) => name);
477
- if (activeModes.length <= 1)
478
- return;
455
+ if (activeModes.length <= 1) return;
479
456
  this._reportWarning(
480
457
  `Only one image layout mode should be enabled. Active modes: ${activeModes.join(", ")}.`
481
458
  );
@@ -490,12 +467,9 @@ var ImageEditor = class {
490
467
  * @public
491
468
  */
492
469
  async loadImage(imageBase64, options = {}) {
493
- if (!this._fabricLoaded)
494
- return;
495
- if (!this.canvas)
496
- return;
497
- if (!imageBase64 || typeof imageBase64 !== "string" || !imageBase64.startsWith("data:image/"))
498
- return;
470
+ if (!this._fabricLoaded) return;
471
+ if (!this.canvas) return;
472
+ if (!imageBase64 || typeof imageBase64 !== "string" || !imageBase64.startsWith("data:image/")) return;
499
473
  this._warnOnImageLayoutOptionConflict();
500
474
  this._setPlaceholderVisible(false);
501
475
  this._syncContainerOverflow({ preserveScroll: options.preserveScroll === true });
@@ -516,8 +490,7 @@ var ImageEditor = class {
516
490
  return new Promise((resolve, reject) => {
517
491
  fabric.Image.fromURL(loadSource, (fabricImage) => {
518
492
  try {
519
- if (!fabricImage)
520
- throw new Error("Image could not be loaded");
493
+ if (!fabricImage) throw new Error("Image could not be loaded");
521
494
  this.canvas.discardActiveObject();
522
495
  this._hideAllMaskLabels();
523
496
  this.canvas.clear();
@@ -611,8 +584,7 @@ var ImageEditor = class {
611
584
  const safeTimeoutMs = Number.isFinite(Number(timeoutMs)) && Number(timeoutMs) > 0 ? Number(timeoutMs) : 3e4;
612
585
  let timerId;
613
586
  const settle = (callback) => {
614
- if (isSettled)
615
- return;
587
+ if (isSettled) return;
616
588
  isSettled = true;
617
589
  clearTimeout(timerId);
618
590
  imageElement.onload = null;
@@ -624,6 +596,7 @@ var ImageEditor = class {
624
596
  try {
625
597
  imageElement.src = "";
626
598
  } catch (error) {
599
+ void error;
627
600
  }
628
601
  }, safeTimeoutMs);
629
602
  imageElement.onload = () => settle(() => resolve(imageElement));
@@ -646,8 +619,7 @@ var ImageEditor = class {
646
619
  offscreenCanvas.width = targetWidth;
647
620
  offscreenCanvas.height = targetHeight;
648
621
  const context = offscreenCanvas.getContext("2d");
649
- if (!context)
650
- throw new Error("2D canvas context is unavailable");
622
+ if (!context) throw new Error("2D canvas context is unavailable");
651
623
  context.drawImage(imageElement, 0, 0, imageElement.naturalWidth, imageElement.naturalHeight, 0, 0, targetWidth, targetHeight);
652
624
  return offscreenCanvas.toDataURL("image/jpeg", quality);
653
625
  }
@@ -664,8 +636,7 @@ var ImageEditor = class {
664
636
  const integerHeight = Math.max(1, Math.round(Number(height) || 1));
665
637
  this.canvas.setWidth(integerWidth);
666
638
  this.canvas.setHeight(integerHeight);
667
- if (typeof this.canvas.calcOffset === "function")
668
- this.canvas.calcOffset();
639
+ if (typeof this.canvas.calcOffset === "function") this.canvas.calcOffset();
669
640
  if (this.canvasElement) {
670
641
  this.canvasElement.style.width = integerWidth + "px";
671
642
  this.canvasElement.style.height = integerHeight + "px";
@@ -675,8 +646,7 @@ var ImageEditor = class {
675
646
  _ceilCanvasDimension(value) {
676
647
  const numericValue = Number(value) || 0;
677
648
  const roundedValue = Math.round(numericValue);
678
- if (Math.abs(numericValue - roundedValue) < 0.01)
679
- return roundedValue;
649
+ if (Math.abs(numericValue - roundedValue) < 0.01) return roundedValue;
680
650
  return Math.ceil(numericValue);
681
651
  }
682
652
  _getContainerViewportSize() {
@@ -686,19 +656,31 @@ var ImageEditor = class {
686
656
  height: Math.max(1, Math.floor(this.options.canvasHeight || 1))
687
657
  };
688
658
  }
659
+ let width = Math.max(1, Math.floor(this.containerElement.clientWidth || this.options.canvasWidth || 1));
660
+ let height = Math.max(1, Math.floor(this.containerElement.clientHeight || this.options.canvasHeight || 1));
689
661
  if (this._hasFixedContainerScrollbars()) {
690
- return {
691
- width: Math.max(1, Math.floor(this.containerElement.clientWidth || this.options.canvasWidth || 1)),
692
- height: Math.max(1, Math.floor(this.containerElement.clientHeight || this.options.canvasHeight || 1))
693
- };
662
+ return { width, height };
663
+ }
664
+ const overflow = this._getContainerOverflowValues();
665
+ const canScrollX = overflow.x.some((value) => value === "auto" || value === "scroll");
666
+ const canScrollY = overflow.y.some((value) => value === "auto" || value === "scroll");
667
+ const hasHorizontalScrollbar = canScrollX && this.containerElement.scrollWidth > this.containerElement.clientWidth;
668
+ const hasVerticalScrollbar = canScrollY && this.containerElement.scrollHeight > this.containerElement.clientHeight;
669
+ if (hasHorizontalScrollbar || hasVerticalScrollbar) {
670
+ const scrollbar = this._getScrollbarSize();
671
+ if (hasVerticalScrollbar) width += scrollbar.width;
672
+ if (hasHorizontalScrollbar) height += scrollbar.height;
694
673
  }
695
- const width = Math.max(1, Math.floor(this.containerElement.clientWidth || this.options.canvasWidth || 1));
696
- const height = Math.max(1, Math.floor(this.containerElement.clientHeight || this.options.canvasHeight || 1));
697
674
  return { width, height };
698
675
  }
699
- _hasFixedContainerScrollbars() {
700
- if (!this.containerElement)
701
- return false;
676
+ /**
677
+ * Reads inline and computed overflow values for both scroll axes.
678
+ *
679
+ * @returns {{x:string[], y:string[]}} Overflow values grouped by axis.
680
+ * @private
681
+ */
682
+ _getContainerOverflowValues() {
683
+ if (!this.containerElement) return { x: [], y: [] };
702
684
  const inlineOverflow = this.containerElement.style.overflow;
703
685
  const inlineOverflowX = this.containerElement.style.overflowX;
704
686
  const inlineOverflowY = this.containerElement.style.overflowY;
@@ -711,7 +693,15 @@ var ImageEditor = class {
711
693
  computedOverflowX = style.overflowX;
712
694
  computedOverflowY = style.overflowY;
713
695
  }
714
- return [inlineOverflow, inlineOverflowX, inlineOverflowY, computedOverflow, computedOverflowX, computedOverflowY].some((value) => value === "scroll");
696
+ return {
697
+ x: [inlineOverflow, inlineOverflowX, computedOverflow, computedOverflowX],
698
+ y: [inlineOverflow, inlineOverflowY, computedOverflow, computedOverflowY]
699
+ };
700
+ }
701
+ _hasFixedContainerScrollbars() {
702
+ if (!this.containerElement) return false;
703
+ const overflow = this._getContainerOverflowValues();
704
+ return [...overflow.x, ...overflow.y].some((value) => value === "scroll");
715
705
  }
716
706
  _getScrollbarSize() {
717
707
  if (this._scrollbarSizeCache) {
@@ -754,15 +744,14 @@ var ImageEditor = class {
754
744
  const scrollbar = this._getScrollbarSize();
755
745
  let hasVertical = false;
756
746
  let hasHorizontal = false;
757
- let effectiveWidth = viewport.width;
758
- let effectiveHeight = viewport.height;
747
+ let effectiveWidth;
748
+ let effectiveHeight;
759
749
  for (let i = 0; i < 4; i += 1) {
760
750
  effectiveWidth = Math.max(1, viewport.width - (hasVertical ? scrollbar.width : 0));
761
751
  effectiveHeight = Math.max(1, viewport.height - (hasHorizontal ? scrollbar.height : 0));
762
752
  const nextHasVertical = contentHeight > effectiveHeight + 0.5;
763
753
  const nextHasHorizontal = contentWidth > effectiveWidth + 0.5;
764
- if (nextHasVertical === hasVertical && nextHasHorizontal === hasHorizontal)
765
- break;
754
+ if (nextHasVertical === hasVertical && nextHasHorizontal === hasHorizontal) break;
766
755
  hasVertical = nextHasVertical;
767
756
  hasHorizontal = nextHasHorizontal;
768
757
  }
@@ -799,8 +788,8 @@ var ImageEditor = class {
799
788
  let scale = 1;
800
789
  let contentWidth = imageWidth;
801
790
  let contentHeight = imageHeight;
802
- let effectiveWidth = viewport.width;
803
- let effectiveHeight = viewport.height;
791
+ let effectiveWidth;
792
+ let effectiveHeight;
804
793
  for (let i = 0; i < 4; i += 1) {
805
794
  effectiveWidth = Math.max(1, viewport.width - (hasVertical ? scrollbar.width : 0));
806
795
  effectiveHeight = Math.max(1, viewport.height - (hasHorizontal ? scrollbar.height : 0));
@@ -809,8 +798,7 @@ var ImageEditor = class {
809
798
  contentHeight = imageHeight * scale;
810
799
  const nextHasVertical = contentHeight > effectiveHeight + 0.5;
811
800
  const nextHasHorizontal = contentWidth > effectiveWidth + 0.5;
812
- if (nextHasVertical === hasVertical && nextHasHorizontal === hasHorizontal)
813
- break;
801
+ if (nextHasVertical === hasVertical && nextHasHorizontal === hasHorizontal) break;
814
802
  hasVertical = nextHasVertical;
815
803
  hasHorizontal = nextHasHorizontal;
816
804
  }
@@ -849,41 +837,48 @@ var ImageEditor = class {
849
837
  stroke: mask && mask.originalStroke || "#ccc",
850
838
  strokeWidth: Number.isFinite(strokeWidth) ? strokeWidth : 1
851
839
  };
852
- if (Number.isFinite(opacity))
853
- style.opacity = opacity;
840
+ if (Number.isFinite(opacity)) style.opacity = opacity;
854
841
  return style;
855
842
  }
856
843
  _withNormalizedMaskStyles(callback) {
857
- if (!this.canvas)
858
- return callback();
844
+ if (!this.canvas) return callback();
859
845
  const masks = this.canvas.getObjects().filter((object) => object.maskId);
860
- const maskStyleBackups = masks.map((mask) => ({
861
- object: mask,
862
- stroke: mask.stroke,
863
- strokeWidth: mask.strokeWidth,
864
- opacity: mask.opacity
865
- }));
846
+ const maskStyleBackups = [];
866
847
  try {
867
848
  masks.forEach((mask) => {
868
- mask.set(this._getMaskNormalStyle(mask));
849
+ const normalStyle = this._getMaskNormalStyle(mask);
850
+ const stylePatch = {};
851
+ Object.keys(normalStyle).forEach((property) => {
852
+ if (mask[property] !== normalStyle[property]) {
853
+ stylePatch[property] = normalStyle[property];
854
+ }
855
+ });
856
+ const changedProperties = Object.keys(stylePatch);
857
+ if (!changedProperties.length) return;
858
+ const backup = { object: mask };
859
+ changedProperties.forEach((property) => {
860
+ backup[property] = mask[property];
861
+ });
862
+ maskStyleBackups.push(backup);
863
+ mask.set(stylePatch);
869
864
  });
870
865
  return callback();
871
866
  } finally {
872
867
  maskStyleBackups.forEach((backup) => {
873
868
  try {
874
- backup.object.set({
875
- stroke: backup.stroke,
876
- strokeWidth: backup.strokeWidth,
877
- opacity: backup.opacity
869
+ const restorePatch = {};
870
+ Object.keys(backup).forEach((property) => {
871
+ if (property !== "object") restorePatch[property] = backup[property];
878
872
  });
873
+ backup.object.set(restorePatch);
879
874
  } catch (error) {
875
+ void error;
880
876
  }
881
877
  });
882
878
  }
883
879
  }
884
880
  _restoreMaskControls(mask) {
885
- if (!mask)
886
- return;
881
+ if (!mask) return;
887
882
  const cornerSize = Number(mask.cornerSize);
888
883
  mask.set({
889
884
  selectable: mask.selectable !== false,
@@ -896,8 +891,7 @@ var ImageEditor = class {
896
891
  transparentCorners: mask.transparentCorners === true,
897
892
  strokeUniform: mask.strokeUniform !== false
898
893
  });
899
- if (typeof mask.setCoords === "function")
900
- mask.setCoords();
894
+ if (typeof mask.setCoords === "function") mask.setCoords();
901
895
  }
902
896
  /**
903
897
  * Captures editor-owned runtime state that Fabric does not include in canvas JSON.
@@ -919,8 +913,7 @@ var ImageEditor = class {
919
913
  };
920
914
  }
921
915
  _serializeCanvasState() {
922
- if (!this.canvas)
923
- return null;
916
+ if (!this.canvas) return null;
924
917
  return this._withNormalizedMaskStyles(() => {
925
918
  const jsonObject = this.canvas.toJSON(this._getStateProperties());
926
919
  if (Array.isArray(jsonObject.objects)) {
@@ -939,8 +932,7 @@ var ImageEditor = class {
939
932
  */
940
933
  _normalizeQuality(quality) {
941
934
  const numericQuality = Number(quality);
942
- if (!Number.isFinite(numericQuality))
943
- return this.options.downsampleQuality ?? 0.92;
935
+ if (!Number.isFinite(numericQuality)) return this.options.downsampleQuality ?? 0.92;
944
936
  return Math.max(0, Math.min(1, numericQuality));
945
937
  }
946
938
  /**
@@ -1013,8 +1005,7 @@ var ImageEditor = class {
1013
1005
  const safeTimeoutMs = Number.isFinite(timeoutMs) && timeoutMs > 0 ? timeoutMs : 3e4;
1014
1006
  let timerId;
1015
1007
  const settle = (callback) => {
1016
- if (isSettled)
1017
- return;
1008
+ if (isSettled) return;
1018
1009
  isSettled = true;
1019
1010
  clearTimeout(timerId);
1020
1011
  imageElement.onload = null;
@@ -1026,6 +1017,7 @@ var ImageEditor = class {
1026
1017
  try {
1027
1018
  imageElement.src = "";
1028
1019
  } catch (error) {
1020
+ void error;
1029
1021
  }
1030
1022
  }, safeTimeoutMs);
1031
1023
  imageElement.onload = () => {
@@ -1039,8 +1031,7 @@ var ImageEditor = class {
1039
1031
  offscreenCanvas.width = scaledSourceWidth;
1040
1032
  offscreenCanvas.height = scaledSourceHeight;
1041
1033
  const context = offscreenCanvas.getContext("2d");
1042
- if (!context)
1043
- throw new Error("2D canvas context is unavailable");
1034
+ if (!context) throw new Error("2D canvas context is unavailable");
1044
1035
  context.drawImage(imageElement, scaledSourceX, scaledSourceY, scaledSourceWidth, scaledSourceHeight, 0, 0, scaledSourceWidth, scaledSourceHeight);
1045
1036
  settle(() => resolve(offscreenCanvas.toDataURL(`image/${format}`, quality)));
1046
1037
  } catch (error) {
@@ -1083,12 +1074,10 @@ var ImageEditor = class {
1083
1074
  * @private
1084
1075
  */
1085
1076
  _getObjectTopLeftPoint(fabricObject) {
1086
- if (!fabricObject)
1087
- return { x: 0, y: 0 };
1077
+ if (!fabricObject) return { x: 0, y: 0 };
1088
1078
  fabricObject.setCoords();
1089
1079
  const coords = typeof fabricObject.getCoords === "function" ? fabricObject.getCoords() : null;
1090
- if (coords && coords.length)
1091
- return coords[0];
1080
+ if (coords && coords.length) return coords[0];
1092
1081
  const boundingRect = fabricObject.getBoundingRect(true, true);
1093
1082
  return { x: boundingRect.left, y: boundingRect.top };
1094
1083
  }
@@ -1102,8 +1091,7 @@ var ImageEditor = class {
1102
1091
  * @private
1103
1092
  */
1104
1093
  _setObjectOriginKeepingPosition(fabricObject, originX, originY, refPoint) {
1105
- if (!fabricObject || !refPoint || !fabricObject.setPositionByOrigin)
1106
- return;
1094
+ if (!fabricObject || !refPoint || !fabricObject.setPositionByOrigin) return;
1107
1095
  fabricObject.set({ originX, originY });
1108
1096
  fabricObject.setPositionByOrigin(refPoint, originX, originY);
1109
1097
  fabricObject.setCoords();
@@ -1115,8 +1103,7 @@ var ImageEditor = class {
1115
1103
  * @private
1116
1104
  */
1117
1105
  _alignObjectBoundingBoxToCanvasTopLeft(fabricObject) {
1118
- if (!fabricObject)
1119
- return;
1106
+ if (!fabricObject) return;
1120
1107
  fabricObject.setCoords();
1121
1108
  const boundingRect = fabricObject.getBoundingRect(true, true);
1122
1109
  const deltaX = boundingRect.left;
@@ -1131,8 +1118,7 @@ var ImageEditor = class {
1131
1118
  * @private
1132
1119
  */
1133
1120
  _updateCanvasSizeToImageBounds() {
1134
- if (!this.originalImage)
1135
- return;
1121
+ if (!this.originalImage) return;
1136
1122
  this.originalImage.setCoords();
1137
1123
  const imageBounds = this.originalImage.getBoundingRect(true, true);
1138
1124
  const size = this._getScrollableCanvasSize(imageBounds.width, imageBounds.height);
@@ -1156,16 +1142,13 @@ var ImageEditor = class {
1156
1142
  * @private
1157
1143
  */
1158
1144
  _expandCanvasToFitObjects(fabricObjects, padding = 10) {
1159
- if (!this.canvas || !Array.isArray(fabricObjects) || !fabricObjects.length || !this._shouldResizeCanvasToContentBounds())
1160
- return;
1145
+ if (!this.canvas || !Array.isArray(fabricObjects) || !fabricObjects.length || !this._shouldResizeCanvasToContentBounds()) return;
1161
1146
  try {
1162
1147
  let requiredWidth = this.canvas.getWidth();
1163
1148
  let requiredHeight = this.canvas.getHeight();
1164
1149
  fabricObjects.forEach((fabricObject) => {
1165
- if (!fabricObject)
1166
- return;
1167
- if (typeof fabricObject.setCoords === "function")
1168
- fabricObject.setCoords();
1150
+ if (!fabricObject) return;
1151
+ if (typeof fabricObject.setCoords === "function") fabricObject.setCoords();
1169
1152
  const boundingRect = fabricObject.getBoundingRect(true, true);
1170
1153
  requiredWidth = Math.max(requiredWidth, Math.ceil(boundingRect.left + boundingRect.width + padding));
1171
1154
  requiredHeight = Math.max(requiredHeight, Math.ceil(boundingRect.top + boundingRect.height + padding));
@@ -1210,10 +1193,8 @@ var ImageEditor = class {
1210
1193
  * @private
1211
1194
  */
1212
1195
  _scaleImageImpl(factor, options = {}) {
1213
- if (!this.originalImage)
1214
- return Promise.resolve();
1215
- if (this.isAnimating)
1216
- return Promise.resolve();
1196
+ if (!this.originalImage) return Promise.resolve();
1197
+ if (this.isAnimating) return Promise.resolve();
1217
1198
  const saveHistory = options.saveHistory !== false;
1218
1199
  factor = Math.max(this.options.minScale, Math.min(this.options.maxScale, factor));
1219
1200
  this.currentScale = factor;
@@ -1244,14 +1225,12 @@ var ImageEditor = class {
1244
1225
  }
1245
1226
  this._alignObjectBoundingBoxToCanvasTopLeft(this.originalImage);
1246
1227
  this.canvas.getObjects().forEach((object) => {
1247
- if (object.maskId)
1248
- this._syncMaskLabel(object);
1228
+ if (object.maskId) this._syncMaskLabel(object);
1249
1229
  });
1250
1230
  this.isAnimating = false;
1251
1231
  this._updateInputs();
1252
1232
  this._updateUI();
1253
- if (saveHistory)
1254
- this.saveState();
1233
+ if (saveHistory) this.saveState();
1255
1234
  }).catch(() => {
1256
1235
  this.isAnimating = false;
1257
1236
  this._updateUI();
@@ -1275,12 +1254,9 @@ var ImageEditor = class {
1275
1254
  * @private
1276
1255
  */
1277
1256
  _rotateImageImpl(degrees, options = {}) {
1278
- if (!this.originalImage)
1279
- return Promise.resolve();
1280
- if (this.isAnimating)
1281
- return Promise.resolve();
1282
- if (isNaN(degrees))
1283
- return Promise.resolve();
1257
+ if (!this.originalImage) return Promise.resolve();
1258
+ if (this.isAnimating) return Promise.resolve();
1259
+ if (isNaN(degrees)) return Promise.resolve();
1284
1260
  const saveHistory = options.saveHistory !== false;
1285
1261
  this.currentRotation = degrees;
1286
1262
  this.isAnimating = true;
@@ -1304,14 +1280,12 @@ var ImageEditor = class {
1304
1280
  const newTopLeft = this._getObjectTopLeftPoint(this.originalImage);
1305
1281
  this._setObjectOriginKeepingPosition(this.originalImage, "left", "top", newTopLeft);
1306
1282
  this.canvas.getObjects().forEach((object) => {
1307
- if (object.maskId)
1308
- this._syncMaskLabel(object);
1283
+ if (object.maskId) this._syncMaskLabel(object);
1309
1284
  });
1310
1285
  this.isAnimating = false;
1311
1286
  this._updateInputs();
1312
1287
  this._updateUI();
1313
- if (saveHistory)
1314
- this.saveState();
1288
+ if (saveHistory) this.saveState();
1315
1289
  }).catch(() => {
1316
1290
  this.isAnimating = false;
1317
1291
  this._updateUI();
@@ -1324,8 +1298,7 @@ var ImageEditor = class {
1324
1298
  * @public
1325
1299
  */
1326
1300
  resetImageTransform() {
1327
- if (!this.originalImage)
1328
- return Promise.resolve();
1301
+ if (!this.originalImage) return Promise.resolve();
1329
1302
  return this.animationQueue.add(async () => {
1330
1303
  const before = this._lastSnapshot || this._serializeCanvasState();
1331
1304
  await this._scaleImageImpl(1, { saveHistory: false });
@@ -1353,8 +1326,7 @@ var ImageEditor = class {
1353
1326
  * @public
1354
1327
  */
1355
1328
  loadFromState(serializedState) {
1356
- if (!serializedState || !this.canvas)
1357
- return Promise.resolve();
1329
+ if (!serializedState || !this.canvas) return Promise.resolve();
1358
1330
  return new Promise((resolve) => {
1359
1331
  try {
1360
1332
  const state = typeof serializedState === "string" ? JSON.parse(serializedState) : serializedState;
@@ -1430,14 +1402,12 @@ var ImageEditor = class {
1430
1402
  * @public
1431
1403
  */
1432
1404
  saveState() {
1433
- if (!this.canvas)
1434
- return;
1405
+ if (!this.canvas) return;
1435
1406
  const activeObject = this.canvas.getActiveObject();
1436
1407
  try {
1437
1408
  const after = this._serializeCanvasState();
1438
1409
  const before = this._lastSnapshot || after;
1439
- if (after === before)
1440
- return;
1410
+ if (after === before) return;
1441
1411
  let executedOnce = false;
1442
1412
  const command = new Command(
1443
1413
  () => {
@@ -1472,12 +1442,9 @@ var ImageEditor = class {
1472
1442
  * @private
1473
1443
  */
1474
1444
  _pushStateTransition(before, after) {
1475
- if (!before || !after)
1476
- return;
1477
- if (before === after)
1478
- return;
1479
- if (!this.historyManager)
1480
- this.historyManager = new HistoryManager(this.maxHistorySize || 50);
1445
+ if (!before || !after) return;
1446
+ if (before === after) return;
1447
+ if (!this.historyManager) this.historyManager = new HistoryManager(this.maxHistorySize || 50);
1481
1448
  const command = new Command(
1482
1449
  () => this.loadFromState(after),
1483
1450
  () => this.loadFromState(before)
@@ -1513,26 +1480,24 @@ var ImageEditor = class {
1513
1480
  });
1514
1481
  }
1515
1482
  _rebindMaskEvents(mask) {
1516
- if (!mask)
1517
- return;
1483
+ if (!mask) return;
1518
1484
  if (mask.__imageEditorMaskHandlers) {
1519
1485
  try {
1520
1486
  mask.off("mouseover", mask.__imageEditorMaskHandlers.mouseover);
1521
1487
  mask.off("mouseout", mask.__imageEditorMaskHandlers.mouseout);
1522
1488
  } catch (error) {
1489
+ void error;
1523
1490
  }
1524
1491
  }
1525
1492
  const metadata = {};
1526
1493
  if (!Number.isFinite(Number(mask.originalAlpha))) {
1527
1494
  metadata.originalAlpha = Number.isFinite(Number(mask.opacity)) ? Number(mask.opacity) : 0.5;
1528
1495
  }
1529
- if (!mask.originalStroke)
1530
- metadata.originalStroke = mask.stroke || "#ccc";
1496
+ if (!mask.originalStroke) metadata.originalStroke = mask.stroke || "#ccc";
1531
1497
  if (!Number.isFinite(Number(mask.originalStrokeWidth))) {
1532
1498
  metadata.originalStrokeWidth = Number.isFinite(Number(mask.strokeWidth)) ? Number(mask.strokeWidth) : 1;
1533
1499
  }
1534
- if (Object.keys(metadata).length)
1535
- mask.set(metadata);
1500
+ if (Object.keys(metadata).length) mask.set(metadata);
1536
1501
  const normalStyle = {
1537
1502
  stroke: mask.originalStroke || "#ccc",
1538
1503
  strokeWidth: mask.originalStrokeWidth,
@@ -1545,13 +1510,11 @@ var ImageEditor = class {
1545
1510
  };
1546
1511
  const mouseover = () => {
1547
1512
  mask.set(hoverStyle);
1548
- if (mask.canvas)
1549
- mask.canvas.requestRenderAll();
1513
+ if (mask.canvas) mask.canvas.requestRenderAll();
1550
1514
  };
1551
1515
  const mouseout = () => {
1552
1516
  mask.set(normalStyle);
1553
- if (mask.canvas)
1554
- mask.canvas.requestRenderAll();
1517
+ if (mask.canvas) mask.canvas.requestRenderAll();
1555
1518
  };
1556
1519
  mask.on("mouseover", mouseover);
1557
1520
  mask.on("mouseout", mouseout);
@@ -1586,8 +1549,7 @@ var ImageEditor = class {
1586
1549
  * @public
1587
1550
  */
1588
1551
  createMask(config = {}) {
1589
- if (!this.canvas)
1590
- return null;
1552
+ if (!this.canvas) return null;
1591
1553
  const shapeType = config.shape || "rect";
1592
1554
  const maskConfig = {
1593
1555
  shape: shapeType,
@@ -1603,14 +1565,22 @@ var ImageEditor = class {
1603
1565
  ...config
1604
1566
  };
1605
1567
  const firstOffset = 10;
1606
- let left = firstOffset;
1607
- let top = firstOffset;
1608
- const resolveValue = (value, fallback) => {
1568
+ let left;
1569
+ let top;
1570
+ const getCanvasBasis = (axis) => {
1571
+ const canvasWidth = this.canvas ? this.canvas.getWidth() : 0;
1572
+ const canvasHeight = this.canvas ? this.canvas.getHeight() : 0;
1573
+ if (axis === "height") return canvasHeight;
1574
+ if (axis === "min") return Math.min(canvasWidth, canvasHeight);
1575
+ return canvasWidth;
1576
+ };
1577
+ const resolveValue = (value, fallback, axis = "width") => {
1609
1578
  if (typeof value === "function")
1610
1579
  return value(this.canvas, this.options);
1611
1580
  if (typeof value === "string" && value.endsWith("%")) {
1612
- const percent = parseFloat(value) / 100;
1613
- return Math.floor((this.canvas ? this.canvas.getWidth() : 0) * percent);
1581
+ const percent = Number.parseFloat(value) / 100;
1582
+ if (!Number.isFinite(percent)) return fallback;
1583
+ return Math.floor(getCanvasBasis(axis) * percent);
1614
1584
  }
1615
1585
  return value != null ? value : fallback;
1616
1586
  };
@@ -1625,11 +1595,11 @@ var ImageEditor = class {
1625
1595
  left = Math.round(previousMaskRight + maskConfig.gap);
1626
1596
  top = previousMask.top ?? firstOffset;
1627
1597
  } else {
1628
- left = resolveValue(maskConfig.left, firstOffset);
1629
- top = resolveValue(maskConfig.top, firstOffset);
1598
+ left = resolveValue(maskConfig.left, firstOffset, "width");
1599
+ top = resolveValue(maskConfig.top, firstOffset, "height");
1630
1600
  }
1631
- maskConfig.width = resolveValue(maskConfig.width, this.options.defaultMaskWidth);
1632
- maskConfig.height = resolveValue(maskConfig.height, this.options.defaultMaskHeight);
1601
+ maskConfig.width = resolveValue(maskConfig.width, this.options.defaultMaskWidth, "width");
1602
+ maskConfig.height = resolveValue(maskConfig.height, this.options.defaultMaskHeight, "height");
1633
1603
  maskConfig.left = left;
1634
1604
  maskConfig.top = top;
1635
1605
  let mask;
@@ -1641,7 +1611,7 @@ var ImageEditor = class {
1641
1611
  mask = new fabric.Circle({
1642
1612
  left,
1643
1613
  top,
1644
- radius: resolveValue(maskConfig.radius, Math.min(maskConfig.width, maskConfig.height) / 2),
1614
+ radius: resolveValue(maskConfig.radius, Math.min(maskConfig.width, maskConfig.height) / 2, "min"),
1645
1615
  fill: maskConfig.color,
1646
1616
  opacity: maskConfig.alpha,
1647
1617
  angle: maskConfig.angle,
@@ -1652,8 +1622,8 @@ var ImageEditor = class {
1652
1622
  mask = new fabric.Ellipse({
1653
1623
  left,
1654
1624
  top,
1655
- rx: resolveValue(maskConfig.rx, maskConfig.width / 2),
1656
- ry: resolveValue(maskConfig.ry, maskConfig.height / 2),
1625
+ rx: resolveValue(maskConfig.rx, maskConfig.width / 2, "width"),
1626
+ ry: resolveValue(maskConfig.ry, maskConfig.height / 2, "height"),
1657
1627
  fill: maskConfig.color,
1658
1628
  opacity: maskConfig.alpha,
1659
1629
  angle: maskConfig.angle,
@@ -1680,8 +1650,8 @@ var ImageEditor = class {
1680
1650
  mask = new fabric.Rect({
1681
1651
  left,
1682
1652
  top,
1683
- width: resolveValue(maskConfig.width, this.options.defaultMaskWidth),
1684
- height: resolveValue(maskConfig.height, this.options.defaultMaskHeight),
1653
+ width: resolveValue(maskConfig.width, this.options.defaultMaskWidth, "width"),
1654
+ height: resolveValue(maskConfig.height, this.options.defaultMaskHeight, "height"),
1685
1655
  fill: maskConfig.color,
1686
1656
  opacity: maskConfig.alpha,
1687
1657
  angle: maskConfig.angle,
@@ -1706,8 +1676,7 @@ var ImageEditor = class {
1706
1676
  opacity: hasStyle("opacity") ? styles.opacity : maskConfig.alpha,
1707
1677
  strokeUniform: "strokeUniform" in maskConfig ? maskConfig.strokeUniform : hasStyle("strokeUniform") ? styles.strokeUniform : true
1708
1678
  };
1709
- if (hasStyle("strokeDashArray"))
1710
- maskSettings.strokeDashArray = styles.strokeDashArray;
1679
+ if (hasStyle("strokeDashArray")) maskSettings.strokeDashArray = styles.strokeDashArray;
1711
1680
  mask.set(maskSettings);
1712
1681
  mask.setCoords();
1713
1682
  mask.set({
@@ -1719,7 +1688,7 @@ var ImageEditor = class {
1719
1688
  this._expandCanvasToFitObject(mask);
1720
1689
  this._lastMaskInitialLeft = left;
1721
1690
  this._lastMaskInitialTop = top;
1722
- this._lastMaskInitialWidth = resolveValue(maskConfig.width, this.options.defaultMaskWidth);
1691
+ this._lastMaskInitialWidth = resolveValue(maskConfig.width, this.options.defaultMaskWidth, "width");
1723
1692
  const maskId = ++this.maskCounter;
1724
1693
  mask.set({
1725
1694
  maskId,
@@ -1728,15 +1697,13 @@ var ImageEditor = class {
1728
1697
  this._lastMask = mask;
1729
1698
  this.canvas.add(mask);
1730
1699
  this.canvas.bringToFront(mask);
1731
- if (maskConfig.selectable)
1732
- this.canvas.setActiveObject(mask);
1700
+ if (maskConfig.selectable) this.canvas.setActiveObject(mask);
1733
1701
  this._handleSelectionChanged([mask]);
1734
1702
  this._updateMaskList();
1735
1703
  this._updateUI();
1736
1704
  this.canvas.renderAll();
1737
1705
  this.saveState();
1738
- if (typeof maskConfig.onCreate === "function")
1739
- maskConfig.onCreate(mask, this.canvas);
1706
+ if (typeof maskConfig.onCreate === "function") maskConfig.onCreate(mask, this.canvas);
1740
1707
  return mask;
1741
1708
  }
1742
1709
  /**
@@ -1756,8 +1723,7 @@ var ImageEditor = class {
1756
1723
  removeSelectedMask() {
1757
1724
  const activeObject = this.canvas.getActiveObject();
1758
1725
  const selectedMasks = this._getModifiedMasks(activeObject);
1759
- if (!selectedMasks.length)
1760
- return;
1726
+ if (!selectedMasks.length) return;
1761
1727
  this.canvas.discardActiveObject();
1762
1728
  selectedMasks.forEach((mask) => {
1763
1729
  this._removeLabelForMask(mask);
@@ -1792,8 +1758,7 @@ var ImageEditor = class {
1792
1758
  this._updateMaskList();
1793
1759
  this._updateUI();
1794
1760
  this.canvas.renderAll();
1795
- if (saveHistory)
1796
- this.saveState();
1761
+ if (saveHistory) this.saveState();
1797
1762
  }
1798
1763
  /**
1799
1764
  * Removes the label associated with the specified mask object, if it exists.
@@ -1802,8 +1767,7 @@ var ImageEditor = class {
1802
1767
  * @private
1803
1768
  */
1804
1769
  _removeLabelForMask(mask) {
1805
- if (!mask || !this.canvas)
1806
- return;
1770
+ if (!mask || !this.canvas) return;
1807
1771
  if (mask.__label) {
1808
1772
  try {
1809
1773
  const canvasObjects = this.canvas.getObjects();
@@ -1811,10 +1775,12 @@ var ImageEditor = class {
1811
1775
  this.canvas.remove(mask.__label);
1812
1776
  }
1813
1777
  } catch (error) {
1778
+ void error;
1814
1779
  }
1815
1780
  try {
1816
1781
  delete mask.__label;
1817
1782
  } catch (error) {
1783
+ void error;
1818
1784
  }
1819
1785
  }
1820
1786
  }
@@ -1830,8 +1796,7 @@ var ImageEditor = class {
1830
1796
  */
1831
1797
  _getMaskCreationIndex(mask) {
1832
1798
  const maskId = Number(mask && mask.maskId);
1833
- if (Number.isFinite(maskId) && maskId > 0)
1834
- return Math.floor(maskId) - 1;
1799
+ if (Number.isFinite(maskId) && maskId > 0) return Math.floor(maskId) - 1;
1835
1800
  const masks = this.canvas ? this.canvas.getObjects().filter((object) => object.maskId) : [];
1836
1801
  return Math.max(0, masks.indexOf(mask));
1837
1802
  }
@@ -1843,8 +1808,7 @@ var ImageEditor = class {
1843
1808
  * @private
1844
1809
  */
1845
1810
  _createLabelForMask(mask) {
1846
- if (!mask || !this.options.maskLabelOnSelect)
1847
- return;
1811
+ if (!mask || !this.options.maskLabelOnSelect) return;
1848
1812
  this._removeLabelForMask(mask);
1849
1813
  let textObject = null;
1850
1814
  if (this.options.label && typeof this.options.label.create === "function") {
@@ -1886,15 +1850,14 @@ var ImageEditor = class {
1886
1850
  * @private
1887
1851
  */
1888
1852
  _hideAllMaskLabels() {
1889
- if (!this.canvas)
1890
- return;
1853
+ if (!this.canvas) return;
1891
1854
  const canvasObjects = this.canvas.getObjects();
1892
1855
  const labels = canvasObjects.filter((object) => object.maskLabel);
1893
1856
  labels.forEach((label) => {
1894
1857
  try {
1895
- if (canvasObjects.includes(label))
1896
- this.canvas.remove(label);
1858
+ if (canvasObjects.includes(label)) this.canvas.remove(label);
1897
1859
  } catch (error) {
1860
+ void error;
1898
1861
  }
1899
1862
  });
1900
1863
  canvasObjects.forEach((object) => {
@@ -1902,6 +1865,7 @@ var ImageEditor = class {
1902
1865
  try {
1903
1866
  delete object.__label;
1904
1867
  } catch (error) {
1868
+ void error;
1905
1869
  }
1906
1870
  }
1907
1871
  });
@@ -1913,15 +1877,11 @@ var ImageEditor = class {
1913
1877
  * @private
1914
1878
  */
1915
1879
  _syncMaskLabel(mask) {
1916
- if (!mask)
1917
- return;
1918
- if (!this.options.maskLabelOnSelect)
1919
- return;
1920
- if (!mask.__label)
1921
- return;
1880
+ if (!mask) return;
1881
+ if (!this.options.maskLabelOnSelect) return;
1882
+ if (!mask.__label) return;
1922
1883
  const coords = mask.getCoords ? mask.getCoords() : null;
1923
- if (!coords || coords.length < 4)
1924
- return;
1884
+ if (!coords || coords.length < 4) return;
1925
1885
  const tl = coords[0];
1926
1886
  const center = mask.getCenterPoint();
1927
1887
  const vx = center.x - tl.x;
@@ -1954,12 +1914,9 @@ var ImageEditor = class {
1954
1914
  * @private
1955
1915
  */
1956
1916
  _showLabelForMask(mask) {
1957
- if (!mask)
1958
- return;
1959
- if (!this.options.maskLabelOnSelect)
1960
- return;
1961
- if (!mask.__label)
1962
- this._createLabelForMask(mask);
1917
+ if (!mask) return;
1918
+ if (!this.options.maskLabelOnSelect) return;
1919
+ if (!mask.__label) this._createLabelForMask(mask);
1963
1920
  mask.__label.set({ visible: true });
1964
1921
  this._syncMaskLabel(mask);
1965
1922
  }
@@ -1979,6 +1936,7 @@ var ImageEditor = class {
1979
1936
  try {
1980
1937
  this.canvas.remove(mask.__label);
1981
1938
  } catch (error) {
1939
+ void error;
1982
1940
  }
1983
1941
  delete mask.__label;
1984
1942
  }
@@ -1991,8 +1949,7 @@ var ImageEditor = class {
1991
1949
  mask.set({ stroke: "#ff0000", strokeWidth: 1 });
1992
1950
  }
1993
1951
  });
1994
- if (selectedMask)
1995
- this._showLabelForMask(selectedMask);
1952
+ if (selectedMask) this._showLabelForMask(selectedMask);
1996
1953
  this._updateMaskListSelection(selectedMask);
1997
1954
  this.canvas.renderAll();
1998
1955
  this._updateUI();
@@ -2004,8 +1961,7 @@ var ImageEditor = class {
2004
1961
  */
2005
1962
  _updateMaskList() {
2006
1963
  const maskListElement = document.getElementById(this.elements.maskList);
2007
- if (!maskListElement)
2008
- return;
1964
+ if (!maskListElement) return;
2009
1965
  maskListElement.innerHTML = "";
2010
1966
  const masks = this.canvas.getObjects().filter((object) => object.maskId);
2011
1967
  masks.forEach((mask) => {
@@ -2027,8 +1983,7 @@ var ImageEditor = class {
2027
1983
  */
2028
1984
  _updateMaskListSelection(selectedMask) {
2029
1985
  const maskListElement = document.getElementById(this.elements.maskList);
2030
- if (!maskListElement)
2031
- return;
1986
+ if (!maskListElement) return;
2032
1987
  const maskItems = maskListElement.querySelectorAll(".mask-item");
2033
1988
  maskItems.forEach((item) => {
2034
1989
  const isSelected = !!selectedMask && item.textContent === selectedMask.maskName;
@@ -2046,11 +2001,9 @@ var ImageEditor = class {
2046
2001
  * @public
2047
2002
  */
2048
2003
  async mergeMasks() {
2049
- if (!this.originalImage)
2050
- return;
2004
+ if (!this.originalImage) return;
2051
2005
  const masks = this.canvas.getObjects().filter((object) => object.maskId);
2052
- if (!masks.length)
2053
- return;
2006
+ if (!masks.length) return;
2054
2007
  this.canvas.discardActiveObject();
2055
2008
  this.canvas.renderAll();
2056
2009
  try {
@@ -2082,8 +2035,7 @@ var ImageEditor = class {
2082
2035
  * @public
2083
2036
  */
2084
2037
  downloadImage(fileName = this.options.defaultDownloadFileName) {
2085
- if (!this.originalImage)
2086
- return;
2038
+ if (!this.originalImage) return;
2087
2039
  const exportImageArea = this.options.exportImageAreaByDefault;
2088
2040
  this.exportImageBase64({ exportImageArea, multiplier: this.options.exportMultiplier }).then((imageBase64) => {
2089
2041
  const link = document.createElement("a");
@@ -2111,8 +2063,7 @@ var ImageEditor = class {
2111
2063
  * @public
2112
2064
  */
2113
2065
  async exportImageBase64(options = {}) {
2114
- if (!this.originalImage)
2115
- throw new Error("No image loaded");
2066
+ if (!this.originalImage) throw new Error("No image loaded");
2116
2067
  const exportImageArea = typeof options.exportImageArea === "boolean" ? options.exportImageArea : this.options.exportImageAreaByDefault;
2117
2068
  const multiplier = options.multiplier || this.options.exportMultiplier || 1;
2118
2069
  const quality = this._normalizeQuality(options.quality ?? this.options.downsampleQuality);
@@ -2140,6 +2091,7 @@ var ImageEditor = class {
2140
2091
  try {
2141
2092
  backup.object.set({ visible: backup.visible });
2142
2093
  } catch (error) {
2094
+ void error;
2143
2095
  }
2144
2096
  });
2145
2097
  this.canvas.renderAll();
@@ -2187,6 +2139,7 @@ var ImageEditor = class {
2187
2139
  });
2188
2140
  backup.object.setCoords();
2189
2141
  } catch (error) {
2142
+ void error;
2190
2143
  }
2191
2144
  });
2192
2145
  this.canvas.renderAll();
@@ -2222,8 +2175,7 @@ var ImageEditor = class {
2222
2175
  * const file = await this.exportImageFile({ mergeMask: false, fileType: 'png' });
2223
2176
  */
2224
2177
  async exportImageFile(options = {}) {
2225
- if (!this.originalImage)
2226
- throw new Error("No image loaded");
2178
+ if (!this.originalImage) throw new Error("No image loaded");
2227
2179
  const {
2228
2180
  mergeMask = true,
2229
2181
  fileType = "jpeg",
@@ -2287,8 +2239,7 @@ var ImageEditor = class {
2287
2239
  }
2288
2240
  async _restoreStateAfterCropFailure(beforeJson, message, error) {
2289
2241
  this._reportError(message, error);
2290
- if (this._cropRect && this.canvas)
2291
- this._removeCropRect();
2242
+ if (this._cropRect && this.canvas) this._removeCropRect();
2292
2243
  this._cropRect = null;
2293
2244
  this._cropMode = false;
2294
2245
  if (this.canvas && this._prevSelectionSetting !== void 0) {
@@ -2303,8 +2254,7 @@ var ImageEditor = class {
2303
2254
  }
2304
2255
  }
2305
2256
  this._updateUI();
2306
- if (this.canvas)
2307
- this.canvas.renderAll();
2257
+ if (this.canvas) this.canvas.renderAll();
2308
2258
  }
2309
2259
  _restoreCropObjectState() {
2310
2260
  if (Array.isArray(this._cropPrevEvented)) {
@@ -2316,14 +2266,14 @@ var ImageEditor = class {
2316
2266
  visible: state.visible
2317
2267
  });
2318
2268
  } catch (error) {
2269
+ void error;
2319
2270
  }
2320
2271
  });
2321
2272
  }
2322
2273
  this._cropPrevEvented = null;
2323
2274
  }
2324
2275
  _removeCropRect() {
2325
- if (!this._cropRect)
2326
- return;
2276
+ if (!this._cropRect) return;
2327
2277
  try {
2328
2278
  if (this._cropHandlers && this._cropHandlers.length) {
2329
2279
  this._cropHandlers.forEach((targetHandlers) => {
@@ -2333,10 +2283,12 @@ var ImageEditor = class {
2333
2283
  });
2334
2284
  }
2335
2285
  } catch (error) {
2286
+ void error;
2336
2287
  }
2337
2288
  try {
2338
2289
  this.canvas.remove(this._cropRect);
2339
2290
  } catch (error) {
2291
+ void error;
2340
2292
  }
2341
2293
  this._cropRect = null;
2342
2294
  this._cropHandlers = [];
@@ -2351,10 +2303,8 @@ var ImageEditor = class {
2351
2303
  * @public
2352
2304
  */
2353
2305
  enterCropMode() {
2354
- if (!this.canvas || !this.originalImage || this._cropMode)
2355
- return;
2356
- if (!this.isImageLoaded())
2357
- return;
2306
+ if (!this.canvas || !this.originalImage || this._cropMode) return;
2307
+ if (!this.isImageLoaded()) return;
2358
2308
  this._cropMode = true;
2359
2309
  this._prevSelectionSetting = this.canvas.selection;
2360
2310
  this.canvas.selection = false;
@@ -2406,10 +2356,10 @@ var ImageEditor = class {
2406
2356
  evented: false,
2407
2357
  selectable: false
2408
2358
  };
2409
- if (shouldHideMasks && (object.maskId || object.maskLabel))
2410
- updates.visible = false;
2359
+ if (shouldHideMasks && (object.maskId || object.maskLabel)) updates.visible = false;
2411
2360
  object.set(updates);
2412
2361
  } catch (error) {
2362
+ void error;
2413
2363
  }
2414
2364
  }
2415
2365
  });
@@ -2423,6 +2373,7 @@ var ImageEditor = class {
2423
2373
  cropRect.setCoords();
2424
2374
  this.canvas.requestRenderAll();
2425
2375
  } catch (error) {
2376
+ void error;
2426
2377
  }
2427
2378
  };
2428
2379
  cropRect.on("modified", handleCropRectModified);
@@ -2446,8 +2397,7 @@ var ImageEditor = class {
2446
2397
  * @public
2447
2398
  */
2448
2399
  cancelCrop() {
2449
- if (!this.canvas || !this._cropMode)
2450
- return;
2400
+ if (!this.canvas || !this._cropMode) return;
2451
2401
  this._removeCropRect();
2452
2402
  this._restoreCropObjectState();
2453
2403
  this._cropMode = false;
@@ -2469,14 +2419,13 @@ var ImageEditor = class {
2469
2419
  * @public
2470
2420
  */
2471
2421
  async applyCrop() {
2472
- if (!this.canvas || !this._cropMode || !this._cropRect)
2473
- return;
2422
+ if (!this.canvas || !this._cropMode || !this._cropRect) return;
2474
2423
  this._cropRect.setCoords();
2475
2424
  const rectBounds = this._cropRect.getBoundingRect(true, true);
2476
- const cropRegion = this._getClampedCanvasRegion(rectBounds);
2425
+ const cropRegion = this._getClampedCanvasRegion(rectBounds, { includePartialPixels: false });
2477
2426
  const shouldPreserveMasks = !!(this.options.crop && this.options.crop.preserveMasksAfterCrop);
2478
2427
  this._restoreCropObjectState();
2479
- let beforeJson = null;
2428
+ let beforeJson;
2480
2429
  try {
2481
2430
  beforeJson = this._serializeCanvasState();
2482
2431
  } catch (error) {
@@ -2547,7 +2496,7 @@ var ImageEditor = class {
2547
2496
  await this._restoreStateAfterCropFailure(beforeJson, "applyCrop: loadImage(croppedBase64) failed", error);
2548
2497
  return;
2549
2498
  }
2550
- let afterJson = null;
2499
+ let afterJson;
2551
2500
  try {
2552
2501
  afterJson = this._serializeCanvasState();
2553
2502
  } catch (error) {
@@ -2570,8 +2519,7 @@ var ImageEditor = class {
2570
2519
  */
2571
2520
  _updateInputs() {
2572
2521
  const scaleInputElement = document.getElementById(this.elements.scaleRate);
2573
- if (scaleInputElement)
2574
- scaleInputElement.value = Math.round(this.currentScale * 100);
2522
+ if (scaleInputElement) scaleInputElement.value = Math.round(this.currentScale * 100);
2575
2523
  }
2576
2524
  /**
2577
2525
  * Updates the enabled/disabled state of various UI controls (buttons)
@@ -2591,8 +2539,7 @@ var ImageEditor = class {
2591
2539
  if (isInCropMode) {
2592
2540
  for (const key of Object.keys(this.elements || {})) {
2593
2541
  const element = document.getElementById(this.elements[key]);
2594
- if (!element)
2595
- continue;
2542
+ if (!element) continue;
2596
2543
  if (key === "applyCropBtn" || key === "cancelCropBtn") {
2597
2544
  this._setDisabled(key, false);
2598
2545
  } else {
@@ -2628,8 +2575,7 @@ var ImageEditor = class {
2628
2575
  */
2629
2576
  _setDisabled(key, disabled) {
2630
2577
  const element = document.getElementById(this.elements[key]);
2631
- if (!element)
2632
- return;
2578
+ if (!element) return;
2633
2579
  if ("disabled" in element) {
2634
2580
  element.disabled = !!disabled;
2635
2581
  return;
@@ -2643,10 +2589,8 @@ var ImageEditor = class {
2643
2589
  }
2644
2590
  }
2645
2591
  _isElementDisabled(element) {
2646
- if (!element)
2647
- return false;
2648
- if ("disabled" in element)
2649
- return !!element.disabled;
2592
+ if (!element) return false;
2593
+ if ("disabled" in element) return !!element.disabled;
2650
2594
  return element.getAttribute("aria-disabled") === "true";
2651
2595
  }
2652
2596
  /**
@@ -2654,8 +2598,7 @@ var ImageEditor = class {
2654
2598
  * @private
2655
2599
  */
2656
2600
  _updatePlaceholderStatus() {
2657
- if (!this.options.showPlaceholder)
2658
- return;
2601
+ if (!this.options.showPlaceholder) return;
2659
2602
  this._setPlaceholderVisible(!this.originalImage);
2660
2603
  }
2661
2604
  /**
@@ -2665,17 +2608,23 @@ var ImageEditor = class {
2665
2608
  * @private
2666
2609
  */
2667
2610
  _setPlaceholderVisible(show) {
2668
- if (!this.placeholderElement || !this.containerElement)
2669
- return;
2670
- if (show) {
2671
- this.placeholderElement.classList.remove("d-none");
2672
- this.placeholderElement.classList.add("d-flex");
2673
- this.containerElement.classList.add("d-none");
2674
- } else {
2675
- this.placeholderElement.classList.remove("d-flex");
2676
- this.placeholderElement.classList.add("d-none");
2677
- this.containerElement.classList.remove("d-none");
2678
- }
2611
+ if (!this.placeholderElement || !this.containerElement) return;
2612
+ this._setElementVisible(this.placeholderElement, show);
2613
+ this._setElementVisible(this.containerElement, !show);
2614
+ }
2615
+ /**
2616
+ * Updates element visibility.
2617
+ *
2618
+ * @param {HTMLElement} element - Element whose visibility should be updated.
2619
+ * @param {boolean} isVisible - If true, removes the hidden state.
2620
+ * @returns {void}
2621
+ * @private
2622
+ */
2623
+ _setElementVisible(element, isVisible) {
2624
+ if (!element) return;
2625
+ element.hidden = !isVisible;
2626
+ element.setAttribute("aria-hidden", isVisible ? "false" : "true");
2627
+ if (isVisible && element.classList) element.classList.remove("d-none");
2679
2628
  }
2680
2629
  /**
2681
2630
  * Cleans up and disposes of the canvas and related references.
@@ -2687,21 +2636,23 @@ var ImageEditor = class {
2687
2636
  for (const key in this._handlersByElementKey || {}) {
2688
2637
  const handlers = this._handlersByElementKey[key] || [];
2689
2638
  const element = document.getElementById(this.elements[key]);
2690
- if (!element)
2691
- continue;
2639
+ if (!element) continue;
2692
2640
  handlers.forEach((handlerRecord) => {
2693
2641
  try {
2694
2642
  element.removeEventListener(handlerRecord.eventName, handlerRecord.handler);
2695
2643
  } catch (error) {
2644
+ void error;
2696
2645
  }
2697
2646
  });
2698
2647
  }
2699
2648
  } catch (error) {
2649
+ void error;
2700
2650
  }
2701
2651
  if (this._cropRect) {
2702
2652
  try {
2703
2653
  this.canvas.remove(this._cropRect);
2704
2654
  } catch (error) {
2655
+ void error;
2705
2656
  }
2706
2657
  this._cropRect = null;
2707
2658
  }
@@ -2709,12 +2660,14 @@ var ImageEditor = class {
2709
2660
  try {
2710
2661
  this.containerElement.style.overflow = this._containerOriginalOverflow;
2711
2662
  } catch (error) {
2663
+ void error;
2712
2664
  }
2713
2665
  }
2714
2666
  if (this.canvas) {
2715
2667
  try {
2716
2668
  this.canvas.dispose();
2717
2669
  } catch (error) {
2670
+ void error;
2718
2671
  }
2719
2672
  this.canvas = null;
2720
2673
  this.canvasElement = null;