@bensitu/image-editor 1.4.1 → 1.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/image-editor.esm.js +239 -66
- package/dist/image-editor.esm.js.map +3 -3
- package/dist/image-editor.esm.min.js +3 -3
- package/dist/image-editor.esm.min.js.map +3 -3
- package/dist/image-editor.esm.min.mjs +3 -3
- package/dist/image-editor.esm.min.mjs.map +3 -3
- package/dist/image-editor.esm.mjs +239 -66
- package/dist/image-editor.esm.mjs.map +3 -3
- package/dist/image-editor.js +239 -66
- package/dist/image-editor.js.map +3 -3
- package/dist/image-editor.min.js +2 -2
- package/dist/image-editor.min.js.map +3 -3
- package/image-editor.d.ts +1 -0
- package/package.json +2 -1
- package/src/image-editor.js +272 -70
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/esm.js", "../src/image-editor.js"],
|
|
4
|
-
"sourcesContent": ["import fabricModule from 'fabric';\nimport ImageEditor, { setFabric } from './image-editor.js';\n\nconst fabricInstance = fabricModule && (fabricModule.fabric || fabricModule.default || fabricModule);\n\nsetFabric(fabricInstance);\n\nexport { ImageEditor };\nexport default ImageEditor;\n", "/**\n * @file image-editor.js\n * @module image-editor\n * @version 1.4.1\n * @author Ben Situ\n * @license MIT\n * @description Lightweight canvas-based image editor with masking/transform/export support.\n */\n\nlet fabric = null;\nconst INTERNAL_OPERATION_TOKEN = Symbol('ImageEditorInternalOperation');\n\n/**\n * Returns the ambient global scope used to discover a globally loaded Fabric.js namespace.\n *\n * @returns {typeof globalThis|null} The global scope, or null when no standard scope is available.\n * @private\n */\nfunction getGlobalScope() {\n if (typeof globalThis !== 'undefined') return globalThis;\n if (typeof self !== 'undefined') return self;\n if (typeof window !== 'undefined') return window;\n return null;\n}\n\n/**\n * Returns the globally registered Fabric.js namespace when one is available.\n *\n * @returns {Object|null} The Fabric.js namespace, or null when Fabric is not registered globally.\n * @private\n */\nfunction getGlobalFabric() {\n const scope = getGlobalScope();\n return scope && scope.fabric ? scope.fabric : null;\n}\n\n/**\n * Registers the Fabric.js namespace used by ImageEditor instances.\n *\n * This helper is exported for the package entry wrappers, not as part of the documented package API.\n *\n * @param {Object} [fabricInstance] - Fabric.js namespace object. When omitted, the global `fabric` namespace is used.\n * @returns {Object|null} The active Fabric.js namespace.\n * @private\n */\nexport function setFabric(fabricInstance) {\n fabric = fabricInstance || getGlobalFabric();\n return fabric;\n}\n\n/**\n * Resolves the active Fabric.js namespace, trying the global namespace as a fallback.\n *\n * @returns {Object|null} The active Fabric.js namespace.\n * @private\n */\nfunction ensureFabric() {\n if (!fabric) setFabric();\n return fabric;\n}\n\n/**\n * @callback ImageLoadedCallback\n * @returns {void}\n */\n\n/**\n * @callback EditorErrorCallback\n * @param {*} error - Recoverable error or warning value, when available.\n * @param {string} message - Human-readable context for the error or warning.\n * @returns {void}\n */\n\n/**\n * @callback MaskValueResolver\n * @param {fabric.Canvas} canvas - Active Fabric canvas.\n * @param {Object} options - Editor options.\n * @returns {number} Resolved numeric mask value.\n */\n\n/**\n * @callback MaskFabricGenerator\n * @param {Object} config - Normalized mask configuration.\n * @param {fabric.Canvas} canvas - Active Fabric canvas.\n * @param {Object} options - Editor options.\n * @returns {fabric.Object} Custom Fabric object to use as the mask.\n */\n\n/**\n * @callback MaskCreateCallback\n * @param {fabric.Object} mask - Created mask object.\n * @param {fabric.Canvas} canvas - Active Fabric canvas.\n * @returns {void}\n */\n\n/**\n * @callback MaskLabelTextCallback\n * @param {fabric.Object} mask - Mask object whose label is being created.\n * @param {number} creationIndex - Stable zero-based creation index derived from the mask id.\n * @returns {string} Label text.\n */\n\n/**\n * @typedef {Object} LoadImageOptions\n * @property {boolean} [preserveScroll=false] - If true, keeps the current scroll position while reloading.\n */\n\n /**\n * Fabric.js-based image editor with masking, transform, crop, history, and export helpers.\n *\n * Requires Fabric.js v5.x through the ESM package entry or a globally available `fabric` namespace.\n *\n * @example\n * const editor = new ImageEditor({ canvasWidth: 1024, canvasHeight: 768 });\n * editor.init();\n *\n * @param {Object} [options={}] - Customization options to override defaults.\n * @param {number} [options.canvasWidth=800] - The initial canvas width in pixels.\n * @param {number} [options.canvasHeight=600] - The initial canvas height in pixels.\n * @param {string} [options.backgroundColor='transparent'] - The canvas background color.\n * @param {number} [options.animationDuration=300] - Duration in ms for scale/rotate animations.\n * @param {number} [options.minScale=0.1] - Minimum image scaling factor.\n * @param {number} [options.maxScale=5.0] - Maximum image scaling factor.\n * @param {number} [options.scaleStep=0.05] - Scale increment/decrement per step.\n * @param {number} [options.rotationStep=90] - Rotation step in degrees.\n * @param {boolean} [options.expandCanvasToImage=true] - If true, expands the canvas to fit the loaded image.\n * @param {boolean} [options.fitImageToCanvas=false] - If true, fits loaded image inside canvas.\n * @param {boolean} [options.coverImageToCanvas=false] - If true, scales image to cover the visible canvas viewport.\n * @param {boolean} [options.downsampleOnLoad=true] - Whether to downsample very large images on load.\n * @param {number} [options.downsampleMaxWidth=4000] - Max width for downsampling.\n * @param {number} [options.downsampleMaxHeight=3000] - Max height for downsampling.\n * @param {number} [options.downsampleQuality=0.92] - JPEG quality for downsampling/export.\n * @param {number} [options.imageLoadTimeoutMs=30000] - Timeout for image decode operations.\n * @param {number} [options.exportMultiplier=1] - Scale output image by this multiplier on export.\n * @param {boolean} [options.exportImageAreaByDefault=true] - Export only the image area (clipped to masks).\n * @param {number} [options.defaultMaskWidth=50] - Default width of new masks.\n * @param {number} [options.defaultMaskHeight=80] - Default height of new masks.\n * @param {boolean} [options.maskRotatable=false] - If true, masks can be rotated.\n * @param {boolean} [options.maskLabelOnSelect=true] - Show label on selected mask.\n * @param {number} [options.maskLabelOffset=3] - Offset for mask labels from top-left corner.\n * @param {string} [options.maskName='mask'] - Prefix for mask names/labels.\n * @param {boolean} [options.groupSelection=false] - If true, Fabric can select multiple masks as an ActiveSelection.\n * @param {boolean} [options.showPlaceholder=true] - If true, shows placeholder when no image is loaded.\n * @param {string|null} [options.initialImageBase64=null] - Base64 string to auto-load as initial image, if any.\n * @param {string} [options.defaultDownloadFileName='edited_image.jpg'] - Default file name for downloads.\n * @param {Object} [options.label] - Mask label customization options.\n * @param {MaskLabelTextCallback} [options.label.getText] - Callback for label text; receives a stable zero-based creation index.\n * @param {Object} [options.crop] - Crop mode customization options.\n * @param {ImageLoadedCallback} [options.onImageLoaded] - Callback invoked after an image load completes.\n * @param {EditorErrorCallback} [options.onError] - Callback invoked for recoverable internal errors.\n * @param {EditorErrorCallback} [options.onWarning] - Callback invoked for recoverable internal warnings.\n */\n class ImageEditor {\n constructor(options = {}) {\n // Default options that callers can override with constructor options.\n const defaultLabel = {\n getText: (mask) => mask.maskName,\n textOptions: {\n fontSize: 12,\n fill: '#fff',\n backgroundColor: 'rgba(0,0,0,0.7)',\n padding: 2,\n fontFamily: 'monospace',\n fontWeight: 'bold',\n selectable: false,\n evented: false,\n originX: 'left',\n originY: 'top'\n }\n };\n const defaultCrop = {\n minWidth: 100,\n minHeight: 100,\n padding: 10,\n hideMasksDuringCrop: true,\n preserveMasksAfterCrop: false,\n allowRotationOfCropRect: false\n };\n const userLabel = options.label || {};\n const userCrop = options.crop || {};\n this.options = {\n canvasWidth: 800,\n canvasHeight: 600,\n backgroundColor: 'transparent',\n\n animationDuration: 300,\n minScale: 0.1,\n maxScale: 5.0,\n scaleStep: 0.05,\n rotationStep: 90,\n\n expandCanvasToImage: true,\n fitImageToCanvas: false,\n coverImageToCanvas: false,\n\n downsampleOnLoad: true,\n downsampleMaxWidth: 4000,\n downsampleMaxHeight: 3000,\n downsampleQuality: 0.92,\n preserveSourceFormat: true,\n downsampleMimeType: null,\n imageLoadTimeoutMs: 30000,\n\n exportMultiplier: 1,\n exportImageAreaByDefault: true,\n\n defaultMaskWidth: 50,\n defaultMaskHeight: 80,\n maskRotatable: false,\n maskLabelOnSelect: true,\n maskLabelOffset: 3,\n maskName: 'mask',\n\n groupSelection: false,\n\n showPlaceholder: true,\n initialImageBase64: null, // Provide a base64 'data:image/...' string here if you want auto-load\n\n defaultDownloadFileName: 'edited_image.jpg',\n onError: null,\n onWarning: null,\n\n ...options,\n label: {\n ...defaultLabel,\n ...userLabel,\n textOptions: {\n ...defaultLabel.textOptions,\n ...(userLabel.textOptions || {})\n }\n },\n crop: {\n ...defaultCrop,\n ...userCrop\n }\n };\n\n // Verify that Fabric.js is present before any canvas work starts.\n this._fabricLoaded = !!ensureFabric();\n if (!this._fabricLoaded) {\n this._reportError('fabric.js is not loaded. Please include fabric.js first. Initialization will be aborted.');\n }\n\n // Runtime state owned by this editor instance.\n this.canvas = null;\n this.canvasElement = null;\n this.containerElement = null;\n this.placeholderElement = null;\n\n this.originalImage = null;\n this.baseImageScale = 1;\n this.currentScale = 1;\n this.currentRotation = 0;\n this.maskCounter = 0;\n this.isAnimating = false;\n this._isLoading = false;\n this._activeOperationName = null;\n this._activeOperationToken = null;\n this.elements = {};\n this.isImageLoadedToCanvas = false;\n this.maxHistorySize = 50;\n\n this._handlersByElementKey = {};\n this._elementCache = {};\n this._elementOriginalPointerEvents = new Map();\n\n this._lastMask = null;\n this._lastMaskInitialLeft = null;\n this._lastMaskInitialTop = null;\n this._lastMaskInitialWidth = null;\n this._lastSnapshot = null;\n\n this._cropMode = false;\n this._cropRect = null;\n this._cropHandlers = [];\n this._cropPrevEvented = null;\n this._prevSelectionSetting = undefined;\n this._containerOriginalOverflow = null;\n this._lastContainerViewportSize = null;\n this._canvasElementOriginalStyle = null;\n this._visibilityStateByElement = new WeakMap();\n this._scrollbarSizeCache = null;\n this._activeAnimationRejectors = new Set();\n this._disposed = false;\n this._initialized = false;\n\n this.onImageLoaded = typeof options.onImageLoaded === 'function' ? options.onImageLoaded : null;\n\n this.animationQueue = new AnimationQueue();\n this.historyManager = new HistoryManager(this.maxHistorySize);\n }\n\n /**\n * Backward-compatible alias for {@link ImageEditor#canvasElement}.\n *\n * @deprecated Use canvasElement instead. This alias will be removed in v2.0.0.\n * @returns {HTMLCanvasElement|null} The canvas element currently owned by the editor.\n */\n get canvasEl() {\n return this.canvasElement;\n }\n\n set canvasEl(value) {\n this.canvasElement = value;\n }\n\n /**\n * Backward-compatible alias for {@link ImageEditor#containerElement}.\n *\n * @deprecated Use containerElement instead. This alias will be removed in v2.0.0.\n * @returns {HTMLElement|null} The canvas viewport/container element.\n */\n get containerEl() {\n return this.containerElement;\n }\n\n set containerEl(value) {\n this.containerElement = value;\n }\n\n /**\n * Backward-compatible alias for {@link ImageEditor#placeholderElement}.\n *\n * @deprecated Use placeholderElement instead. This alias will be removed in v2.0.0.\n * @returns {HTMLElement|null} The placeholder element shown before an image loads.\n */\n get placeholderEl() {\n return this.placeholderElement;\n }\n\n set placeholderEl(value) {\n this.placeholderElement = value;\n }\n\n /**\n * Initializes the editor, binds to DOM elements, sets up event handlers,\n * and (optionally) loads an initial image.\n * Use this method to set up the editor UI before interacting with it.\n *\n * @param {Object} [idMap={}] - Optional mapping from logical element names to actual DOM element IDs.\n * Supported keys include: canvas, canvasContainer, imgPlaceholder, scaleRate, rotationLeftInput,\n * rotationRightInput, rotateLeftBtn, rotateRightBtn, addMaskBtn, removeMaskBtn, removeAllMasksBtn,\n * mergeBtn, downloadBtn, maskList, zoomInBtn, zoomOutBtn, resetBtn, undoBtn, redoBtn, imageInput,\n * uploadArea, cropBtn, applyCropBtn, and cancelCropBtn. Unknown keys are ignored.\n *\n * @returns {void}\n *\n * @public\n *\n * @example\n * editor.init({\n * canvas: 'myFabricCanvasId',\n * downloadBtn: 'myDownloadButtonId'\n * });\n */\n init(idMap = {}) {\n if (!this._fabricLoaded) return;\n if (this._initialized || this.canvas) this.dispose();\n this._disposed = false;\n this._initialized = true;\n this.animationQueue = new AnimationQueue();\n this.historyManager = new HistoryManager(this.maxHistorySize);\n this._visibilityStateByElement = new WeakMap();\n this._activeAnimationRejectors = new Set();\n this._isLoading = false;\n this._activeOperationName = null;\n this._activeOperationToken = null;\n this._elementOriginalPointerEvents = new Map();\n this._containerOriginalOverflow = null;\n this._lastContainerViewportSize = null;\n this._canvasElementOriginalStyle = null;\n\n const defaults = {\n canvas: 'fabricCanvas',\n canvasContainer: null, // Pass an ID here if you have a scrollable viewport container\n imgPlaceholder: 'imgPlaceholder',\n scaleRate: 'scaleRate',\n rotationLeftInput: 'rotationLeftInput',\n rotationRightInput: 'rotationRightInput',\n rotateLeftBtn: 'rotateLeftBtn',\n rotateRightBtn: 'rotateRightBtn',\n addMaskBtn: 'addMaskBtn',\n removeMaskBtn: 'removeMaskBtn',\n removeAllMasksBtn: 'removeAllMasksBtn',\n mergeBtn: 'mergeBtn',\n downloadBtn: 'downloadBtn',\n maskList: 'maskList',\n zoomInBtn: 'zoomInBtn',\n zoomOutBtn: 'zoomOutBtn',\n resetBtn: 'resetBtn',\n undoBtn: 'undoBtn',\n redoBtn: 'redoBtn',\n imageInput: 'imageInput',\n cropBtn: 'cropBtn',\n applyCropBtn: 'applyCropBtn',\n cancelCropBtn: 'cancelCropBtn'\n };\n\n this.elements = { ...defaults, ...idMap };\n this._elementCache = {};\n\n this._initCanvas();\n this._bindEvents();\n this._updateInputs();\n this._updateMaskList();\n this._updateUI();\n\n // Auto-load initial image if provided\n if (this.options.initialImageBase64) {\n this.loadImage(this.options.initialImageBase64);\n } else {\n this._updatePlaceholderStatus();\n }\n }\n\n _reportError(message, error = null) {\n const handler = this.options && this.options.onError;\n if (typeof handler !== 'function') return;\n\n try {\n handler(error, message);\n } catch {\n // Ignore observer failures so editor recovery paths remain stable.\n }\n }\n\n _reportWarning(message, error = null) {\n const handler = this.options && this.options.onWarning;\n if (typeof handler !== 'function') return;\n\n try {\n handler(error, message);\n } catch {\n // Ignore observer failures so editor recovery paths remain stable.\n }\n }\n\n /**\n * Initializes the Fabric canvas, viewport elements, and selection event handlers.\n *\n * @returns {void}\n * @private\n */\n _initCanvas() {\n const canvasElement = this._getElement('canvas');\n if (!canvasElement) throw new Error('Canvas is not found: ' + this.elements.canvas);\n this.canvasElement = canvasElement;\n this._canvasElementOriginalStyle = {\n display: canvasElement.style.display || '',\n width: canvasElement.style.width || '',\n height: canvasElement.style.height || '',\n maxWidth: canvasElement.style.maxWidth || ''\n };\n\n // Decide which element acts as the viewport for size fallback and scrolling.\n if (this.elements.canvasContainer) {\n const containerElement = this._getElement('canvasContainer');\n this.containerElement = containerElement || canvasElement.parentElement;\n } else {\n this.containerElement = canvasElement.parentElement;\n }\n\n this.placeholderElement = this._getElement('imgPlaceholder') || null;\n\n // Prefer a measured container size when it is available.\n let initialWidth = this.options.canvasWidth;\n let initialHeight = this.options.canvasHeight;\n if (this.containerElement) {\n const containerWidth = Math.floor(this.containerElement.clientWidth);\n const containerHeight = Math.floor(this.containerElement.clientHeight);\n if (containerWidth > 0 && containerHeight > 0) {\n initialWidth = containerWidth;\n initialHeight = containerHeight;\n\n this._lastContainerViewportSize = {\n width: containerWidth,\n height: containerHeight\n };\n }\n }\n\n this.canvas = new fabric.Canvas(canvasElement, {\n width: initialWidth,\n height: initialHeight,\n backgroundColor: this.options.backgroundColor,\n selection: this.options.groupSelection,\n preserveObjectStacking: true\n });\n\n // Fabric event wiring keeps selection, mask labels, and history in sync.\n this.canvas.on('selection:created', (event) => this._handleSelectionChanged(event.selected));\n this.canvas.on('selection:updated', (event) => this._handleSelectionChanged(event.selected));\n this.canvas.on('selection:cleared', () => this._handleSelectionChanged([]));\n this.canvas.on('object:moving', (event) => { if (event.target && event.target.maskId) this._syncMaskLabel(event.target); });\n this.canvas.on('object:scaling', (event) => { if (event.target && event.target.maskId) this._syncMaskLabel(event.target); });\n this.canvas.on('object:rotating', (event) => { if (event.target && event.target.maskId) this._syncMaskLabel(event.target); });\n this.canvas.on('object:modified', (event) => this._handleObjectModified(event.target));\n\n // Avoid inline-element whitespace artifacts around the canvas.\n this.canvasElement.style.display = 'block';\n }\n\n /**\n * Returns a configured DOM element and caches lookups for hot UI paths.\n *\n * @param {string} key - Key in the configured element map.\n * @returns {HTMLElement|null} The configured element, or null when missing.\n * @private\n */\n _getElement(key) {\n const id = this.elements && this.elements[key];\n if (!id) return null;\n if (this._elementCache && Object.prototype.hasOwnProperty.call(this._elementCache, key)) {\n return this._elementCache[key];\n }\n const element = document.getElementById(id);\n if (this._elementCache) this._elementCache[key] = element || null;\n return element || null;\n }\n\n /**\n * Records a history entry after Fabric finishes modifying one or more masks.\n *\n * @param {fabric.Object|fabric.ActiveSelection|null} target - Modified Fabric object or selection.\n * @returns {void}\n * @private\n */\n _handleObjectModified(target) {\n const masks = this._getModifiedMasks(target);\n if (!masks.length) return;\n masks.forEach(mask => {\n if (typeof mask.setCoords === 'function') mask.setCoords();\n this._syncMaskLabel(mask);\n });\n this._expandCanvasToFitObjects(masks);\n this.saveState();\n }\n\n /**\n * Extracts editable mask objects from a Fabric modification target.\n *\n * @param {fabric.Object|fabric.ActiveSelection|null} target - Fabric object or active selection.\n * @returns {Array<fabric.Object>} Modified mask objects.\n * @private\n */\n _getModifiedMasks(target) {\n if (!target) return [];\n if (target.maskId) return [target];\n\n const objects = typeof target.getObjects === 'function' ? target.getObjects() : [];\n\n return Array.isArray(objects) ? objects.filter(object => object && object.maskId) : [];\n }\n\n /**\n * Updates container overflow behavior for fit and cover image modes.\n *\n * @param {Object} [options={}] - Overflow update options.\n * @param {boolean} [options.preserveScroll=false] - If true, keeps the current scroll offsets.\n * @returns {void}\n * @private\n */\n _syncContainerOverflow(options = {}) {\n if (!this.containerElement || !this.containerElement.style) return;\n this._captureContainerOverflowState();\n\n const shouldPreserveScroll = options.preserveScroll === true;\n if (this.options.coverImageToCanvas) {\n this.containerElement.style.overflow = 'scroll';\n if (!shouldPreserveScroll) {\n this.containerElement.scrollLeft = 0;\n this.containerElement.scrollTop = 0;\n }\n } else if (this.options.fitImageToCanvas) {\n this.containerElement.style.overflow = 'auto';\n if (!shouldPreserveScroll) {\n this.containerElement.scrollLeft = 0;\n this.containerElement.scrollTop = 0;\n }\n } else {\n this._restoreContainerOverflowState();\n }\n }\n\n _captureContainerOverflowState() {\n if (!this.containerElement || !this.containerElement.style || this._containerOriginalOverflow) return;\n this._containerOriginalOverflow = {\n overflow: this.containerElement.style.overflow || '',\n overflowX: this.containerElement.style.overflowX || '',\n overflowY: this.containerElement.style.overflowY || ''\n };\n }\n\n _restoreContainerOverflowState() {\n if (!this.containerElement || !this.containerElement.style || !this._containerOriginalOverflow) return;\n this.containerElement.style.overflow = this._containerOriginalOverflow.overflow;\n this.containerElement.style.overflowX = this._containerOriginalOverflow.overflowX;\n this.containerElement.style.overflowY = this._containerOriginalOverflow.overflowY;\n }\n\n _restoreContainerOverflowSnapshot(snapshot) {\n if (!this.containerElement || !this.containerElement.style || !snapshot) return;\n this.containerElement.style.overflow = snapshot.overflow || '';\n this.containerElement.style.overflowX = snapshot.overflowX || '';\n this.containerElement.style.overflowY = snapshot.overflowY || '';\n }\n\n /** \n * DOM / UI bindings\n * @private\n */\n _bindEvents() {\n // Click anywhere on the upload area opens the native file dialog\n this._bindIfExists('uploadArea', 'click', () => {\n const uploadAreaElement = this._getElement('uploadArea');\n if (this._isElementDisabled(uploadAreaElement)) return;\n this._getElement('imageInput')?.click();\n });\n // File-input change\n this._bindIfExists('imageInput', 'change', (event) => {\n const file = event.target.files && event.target.files[0];\n if (file) {\n this._loadImageFile(file)\n .catch(error => this._reportError('Image file could not be loaded', error))\n .finally(() => {\n event.target.value = '';\n });\n }\n });\n // Zoom & reset\n this._bindIfExists('zoomInBtn', 'click', () => this.scaleImage(this.currentScale + this.options.scaleStep).catch(error => this._reportError('scaleImage failed', error)));\n this._bindIfExists('zoomOutBtn', 'click', () => this.scaleImage(this.currentScale - this.options.scaleStep).catch(error => this._reportError('scaleImage failed', error)));\n this._bindIfExists('resetBtn', 'click', () => { this.resetImageTransform().catch(error => this._reportError('resetImageTransform failed', error)); });\n // Mask management\n this._bindIfExists('addMaskBtn', 'click', () => this.createMask());\n this._bindIfExists('removeMaskBtn', 'click', () => this.removeSelectedMask());\n this._bindIfExists('removeAllMasksBtn', 'click', () => this.removeAllMasks());\n // Merge + download\n this._bindIfExists('mergeBtn', 'click', () => this.mergeMasks().catch(error => this._reportError('merge error', error)));\n this._bindIfExists('downloadBtn', 'click', () => this.downloadImage());\n // Undo + Redo\n this._bindIfExists('undoBtn', 'click', () => this.undo().catch(error => this._reportError('undo failed', error)));\n this._bindIfExists('redoBtn', 'click', () => this.redo().catch(error => this._reportError('redo failed', error)));\n\n // Rotation buttons (step can be overridden by two input fields)\n this._bindIfExists('rotateLeftBtn', 'click', () => {\n const rotationInputElement = this._getElement('rotationLeftInput');\n let step = this.options.rotationStep;\n if (rotationInputElement) {\n const parsedStep = parseFloat(rotationInputElement.value);\n if (!isNaN(parsedStep)) step = parsedStep;\n }\n this.rotateImage(this.currentRotation - step).catch(error => this._reportError('rotateImage failed', error));\n });\n this._bindIfExists('rotateRightBtn', 'click', () => {\n const rotationInputElement = this._getElement('rotationRightInput');\n let step = this.options.rotationStep;\n if (rotationInputElement) {\n const parsedStep = parseFloat(rotationInputElement.value);\n if (!isNaN(parsedStep)) step = parsedStep;\n }\n this.rotateImage(this.currentRotation + step).catch(error => this._reportError('rotateImage failed', error));\n });\n\n // Crop bindings (optional: bound only if element IDs exist in elements)\n this._bindIfExists('cropBtn', 'click', () => this.enterCropMode());\n this._bindIfExists('applyCropBtn', 'click', () => { this.applyCrop().catch(error => this._reportError('applyCrop failed', error)); });\n this._bindIfExists('cancelCropBtn', 'click', () => this.cancelCrop());\n this._bindIfExists('maskList', 'click', (event) => this._handleMaskListClick(event));\n }\n\n /**\n * Binds a DOM event listener when the configured element exists and records it for disposal.\n *\n * @param {string} key - Key in this.elements for the target DOM element.\n * @param {string} eventName - DOM event name to listen for.\n * @param {EventListener} handler - Event listener callback.\n * @private\n */\n _bindIfExists(key, eventName, handler) {\n const element = this._getElement(key);\n if (element) {\n element.addEventListener(eventName, handler);\n this._handlersByElementKey = this._handlersByElementKey || {};\n if (!this._handlersByElementKey[key]) this._handlersByElementKey[key] = [];\n this._handlersByElementKey[key].push({ eventName, handler });\n }\n }\n\n /**\n * Reads an image File as a data URL and loads it into the Fabric canvas.\n *\n * @param {File} file - Image file selected by the user.\n * @returns {Promise<void>} Resolves after the selected file is loaded.\n * @private\n */\n _loadImageFile(file) {\n if (!this._isSupportedImageFile(file)) {\n const error = new Error('Selected file is not a supported image');\n this._reportError('Selected file is not a supported image', error);\n return Promise.reject(error);\n }\n\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = (event) => {\n this.loadImage(event.target.result)\n .then(resolve)\n .catch(reject);\n };\n reader.onerror = (event) => {\n const error = new Error('Image file could not be read');\n this._reportError('Image file could not be read', event);\n reject(error);\n };\n reader.readAsDataURL(file);\n });\n }\n\n _isSupportedImageFile(file) {\n if (!file) return false;\n if (typeof file.type === 'string' && file.type.startsWith('image/')) return true;\n const fileName = String(file.name || '');\n return /\\.(avif|bmp|gif|jpe?g|png|webp)$/i.test(fileName);\n }\n\n /**\n * Warns when more than one mutually exclusive image layout mode is enabled.\n *\n * @returns {void}\n * @private\n */\n _warnOnImageLayoutOptionConflict() {\n const activeModes = [\n ['fitImageToCanvas', this.options.fitImageToCanvas],\n ['coverImageToCanvas', this.options.coverImageToCanvas],\n ['expandCanvasToImage', this.options.expandCanvasToImage]\n ].filter(([, isEnabled]) => !!isEnabled).map(([name]) => name);\n\n if (activeModes.length <= 1) return;\n this._reportWarning(\n `Only one image layout mode should be enabled. Active modes: ${activeModes.join(', ')}.`\n );\n }\n\n /**\n * Loads a base64 data URL into the Fabric canvas as the base image.\n *\n * @async\n * @param {string} imageBase64 - Image data URL beginning with `data:image/`.\n * @param {LoadImageOptions} [options={}] - Optional load behavior.\n * @returns {Promise<void>} Resolves after the Fabric image is added to the canvas.\n * @public\n */\n async loadImage(imageBase64, options = {}) {\n if (!this._fabricLoaded) return;\n if (!this.canvas || this._disposed) return;\n if (!imageBase64 || typeof imageBase64 !== 'string' || !imageBase64.startsWith('data:image/')) return;\n this._assertIdleForOperation('loadImage', options);\n\n this._isLoading = true;\n this._updateUI();\n this._warnOnImageLayoutOptionConflict();\n const transaction = this._captureLoadImageTransaction();\n\n try {\n const imageElement = await this._createImageElement(imageBase64);\n if (this._disposed || !this.canvas) throw new Error('Editor was disposed while loading image');\n\n let loadSource = imageBase64;\n if (this.options.downsampleOnLoad) {\n const shouldResize =\n imageElement.naturalWidth > this.options.downsampleMaxWidth ||\n imageElement.naturalHeight > this.options.downsampleMaxHeight;\n if (shouldResize) {\n const ratio = Math.min(\n this.options.downsampleMaxWidth / imageElement.naturalWidth,\n this.options.downsampleMaxHeight / imageElement.naturalHeight\n );\n const targetWidth = Math.round(imageElement.naturalWidth * ratio);\n const targetHeight = Math.round(imageElement.naturalHeight * ratio);\n loadSource = this._resampleImageToDataURL(\n imageElement,\n targetWidth,\n targetHeight,\n this._normalizeQuality(this.options.downsampleQuality),\n imageBase64\n );\n }\n }\n\n const fabricImage = await this._createFabricImageFromURL(loadSource);\n if (this._disposed || !this.canvas) throw new Error('Editor was disposed while loading image');\n \n this.canvas.discardActiveObject();\n this._hideAllMaskLabels();\n this.canvas.clear();\n this.canvas.setBackgroundColor(this.options.backgroundColor, this.canvas.renderAll.bind(this.canvas));\n\n fabricImage.set({ originX: 'left', originY: 'top', selectable: false, evented: false });\n this._setPlaceholderVisible(false);\n this._syncContainerOverflow({ preserveScroll: options.preserveScroll === true });\n\n const imageWidth = fabricImage.width;\n const imageHeight = fabricImage.height;\n\n const viewport = this._getContainerViewportSize();\n const minWidth = viewport.width;\n const minHeight = viewport.height;\n\n if (this.options.fitImageToCanvas) {\n const canvasWidth = Math.max(1, minWidth - 1);\n const canvasHeight = Math.max(1, minHeight - 1);\n this._setCanvasSizeInt(canvasWidth, canvasHeight);\n const fitScale = Math.min(canvasWidth / imageWidth, canvasHeight / imageHeight, 1);\n fabricImage.set({ left: 0, top: 0 });\n fabricImage.scale(fitScale);\n this.baseImageScale = fabricImage.scaleX || 1;\n } else if (this.options.coverImageToCanvas) {\n const layout = this._calculateCoverCanvasLayout(imageWidth, imageHeight);\n this._setCanvasSizeInt(layout.canvasWidth, layout.canvasHeight);\n fabricImage.set({ left: 0, top: 0 });\n fabricImage.scale(layout.scale);\n this.baseImageScale = fabricImage.scaleX || 1;\n } else if (this.options.expandCanvasToImage) {\n const canvasWidth = Math.max(minWidth, Math.floor(imageWidth));\n const canvasHeight = Math.max(minHeight, Math.floor(imageHeight));\n this._setCanvasSizeInt(canvasWidth, canvasHeight);\n fabricImage.set({ left: 0, top: 0 });\n fabricImage.scale(1);\n this.baseImageScale = 1;\n } else {\n const canvasWidth = Math.max(this.options.canvasWidth, minWidth);\n const canvasHeight = Math.max(this.options.canvasHeight, minHeight);\n this._setCanvasSizeInt(canvasWidth, canvasHeight);\n const fitScale = Math.min(canvasWidth / imageWidth, canvasHeight / imageHeight, 1);\n fabricImage.set({ left: 0, top: 0 });\n fabricImage.scale(fitScale);\n this.baseImageScale = fabricImage.scaleX || 1;\n }\n\n this.originalImage = fabricImage;\n this.canvas.add(fabricImage);\n this.canvas.sendToBack(fabricImage);\n\n this._clearMaskPlacementMemory();\n if (options.resetMaskCounter !== false) this.maskCounter = 0;\n this.currentScale = 1;\n this.currentRotation = 0;\n\n // this._setPlaceholderVisible(false);\n this._updateInputs();\n this._updateMaskList();\n this.isImageLoadedToCanvas = true;\n this._updateUI();\n this.canvas.renderAll();\n this._lastSnapshot = this._captureCanvasStateOrThrow('loadImage');\n\n if (typeof this.onImageLoaded === 'function') {\n this.onImageLoaded();\n }\n } catch (error) {\n await this._rollbackLoadImageTransaction(transaction);\n throw error;\n } finally {\n this._isLoading = false;\n if (!this._disposed && this.canvas) this._updateUI();\n }\n }\n\n /**\n * Checks whether there is a loaded image on the current canvas.\n * @returns {boolean} true if loaded, false if not\n */\n isImageLoaded() {\n const fabricInstance = ensureFabric();\n return !!(\n this.originalImage &&\n fabricInstance &&\n this.originalImage instanceof fabricInstance.Image &&\n this.originalImage.width > 0 &&\n this.originalImage.height > 0\n );\n }\n\n /**\n * Creates an HTMLImageElement from a given data URL.\n * \n * @param {string} dataUrl - A data URL representing the image (e.g., \"data:image/png;base64,...\").\n * @param {number} [timeoutMs=this.options.imageLoadTimeoutMs] - Maximum decode time before rejecting.\n * @returns {Promise<HTMLImageElement>} A promise that resolves to the created image element when loaded, or rejects on error.\n * @private\n */\n _createImageElement(dataUrl, timeoutMs = this.options.imageLoadTimeoutMs) {\n return new Promise((resolve, reject) => {\n const imageElement = new Image();\n let isSettled = false;\n const safeTimeoutMs = Number.isFinite(Number(timeoutMs)) && Number(timeoutMs) > 0\n ? Number(timeoutMs)\n : 30000;\n let timerId;\n const settle = (callback) => {\n if (isSettled) return;\n isSettled = true;\n clearTimeout(timerId);\n imageElement.onload = null;\n imageElement.onerror = null;\n callback();\n };\n timerId = setTimeout(() => {\n settle(() => reject(new Error('Image load timed out')));\n try { imageElement.src = ''; } catch (error) { void error; }\n }, safeTimeoutMs);\n imageElement.onload = () => settle(() => resolve(imageElement));\n imageElement.onerror = (error) => settle(() => reject(error));\n imageElement.src = dataUrl;\n });\n }\n\n _createFabricImageFromURL(dataUrl, timeoutMs = this.options.imageLoadTimeoutMs) {\n return new Promise((resolve, reject) => {\n const safeTimeoutMs = this._getSafeTimeoutMs(timeoutMs);\n let isSettled = false;\n let timerId;\n const settle = (callback) => {\n if (isSettled) return;\n isSettled = true;\n clearTimeout(timerId);\n callback();\n };\n\n timerId = setTimeout(() => {\n settle(() => reject(new Error('Fabric image load timed out')));\n }, safeTimeoutMs);\n\n try {\n fabric.Image.fromURL(dataUrl, (fabricImage) => {\n settle(() => {\n if (!fabricImage) {\n reject(new Error('Image could not be loaded'));\n return;\n }\n resolve(fabricImage);\n });\n }, { crossOrigin: 'anonymous' });\n } catch (error) {\n settle(() => reject(error));\n }\n });\n }\n\n _getSafeTimeoutMs(timeoutMs) {\n const safeTimeoutMs = Number(timeoutMs);\n return Number.isFinite(safeTimeoutMs) && safeTimeoutMs > 0 ? safeTimeoutMs : 30000;\n }\n\n _captureLoadImageTransaction() {\n return {\n canvasState: this._serializeCanvasState(),\n originalImage: this.originalImage,\n baseImageScale: this.baseImageScale,\n currentScale: this.currentScale,\n currentRotation: this.currentRotation,\n maskCounter: this.maskCounter,\n isImageLoadedToCanvas: this.isImageLoadedToCanvas,\n lastSnapshot: this._lastSnapshot,\n lastMask: this._lastMask,\n lastMaskInitialLeft: this._lastMaskInitialLeft,\n lastMaskInitialTop: this._lastMaskInitialTop,\n lastMaskInitialWidth: this._lastMaskInitialWidth,\n containerOverflow: this.containerElement && this.containerElement.style ? {\n overflow: this.containerElement.style.overflow || '',\n overflowX: this.containerElement.style.overflowX || '',\n overflowY: this.containerElement.style.overflowY || ''\n } : null,\n scrollLeft: this.containerElement ? this.containerElement.scrollLeft : 0,\n scrollTop: this.containerElement ? this.containerElement.scrollTop : 0,\n placeholderVisibility: this._captureElementVisibility(this.placeholderElement),\n canvasVisibility: this._captureElementVisibility(this._getCanvasVisibilityElement())\n };\n }\n\n async _rollbackLoadImageTransaction(transaction) {\n if (!transaction || !this.canvas || this._disposed) return;\n let didRestoreCanvasState = false;\n try {\n if (transaction.canvasState) {\n await this.loadFromState(transaction.canvasState);\n didRestoreCanvasState = true;\n }\n } catch (error) {\n this._lastMask = null;\n this._reportError('loadImage rollback failed', error);\n }\n\n this.baseImageScale = transaction.baseImageScale;\n this.currentScale = transaction.currentScale;\n this.currentRotation = transaction.currentRotation;\n this.maskCounter = transaction.maskCounter;\n this.isImageLoadedToCanvas = transaction.isImageLoadedToCanvas;\n this._lastSnapshot = transaction.lastSnapshot;\n if (didRestoreCanvasState) {\n this._restoreLastMaskReference(transaction.lastMask);\n } else {\n this._lastMask = null;\n }\n this._lastMaskInitialLeft = transaction.lastMaskInitialLeft;\n this._lastMaskInitialTop = transaction.lastMaskInitialTop;\n this._lastMaskInitialWidth = transaction.lastMaskInitialWidth;\n this._restoreElementVisibility(this.placeholderElement, transaction.placeholderVisibility);\n this._restoreElementVisibility(this._getCanvasVisibilityElement(), transaction.canvasVisibility);\n if (this.containerElement) {\n this.containerElement.scrollLeft = transaction.scrollLeft;\n this.containerElement.scrollTop = transaction.scrollTop;\n this._restoreContainerOverflowSnapshot(transaction.containerOverflow);\n }\n this._updateInputs();\n this._updateMaskList();\n this._updateUI();\n if (this.canvas) this.canvas.renderAll();\n }\n\n _restoreLastMaskReference(previousLastMask) {\n if (!this.canvas) {\n this._lastMask = null;\n return;\n }\n\n const masks = this.canvas.getObjects().filter(object => object.maskId);\n const previousMaskId = previousLastMask && previousLastMask.maskId;\n this._lastMask = masks.find(mask => mask.maskId === previousMaskId) || masks[masks.length - 1] || null;\n if (!this._lastMask) {\n this._lastMaskInitialLeft = null;\n this._lastMaskInitialTop = null;\n this._lastMaskInitialWidth = null;\n }\n }\n\n /**\n * Resamples the given image element to a new width and height and returns the result as a data URL.\n * \n * @param {HTMLImageElement} imageElement - The image element to resample.\n * @param {number} targetWidth - Target width (in pixels) for the resampled image.\n * @param {number} targetHeight - Target height (in pixels) for the resampled image.\n * @param {number} [quality=0.92] - Image quality between 0 and 1 for lossy formats.\n * @param {string|null} [sourceDataUrl=null] - Source data URL used to preserve alpha-capable formats.\n * @returns {string} A data URL representing the resampled image.\n * @private\n */\n _resampleImageToDataURL(imageElement, targetWidth, targetHeight, quality = 0.92, sourceDataUrl = null) {\n const offscreenCanvas = document.createElement('canvas');\n offscreenCanvas.width = targetWidth;\n offscreenCanvas.height = targetHeight;\n const context = offscreenCanvas.getContext('2d');\n if (!context) throw new Error('2D canvas context is unavailable');\n context.drawImage(imageElement, 0, 0, imageElement.naturalWidth, imageElement.naturalHeight, 0, 0, targetWidth, targetHeight);\n return offscreenCanvas.toDataURL(this._getDownsampleMimeType(sourceDataUrl), quality);\n }\n\n _getDataUrlMimeType(dataUrl) {\n const match = String(dataUrl || '').match(/^data:([^;,]+)[;,]/i);\n return match ? match[1].toLowerCase() : '';\n }\n\n _getDownsampleMimeType(sourceDataUrl) {\n if (this.options.downsampleMimeType) {\n const requestedFormat = this._normalizeImageFormat(this.options.downsampleMimeType);\n return `image/${requestedFormat}`;\n }\n const sourceMimeType = this._getDataUrlMimeType(sourceDataUrl);\n if (this.options.preserveSourceFormat !== false && (sourceMimeType === 'image/png' || sourceMimeType === 'image/webp')) {\n return sourceMimeType;\n }\n return 'image/jpeg';\n }\n\n _captureCanvasStateOrThrow(context) {\n const snapshot = this._serializeCanvasState();\n if (!snapshot) throw new Error(`${context}: canvas state is unavailable`);\n return snapshot;\n }\n\n /** \n * Sets canvas size to integer width and height values to prevent scrollbars due to sub-pixel rendering.\n * Also updates the corresponding style attributes.\n * \n * @param {number} width - Canvas width in pixels.\n * @param {number} height - Canvas height in pixels.\n * @private\n */\n _setCanvasSizeInt(width, height) {\n const integerWidth = Math.max(1, Math.round(Number(width) || 1));\n const integerHeight = Math.max(1, Math.round(Number(height) || 1));\n // Set fabric internal and also style attributes to keep DOM consistent\n this.canvas.setWidth(integerWidth);\n this.canvas.setHeight(integerHeight);\n if (typeof this.canvas.calcOffset === 'function') this.canvas.calcOffset();\n // Keep DOM element in sync (avoid fractional CSS pixels)\n if (this.canvasElement) {\n this.canvasElement.style.width = integerWidth + 'px';\n this.canvasElement.style.height = integerHeight + 'px';\n }\n }\n\n _ceilCanvasDimension(value) {\n const numericValue = Number(value) || 0;\n const roundedValue = Math.round(numericValue);\n if (Math.abs(numericValue - roundedValue) < 0.01) return roundedValue;\n return Math.ceil(numericValue);\n }\n\n _getContainerViewportSize() {\n if (!this.containerElement) {\n return {\n width: Math.max(1, Math.floor(this.options.canvasWidth || 1)),\n height: Math.max(1, Math.floor(this.options.canvasHeight || 1))\n };\n }\n\n const measuredWidth = Math.floor(this.containerElement.clientWidth || 0);\n const measuredHeight = Math.floor(this.containerElement.clientHeight || 0);\n let width = Math.max(1, measuredWidth || this._lastContainerViewportSize?.width || this.options.canvasWidth || 1);\n let height = Math.max(1, measuredHeight || this._lastContainerViewportSize?.height || this.options.canvasHeight || 1);\n\n if (measuredWidth > 0 && measuredHeight > 0) {\n this._lastContainerViewportSize = { width: measuredWidth, height: measuredHeight };\n }\n\n if (this._hasFixedContainerScrollbars()) {\n return { width, height };\n }\n\n const overflow = this._getContainerOverflowValues();\n const canScrollX = overflow.x.some(value => value === 'auto' || value === 'scroll');\n const canScrollY = overflow.y.some(value => value === 'auto' || value === 'scroll');\n const hasHorizontalScrollbar = canScrollX && this.containerElement.scrollWidth > this.containerElement.clientWidth;\n const hasVerticalScrollbar = canScrollY && this.containerElement.scrollHeight > this.containerElement.clientHeight;\n\n if (hasHorizontalScrollbar || hasVerticalScrollbar) {\n const scrollbar = this._getScrollbarSize();\n if (hasVerticalScrollbar) width += scrollbar.width;\n if (hasHorizontalScrollbar) height += scrollbar.height;\n }\n\n return { width, height };\n }\n\n /**\n * Reads inline and computed overflow values for both scroll axes.\n *\n * @returns {{x:string[], y:string[]}} Overflow values grouped by axis.\n * @private\n */\n _getContainerOverflowValues() {\n if (!this.containerElement) return { x: [], y: [] };\n const inlineOverflow = this.containerElement.style.overflow;\n const inlineOverflowX = this.containerElement.style.overflowX;\n const inlineOverflowY = this.containerElement.style.overflowY;\n let computedOverflow = '';\n let computedOverflowX = '';\n let computedOverflowY = '';\n\n if (typeof window !== 'undefined' && typeof window.getComputedStyle === 'function') {\n const style = window.getComputedStyle(this.containerElement);\n computedOverflow = style.overflow;\n computedOverflowX = style.overflowX;\n computedOverflowY = style.overflowY;\n }\n\n return {\n x: [inlineOverflow, inlineOverflowX, computedOverflow, computedOverflowX],\n y: [inlineOverflow, inlineOverflowY, computedOverflow, computedOverflowY]\n };\n }\n\n _hasFixedContainerScrollbars() {\n if (!this.containerElement) return false;\n const overflow = this._getContainerOverflowValues();\n return [...overflow.x, ...overflow.y].some(value => value === 'scroll');\n }\n\n _getScrollbarSize() {\n if (this._scrollbarSizeCache) {\n return { ...this._scrollbarSizeCache };\n }\n if (typeof document === 'undefined' || !document.createElement || !document.body) {\n return { width: 0, height: 0 };\n }\n\n const probe = document.createElement('div');\n probe.style.position = 'absolute';\n probe.style.visibility = 'hidden';\n probe.style.overflow = 'scroll';\n probe.style.width = '100px';\n probe.style.height = '100px';\n probe.style.top = '-9999px';\n document.body.appendChild(probe);\n\n const width = Math.max(0, probe.offsetWidth - probe.clientWidth);\n const height = Math.max(0, probe.offsetHeight - probe.clientHeight);\n document.body.removeChild(probe);\n\n this._scrollbarSizeCache = { width, height };\n return { ...this._scrollbarSizeCache };\n }\n\n _getScrollSafetyMargin() {\n return 2;\n }\n\n _getScrollableCanvasSize(contentWidth, contentHeight, viewport = this._getContainerViewportSize()) {\n if (this._hasFixedContainerScrollbars()) {\n const safetyMargin = this._getScrollSafetyMargin();\n const safeWidth = Math.max(1, viewport.width - safetyMargin);\n const safeHeight = Math.max(1, viewport.height - safetyMargin);\n return {\n width: contentWidth > viewport.width + 0.5 ? this._ceilCanvasDimension(contentWidth) : safeWidth,\n height: contentHeight > viewport.height + 0.5 ? this._ceilCanvasDimension(contentHeight) : safeHeight,\n viewportWidth: viewport.width,\n viewportHeight: viewport.height,\n hasHorizontal: true,\n hasVertical: true\n };\n }\n\n const scrollbar = this._getScrollbarSize();\n let hasVertical = false;\n let hasHorizontal = false;\n let effectiveWidth;\n let effectiveHeight;\n\n for (let i = 0; i < 4; i += 1) {\n effectiveWidth = Math.max(1, viewport.width - (hasVertical ? scrollbar.width : 0));\n effectiveHeight = Math.max(1, viewport.height - (hasHorizontal ? scrollbar.height : 0));\n\n const nextHasVertical = contentHeight > effectiveHeight + 0.5;\n const nextHasHorizontal = contentWidth > effectiveWidth + 0.5;\n\n if (nextHasVertical === hasVertical && nextHasHorizontal === hasHorizontal) break;\n hasVertical = nextHasVertical;\n hasHorizontal = nextHasHorizontal;\n }\n\n effectiveWidth = Math.max(1, viewport.width - (hasVertical ? scrollbar.width : 0));\n effectiveHeight = Math.max(1, viewport.height - (hasHorizontal ? scrollbar.height : 0));\n\n return {\n width: hasHorizontal ? this._ceilCanvasDimension(contentWidth) : effectiveWidth,\n height: hasVertical ? this._ceilCanvasDimension(contentHeight) : effectiveHeight,\n viewportWidth: effectiveWidth,\n viewportHeight: effectiveHeight,\n hasHorizontal,\n hasVertical\n };\n }\n\n _calculateCoverCanvasLayout(imageWidth, imageHeight) {\n const viewport = this._getContainerViewportSize();\n\n if (this._hasFixedContainerScrollbars()) {\n const safetyMargin = this._getScrollSafetyMargin();\n const targetWidth = Math.max(1, viewport.width - safetyMargin);\n const targetHeight = Math.max(1, viewport.height - safetyMargin);\n const scale = Math.min(1, Math.max(targetWidth / imageWidth, targetHeight / imageHeight));\n const contentWidth = imageWidth * scale;\n const contentHeight = imageHeight * scale;\n const canvasSize = this._getScrollableCanvasSize(contentWidth, contentHeight, viewport);\n return {\n scale,\n canvasWidth: canvasSize.width,\n canvasHeight: canvasSize.height\n };\n }\n\n const scrollbar = this._getScrollbarSize();\n let hasVertical = false;\n let hasHorizontal = false;\n let scale = 1;\n let contentWidth = imageWidth;\n let contentHeight = imageHeight;\n let effectiveWidth;\n let effectiveHeight;\n\n for (let i = 0; i < 4; i += 1) {\n effectiveWidth = Math.max(1, viewport.width - (hasVertical ? scrollbar.width : 0));\n effectiveHeight = Math.max(1, viewport.height - (hasHorizontal ? scrollbar.height : 0));\n scale = Math.min(1, Math.max(effectiveWidth / imageWidth, effectiveHeight / imageHeight));\n contentWidth = imageWidth * scale;\n contentHeight = imageHeight * scale;\n\n const nextHasVertical = contentHeight > effectiveHeight + 0.5;\n const nextHasHorizontal = contentWidth > effectiveWidth + 0.5;\n\n if (nextHasVertical === hasVertical && nextHasHorizontal === hasHorizontal) break;\n hasVertical = nextHasVertical;\n hasHorizontal = nextHasHorizontal;\n }\n\n const canvasSize = this._getScrollableCanvasSize(contentWidth, contentHeight, viewport);\n return {\n scale,\n canvasWidth: canvasSize.width,\n canvasHeight: canvasSize.height\n };\n }\n\n _getStateProperties() {\n return [\n 'maskId',\n 'maskName',\n 'maskLabel',\n 'isCropRect',\n 'originalAlpha',\n 'originalStroke',\n 'originalStrokeWidth',\n 'selectable',\n 'evented',\n 'hasControls',\n 'lockRotation',\n 'borderColor',\n 'cornerColor',\n 'cornerSize',\n 'transparentCorners',\n 'strokeUniform',\n 'strokeDashArray'\n ];\n }\n\n _getMaskNormalStyle(mask) {\n const strokeWidth = Number(mask && mask.originalStrokeWidth);\n const opacity = Number(mask && mask.originalAlpha);\n const style = {\n stroke: (mask && mask.originalStroke) || '#ccc',\n strokeWidth: Number.isFinite(strokeWidth) ? strokeWidth : 1\n };\n if (Number.isFinite(opacity)) style.opacity = opacity;\n return style;\n }\n\n _withNormalizedMaskStyles(callback) {\n if (!this.canvas) return callback();\n const masks = this.canvas.getObjects().filter(object => object.maskId);\n const maskStyleBackups = [];\n\n try {\n masks.forEach(mask => {\n const normalStyle = this._getMaskNormalStyle(mask);\n const stylePatch = {};\n Object.keys(normalStyle).forEach(property => {\n if (mask[property] !== normalStyle[property]) {\n stylePatch[property] = normalStyle[property];\n }\n });\n const changedProperties = Object.keys(stylePatch);\n if (!changedProperties.length) return;\n\n const backup = { object: mask };\n changedProperties.forEach(property => {\n backup[property] = mask[property];\n });\n maskStyleBackups.push(backup);\n mask.set(stylePatch);\n });\n const result = callback();\n if (result && typeof result.then === 'function') {\n throw new Error('_withNormalizedMaskStyles callback must be synchronous');\n }\n return result;\n } finally {\n maskStyleBackups.forEach(backup => {\n try {\n const restorePatch = {};\n Object.keys(backup).forEach(property => {\n if (property !== 'object') restorePatch[property] = backup[property];\n });\n backup.object.set(restorePatch);\n } catch (error) { void error; }\n });\n }\n }\n\n _restoreMaskControls(mask) {\n if (!mask) return;\n\n const cornerSize = Number(mask.cornerSize);\n mask.set({\n selectable: mask.selectable !== false,\n evented: mask.evented !== false,\n hasControls: mask.hasControls !== false,\n lockRotation: typeof mask.lockRotation === 'boolean' ? mask.lockRotation : !this.options.maskRotatable,\n borderColor: mask.borderColor || 'red',\n cornerColor: mask.cornerColor || 'black',\n cornerSize: Number.isFinite(cornerSize) ? cornerSize : 8,\n transparentCorners: mask.transparentCorners === true,\n strokeUniform: mask.strokeUniform !== false\n });\n if (typeof mask.setCoords === 'function') mask.setCoords();\n }\n\n /**\n * Captures editor-owned runtime state that Fabric does not include in canvas JSON.\n *\n * @returns {{version:number, baseImageScale:number, currentScale:number, currentRotation:number, maskCounter:number}} Serializable editor metadata.\n * @private\n */\n _serializeEditorMetadata() {\n const baseImageScale = Number(this.baseImageScale);\n const currentScale = Number(this.currentScale);\n const currentRotation = Number(this.currentRotation);\n const maskCounter = Number(this.maskCounter);\n\n return {\n version: 1,\n baseImageScale: Number.isFinite(baseImageScale) && baseImageScale > 0 ? baseImageScale : 1,\n currentScale: Number.isFinite(currentScale) && currentScale > 0 ? currentScale : 1,\n currentRotation: Number.isFinite(currentRotation) ? currentRotation : 0,\n maskCounter: Number.isFinite(maskCounter) && maskCounter > 0 ? Math.floor(maskCounter) : 0\n };\n }\n\n _serializeCanvasState() {\n if (!this.canvas) return null;\n return this._withNormalizedMaskStyles(() => {\n const jsonObject = this.canvas.toJSON(this._getStateProperties());\n if (Array.isArray(jsonObject.objects)) {\n jsonObject.objects = jsonObject.objects.filter(object => !object.isCropRect && !object.maskLabel);\n }\n jsonObject.imageEditorMetadata = this._serializeEditorMetadata();\n return JSON.stringify(jsonObject);\n });\n }\n\n /**\n * Normalizes a lossy image quality value to Fabric/canvas's 0..1 range.\n *\n * @param {number} quality - Requested image quality.\n * @returns {number} A finite quality value between 0 and 1.\n * @private\n */\n _normalizeQuality(quality, fallback = undefined) {\n const fallbackQuality = fallback == null ? this.options.downsampleQuality : fallback;\n const numericFallback = fallbackQuality == null ? NaN : Number(fallbackQuality);\n const safeFallback = Number.isFinite(numericFallback)\n ? Math.max(0, Math.min(1, numericFallback))\n : 0.92;\n if (quality == null) return safeFallback;\n const numericQuality = Number(quality);\n if (!Number.isFinite(numericQuality)) return safeFallback;\n return Math.max(0, Math.min(1, numericQuality));\n }\n\n /**\n * Normalizes public image format aliases to canvas export format names.\n *\n * @param {string} format - Requested image format or MIME type.\n * @returns {'jpeg'|'png'|'webp'} Canvas-compatible image format.\n * @private\n */\n _normalizeImageFormat(format) {\n const typeMapping = {\n 'jpeg': 'jpeg',\n 'jpg': 'jpeg',\n 'image/jpeg': 'jpeg',\n 'png': 'png',\n 'image/png': 'png',\n 'webp': 'webp',\n 'image/webp': 'webp'\n };\n return typeMapping[String(format || 'jpeg').toLowerCase()] || 'jpeg';\n }\n\n /**\n * Converts a bounding rectangle into a canvas-safe integer source region.\n *\n * @param {{left:number, top:number, width:number, height:number}} bounds - Bounds in canvas coordinates.\n * @param {Object} [options={}] - Region rounding options.\n * @param {boolean} [options.includePartialPixels=true] - If false, excludes partially covered trailing pixels.\n * @returns {{sourceX:number, sourceY:number, sourceWidth:number, sourceHeight:number}} Clamped source region.\n * @private\n */\n _getClampedCanvasRegion(bounds, options = {}) {\n const canvasWidth = Math.max(1, Math.round(this.canvas.getWidth()));\n const canvasHeight = Math.max(1, Math.round(this.canvas.getHeight()));\n const left = Number(bounds.left) || 0;\n const top = Number(bounds.top) || 0;\n const width = Math.max(0, Number(bounds.width) || 0);\n const height = Math.max(0, Number(bounds.height) || 0);\n const includePartialPixels = options.includePartialPixels !== false;\n const roundEnd = includePartialPixels ? Math.ceil : Math.floor;\n const sourceX = Math.min(canvasWidth - 1, Math.max(0, Math.floor(left)));\n const sourceY = Math.min(canvasHeight - 1, Math.max(0, Math.floor(top)));\n const endX = Math.min(canvasWidth, Math.max(sourceX + 1, roundEnd(left + width)));\n const endY = Math.min(canvasHeight, Math.max(sourceY + 1, roundEnd(top + height)));\n\n return {\n sourceX,\n sourceY,\n sourceWidth: Math.max(1, endX - sourceX),\n sourceHeight: Math.max(1, endY - sourceY)\n };\n }\n\n _hasFractionalCanvasEdge(value) {\n const numericValue = Number(value);\n if (!Number.isFinite(numericValue)) return false;\n return Math.abs(numericValue - Math.round(numericValue)) > 0.01;\n }\n\n _getPartialExportEdges(bounds) {\n if (!bounds) return null;\n const angle = Math.abs((Number(this.originalImage && this.originalImage.angle) || 0) % 90);\n const isAxisAligned = angle < 0.01 || Math.abs(angle - 90) < 0.01;\n if (!isAxisAligned) return null;\n\n return {\n left: this._hasFractionalCanvasEdge(bounds.left),\n top: this._hasFractionalCanvasEdge(bounds.top),\n right: this._hasFractionalCanvasEdge((Number(bounds.left) || 0) + (Number(bounds.width) || 0)),\n bottom: this._hasFractionalCanvasEdge((Number(bounds.top) || 0) + (Number(bounds.height) || 0))\n };\n }\n\n async _sealPartialTransparentEdges(dataUrl, edges) {\n if (!edges || !Object.values(edges).some(Boolean)) return dataUrl;\n\n const imageElement = await this._createImageElement(dataUrl);\n const width = Math.max(1, imageElement.naturalWidth || imageElement.width || 1);\n const height = Math.max(1, imageElement.naturalHeight || imageElement.height || 1);\n const offscreenCanvas = document.createElement('canvas');\n offscreenCanvas.width = width;\n offscreenCanvas.height = height;\n const context = offscreenCanvas.getContext('2d');\n if (!context) throw new Error('2D canvas context is unavailable');\n context.drawImage(imageElement, 0, 0, width, height);\n\n const imageData = context.getImageData(0, 0, width, height);\n const pixels = imageData.data;\n const sealPixel = (x, y, fallbackX, fallbackY) => {\n const index = (y * width + x) * 4;\n const fallbackIndex = (fallbackY * width + fallbackX) * 4;\n if (pixels[index + 3] === 0 && pixels[fallbackIndex + 3] > 0) {\n pixels[index] = pixels[fallbackIndex];\n pixels[index + 1] = pixels[fallbackIndex + 1];\n pixels[index + 2] = pixels[fallbackIndex + 2];\n pixels[index + 3] = pixels[fallbackIndex + 3];\n }\n if (pixels[index + 3] > 0 && pixels[index + 3] < 255) {\n pixels[index + 3] = 255;\n }\n };\n\n if (edges.left && width > 1) {\n for (let y = 0; y < height; y += 1) sealPixel(0, y, 1, y);\n }\n if (edges.right && width > 1) {\n for (let y = 0; y < height; y += 1) sealPixel(width - 1, y, width - 2, y);\n }\n if (edges.top && height > 1) {\n for (let x = 0; x < width; x += 1) sealPixel(x, 0, x, 1);\n }\n if (edges.bottom && height > 1) {\n for (let x = 0; x < width; x += 1) sealPixel(x, height - 1, x, height - 2);\n }\n\n context.putImageData(imageData, 0, 0);\n return offscreenCanvas.toDataURL('image/png');\n }\n\n /**\n * Exports a source region directly through Fabric's region export options.\n *\n * @param {Object} region - Canvas source region and export options.\n * @param {number} region.sourceX - Source region x coordinate.\n * @param {number} region.sourceY - Source region y coordinate.\n * @param {number} region.sourceWidth - Source region width.\n * @param {number} region.sourceHeight - Source region height.\n * @param {number} [region.multiplier=1] - Export multiplier.\n * @param {number} [region.quality=0.92] - Output image quality for lossy formats.\n * @param {'jpeg'|'png'|'webp'} [region.format='jpeg'] - Output image format.\n * @param {Object|null} [region.sealPartialEdges=null] - Fractional canvas edges whose alpha should be sealed.\n * @returns {Promise<string>} Resolves with an image data URL for the cropped region.\n * @private\n */\n async _exportCanvasRegionToDataURL({ sourceX, sourceY, sourceWidth, sourceHeight, multiplier = 1, quality = 0.92, format = 'jpeg', sealPartialEdges = null }) {\n const safeMultiplier = Math.max(1, Number(multiplier) || 1);\n const safeFormat = this._normalizeImageFormat(format);\n const exportFormat = safeFormat === 'jpeg' ? 'png' : safeFormat;\n let regionDataUrl = this.canvas.toDataURL({\n format: exportFormat,\n quality,\n multiplier: safeMultiplier,\n left: sourceX,\n top: sourceY,\n width: sourceWidth,\n height: sourceHeight\n });\n\n regionDataUrl = await this._sealPartialTransparentEdges(regionDataUrl, sealPartialEdges);\n if (safeFormat !== 'jpeg') return regionDataUrl;\n return this._convertDataUrlToOpaqueJpeg(regionDataUrl, quality);\n }\n\n async _convertDataUrlToOpaqueJpeg(dataUrl, quality = 0.92) {\n const imageElement = await this._createImageElement(dataUrl);\n const width = Math.max(1, imageElement.naturalWidth || imageElement.width || 1);\n const height = Math.max(1, imageElement.naturalHeight || imageElement.height || 1);\n const offscreenCanvas = document.createElement('canvas');\n offscreenCanvas.width = width;\n offscreenCanvas.height = height;\n const context = offscreenCanvas.getContext('2d');\n if (!context) throw new Error('2D canvas context is unavailable');\n context.fillStyle = this._getJpegBackgroundColor();\n context.fillRect(0, 0, width, height);\n context.drawImage(imageElement, 0, 0, width, height);\n return offscreenCanvas.toDataURL('image/jpeg', this._normalizeQuality(quality));\n }\n\n _getJpegBackgroundColor() {\n const backgroundColor = String(this.options.backgroundColor || '').trim();\n if (!backgroundColor || backgroundColor === 'transparent') return '#ffffff';\n if (/^rgba\\([^)]*,\\s*0(?:\\.0+)?\\s*\\)$/i.test(backgroundColor)) return '#ffffff';\n return backgroundColor;\n }\n\n /** \n * Gets the top-left corner coordinates of the given object.\n * Used for geometry calculations (e.g., scale, rotate).\n * \n * @param {Object} fabricObject - The object for which to get the top-left coordinates. Should support setCoords and getCoords/getBoundingRect methods.\n * @returns {{x: number, y: number}} The top-left corner point as an object with x and y properties.\n * @private\n */\n _getObjectTopLeftPoint(fabricObject) {\n if (!fabricObject) return { x: 0, y: 0 };\n fabricObject.setCoords();\n const boundingRect = fabricObject.getBoundingRect(true, true);\n return { x: boundingRect.left, y: boundingRect.top };\n }\n\n _getObjectCoordinateTopLeftPoint(fabricObject) {\n if (!fabricObject) return { x: 0, y: 0 };\n fabricObject.setCoords();\n const coords = typeof fabricObject.getCoords === 'function' ? fabricObject.getCoords() : null;\n if (coords && coords.length) return coords[0];\n return this._getObjectTopLeftPoint(fabricObject);\n }\n\n _getObjectOriginPoint(fabricObject, originX, originY) {\n if (!fabricObject) return { x: 0, y: 0 };\n if (typeof fabricObject.getPointByOrigin === 'function') {\n return fabricObject.getPointByOrigin(originX, originY);\n }\n return this._getObjectTopLeftPoint(fabricObject);\n }\n\n _translateObjectByCanvasOffset(fabricObject, deltaX, deltaY) {\n if (!fabricObject) return;\n if (typeof fabricObject.getCenterPoint === 'function' && typeof fabricObject.setPositionByOrigin === 'function') {\n const center = fabricObject.getCenterPoint();\n const nextCenter = new fabric.Point(center.x + deltaX, center.y + deltaY);\n fabricObject.setPositionByOrigin(nextCenter, 'center', 'center');\n } else {\n fabricObject.set({\n left: (fabricObject.left || 0) + deltaX,\n top: (fabricObject.top || 0) + deltaY\n });\n }\n fabricObject.setCoords();\n }\n\n /**\n * Sets the object's origin at the specified origin point, keeping a reference point fixed in position.\n * \n * @param {Object} fabricObject - The object to modify. Should support set, setPositionByOrigin, and setCoords.\n * @param {string} originX - The new originX (\"left\", \"center\", \"right\", etc.).\n * @param {string} originY - The new originY (\"top\", \"center\", \"bottom\", etc.).\n * @param {{x: number, y: number}} refPoint - The point to keep fixed while setting the new origins.\n * @private\n */\n _setObjectOriginKeepingPosition(fabricObject, originX, originY, refPoint) {\n if (!fabricObject || !refPoint || !fabricObject.setPositionByOrigin) return;\n fabricObject.set({ originX, originY });\n fabricObject.setPositionByOrigin(refPoint, originX, originY);\n fabricObject.setCoords();\n }\n\n /**\n * Moves the object so its bounding box aligns with the canvas's top-left corner (0, 0).\n * \n * @param {Object} fabricObject - The object to align.\n * @private\n */\n _alignObjectBoundingBoxToCanvasTopLeft(fabricObject) {\n if (!fabricObject) return;\n fabricObject.setCoords();\n const boundingRect = fabricObject.getBoundingRect(true, true);\n const deltaX = boundingRect.left;\n const deltaY = boundingRect.top;\n fabricObject.set({ left: (fabricObject.left || 0) - deltaX, top: (fabricObject.top || 0) - deltaY });\n fabricObject.setCoords();\n this.canvas.renderAll();\n }\n\n /**\n * Updates the canvas size to match the bounding box of the original image,\n * ensuring that the canvas is always at least as large as its container.\n * @private\n */\n _updateCanvasSizeToImageBounds() {\n if (!this.originalImage) return;\n this.originalImage.setCoords();\n const imageBounds = this.originalImage.getBoundingRect(true, true);\n\n const size = this._getScrollableCanvasSize(imageBounds.width, imageBounds.height);\n this._setCanvasSizeInt(size.width, size.height);\n }\n\n /**\n * Whether post-load edits should resize the canvas to keep transformed content visible.\n *\n * @returns {boolean} True when canvas bounds should follow edited image or mask bounds.\n * @private\n */\n _shouldResizeCanvasToContentBounds() {\n return !!(this.options.expandCanvasToImage || this.options.coverImageToCanvas || this.options.fitImageToCanvas);\n }\n\n /**\n * Expands the canvas once so all provided objects remain visible after an edit.\n *\n * @param {Array<fabric.Object>} fabricObjects - Objects whose bounds should fit inside the canvas.\n * @param {number} [padding=10] - Extra canvas space after the farthest object edge.\n * @returns {void}\n * @private\n */\n _expandCanvasToFitObjects(fabricObjects, padding = 10) {\n if (!this.canvas || !Array.isArray(fabricObjects) || !fabricObjects.length || !this._shouldResizeCanvasToContentBounds()) return;\n try {\n const currentWidth = this.canvas.getWidth();\n const currentHeight = this.canvas.getHeight();\n let requiredWidth = currentWidth;\n let requiredHeight = currentHeight;\n fabricObjects.forEach(fabricObject => {\n if (!fabricObject) return;\n if (typeof fabricObject.setCoords === 'function') fabricObject.setCoords();\n const boundingRect = fabricObject.getBoundingRect(true, true);\n requiredWidth = Math.max(requiredWidth, Math.ceil(boundingRect.left + boundingRect.width + padding));\n requiredHeight = Math.max(requiredHeight, Math.ceil(boundingRect.top + boundingRect.height + padding));\n });\n const shouldUseScrollSafeViewport = this.options.fitImageToCanvas || this.options.coverImageToCanvas;\n\n let minWidth = 0;\n let minHeight = 0;\n if (shouldUseScrollSafeViewport) {\n const viewport = this._getContainerViewportSize();\n const safetyMargin = this._getScrollSafetyMargin();\n\n minWidth = Math.max(1, viewport.width - safetyMargin);\n minHeight = Math.max(1, viewport.height - safetyMargin);\n } else if (this.containerElement) {\n minWidth = Math.floor(this.containerElement.clientWidth || 0);\n minHeight = Math.floor(this.containerElement.clientHeight || 0);\n }\n const newWidth = Math.max(currentWidth, minWidth, requiredWidth);\n const newHeight = Math.max(currentHeight, minHeight, requiredHeight);\n if (newWidth !== currentWidth || newHeight !== currentHeight) {\n this._setCanvasSizeInt(newWidth, newHeight);\n }\n } catch (error) {\n this._reportWarning('expandCanvasToFitObjects: failed to expand canvas', error);\n }\n }\n\n /**\n * Expands the canvas so one object remains visible after an edit.\n *\n * @param {fabric.Object} fabricObject - Object whose bounds should fit inside the canvas.\n * @param {number} [padding=10] - Extra canvas space after the object edge.\n * @returns {void}\n * @private\n */\n _expandCanvasToFitObject(fabricObject, padding = 10) {\n this._expandCanvasToFitObjects([fabricObject], padding);\n }\n\n /** \n * Scales the original image by a given factor, with animation.\n * Returns a promise that resolves when the scale animation is complete.\n * @param {number} factor - The scaling factor (will be clamped between `options.minScale` and `options.maxScale`).\n * @returns {Promise<void>} Promise that resolves once the scaling animation finishes.\n * @public\n */\n scaleImage(factor, options = {}) {\n try {\n this._assertCanQueueAnimation('scaleImage', options);\n } catch (error) {\n return Promise.reject(error);\n }\n return this.animationQueue.add(() => this._scaleImageImpl(factor, options))\n .finally(() => {\n if (!this._disposed && this.canvas) this._updateUI();\n });\n }\n\n _getInternalOperationToken(options) {\n return options && options[INTERNAL_OPERATION_TOKEN];\n }\n\n _isOwnInternalOperation(options) {\n const token = this._getInternalOperationToken(options);\n return !!token && token === this._activeOperationToken;\n }\n\n _beginBusyOperation(operationName) {\n const token = Symbol(operationName);\n this._activeOperationName = operationName;\n this._activeOperationToken = token;\n this._updateUI();\n return token;\n }\n\n _endBusyOperation(token) {\n if (token && token === this._activeOperationToken) {\n this._activeOperationName = null;\n this._activeOperationToken = null;\n this._updateUI();\n }\n }\n\n _withInternalOperationOptions(token, options = {}) {\n return {\n ...options,\n [INTERNAL_OPERATION_TOKEN]: token\n };\n }\n\n _assertEditorAvailable(operationName) {\n if (this._disposed || !this.canvas) throw new Error(`${operationName} cannot run after the editor has been disposed`);\n }\n\n _assertIdleForOperation(operationName, options = {}) {\n this._assertEditorAvailable(operationName);\n const isOwnInternalOperation = this._isOwnInternalOperation(options);\n if (this.isAnimating || (this.animationQueue && this.animationQueue.isBusy())) {\n throw new Error(`${operationName} cannot run while an animation is running`);\n }\n if (this._isLoading && !isOwnInternalOperation) {\n throw new Error(`${operationName} cannot run while an image is loading`);\n }\n if (this._activeOperationToken && !isOwnInternalOperation) {\n throw new Error(`${operationName} cannot run while ${this._activeOperationName || 'another operation'} is running`);\n }\n }\n\n _assertCanQueueAnimation(operationName, options = {}) {\n this._assertEditorAvailable(operationName);\n if (this._isLoading && !this._isOwnInternalOperation(options)) {\n throw new Error(`${operationName} cannot run while an image is loading`);\n }\n if (this._activeOperationToken && !this._isOwnInternalOperation(options)) {\n throw new Error(`${operationName} cannot run while ${this._activeOperationName || 'another operation'} is running`);\n }\n }\n\n _canMutateNow(operationName, options = {}) {\n try {\n this._assertIdleForOperation(operationName, options);\n return true;\n } catch (error) {\n this._reportError(`${operationName} blocked`, error);\n return false;\n }\n }\n\n _rejectActiveAnimations(reason) {\n const error = reason instanceof Error ? reason : new Error(String(reason || 'Animation cancelled'));\n this._activeAnimationRejectors.forEach(reject => {\n try { reject(error); } catch (rejectError) { void rejectError; }\n });\n this._activeAnimationRejectors.clear();\n }\n\n _animateFabricProperty(fabricObject, property, value) {\n return new Promise((resolve, reject) => {\n if (this._disposed || !this.canvas || !fabricObject) {\n reject(new Error('Animation cannot start after editor disposal'));\n return;\n }\n\n let isSettled = false;\n const duration = Math.max(0, Number(this.options.animationDuration) || 0);\n const timeoutMs = Math.max(1000, duration + 1000);\n let timerId;\n const settle = (callback) => {\n if (isSettled) return;\n isSettled = true;\n clearTimeout(timerId);\n this._activeAnimationRejectors.delete(reject);\n callback();\n };\n\n this._activeAnimationRejectors.add(reject);\n timerId = setTimeout(() => {\n settle(() => reject(new Error(`Animation timed out while changing ${property}`)));\n }, timeoutMs);\n\n try {\n fabricObject.animate(property, value, {\n duration,\n onChange: () => {\n if (!this._disposed && this.canvas) this.canvas.renderAll();\n },\n onComplete: () => settle(resolve)\n });\n } catch (error) {\n settle(() => reject(error));\n }\n });\n }\n\n /** \n * Scales the original image by a given factor, with animation.\n * Returns a promise that resolves when the scale animation is complete.\n * @param {number} factor - The scaling factor (will be clamped between `options.minScale` and `options.maxScale`).\n * @returns {Promise<void>} Promise that resolves once the scaling animation finishes.\n * @private\n */\n async _scaleImageImpl(factor, options = {}) {\n if (!this.originalImage || this._disposed) return;\n if (this.isAnimating) return;\n const saveHistory = options.saveHistory !== false;\n let didStartAnimation = false;\n try {\n factor = Math.max(this.options.minScale, Math.min(this.options.maxScale, factor));\n this.currentScale = factor;\n this.isAnimating = true;\n didStartAnimation = true;\n this._updateUI();\n\n const targetScale = this.baseImageScale * factor;\n\n const topLeft = this._getObjectTopLeftPoint(this.originalImage);\n this._setObjectOriginKeepingPosition(this.originalImage, 'left', 'top', topLeft);\n\n await Promise.all([\n this._animateFabricProperty(this.originalImage, 'scaleX', targetScale),\n this._animateFabricProperty(this.originalImage, 'scaleY', targetScale)\n ]);\n if (this._disposed || !this.canvas || !this.originalImage) throw new Error('Editor was disposed during scale animation');\n\n this.originalImage.set({ scaleX: targetScale, scaleY: targetScale });\n this.originalImage.setCoords();\n\n if (this._shouldResizeCanvasToContentBounds()) {\n this._updateCanvasSizeToImageBounds();\n }\n\n this._alignObjectBoundingBoxToCanvasTopLeft(this.originalImage);\n\n this.canvas.getObjects().forEach(object => { if (object.maskId) this._syncMaskLabel(object); });\n\n this._updateInputs();\n if (saveHistory) this.saveState();\n } finally {\n if (didStartAnimation) {\n this.isAnimating = false;\n this._updateInputs();\n this._updateUI();\n }\n }\n }\n\n /** \n * Rotates the original image by a given number of degrees, with animation.\n * Returns a promise that resolves when the rotation animation is complete.\n * @param {number} degrees - The angle in degrees to rotate the image.\n * @returns {Promise<void>} Promise that resolves once the rotation animation finishes.\n * @public\n */\n rotateImage(degrees, options = {}) {\n try {\n this._assertCanQueueAnimation('rotateImage', options);\n } catch (error) {\n return Promise.reject(error);\n }\n return this.animationQueue.add(() => this._rotateImageImpl(degrees, options))\n .finally(() => {\n if (!this._disposed && this.canvas) this._updateUI();\n });\n }\n\n /** \n * Rotates the original image by a given number of degrees, with animation.\n * Returns a promise that resolves when the rotation animation is complete.\n * @param {number} degrees - The angle in degrees to rotate the image.\n * @returns {Promise<void>} Promise that resolves once the rotation animation finishes.\n * @private\n */\n async _rotateImageImpl(degrees, options = {}) {\n if (!this.originalImage || this._disposed) return;\n if (this.isAnimating) return;\n if (isNaN(degrees)) return;\n const saveHistory = options.saveHistory !== false;\n const image = this.originalImage;\n const previousOriginX = image.originX || 'left';\n const previousOriginY = image.originY || 'top';\n const previousOriginPoint = this._getObjectOriginPoint(image, previousOriginX, previousOriginY);\n let didStartAnimation = false;\n let didCompleteRotation = false;\n try {\n this.currentRotation = degrees;\n this.isAnimating = true;\n didStartAnimation = true;\n this._updateUI();\n\n const center = image.getCenterPoint();\n this._setObjectOriginKeepingPosition(image, 'center', 'center', center);\n\n await this._animateFabricProperty(image, 'angle', degrees);\n if (this._disposed || !this.canvas || !this.originalImage) throw new Error('Editor was disposed during rotation animation');\n\n this.originalImage.set('angle', degrees);\n this.originalImage.setCoords();\n\n if (this._shouldResizeCanvasToContentBounds()) {\n this._updateCanvasSizeToImageBounds();\n }\n\n this._alignObjectBoundingBoxToCanvasTopLeft(this.originalImage);\n\n const newTopLeft = this._getObjectCoordinateTopLeftPoint(this.originalImage);\n this._setObjectOriginKeepingPosition(this.originalImage, 'left', 'top', newTopLeft);\n\n this.canvas.getObjects().forEach(object => { if (object.maskId) this._syncMaskLabel(object); });\n\n this._updateInputs();\n if (saveHistory) this.saveState();\n didCompleteRotation = true;\n } finally {\n if (!didCompleteRotation && !this._disposed && image) {\n this._setObjectOriginKeepingPosition(image, previousOriginX, previousOriginY, previousOriginPoint);\n }\n if (didStartAnimation) {\n this.isAnimating = false;\n this._updateInputs();\n this._updateUI();\n }\n }\n }\n\n /**\n * Resets the image transform: scales to 1 and rotates to 0 degrees.\n *\n * @returns {Promise<void>} Resolves when the reset history transition has been recorded.\n * @public\n */\n resetImageTransform() {\n if (!this.originalImage) return Promise.resolve();\n try {\n this._assertCanQueueAnimation('resetImageTransform');\n } catch (error) {\n return Promise.reject(error);\n }\n\n return this.animationQueue.add(async () => {\n const before = this._lastSnapshot || this._captureCanvasStateOrThrow('resetImageTransform');\n await this._scaleImageImpl(1, { saveHistory: false });\n await this._rotateImageImpl(0, { saveHistory: false });\n const after = this._captureCanvasStateOrThrow('resetImageTransform');\n this._pushStateTransition(before, after);\n }).finally(() => {\n if (!this._disposed && this.canvas) this._updateUI();\n }).catch(error => {\n this._reportError('resetImageTransform() failed', error);\n throw error;\n });\n }\n\n /**\n * Backward-compatible alias for {@link ImageEditor#resetImageTransform}.\n *\n * @deprecated Use resetImageTransform() instead. This alias will be removed in v2.0.0.\n * @returns {Promise<void>} Resolves when the image transform reset is complete.\n */\n reset() {\n return this.resetImageTransform();\n }\n\n /**\n * Restores a serialized canvas state and rebinds editor-specific mask/image metadata.\n *\n * @param {string|Object} serializedState - State returned by `_serializeCanvasState()` as a JSON string or object.\n * @returns {Promise<void>} Resolves after Fabric has loaded the state and UI state has been refreshed.\n * @public\n */\n loadFromState(serializedState) {\n if (!serializedState || !this.canvas || this._disposed) return Promise.resolve();\n if (this._cropMode || this._cropRect) {\n this._removeCropRect();\n this._restoreCropObjectState();\n this._cropMode = false;\n if (this._prevSelectionSetting !== undefined && this.canvas) {\n this.canvas.selection = !!this._prevSelectionSetting;\n }\n this._prevSelectionSetting = undefined;\n }\n\n return new Promise((resolve, reject) => {\n try {\n const state = (typeof serializedState === 'string')\n ? JSON.parse(serializedState)\n : serializedState;\n const editorMetadata = state && state.imageEditorMetadata ? state.imageEditorMetadata : null;\n\n this.canvas.loadFromJSON(state, async () => {\n try {\n if (this._disposed || !this.canvas) {\n reject(new Error('Editor was disposed while loading state'));\n return;\n }\n await this._waitForFabricImagesReady(this.canvas.getObjects());\n if (this._disposed || !this.canvas) {\n reject(new Error('Editor was disposed while loading state'));\n return;\n }\n this._hideAllMaskLabels();\n const canvasObjects = this.canvas.getObjects();\n this.originalImage = canvasObjects.find(object => object.type === 'image' && !object.maskId) || null;\n\n if (this.originalImage) {\n this.originalImage.set({ originX: 'left', originY: 'top', selectable: false, evented: false, hasControls: false, hoverCursor: 'default' });\n this.canvas.sendToBack(this.originalImage);\n const restoredBaseScale = Number(editorMetadata && editorMetadata.baseImageScale);\n const restoredCurrentScale = Number(editorMetadata && editorMetadata.currentScale);\n const restoredCurrentRotation = Number(editorMetadata && editorMetadata.currentRotation);\n\n if (Number.isFinite(restoredBaseScale) && restoredBaseScale > 0) {\n this.baseImageScale = restoredBaseScale;\n }\n\n if (Number.isFinite(restoredCurrentScale) && restoredCurrentScale > 0) {\n this.currentScale = restoredCurrentScale;\n } else {\n const baseScale = Number(this.baseImageScale) || 1;\n const imageScale = Number(this.originalImage.scaleX) || baseScale;\n this.currentScale = imageScale / baseScale;\n }\n\n this.currentRotation = Number.isFinite(restoredCurrentRotation)\n ? restoredCurrentRotation\n : (Number(this.originalImage.angle) || 0);\n } else {\n this.baseImageScale = 1;\n this.currentScale = 1;\n this.currentRotation = 0;\n }\n\n const masks = canvasObjects.filter(object => object.maskId);\n masks.forEach(mask => {\n this._restoreMaskControls(mask);\n this._rebindMaskEvents(mask);\n mask.set(this._getMaskNormalStyle(mask));\n });\n const restoredMaskCounter = Number(editorMetadata && editorMetadata.maskCounter);\n const maxMaskId = masks.reduce((max, mask) =>\n Math.max(max, mask.maskId), 0);\n this.maskCounter = Number.isFinite(restoredMaskCounter) && restoredMaskCounter >= maxMaskId\n ? Math.floor(restoredMaskCounter)\n : maxMaskId;\n this._lastMask = masks.length ? masks[masks.length - 1] : null;\n if (!this._lastMask) {\n this._lastMaskInitialLeft = null;\n this._lastMaskInitialTop = null;\n this._lastMaskInitialWidth = null;\n }\n this.isImageLoadedToCanvas = !!this.originalImage;\n\n this.canvas.renderAll();\n this._updateInputs();\n this._updateMaskList();\n this._updatePlaceholderStatus();\n this._lastSnapshot = this._serializeCanvasState();\n this._updateUI();\n resolve();\n } catch (callbackError) {\n this._reportError('loadFromState() failed', callbackError);\n reject(callbackError);\n }\n });\n\n } catch (error) {\n this._reportError('loadFromState() failed', error);\n reject(error);\n }\n });\n }\n\n async _waitForFabricImagesReady(canvasObjects) {\n const imageObjects = (canvasObjects || []).filter(object => object && object.type === 'image');\n await Promise.all(imageObjects.map(object => this._waitForImageElementReady(\n typeof object.getElement === 'function' ? object.getElement() : object._element\n )));\n }\n\n _waitForImageElementReady(imageElement) {\n if (!imageElement) return Promise.resolve();\n if (imageElement.complete || imageElement.naturalWidth > 0 || imageElement.width > 0) return Promise.resolve();\n return new Promise((resolve, reject) => {\n let isSettled = false;\n const timerId = setTimeout(() => {\n settle(() => reject(new Error('Image load timed out while restoring state')));\n }, this._getSafeTimeoutMs(this.options.imageLoadTimeoutMs));\n const settle = (callback) => {\n if (isSettled) return;\n isSettled = true;\n clearTimeout(timerId);\n if (typeof imageElement.removeEventListener === 'function') {\n imageElement.removeEventListener('load', handleLoad);\n imageElement.removeEventListener('error', handleError);\n } else {\n imageElement.onload = null;\n imageElement.onerror = null;\n }\n callback();\n };\n const handleLoad = () => settle(resolve);\n const handleError = (error) => settle(() => reject(error));\n if (typeof imageElement.addEventListener === 'function') {\n imageElement.addEventListener('load', handleLoad, { once: true });\n imageElement.addEventListener('error', handleError, { once: true });\n } else {\n imageElement.onload = handleLoad;\n imageElement.onerror = handleError;\n }\n });\n }\n\n /**\n * Saves the current editable canvas state as an undoable history transition.\n *\n * Labels are hidden before serialization because labels are UI overlays, while mask metadata is kept on\n * mask objects and restored by `loadFromState()`.\n *\n * @returns {void}\n * @public\n */\n saveState() {\n if (!this.canvas) return;\n\n try {\n const after = this._captureCanvasStateOrThrow('saveState');\n const before = this._lastSnapshot || after;\n if (after === before) return;\n let executedOnce = false;\n\n const command = new Command(\n () => {\n if (executedOnce) {\n return this.loadFromState(after);\n }\n executedOnce = true;\n return undefined;\n },\n () => this.loadFromState(before)\n );\n\n this.historyManager.execute(command);\n this._lastSnapshot = after;\n } catch (error) {\n this._reportWarning('saveState: failed to save canvas snapshot', error);\n } finally {\n this._updateUI();\n }\n }\n\n /**\n * Pushes a precomputed before/after state transition into history.\n *\n * Use this for operations such as crop and merge that build their snapshots around asynchronous image\n * loading, where the \"after\" state is already applied before the history command is recorded.\n *\n * @param {string} before - Serialized state before the operation.\n * @param {string} after - Serialized state after the operation.\n * @returns {void}\n * @private\n */\n _pushStateTransition(before, after) {\n if (!before || !after) {\n this._reportWarning('History transition skipped because a canvas snapshot is unavailable');\n return;\n }\n if (before === after) return;\n if (!this.historyManager) this.historyManager = new HistoryManager(this.maxHistorySize || 50);\n\n const command = new Command(\n () => this.loadFromState(after),\n () => this.loadFromState(before)\n );\n this.historyManager.push(command);\n this._lastSnapshot = after;\n this._updateUI();\n }\n\n /**\n * Undo the last state change, if possible.\n *\n * @returns {Promise<void>} Resolves after the history manager finishes the queued undo.\n * @public\n */\n undo() {\n return this.historyManager.undo()\n .then(() => { this._updateUI(); })\n .catch(error => {\n this._reportError('undo failed', error);\n throw error;\n });\n }\n\n /**\n * Redo the next state change, if possible.\n *\n * @returns {Promise<void>} Resolves after the history manager finishes the queued redo.\n * @public\n */\n redo() {\n return this.historyManager.redo()\n .then(() => { this._updateUI(); })\n .catch(error => {\n this._reportError('redo failed', error);\n throw error;\n });\n }\n\n _rebindMaskEvents(mask) {\n if (!mask) return;\n if (mask.__imageEditorMaskHandlers) {\n try {\n mask.off('mouseover', mask.__imageEditorMaskHandlers.mouseover);\n mask.off('mouseout', mask.__imageEditorMaskHandlers.mouseout);\n } catch (error) { void error; }\n }\n\n const metadata = {};\n if (!Number.isFinite(Number(mask.originalAlpha))) {\n metadata.originalAlpha = Number.isFinite(Number(mask.opacity)) ? Number(mask.opacity) : 0.5;\n }\n if (!mask.originalStroke) metadata.originalStroke = mask.stroke || '#ccc';\n if (!Number.isFinite(Number(mask.originalStrokeWidth))) {\n metadata.originalStrokeWidth = Number.isFinite(Number(mask.strokeWidth)) ? Number(mask.strokeWidth) : 1;\n }\n if (Object.keys(metadata).length) mask.set(metadata);\n\n const mouseover = () => {\n const opacity = Number(mask.originalAlpha);\n mask.set({\n stroke: '#ff5500',\n strokeWidth: 2,\n opacity: Math.min((Number.isFinite(opacity) ? opacity : 0.5) + 0.2, 1)\n });\n if (mask.canvas) mask.canvas.requestRenderAll();\n };\n const mouseout = () => {\n mask.set(this._getMaskNormalStyle(mask));\n if (mask.canvas) mask.canvas.requestRenderAll();\n };\n\n mask.on('mouseover', mouseover);\n mask.on('mouseout', mouseout);\n mask.__imageEditorMaskHandlers = { mouseover, mouseout };\n }\n\n /**\n * Creates a mask and adds it to the canvas.\n *\n * Placement is based on explicit `left`/`top` values when provided; otherwise each new mask is placed\n * after the previously created mask. Fabric object properties are applied through `set()` and `setCoords()`\n * so controls and hit testing stay in sync with Fabric 5.x behavior.\n *\n * @param {Object} [config={}] - Optional mask configuration overrides.\n * @param {string} [config.shape='rect'] - Mask shape: `rect`, `circle`, `ellipse`, `polygon`, or a custom shape handled by `fabricGenerator`.\n * @param {Array<{x:number,y:number}>|Array<Array<number>>} [config.points] - Polygon points.\n * @param {number|string|MaskValueResolver} [config.width] - Width in pixels, percentage string, or resolver callback.\n * @param {number|string|MaskValueResolver} [config.height] - Height in pixels, percentage string, or resolver callback.\n * @param {number|string|MaskValueResolver} [config.radius] - Circle radius in pixels, percentage string, or resolver callback.\n * @param {number|string|MaskValueResolver} [config.rx] - Ellipse horizontal radius or rectangle corner radius.\n * @param {number|string|MaskValueResolver} [config.ry] - Ellipse vertical radius or rectangle corner radius.\n * @param {number|string|MaskValueResolver} [config.left] - Left position in pixels, percentage string, or resolver callback.\n * @param {number|string|MaskValueResolver} [config.top] - Top position in pixels, percentage string, or resolver callback.\n * @param {number} [config.angle=0] - Rotation angle in degrees.\n * @param {string} [config.color='rgba(0,0,0,0.5)'] - Fill color.\n * @param {number} [config.alpha=0.5] - Opacity from 0 to 1.\n * @param {boolean} [config.selectable=true] - Whether the mask can be selected.\n * @param {boolean} [config.hasControls=true] - Whether Fabric transform controls are shown.\n * @param {Object} [config.styles] - Additional Fabric style properties, such as `stroke` or `strokeDashArray`.\n * @param {MaskFabricGenerator} [config.fabricGenerator] - Factory callback that returns a custom Fabric object.\n * @param {MaskCreateCallback} [config.onCreate] - Callback invoked after the mask is added to the canvas.\n * @returns {fabric.Object|null} The created mask object, or null if the canvas is not initialized.\n * @public\n */\n createMask(config = {}) {\n if (!this.canvas) return null;\n if (!this._canMutateNow('createMask')) return null;\n const shapeType = config.shape || 'rect';\n // Normalize mask defaults before applying caller-provided overrides.\n const maskConfig = {\n shape: shapeType,\n width: this.options.defaultMaskWidth,\n height: this.options.defaultMaskHeight,\n color: 'rgba(0,0,0,0.5)',\n alpha: 0.5,\n gap: 5,\n left: undefined,\n top: undefined,\n angle: 0,\n selectable: true,\n ...config\n };\n\n // Always start placement relative to canvas left/top.\n const firstOffset = 10;\n let left;\n let top;\n\n const getCanvasBasis = (axis) => {\n const canvasWidth = this.canvas ? this.canvas.getWidth() : 0;\n const canvasHeight = this.canvas ? this.canvas.getHeight() : 0;\n if (axis === 'height') return canvasHeight;\n if (axis === 'min') return Math.min(canvasWidth, canvasHeight);\n return canvasWidth;\n };\n\n const resolveValue = (value, fallback, axis = 'width') => {\n if (typeof value === 'function')\n return value(this.canvas, this.options);\n if (typeof value === 'string' && value.endsWith('%')) {\n const percent = Number.parseFloat(value) / 100;\n if (!Number.isFinite(percent)) return fallback;\n return Math.floor(getCanvasBasis(axis) * percent);\n }\n return value != null ? value : fallback;\n };\n\n if (maskConfig.left === undefined && this._lastMask) {\n const previousMask = this._lastMask;\n if (typeof previousMask.setCoords === 'function') previousMask.setCoords();\n const previousBounds = typeof previousMask.getBoundingRect === 'function'\n ? previousMask.getBoundingRect(true, true)\n : { left: previousMask.left || firstOffset, top: previousMask.top || firstOffset, width: previousMask.width || 0 };\n left = Math.round(previousBounds.left + previousBounds.width + maskConfig.gap);\n top = Math.round(previousBounds.top ?? firstOffset);\n } else {\n left = resolveValue(maskConfig.left, firstOffset, 'width');\n top = resolveValue(maskConfig.top, firstOffset, 'height');\n }\n\n maskConfig.width = resolveValue(maskConfig.width, this.options.defaultMaskWidth, 'width');\n maskConfig.height = resolveValue(maskConfig.height, this.options.defaultMaskHeight, 'height');\n maskConfig.left = left;\n maskConfig.top = top;\n\n let mask;\n if (typeof maskConfig.fabricGenerator === 'function') {\n mask = maskConfig.fabricGenerator(maskConfig, this.canvas, this.options);\n } else {\n switch (shapeType) {\n case 'circle':\n mask = new fabric.Circle({\n left, top,\n radius: resolveValue(maskConfig.radius, Math.min(maskConfig.width, maskConfig.height) / 2, 'min'),\n fill: maskConfig.color,\n opacity: maskConfig.alpha,\n angle: maskConfig.angle,\n ...maskConfig.styles\n });\n break;\n case 'ellipse':\n mask = new fabric.Ellipse({\n left, top,\n rx: resolveValue(maskConfig.rx, maskConfig.width / 2, 'width'),\n ry: resolveValue(maskConfig.ry, maskConfig.height / 2, 'height'),\n fill: maskConfig.color,\n opacity: maskConfig.alpha,\n angle: maskConfig.angle,\n ...maskConfig.styles\n });\n break;\n case 'polygon': {\n let polygonPoints = maskConfig.points || [];\n if (Array.isArray(polygonPoints) && polygonPoints.length) {\n // Ensure numeric {x,y} objects for fabric.Polygon.\n polygonPoints = polygonPoints.map(point => Array.isArray(point)\n ? { x: Number(point[0]), y: Number(point[1]) }\n : { x: Number(point.x), y: Number(point.y) });\n }\n mask = new fabric.Polygon(polygonPoints, {\n left, top,\n fill: maskConfig.color,\n opacity: maskConfig.alpha,\n angle: maskConfig.angle,\n ...maskConfig.styles\n });\n break;\n }\n case 'rect':\n default:\n mask = new fabric.Rect({\n left, top,\n width: resolveValue(maskConfig.width, this.options.defaultMaskWidth, 'width'),\n height: resolveValue(maskConfig.height, this.options.defaultMaskHeight, 'height'),\n fill: maskConfig.color,\n opacity: maskConfig.alpha,\n angle: maskConfig.angle,\n rx: maskConfig.rx,\n ry: maskConfig.ry,\n ...maskConfig.styles\n });\n }\n }\n\n const styles = maskConfig.styles || {};\n const hasStyle = property => Object.prototype.hasOwnProperty.call(styles, property);\n const maskSettings = {\n selectable: maskConfig.selectable !== false,\n hasControls: ('hasControls' in maskConfig) ? maskConfig.hasControls : true,\n lockRotation: !this.options.maskRotatable,\n borderColor: ('borderColor' in maskConfig) ? maskConfig.borderColor : 'red',\n cornerColor: ('cornerColor' in maskConfig) ? maskConfig.cornerColor : 'black',\n cornerSize: ('cornerSize' in maskConfig) ? maskConfig.cornerSize : 8,\n transparentCorners: ('transparentCorners' in maskConfig) ? maskConfig.transparentCorners : false,\n stroke: hasStyle('stroke') ? styles.stroke : '#ccc',\n strokeWidth: hasStyle('strokeWidth') ? styles.strokeWidth : 1,\n opacity: hasStyle('opacity') ? styles.opacity : maskConfig.alpha,\n strokeUniform: ('strokeUniform' in maskConfig) ? maskConfig.strokeUniform : (hasStyle('strokeUniform') ? styles.strokeUniform : true)\n };\n if (hasStyle('strokeDashArray')) maskSettings.strokeDashArray = styles.strokeDashArray;\n mask.set(maskSettings);\n mask.setCoords();\n\n mask.set({\n originalAlpha: Number.isFinite(Number(mask.opacity)) ? Number(mask.opacity) : maskConfig.alpha,\n originalStroke: mask.stroke || '#ccc',\n originalStrokeWidth: Number.isFinite(Number(mask.strokeWidth)) ? Number(mask.strokeWidth) : 1\n });\n this._rebindMaskEvents(mask);\n this._expandCanvasToFitObject(mask);\n\n // Store placement values so the next mask can be positioned beside this one.\n this._lastMaskInitialLeft = left;\n this._lastMaskInitialTop = top;\n this._lastMaskInitialWidth = resolveValue(maskConfig.width, this.options.defaultMaskWidth, 'width');\n\n const maskId = ++this.maskCounter;\n mask.set({\n maskId,\n maskName: `${this.options.maskName}${maskId}`\n });\n this._lastMask = mask;\n\n this.canvas.add(mask);\n this.canvas.bringToFront(mask);\n if (maskConfig.selectable) this.canvas.setActiveObject(mask);\n this._handleSelectionChanged([mask]);\n this._updateMaskList();\n this._updateUI();\n this.canvas.renderAll();\n this.saveState();\n\n if (typeof maskConfig.onCreate === 'function') maskConfig.onCreate(mask, this.canvas);\n return mask;\n }\n\n /**\n * Backward-compatible alias for {@link ImageEditor#createMask}.\n *\n * @deprecated Use createMask() instead. This alias will be removed in v2.0.0.\n * @param {Object} [config={}] - Mask configuration passed to createMask().\n * @returns {fabric.Object|null} The created mask object, or null if the canvas is not initialized.\n */\n addMask(config = {}) {\n return this.createMask(config);\n }\n\n /**\n * Removes the currently selected mask from the canvas, if any.\n * The associated label is also removed. UI and mask list are updated.\n */\n removeSelectedMask() {\n if (!this.canvas) return;\n if (!this._canMutateNow('removeSelectedMask')) return;\n const activeObject = this.canvas.getActiveObject();\n const selectedMasks = this._getModifiedMasks(activeObject);\n if (!selectedMasks.length) return;\n\n this.canvas.discardActiveObject();\n selectedMasks.forEach(mask => {\n this._removeLabelForMask(mask);\n this.canvas.remove(mask);\n });\n\n const masks = this.canvas.getObjects().filter(object => object.maskId);\n this._lastMask = masks.length ? masks[masks.length - 1] : null;\n if (!this._lastMask) {\n this._lastMaskInitialLeft = null;\n this._lastMaskInitialTop = null;\n this._lastMaskInitialWidth = null;\n }\n this._updateMaskList();\n this._updateUI();\n this.canvas.renderAll();\n this.saveState();\n }\n\n /**\n * Removes all masks from the canvas, including their labels.\n * UI and internal mask placement memory are reset.\n */\n removeAllMasks(options = {}) {\n if (!this.canvas) return;\n if (!this._canMutateNow('removeAllMasks', options)) return;\n const saveHistory = options.saveHistory !== false;\n const masks = this.canvas.getObjects().filter(object => object.maskId);\n masks.forEach(mask => this._removeLabelForMask(mask));\n masks.forEach(mask => this.canvas.remove(mask));\n this.canvas.discardActiveObject();\n this._lastMask = null;\n this._lastMaskInitialLeft = null;\n this._lastMaskInitialTop = null;\n this._lastMaskInitialWidth = null;\n this._updateMaskList();\n this._updateUI();\n this.canvas.renderAll();\n if (saveHistory) this.saveState();\n }\n\n /**\n * Removes the label associated with the specified mask object, if it exists.\n * \n * @param {fabric.Object} mask - The mask object whose label should be removed.\n * @private\n */\n _removeLabelForMask(mask) {\n if (!mask || !this.canvas) return;\n if (mask.__label) {\n try {\n const canvasObjects = this.canvas.getObjects();\n if (canvasObjects.includes(mask.__label)) {\n this.canvas.remove(mask.__label);\n }\n } catch (error) { void error; }\n try { delete mask.__label; } catch (error) { void error; }\n }\n }\n\n /**\n * Returns a stable zero-based creation index for label callbacks.\n *\n * Mask ids are one-based and are not renumbered after deletion, so this value remains stable for the\n * lifetime of a mask.\n *\n * @param {fabric.Object} mask - Mask object.\n * @returns {number} Stable zero-based creation index.\n * @private\n */\n _getMaskCreationIndex(mask) {\n const maskId = Number(mask && mask.maskId);\n if (Number.isFinite(maskId) && maskId > 0) return Math.floor(maskId) - 1;\n\n const masks = this.canvas ? this.canvas.getObjects().filter(object => object.maskId) : [];\n return Math.max(0, masks.indexOf(mask));\n }\n\n /**\n * Creates and adds a custom label (fabric.Text or fabric.IText) for the mask.\n * The label is default bound to the top-left of the mask and managed as a non-interactive overlay.\n * \n * @param {fabric.Object} mask - The mask to create a label for.\n * @private\n */\n _createLabelForMask(mask) {\n if (!mask || !this.options.maskLabelOnSelect) return;\n this._removeLabelForMask(mask);\n let textObject = null;\n if (this.options.label && typeof this.options.label.create === 'function') {\n textObject = this.options.label.create(mask, fabric);\n if (!textObject || typeof textObject.set !== 'function') {\n this._reportWarning('label.create() returned an invalid Fabric object; using the default label');\n textObject = null;\n }\n }\n if (!textObject) {\n let labelText = mask.maskName;\n let textOptions = {\n left: 0,\n top: 0,\n fontSize: 12,\n fill: '#fff',\n backgroundColor: 'rgba(0,0,0,0.7)',\n selectable: false,\n evented: false,\n padding: 2,\n originX: 'left',\n originY: 'top'\n };\n if (this.options.label) {\n if (typeof this.options.label.getText === 'function') {\n labelText = this.options.label.getText(mask, this._getMaskCreationIndex(mask));\n }\n // Merge external styles\n if (this.options.label.textOptions) {\n Object.assign(textOptions, this.options.label.textOptions);\n }\n }\n textObject = new fabric.Text(labelText, textOptions);\n }\n\n textObject.maskLabel = true;\n mask.__label = textObject;\n this.canvas.add(textObject);\n this.canvas.bringToFront(textObject);\n this._syncMaskLabel(mask);\n }\n\n /**\n * Hides (removes) all mask labels from the canvas.\n * Internal label references on mask objects are also deleted.\n * @private\n */\n _hideAllMaskLabels() {\n if (!this.canvas) return;\n const canvasObjects = this.canvas.getObjects();\n const labels = canvasObjects.filter(object => object.maskLabel);\n labels.forEach(label => {\n try {\n if (canvasObjects.includes(label)) this.canvas.remove(label);\n } catch (error) { void error; }\n });\n canvasObjects.forEach(object => {\n if (object.maskId && object.__label) {\n try { delete object.__label; } catch (error) { void error; }\n }\n });\n }\n\n /**\n * Synchronizes the position, angle, and visibility of the mask's label so that it appears properly above the mask.\n * \n * @param {fabric.Object} mask - The mask whose label should be repositioned.\n * @private\n */\n _syncMaskLabel(mask) {\n if (!mask) return;\n if (!this.options.maskLabelOnSelect) return;\n if (!mask.__label) return;\n\n if (typeof mask.setCoords === 'function') mask.setCoords();\n const bounds = mask.getBoundingRect ? mask.getBoundingRect(true, true) : null;\n if (!bounds) return;\n\n const tl = { x: bounds.left, y: bounds.top };\n const center = mask.getCenterPoint();\n\n const vx = center.x - tl.x;\n const vy = center.y - tl.y;\n const dist = Math.sqrt(vx * vx + vy * vy) || 1;\n const ux = vx / dist;\n const uy = vy / dist;\n\n const offset = Math.max(0, this.options.maskLabelOffset ?? 3);\n\n const px = tl.x + ux * offset;\n const py = tl.y + uy * offset;\n\n mask.__label.set({\n left: Math.round(px),\n top: Math.round(py),\n angle: mask.angle || 0,\n originX: 'left',\n originY: 'top',\n visible: true\n });\n mask.__label.setCoords();\n if (typeof this.canvas.requestRenderAll === 'function') {\n this.canvas.requestRenderAll();\n } else {\n this.canvas.renderAll();\n }\n }\n\n /**\n * Shows the label for the given mask, creating it if necessary and synchronizing its position.\n * \n * @param {fabric.Object} mask - The mask whose label should be shown.\n * @private\n */\n _showLabelForMask(mask) {\n if (!mask) return;\n if (!this.options.maskLabelOnSelect) return;\n if (!mask.__label) this._createLabelForMask(mask);\n mask.__label.set({ visible: true });\n this._syncMaskLabel(mask);\n }\n\n /**\n * Handles changes to the selection of canvas objects (masks),\n * updates mask stroke and label display, and syncs mask list selection.\n *\n * @param {Array<Object>} selected - The currently selected objects (e.g. [mask] or []).\n * @private\n */\n _handleSelectionChanged(selected) {\n const selectedMask = (selected || []).find(object => object.maskId);\n const masks = this.canvas.getObjects().filter(object => object.maskId);\n masks.forEach(mask => {\n if (mask !== selectedMask) {\n if (mask.__label) {\n try { this.canvas.remove(mask.__label); } catch (error) { void error; }\n delete mask.__label;\n }\n const originalStrokeWidth = Number(mask.originalStrokeWidth);\n mask.set({\n stroke: mask.originalStroke || '#ccc',\n strokeWidth: Number.isFinite(originalStrokeWidth) ? originalStrokeWidth : 1\n });\n } else {\n mask.set({ stroke: '#ff0000', strokeWidth: 1 });\n }\n });\n\n if (selectedMask) this._showLabelForMask(selectedMask);\n\n this._updateMaskListSelection(selectedMask);\n this.canvas.renderAll();\n this._updateUI();\n }\n\n /**\n * Updates the mask list in the DOM to reflect the current masks on the canvas.\n * Each list entry becomes a clickable element for mask selection.\n * @private\n */\n _updateMaskList() {\n const maskListElement = this._getElement('maskList');\n if (!maskListElement) return;\n maskListElement.innerHTML = '';\n const masks = this.canvas.getObjects().filter(object => object.maskId);\n masks.forEach(mask => {\n const listItemElement = document.createElement('li');\n listItemElement.className = 'list-group-item mask-item';\n listItemElement.textContent = mask.maskName;\n listItemElement.dataset.maskId = String(mask.maskId);\n maskListElement.appendChild(listItemElement);\n });\n }\n\n _handleMaskListClick(event) {\n if (!this.canvas) return;\n const itemElement = event.target && event.target.closest ? event.target.closest('.mask-item') : null;\n if (!itemElement || !itemElement.dataset) return;\n const maskId = Number(itemElement.dataset.maskId);\n const mask = this.canvas.getObjects().find(object => Number(object.maskId) === maskId);\n if (!mask) return;\n this.canvas.setActiveObject(mask);\n this._handleSelectionChanged([mask]);\n }\n\n /**\n * Updates the visual selection (CSS 'active') state for the mask list in the DOM.\n * \n * @param {Object|null} selectedMask - The currently selected mask, or null if none selected.\n * @private\n */\n _updateMaskListSelection(selectedMask) {\n const maskListElement = this._getElement('maskList');\n if (!maskListElement) return;\n const maskItems = maskListElement.querySelectorAll('.mask-item');\n maskItems.forEach(item => {\n const isSelected = !!selectedMask && Number(item.dataset.maskId) === Number(selectedMask.maskId);\n item.classList.toggle('active', isSelected);\n item.classList.toggle('selected', isSelected);\n });\n }\n\n /**\n * Flattens the current masks into the base image and reloads the flattened image.\n *\n * This removes editable mask objects after export and records the operation as one undoable history transition.\n * It does nothing when no base image or no masks exist.\n *\n * @async\n * @returns {Promise<void>} Resolves when the flattened image has been loaded.\n * @public\n */\n async mergeMasks() {\n if (!this.originalImage) return;\n this._assertIdleForOperation('mergeMasks');\n const masks = this.canvas.getObjects().filter(object => object.maskId);\n if (!masks.length) return;\n const beforeJson = this._serializeCanvasState();\n const operationToken = this._beginBusyOperation('mergeMasks');\n\n this.canvas.discardActiveObject();\n this.canvas.renderAll();\n\n try {\n const merged = await this.exportImageBase64(this._withInternalOperationOptions(operationToken, {\n exportImageArea: true,\n multiplier: this.options.exportMultiplier,\n fileType: 'png'\n }));\n this.removeAllMasks(this._withInternalOperationOptions(operationToken, { saveHistory: false }));\n await this.loadImage(merged, this._withInternalOperationOptions(operationToken, {\n preserveScroll: true,\n resetMaskCounter: false\n }));\n const afterJson = this._serializeCanvasState();\n this._pushStateTransition(beforeJson, afterJson);\n } catch (error) {\n this._reportError('merge error', error);\n try {\n await this.loadFromState(beforeJson);\n } catch (restoreError) {\n this._reportError('mergeMasks rollback failed', restoreError);\n }\n throw error;\n } finally {\n this._endBusyOperation(operationToken);\n }\n }\n\n /**\n * Backward-compatible alias for {@link ImageEditor#mergeMasks}.\n *\n * @deprecated Use mergeMasks() instead. This alias will be removed in v2.0.0.\n * @returns {Promise<void>} Resolves when mask flattening is complete.\n */\n async merge() {\n return this.mergeMasks();\n }\n\n /**\n * Triggers a JPEG image download of the current canvas.\n *\n * The image area and multiplier are controlled by options.\n * @param {string} [fileName=this.options.defaultDownloadFileName] - Desired download file name.\n * @returns {void}\n * @public\n */\n downloadImage(fileName = this.options.defaultDownloadFileName) {\n if (!this.originalImage) return;\n if (!this._canMutateNow('downloadImage')) return;\n const exportImageArea = this.options.exportImageAreaByDefault;\n this.exportImageBase64({ exportImageArea, multiplier: this.options.exportMultiplier })\n .then(imageBase64 => {\n const link = document.createElement('a');\n link.download = fileName;\n link.href = imageBase64;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n })\n .catch(error => this._reportError('download error', error));\n }\n\n /**\n * Exports the current image as a Base64-encoded data URL.\n *\n * When `exportImageArea` is false, the export omits masks and labels. When it is true, masks are\n * temporarily rendered as opaque export shapes and then restored, so editable mask state is not mutated.\n *\n * @async\n * @param {Object} [options={}] - Export options.\n * @param {boolean} [options.exportImageArea] - If true, exports only the image bounding area with masks cropped and blended.\n * @param {number} [options.multiplier=1] - Scaling multiplier for output (resolution).\n * @param {number} [options.quality=0.92] - Image quality between 0 and 1 for lossy formats.\n * @param {string} [options.fileType='jpeg'] - Output file type ('jpeg' | 'png' | 'webp').\n * @returns {Promise<string>} Resolves with an image data URL.\n * @throws {Error} If there is no image loaded.\n * @public\n */\n async exportImageBase64(options = {}) {\n if (!this.originalImage) throw new Error('No image loaded');\n this._assertIdleForOperation('exportImageBase64', options);\n const exportImageArea = typeof options.exportImageArea === 'boolean' ? options.exportImageArea : this.options.exportImageAreaByDefault;\n const multiplier = options.multiplier || this.options.exportMultiplier || 1;\n const quality = this._normalizeQuality(options.quality ?? this.options.downsampleQuality);\n const format = this._normalizeImageFormat(options.fileType || options.format);\n\n if (!exportImageArea) {\n const masks = this.canvas.getObjects().filter(object => object.maskId || object.maskLabel);\n const maskVisibilityBackups = masks.map(mask => ({ object: mask, visible: mask.visible }));\n\n try {\n masks.forEach(mask => { mask.set({ visible: false }); });\n this.canvas.discardActiveObject();\n this.canvas.renderAll();\n\n this.originalImage.setCoords();\n const imageBounds = this.originalImage.getBoundingRect(true, true);\n const exportRegion = this._getClampedCanvasRegion(imageBounds);\n return await this._exportCanvasRegionToDataURL({\n ...exportRegion,\n multiplier,\n quality,\n format,\n sealPartialEdges: this._getPartialExportEdges(imageBounds)\n });\n } finally {\n maskVisibilityBackups.forEach(backup => {\n try { backup.object.set({ visible: backup.visible }); } catch (error) { void error; }\n });\n this.canvas.renderAll();\n }\n }\n\n // Render masks as export shapes without mutating their editable styles.\n const masks = this.canvas.getObjects().filter(object => object.maskId);\n const maskStyleBackups = masks.map(mask => ({\n object: mask,\n opacity: mask.opacity,\n fill: mask.fill,\n strokeWidth: mask.strokeWidth,\n stroke: mask.stroke,\n selectable: mask.selectable,\n lockRotation: mask.lockRotation\n }));\n\n let finalBase64;\n try {\n // Labels are UI overlays and should not be part of the flattened export.\n masks.forEach(mask => this._removeLabelForMask(mask));\n this.canvas.discardActiveObject();\n this.canvas.renderAll();\n\n // The export treats masks as opaque shapes with no editable border.\n masks.forEach(mask => {\n mask.set({ opacity: 1, fill: '#000000', strokeWidth: 0, stroke: null, selectable: false });\n mask.setCoords();\n });\n this.canvas.renderAll();\n\n // Compute an integer canvas region for the base image.\n this.originalImage.setCoords();\n const imageBounds = this.originalImage.getBoundingRect(true, true);\n const exportRegion = this._getClampedCanvasRegion(imageBounds);\n\n // Crop precisely in offscreen canvas\n finalBase64 = await this._exportCanvasRegionToDataURL({\n ...exportRegion,\n multiplier,\n quality,\n format,\n sealPartialEdges: this._getPartialExportEdges(imageBounds)\n });\n } finally {\n maskStyleBackups.forEach(backup => {\n try {\n backup.object.set({\n opacity: backup.opacity,\n fill: backup.fill,\n strokeWidth: backup.strokeWidth,\n stroke: backup.stroke,\n selectable: backup.selectable,\n lockRotation: backup.lockRotation\n });\n backup.object.setCoords();\n } catch (error) { void error; }\n });\n\n this.canvas.renderAll();\n }\n\n return finalBase64;\n }\n\n /**\n * Backward-compatible alias for {@link ImageEditor#exportImageBase64}.\n *\n * @deprecated Use exportImageBase64() instead. This alias will be removed in v2.0.0.\n * @param {Object} [options={}] - Export options passed to exportImageBase64().\n * @returns {Promise<string>} Resolves with an image data URL.\n */\n async getImageBase64(options = {}) {\n return this.exportImageBase64(options);\n }\n\n /**\n * Exports the current image as a File object.\n *\n * The export can include flattened masks (`mergeMask: true`) or only the plain base image (`mergeMask: false`).\n * Supported output formats are JPEG, PNG, and WebP.\n * \n * @async\n * @param {Object} [options={}] - Export options.\n * @param {boolean} [options.mergeMask=true] - If true, export image area with masks merged; if false, export the plain image without masks.\n * @param {string} [options.fileType='jpeg'] - Output file type ('jpeg' | 'png' | 'webp'). Defaults to 'jpeg' on invalid input.\n * @param {number} [options.quality=0.92] - Image quality for lossy types (0-1, default based on options.downsampleQuality).\n * @param {number} [options.multiplier=1] - Output resolution multiplier.\n * @param {string} [options.fileName] - Optional file name (only used for download).\n * @returns {Promise<File>} Resolves with the exported image as a File object.\n * \n * @example\n * const file = await this.exportImageFile({ mergeMask: false, fileType: 'png' });\n */\n async exportImageFile(options = {}) {\n if (!this.originalImage) throw new Error('No image loaded');\n this._assertIdleForOperation('exportImageFile');\n const {\n mergeMask = true,\n fileType = 'jpeg',\n quality = this.options.downsampleQuality ?? 0.92,\n multiplier = this.options.exportMultiplier ?? 1,\n fileName = this.options.defaultDownloadFileName ?? 'exported_image.jpg'\n } = options;\n\n const safeFileType = this._normalizeImageFormat(fileType);\n const normalizedQuality = this._normalizeQuality(quality);\n\n // Generate the data URL in the requested export mode.\n let imageBase64;\n if (mergeMask) {\n imageBase64 = await this.exportImageBase64({\n exportImageArea: true,\n multiplier,\n quality: normalizedQuality,\n fileType: safeFileType\n });\n } else {\n imageBase64 = await this.exportImageBase64({\n exportImageArea: false,\n multiplier,\n quality: normalizedQuality,\n fileType: safeFileType\n });\n }\n\n // Convert to the required image format\n let imageDataUrl = imageBase64;\n if (!imageDataUrl.startsWith(`data:image/${safeFileType}`)) {\n // Redraw the exported data URL when the browser returned a different image format.\n imageDataUrl = await new Promise((resolve, reject) => {\n const imageElement = new window.Image();\n imageElement.crossOrigin = \"Anonymous\";\n imageElement.onload = () => {\n try {\n const offscreenCanvas = document.createElement('canvas');\n offscreenCanvas.width = imageElement.width;\n offscreenCanvas.height = imageElement.height;\n const context = offscreenCanvas.getContext('2d');\n if (!context) throw new Error('Unable to create 2D canvas context for export conversion');\n context.drawImage(imageElement, 0, 0);\n const convertedDataUrl = offscreenCanvas.toDataURL(`image/${safeFileType}`, normalizedQuality);\n resolve(convertedDataUrl);\n } catch (error) { reject(error); }\n };\n imageElement.onerror = reject;\n imageElement.src = imageBase64;\n });\n }\n\n // Convert the final data URL to a File with the requested MIME type.\n const binaryString = atob(imageDataUrl.split(',')[1]);\n const mime = `image/${safeFileType}`;\n let byteIndex = binaryString.length;\n const bytes = new Uint8Array(byteIndex);\n while (byteIndex--) {\n bytes[byteIndex] = binaryString.charCodeAt(byteIndex);\n }\n return new File([bytes], fileName, { type: mime });\n }\n\n _clearMaskPlacementMemory() {\n this._lastMask = null;\n this._lastMaskInitialLeft = null;\n this._lastMaskInitialTop = null;\n this._lastMaskInitialWidth = null;\n }\n\n async _restoreStateAfterCropFailure(beforeJson, message, error) {\n this._reportError(message, error);\n\n if (this._cropRect && this.canvas) this._removeCropRect();\n this._cropRect = null;\n this._cropMode = false;\n if (this.canvas && this._prevSelectionSetting !== undefined) {\n this.canvas.selection = !!this._prevSelectionSetting;\n }\n this._prevSelectionSetting = undefined;\n\n if (beforeJson) {\n try {\n await this.loadFromState(beforeJson);\n } catch (restoreError) {\n this._reportError('applyCrop: rollback failed', restoreError);\n }\n }\n\n this._updateUI();\n if (this.canvas) this.canvas.renderAll();\n }\n\n _restoreCropObjectState() {\n if (Array.isArray(this._cropPrevEvented)) {\n this._cropPrevEvented.forEach(state => {\n try {\n state.object.set({\n evented: state.evented,\n selectable: state.selectable,\n visible: state.visible\n });\n } catch (error) { void error; }\n });\n }\n this._cropPrevEvented = null;\n }\n\n _removeCropRect() {\n if (!this._cropRect) return;\n try {\n if (this._cropHandlers && this._cropHandlers.length) {\n this._cropHandlers.forEach(targetHandlers => {\n targetHandlers.handlers.forEach(handlerRecord => {\n if (targetHandlers.target && typeof targetHandlers.target.off === 'function') {\n targetHandlers.target.off(handlerRecord.eventName, handlerRecord.handler);\n }\n });\n });\n }\n } catch (error) { void error; }\n\n try { if (this.canvas) this.canvas.remove(this._cropRect); } catch (error) { void error; }\n this._cropRect = null;\n this._cropHandlers = [];\n }\n\n /**\n * Enters crop mode by creating a resizable crop rectangle above the base image.\n *\n * Other canvas objects are made non-interactive while crop mode is active. Masks can be hidden during\n * cropping when `crop.hideMasksDuringCrop` is enabled.\n *\n * @returns {void}\n * @public\n */\n enterCropMode() {\n if (!this.canvas || !this.originalImage || this._cropMode) return;\n if (!this._canMutateNow('enterCropMode')) return;\n if (!this.isImageLoaded()) return;\n this._removeCropRect();\n this._cropMode = true;\n\n // Disable group selection so only the crop rectangle can be manipulated.\n this._prevSelectionSetting = this.canvas.selection;\n this.canvas.selection = false;\n\n // Clear the current selection before activating the crop rectangle.\n this.canvas.discardActiveObject();\n\n // Create the initial crop rectangle inside the image bounds.\n this.originalImage.setCoords();\n const imageBounds = this.originalImage.getBoundingRect(true, true);\n // Use a small inset so the user can see the crop boundary.\n const padding = (this.options.crop && this.options.crop.padding) ? this.options.crop.padding : 10;\n const left = Math.max(0, Math.floor(imageBounds.left + padding));\n const top = Math.max(0, Math.floor(imageBounds.top + padding));\n const maxCropWidth = Math.max(1, Math.floor(imageBounds.width - padding * 2));\n const maxCropHeight = Math.max(1, Math.floor(imageBounds.height - padding * 2));\n const configuredMinWidth = Math.max(1, Number(this.options.crop.minWidth) || 50);\n const configuredMinHeight = Math.max(1, Number(this.options.crop.minHeight) || 50);\n const minCropWidth = Math.min(configuredMinWidth, maxCropWidth);\n const minCropHeight = Math.min(configuredMinHeight, maxCropHeight);\n const width = minCropWidth;\n const height = minCropHeight;\n\n // Visual style for the temporary crop rectangle.\n const cropRect = new fabric.Rect({\n left, top,\n width, height,\n fill: 'rgba(0,0,0,0.12)',\n stroke: '#00aaff',\n strokeDashArray: [6, 4],\n strokeWidth: 1,\n strokeUniform: true,\n selectable: true,\n hasRotatingPoint: !!(this.options.crop && this.options.crop.allowRotationOfCropRect),\n lockRotation: !(this.options.crop && this.options.crop.allowRotationOfCropRect),\n cornerSize: 8,\n objectCaching: false,\n originX: 'left',\n originY: 'top',\n lockScalingFlip: true\n });\n\n // Ensure the crop rect is above everything\n this.canvas.add(cropRect);\n cropRect.isCropRect = true;\n this.canvas.bringToFront(cropRect);\n this.canvas.setActiveObject(cropRect);\n\n // Store the crop rectangle so apply/cancel can clean it up.\n this._cropRect = cropRect;\n\n // Keep only the crop rectangle interactive while preserving each object's previous state.\n this._cropPrevEvented = [];\n const shouldHideMasks = !!(this.options.crop && this.options.crop.hideMasksDuringCrop);\n this.canvas.getObjects().forEach(object => {\n if (object !== cropRect) {\n this._cropPrevEvented.push({ object, evented: object.evented, selectable: object.selectable, visible: object.visible });\n try {\n const updates = {\n evented: false,\n selectable: false\n };\n if (shouldHideMasks && (object.maskId || object.maskLabel)) updates.visible = false;\n object.set(updates);\n } catch (error) { void error; }\n }\n });\n\n // Keep Fabric controls and configured size limits in sync as the crop rectangle changes.\n const handleCropRectModified = () => {\n try {\n const cropWidth = Math.max(1, Number(cropRect.width) || 1);\n const cropHeight = Math.max(1, Number(cropRect.height) || 1);\n const nextScaleX = Math.min(maxCropWidth / cropWidth, Math.max(minCropWidth / cropWidth, Number(cropRect.scaleX) || 1));\n const nextScaleY = Math.min(maxCropHeight / cropHeight, Math.max(minCropHeight / cropHeight, Number(cropRect.scaleY) || 1));\n cropRect.set({ scaleX: nextScaleX, scaleY: nextScaleY });\n cropRect.setCoords();\n this.canvas.requestRenderAll();\n } catch (error) { void error; }\n };\n cropRect.on('modified', handleCropRectModified);\n cropRect.on('moving', handleCropRectModified);\n cropRect.on('scaling', handleCropRectModified);\n\n // Store handlers so cancel/apply/dispose can unbind them.\n this._cropHandlers.push({\n target: cropRect,\n handlers: [\n { eventName: 'modified', handler: handleCropRectModified },\n { eventName: 'moving', handler: handleCropRectModified },\n { eventName: 'scaling', handler: handleCropRectModified }\n ]\n });\n\n this._updateUI();\n this.canvas.renderAll();\n }\n\n /**\n * Cancels crop mode and removes the temporary crop rectangle.\n *\n * @returns {void}\n * @public\n */\n cancelCrop() {\n if (!this.canvas || !this._cropMode) return;\n this._removeCropRect();\n this._restoreCropObjectState();\n this._cropMode = false;\n // Restore the canvas selection setting that was active before crop mode.\n this.canvas.selection = !!this._prevSelectionSetting;\n this._prevSelectionSetting = undefined;\n\n this.canvas.discardActiveObject();\n this._updateUI();\n this.canvas.renderAll();\n }\n\n /**\n * Applies the current crop rectangle to the base image.\n *\n * Masks are removed by default. When `crop.preserveMasksAfterCrop` is true, masks that intersect the crop\n * region are shifted into the cropped coordinate space and remain editable. The operation is recorded as a\n * single undoable history transition.\n *\n * @async\n * @returns {Promise<void>} Resolves after the cropped image has been loaded and history is updated.\n * @public\n */\n async applyCrop() {\n if (!this.canvas || !this._cropMode || !this._cropRect) return;\n this._assertIdleForOperation('applyCrop');\n\n // Fabric does not update control coordinates automatically after programmatic transforms.\n this._cropRect.setCoords();\n const rectBounds = this._cropRect.getBoundingRect(true, true);\n\n const cropRegion = this._getClampedCanvasRegion(rectBounds, { includePartialPixels: false });\n const shouldPreserveMasks = !!(this.options.crop && this.options.crop.preserveMasksAfterCrop);\n\n this._restoreCropObjectState();\n\n let beforeJson;\n try {\n beforeJson = this._serializeCanvasState();\n } catch (error) {\n this._reportWarning('applyCrop: could not serialize before state', error);\n beforeJson = null;\n }\n\n const preservedMasks = [];\n\n try {\n const masks = this.canvas.getObjects().filter(object => object.maskId);\n if (masks && masks.length) {\n masks.forEach(mask => {\n try {\n mask.setCoords();\n const maskBounds = mask.getBoundingRect(true, true);\n const intersectsCrop =\n maskBounds.left < cropRegion.sourceX + cropRegion.sourceWidth &&\n maskBounds.left + maskBounds.width > cropRegion.sourceX &&\n maskBounds.top < cropRegion.sourceY + cropRegion.sourceHeight &&\n maskBounds.top + maskBounds.height > cropRegion.sourceY;\n this._removeLabelForMask(mask);\n this.canvas.remove(mask);\n if (shouldPreserveMasks && intersectsCrop) {\n this._translateObjectByCanvasOffset(mask, -cropRegion.sourceX, -cropRegion.sourceY);\n mask.set({ visible: true });\n preservedMasks.push(mask);\n }\n } catch (error) {\n this._reportWarning('applyCrop: failed to remove mask', error);\n }\n });\n this._clearMaskPlacementMemory();\n this.canvas.discardActiveObject();\n this.canvas.renderAll();\n }\n } catch (error) {\n this._reportWarning('applyCrop: error while removing masks', error);\n }\n\n this._removeCropRect();\n\n // End crop mode before loading the cropped image.\n this._cropMode = false;\n this.canvas.selection = !!this._prevSelectionSetting;\n this._prevSelectionSetting = undefined;\n\n // Export the crop region from the current canvas.\n let croppedBase64;\n try {\n croppedBase64 = await this._exportCanvasRegionToDataURL({\n ...cropRegion,\n multiplier: 1,\n quality: this._normalizeQuality(this.options.downsampleQuality),\n format: 'jpeg'\n });\n } catch (error) {\n await this._restoreStateAfterCropFailure(beforeJson, 'applyCrop: failed to create cropped image', error);\n return;\n }\n\n // Load the cropped image as the new base image.\n try {\n await this.loadImage(croppedBase64, { resetMaskCounter: false });\n if (preservedMasks.length) {\n preservedMasks.forEach(mask => {\n this._rebindMaskEvents(mask);\n this.canvas.add(mask);\n this.canvas.bringToFront(mask);\n });\n this._lastMask = preservedMasks[preservedMasks.length - 1];\n this.maskCounter = preservedMasks.reduce((max, mask) => Math.max(max, mask.maskId || 0), this.maskCounter);\n this._updateMaskList();\n this.canvas.renderAll();\n }\n } catch (error) {\n await this._restoreStateAfterCropFailure(beforeJson, 'applyCrop: loadImage(croppedBase64) failed', error);\n return;\n }\n\n // Create an after snapshot and push one history command for the crop operation.\n let afterJson;\n try {\n afterJson = preservedMasks.length ? this._serializeCanvasState() : this._lastSnapshot;\n } catch (error) {\n this._reportWarning('applyCrop: failed to serialize after state', error);\n afterJson = null;\n }\n\n try {\n this._pushStateTransition(beforeJson, afterJson);\n } catch (error) {\n this._reportWarning('applyCrop: failed to push history command', error);\n }\n\n // Refresh UI state after crop completion.\n this._updateUI();\n this.canvas.renderAll();\n }\n\n\n /* ---------- Misc / UI ---------- */\n\n /**\n * Updates the scale input field in the UI to reflect the current scale.\n * Sets the value (as percentage) if the element is present.\n * @private\n */\n _updateInputs() {\n const scaleInputElement = this._getElement('scaleRate');\n if (scaleInputElement) scaleInputElement.value = Math.round(this.currentScale * 100);\n }\n\n /**\n * Updates the enabled/disabled state of various UI controls (buttons)\n * based on the current application state (image/mask presence, animation, etc).\n * @private\n */\n _updateUI() {\n if (!this.canvas) return;\n const hasImage = !!this.originalImage;\n const masks = hasImage ? this.canvas.getObjects().filter(object => object.maskId) : [];\n const hasMasks = masks.length > 0;\n const activeObject = this.canvas.getActiveObject();\n const hasSelectedMask = activeObject && activeObject.maskId;\n const isDefaultTransform = this.currentScale === 1 && this.currentRotation === 0;\n const canUndo = this.historyManager?.canUndo();\n const canRedo = this.historyManager?.canRedo();\n const isInCropMode = !!this._cropMode;\n const isBusy = this.isAnimating || this._isLoading || !!this._activeOperationToken || !!(this.animationQueue && this.animationQueue.isBusy());\n\n if (isInCropMode) {\n // Disable all controls except the crop action buttons while crop mode is active.\n for (const key of Object.keys(this.elements || {})) {\n const element = this._getElement(key);\n if (!element) continue;\n if (key === 'applyCropBtn' || key === 'cancelCropBtn') {\n this._setDisabled(key, false);\n } else {\n this._setDisabled(key, true);\n }\n }\n return;\n }\n\n this._setDisabled('zoomInBtn', !hasImage || isBusy || this.currentScale >= this.options.maxScale);\n this._setDisabled('zoomOutBtn', !hasImage || isBusy || this.currentScale <= this.options.minScale);\n this._setDisabled('rotateLeftBtn', !hasImage || isBusy);\n this._setDisabled('rotateRightBtn', !hasImage || isBusy);\n this._setDisabled('addMaskBtn', !hasImage || isBusy);\n this._setDisabled('removeMaskBtn', !hasSelectedMask || isBusy);\n this._setDisabled('removeAllMasksBtn', !hasMasks || isBusy);\n this._setDisabled('mergeBtn', !hasImage || !hasMasks || isBusy);\n this._setDisabled('downloadBtn', !hasImage || isBusy);\n this._setDisabled('resetBtn', !hasImage || isDefaultTransform || isBusy);\n this._setDisabled('undoBtn', !hasImage || isBusy || !canUndo);\n this._setDisabled('redoBtn', !hasImage || isBusy || !canRedo);\n this._setDisabled('cropBtn', !hasImage || isBusy);\n this._setDisabled('applyCropBtn', true);\n this._setDisabled('cancelCropBtn', true);\n this._setDisabled('imageInput', isBusy);\n this._setDisabled('uploadArea', isBusy);\n }\n\n /**\n * Enables or disables a specific UI element (typically a button) by its key.\n * \n * @param {string} key - Key of the element in this.elements (e.g. 'zoomInBtn').\n * @param {boolean} disabled - If true, disables the element; otherwise enables.\n * @private\n */\n _setDisabled(key, disabled) {\n const element = this._getElement(key);\n if (!element) return;\n if ('disabled' in element) {\n element.disabled = !!disabled;\n return;\n }\n if (!this._elementOriginalPointerEvents) this._elementOriginalPointerEvents = new Map();\n if (!this._elementOriginalPointerEvents.has(key)) {\n this._elementOriginalPointerEvents.set(key, element.style.pointerEvents || '');\n }\n\n if (disabled) {\n element.setAttribute('aria-disabled', 'true');\n element.style.pointerEvents = 'none';\n } else {\n element.removeAttribute('aria-disabled');\n element.style.pointerEvents = this._elementOriginalPointerEvents.get(key) ?? '';\n }\n }\n\n _isElementDisabled(element) {\n if (!element) return false;\n if ('disabled' in element) return !!element.disabled;\n return element.getAttribute('aria-disabled') === 'true';\n }\n\n /**\n * Updates placeholder and canvas container visibility based on whether an image is loaded.\n * @private\n */\n _updatePlaceholderStatus() {\n if (!this.options.showPlaceholder) return;\n this._setPlaceholderVisible(!this.originalImage);\n }\n\n /**\n * Shows or hides the placeholder and canvas container.\n *\n * @param {boolean} show - If true, displays the placeholder; otherwise displays the canvas container.\n * @private\n */\n _setPlaceholderVisible(show) {\n if (this.placeholderElement) this._setElementVisible(this.placeholderElement, show);\n const canvasVisibilityElement = this._getCanvasVisibilityElement();\n if (canvasVisibilityElement && canvasVisibilityElement !== this.placeholderElement) {\n this._setElementVisible(canvasVisibilityElement, !show);\n }\n }\n\n _getCanvasVisibilityElement() {\n const wrapperElement = this.canvas && this.canvas.wrapperEl ? this.canvas.wrapperEl : null;\n if (\n this.containerElement &&\n this.placeholderElement &&\n (this.containerElement === this.placeholderElement || this.containerElement.contains(this.placeholderElement))\n ) {\n return wrapperElement || this.canvasElement;\n }\n return this.containerElement || wrapperElement || this.canvasElement;\n }\n\n /**\n * Updates element visibility.\n *\n * @param {HTMLElement} element - Element whose visibility should be updated.\n * @param {boolean} isVisible - If true, removes the hidden state.\n * @returns {void}\n * @private\n */\n _setElementVisible(element, isVisible) {\n if (!element) return;\n this._rememberElementVisibility(element);\n element.hidden = !isVisible;\n element.setAttribute('aria-hidden', isVisible ? 'false' : 'true');\n if (element.classList) {\n element.classList.toggle('d-none', !isVisible);\n }\n }\n\n _rememberElementVisibility(element) {\n if (!element || this._visibilityStateByElement.has(element)) return;\n this._visibilityStateByElement.set(element, this._captureElementVisibility(element));\n }\n\n _captureElementVisibility(element) {\n if (!element) return null;\n return {\n hidden: element.hidden,\n ariaHidden: element.getAttribute('aria-hidden'),\n className: element.className\n };\n }\n\n _restoreElementVisibility(element, state) {\n if (!element || !state) return;\n element.hidden = !!state.hidden;\n if (state.ariaHidden === null) {\n element.removeAttribute('aria-hidden');\n } else {\n element.setAttribute('aria-hidden', state.ariaHidden);\n }\n element.className = state.className || '';\n }\n\n /**\n * Cleans up and disposes of the canvas and related references.\n * Call this method to free memory and remove canvas listeners when the editor is no longer needed.\n * @public\n */\n dispose() {\n this._disposed = true;\n this._rejectActiveAnimations(new Error('Editor disposed during animation'));\n if (this.animationQueue) {\n this.animationQueue.cancelAll(new Error('Editor disposed'));\n }\n this._isLoading = false;\n this._activeOperationName = null;\n this._activeOperationToken = null;\n\n // Remove bound DOM event listeners\n try {\n for (const [key, handlers] of Object.entries(this._handlersByElementKey || {})) {\n const element = this._getElement(key);\n if (!element) continue;\n handlers.forEach(handlerRecord => {\n try { element.removeEventListener(handlerRecord.eventName, handlerRecord.handler); } catch (error) { void error; }\n });\n }\n } catch (error) { void error; }\n\n if (this._cropRect) {\n try { this.canvas.remove(this._cropRect); } catch (error) { void error; }\n this._cropRect = null;\n }\n\n if (this.containerElement && this._containerOriginalOverflow) {\n try { this._restoreContainerOverflowState(); } catch (error) { void error; }\n }\n\n if (this._visibilityStateByElement) {\n try {\n [this.placeholderElement, this._getCanvasVisibilityElement()].forEach(element => {\n const state = element ? this._visibilityStateByElement.get(element) : null;\n if (state) this._restoreElementVisibility(element, state);\n });\n } catch (error) { void error; }\n }\n\n if (this.canvasElement && this._canvasElementOriginalStyle) {\n try {\n this.canvasElement.style.display = this._canvasElementOriginalStyle.display;\n this.canvasElement.style.width = this._canvasElementOriginalStyle.width;\n this.canvasElement.style.height = this._canvasElementOriginalStyle.height;\n } catch (error) { void error; }\n }\n\n if (this.canvas) {\n try { this.canvas.dispose(); } catch (error) { void error; }\n this.canvas = null;\n this.canvasElement = null;\n this.isImageLoadedToCanvas = false;\n }\n this._handlersByElementKey = {};\n this._elementCache = {};\n this._elementOriginalPointerEvents = new Map();\n this._clearMaskPlacementMemory();\n this.originalImage = null;\n this.baseImageScale = 1;\n this.currentScale = 1;\n this.currentRotation = 0;\n this.isAnimating = false;\n this._isLoading = false;\n this._cropMode = false;\n this._cropRect = null;\n this._cropHandlers = [];\n this._cropPrevEvented = null;\n this._prevSelectionSetting = undefined;\n this._lastContainerViewportSize = null;\n this._initialized = false;\n }\n }\n\n /**\n * @callback AnimationTaskCallback\n * @returns {unknown} Animation result or awaitable animation result.\n */\n\n /**\n * @callback PromiseResolveCallback\n * @param {unknown} value - Promise resolution value.\n * @returns {void}\n */\n\n /**\n * @callback PromiseRejectCallback\n * @param {unknown} reason - Promise rejection reason.\n * @returns {void}\n */\n\n /**\n * @typedef {Object} QueuedAnimationTask\n * @property {AnimationTaskCallback} animationFn - Queued animation function.\n * @property {PromiseResolveCallback} resolve - Promise resolver for the queued animation.\n * @property {PromiseRejectCallback} reject - Promise rejecter for the queued animation.\n */\n\n /**\n * @callback HistoryTaskCallback\n * @returns {void|Promise<void>} Result of a history operation.\n */\n\n /**\n * FIFO queue that serializes transform animations so Fabric state changes do not overlap.\n *\n * @private\n */\n class AnimationQueue {\n /**\n * Creates an empty animation queue.\n */\n constructor() {\n /**\n * Pending animation descriptors.\n * @type {Array<QueuedAnimationTask>}\n */\n this.animationTasks = [];\n /**\n * Whether an animation task is currently running.\n * @type {boolean}\n */\n this.isRunning = false;\n this.currentTask = null;\n this._generation = 0;\n }\n\n /**\n * Adds an animation function to the queue.\n *\n * @param {AnimationTaskCallback} animationFn - Function that returns a value, Promise, or awaitable animation result.\n * @returns {Promise<unknown>} Resolves or rejects with the queued animation result.\n */\n async add(animationFn) {\n return new Promise((resolve, reject) => {\n this.animationTasks.push({ animationFn, resolve, reject, isSettled: false });\n if (!this.isRunning) {\n this._drainQueue();\n }\n });\n }\n\n isBusy() {\n return this.isRunning || this.animationTasks.length > 0;\n }\n\n cancelAll(reason = new Error('Animation queue cancelled')) {\n this._generation += 1;\n const cancellationError = reason instanceof Error ? reason : new Error(String(reason));\n const tasks = [\n ...(this.currentTask ? [this.currentTask] : []),\n ...this.animationTasks.splice(0)\n ];\n tasks.forEach(task => {\n if (!task || task.isSettled) return;\n task.isSettled = true;\n task.reject(cancellationError);\n });\n this.isRunning = false;\n this.currentTask = null;\n }\n\n /**\n * Runs queued animation tasks sequentially until the queue is empty.\n *\n * @private\n * @returns {Promise<void>}\n */\n async _drainQueue() {\n if (this.isRunning) return;\n const generation = this._generation;\n this.isRunning = true;\n\n try {\n while (this.animationTasks.length > 0 && generation === this._generation) {\n const task = this.animationTasks.shift();\n this.currentTask = task;\n\n try {\n const result = await task.animationFn();\n if (generation === this._generation && !task.isSettled) {\n task.isSettled = true;\n task.resolve(result);\n }\n } catch (error) {\n if (generation === this._generation && !task.isSettled) {\n task.isSettled = true;\n task.reject(error);\n }\n } finally {\n if (generation === this._generation && this.currentTask === task) this.currentTask = null;\n }\n }\n } finally {\n if (generation === this._generation) {\n this.isRunning = false;\n this.currentTask = null;\n }\n }\n }\n }\n\n /**\n * Undoable command with paired execute and undo operations.\n *\n * @private\n */\n class Command {\n /**\n * @param {HistoryTaskCallback} execute - Function that performs the action.\n * @param {HistoryTaskCallback} undo - Function that reverts the action.\n */\n constructor(execute, undo) {\n /**\n * Executes the command.\n * @type {HistoryTaskCallback}\n */\n this.execute = execute;\n /**\n * Undoes the command.\n * @type {HistoryTaskCallback}\n */\n this.undo = undo;\n }\n }\n\n /**\n * Manages undo/redo history and serializes asynchronous history operations.\n *\n * @private\n */\n class HistoryManager {\n /**\n * @param {number} [maxSize=50] - Maximum number of commands to keep in history.\n */\n constructor(maxSize = 50) {\n /** @type {Array<Command>} */\n this.history = [];\n /** @type {number} */\n this.currentIndex = -1;\n /** @type {number} */\n this.maxSize = maxSize;\n /** @type {Promise<void>} */\n this.pending = Promise.resolve();\n }\n\n /**\n * Queues a history task after the previously queued undo/redo task completes.\n *\n * @param {HistoryTaskCallback} task - Task to run after earlier history work settles.\n * @returns {Promise<void>} Resolves or rejects with the queued task result.\n * @private\n */\n enqueue(task) {\n const nextTask = this.pending.then(() => Promise.resolve().then(task));\n this.pending = nextTask.catch(() => undefined);\n return nextTask;\n }\n\n /**\n * Executes a new command and pushes it onto the history stack.\n * Truncates any \"future\" history when branching.\n *\n * @param {Command} command The command to execute.\n * @returns {void}\n */\n execute(command) {\n const result = command.execute();\n if (result && typeof result.then === 'function') {\n return Promise.resolve(result).then(() => {\n this.push(command);\n });\n }\n this.push(command);\n return result;\n }\n\n /**\n * Pushes an already-applied command onto the history stack.\n * Truncates any \"future\" history when branching.\n *\n * @param {Command} command The command to push.\n * @returns {void}\n */\n push(command) {\n // Discard redo commands when a new branch is created.\n if (this.currentIndex < this.history.length - 1) {\n this.history = this.history.slice(0, this.currentIndex + 1);\n }\n\n this.history.push(command);\n\n if (this.history.length > this.maxSize) {\n this.history.shift();\n }\n this.currentIndex = this.history.length - 1;\n }\n\n /**\n * Checks whether an undo operation is possible.\n *\n * @returns {boolean} True if undo can be performed.\n */\n canUndo() {\n return this.currentIndex >= 0;\n }\n\n /**\n * Checks whether a redo operation is possible.\n *\n * @returns {boolean} True if redo can be performed.\n */\n canRedo() {\n return this.currentIndex < this.history.length - 1;\n }\n\n /**\n * Undoes the last executed command if possible.\n *\n * @returns {Promise<void>} Resolves after the undo task completes.\n */\n undo() {\n return this.enqueue(async () => {\n if (this.currentIndex >= 0) {\n const index = this.currentIndex;\n await this.history[index].undo();\n this.currentIndex = index - 1;\n }\n });\n }\n\n /**\n * Redoes the next command in history if possible.\n *\n * @returns {Promise<void>} Resolves after the redo task completes.\n */\n redo() {\n return this.enqueue(async () => {\n if (this.currentIndex < this.history.length - 1) {\n const index = this.currentIndex + 1;\n await this.history[index].execute();\n this.currentIndex = index;\n }\n });\n }\n }\n\nexport { ImageEditor };\nexport default ImageEditor;\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["fabricModule", "fabric", "INTERNAL_OPERATION_TOKEN", "getGlobalScope", "getGlobalFabric", "scope", "setFabric", "fabricInstance", "ensureFabric", "ImageEditor", "options", "defaultLabel", "mask", "defaultCrop", "userLabel", "userCrop", "AnimationQueue", "HistoryManager", "value", "idMap", "defaults", "message", "error", "handler", "canvasElement", "containerElement", "initialWidth", "initialHeight", "containerWidth", "containerHeight", "event", "key", "id", "element", "target", "masks", "objects", "object", "shouldPreserveScroll", "snapshot", "uploadAreaElement", "file", "rotationInputElement", "step", "parsedStep", "eventName", "resolve", "reject", "reader", "fileName", "activeModes", "isEnabled", "name", "imageBase64", "transaction", "imageElement", "loadSource", "ratio", "targetWidth", "targetHeight", "fabricImage", "imageWidth", "imageHeight", "viewport", "minWidth", "minHeight", "canvasWidth", "canvasHeight", "fitScale", "layout", "dataUrl", "timeoutMs", "isSettled", "safeTimeoutMs", "timerId", "settle", "callback", "didRestoreCanvasState", "previousLastMask", "previousMaskId", "quality", "sourceDataUrl", "offscreenCanvas", "context", "match", "sourceMimeType", "width", "height", "integerWidth", "integerHeight", "numericValue", "roundedValue", "measuredWidth", "measuredHeight", "overflow", "canScrollX", "canScrollY", "hasHorizontalScrollbar", "hasVerticalScrollbar", "scrollbar", "inlineOverflow", "inlineOverflowX", "inlineOverflowY", "computedOverflow", "computedOverflowX", "computedOverflowY", "style", "probe", "contentWidth", "contentHeight", "safetyMargin", "safeWidth", "safeHeight", "hasVertical", "hasHorizontal", "effectiveWidth", "effectiveHeight", "i", "nextHasVertical", "nextHasHorizontal", "scale", "canvasSize", "strokeWidth", "opacity", "maskStyleBackups", "normalStyle", "stylePatch", "property", "changedProperties", "backup", "result", "restorePatch", "cornerSize", "baseImageScale", "currentScale", "currentRotation", "maskCounter", "jsonObject", "fallback", "fallbackQuality", "numericFallback", "safeFallback", "numericQuality", "format", "bounds", "left", "top", "roundEnd", "sourceX", "sourceY", "endX", "endY", "angle", "edges", "imageData", "pixels", "sealPixel", "x", "y", "fallbackX", "fallbackY", "index", "fallbackIndex", "
|
|
4
|
+
"sourcesContent": ["import fabricModule from 'fabric';\nimport ImageEditor, { setFabric } from './image-editor.js';\n\nconst fabricInstance = fabricModule && (fabricModule.fabric || fabricModule.default || fabricModule);\n\nsetFabric(fabricInstance);\n\nexport { ImageEditor };\nexport default ImageEditor;\n", "/**\n * @file image-editor.js\n * @module image-editor\n * @version 1.4.2\n * @author Ben Situ\n * @license MIT\n * @description Lightweight canvas-based image editor with masking/transform/export support.\n */\n\nlet fabric = null;\nconst INTERNAL_OPERATION_TOKEN = Symbol.for('ImageEditorInternalOperation');\n\n/**\n * Returns the ambient global scope used to discover a globally loaded Fabric.js namespace.\n *\n * @returns {typeof globalThis|null} The global scope, or null when no standard scope is available.\n * @private\n */\nfunction getGlobalScope() {\n if (typeof globalThis !== 'undefined') return globalThis;\n if (typeof self !== 'undefined') return self;\n if (typeof window !== 'undefined') return window;\n return null;\n}\n\n/**\n * Returns the globally registered Fabric.js namespace when one is available.\n *\n * @returns {Object|null} The Fabric.js namespace, or null when Fabric is not registered globally.\n * @private\n */\nfunction getGlobalFabric() {\n const scope = getGlobalScope();\n return scope && scope.fabric ? scope.fabric : null;\n}\n\n/**\n * Registers the Fabric.js namespace used by ImageEditor instances.\n *\n * This helper is exported for the package entry wrappers, not as part of the documented package API.\n *\n * @param {Object} [fabricInstance] - Fabric.js namespace object. When omitted, the global `fabric` namespace is used.\n * @returns {Object|null} The active Fabric.js namespace.\n * @private\n */\nexport function setFabric(fabricInstance) {\n fabric = fabricInstance || getGlobalFabric();\n return fabric;\n}\n\n/**\n * Resolves the active Fabric.js namespace, trying the global namespace as a fallback.\n *\n * @returns {Object|null} The active Fabric.js namespace.\n * @private\n */\nfunction ensureFabric() {\n if (!fabric) setFabric();\n return fabric;\n}\n\n/**\n * @callback ImageLoadedCallback\n * @returns {void}\n */\n\n/**\n * @callback EditorErrorCallback\n * @param {*} error - Recoverable error or warning value, when available.\n * @param {string} message - Human-readable context for the error or warning.\n * @returns {void}\n */\n\n/**\n * @callback MaskValueResolver\n * @param {fabric.Canvas} canvas - Active Fabric canvas.\n * @param {Object} options - Editor options.\n * @returns {number} Resolved numeric mask value.\n */\n\n/**\n * @callback MaskFabricGenerator\n * @param {Object} config - Normalized mask configuration.\n * @param {fabric.Canvas} canvas - Active Fabric canvas.\n * @param {Object} options - Editor options.\n * @returns {fabric.Object} Custom Fabric object to use as the mask.\n */\n\n/**\n * @callback MaskCreateCallback\n * @param {fabric.Object} mask - Created mask object.\n * @param {fabric.Canvas} canvas - Active Fabric canvas.\n * @returns {void}\n */\n\n/**\n * @callback MaskLabelTextCallback\n * @param {fabric.Object} mask - Mask object whose label is being created.\n * @param {number} creationIndex - Stable zero-based creation index derived from the mask id.\n * @returns {string} Label text.\n */\n\n/**\n * @typedef {Object} LoadImageOptions\n * @property {boolean} [preserveScroll=false] - If true, keeps the current scroll position while reloading.\n */\n\n /**\n * Fabric.js-based image editor with masking, transform, crop, history, and export helpers.\n *\n * Requires Fabric.js v5.x through the ESM package entry or a globally available `fabric` namespace.\n *\n * @example\n * const editor = new ImageEditor({ canvasWidth: 1024, canvasHeight: 768 });\n * editor.init();\n *\n * @param {Object} [options={}] - Customization options to override defaults.\n * @param {number} [options.canvasWidth=800] - The initial canvas width in pixels.\n * @param {number} [options.canvasHeight=600] - The initial canvas height in pixels.\n * @param {string} [options.backgroundColor='transparent'] - The canvas background color.\n * @param {number} [options.animationDuration=300] - Duration in ms for scale/rotate animations.\n * @param {number} [options.minScale=0.1] - Minimum image scaling factor.\n * @param {number} [options.maxScale=5.0] - Maximum image scaling factor.\n * @param {number} [options.scaleStep=0.05] - Scale increment/decrement per step.\n * @param {number} [options.rotationStep=90] - Rotation step in degrees.\n * @param {boolean} [options.expandCanvasToImage=true] - If true, expands the canvas to fit the loaded image.\n * @param {boolean} [options.fitImageToCanvas=false] - If true, fits loaded image inside canvas.\n * @param {boolean} [options.coverImageToCanvas=false] - If true, scales image to cover the visible canvas viewport.\n * @param {boolean} [options.downsampleOnLoad=true] - Whether to downsample very large images on load.\n * @param {number} [options.downsampleMaxWidth=4000] - Max width for downsampling.\n * @param {number} [options.downsampleMaxHeight=3000] - Max height for downsampling.\n * @param {number} [options.downsampleQuality=0.92] - JPEG quality for downsampling/export.\n * @param {number} [options.imageLoadTimeoutMs=30000] - Timeout for image decode operations.\n * @param {number} [options.exportMultiplier=1] - Scale output image by this multiplier on export.\n * @param {boolean} [options.exportImageAreaByDefault=true] - Export only the image area (clipped to masks).\n * @param {number} [options.defaultMaskWidth=50] - Default width of new masks.\n * @param {number} [options.defaultMaskHeight=80] - Default height of new masks.\n * @param {boolean} [options.maskRotatable=false] - If true, masks can be rotated.\n * @param {boolean} [options.maskLabelOnSelect=true] - Show label on selected mask.\n * @param {number} [options.maskLabelOffset=3] - Offset for mask labels from top-left corner.\n * @param {string} [options.maskName='mask'] - Prefix for mask names/labels.\n * @param {boolean} [options.groupSelection=false] - If true, Fabric can select multiple masks as an ActiveSelection.\n * @param {boolean} [options.showPlaceholder=true] - If true, shows placeholder when no image is loaded.\n * @param {string|null} [options.initialImageBase64=null] - Base64 string to auto-load as initial image, if any.\n * @param {string} [options.defaultDownloadFileName='edited_image.jpg'] - Default file name for downloads.\n * @param {Object} [options.label] - Mask label customization options.\n * @param {MaskLabelTextCallback} [options.label.getText] - Callback for label text; receives a stable zero-based creation index.\n * @param {Object} [options.crop] - Crop mode customization options.\n * @param {ImageLoadedCallback} [options.onImageLoaded] - Callback invoked after an image load completes.\n * @param {EditorErrorCallback} [options.onError] - Callback invoked for recoverable internal errors.\n * @param {EditorErrorCallback} [options.onWarning] - Callback invoked for recoverable internal warnings.\n */\n class ImageEditor {\n constructor(options = {}) {\n // Default options that callers can override with constructor options.\n const defaultLabel = {\n getText: (mask) => mask.maskName,\n textOptions: {\n fontSize: 12,\n fill: '#fff',\n backgroundColor: 'rgba(0,0,0,0.7)',\n padding: 2,\n fontFamily: 'monospace',\n fontWeight: 'bold',\n selectable: false,\n evented: false,\n originX: 'left',\n originY: 'top'\n }\n };\n const defaultCrop = {\n minWidth: 100,\n minHeight: 100,\n padding: 10,\n hideMasksDuringCrop: true,\n preserveMasksAfterCrop: false,\n allowRotationOfCropRect: false\n };\n const userLabel = options.label || {};\n const userCrop = options.crop || {};\n this.options = {\n canvasWidth: 800,\n canvasHeight: 600,\n backgroundColor: 'transparent',\n\n animationDuration: 300,\n minScale: 0.1,\n maxScale: 5.0,\n scaleStep: 0.05,\n rotationStep: 90,\n\n expandCanvasToImage: true,\n fitImageToCanvas: false,\n coverImageToCanvas: false,\n\n downsampleOnLoad: true,\n downsampleMaxWidth: 4000,\n downsampleMaxHeight: 3000,\n downsampleQuality: 0.92,\n preserveSourceFormat: true,\n downsampleMimeType: null,\n imageLoadTimeoutMs: 30000,\n\n exportMultiplier: 1,\n exportImageAreaByDefault: true,\n\n defaultMaskWidth: 50,\n defaultMaskHeight: 80,\n maskRotatable: false,\n maskLabelOnSelect: true,\n maskLabelOffset: 3,\n maskName: 'mask',\n\n groupSelection: false,\n\n showPlaceholder: true,\n initialImageBase64: null, // Provide a base64 'data:image/...' string here if you want auto-load\n\n defaultDownloadFileName: 'edited_image.jpg',\n onError: null,\n onWarning: null,\n\n ...options,\n label: {\n ...defaultLabel,\n ...userLabel,\n textOptions: {\n ...defaultLabel.textOptions,\n ...(userLabel.textOptions || {})\n }\n },\n crop: {\n ...defaultCrop,\n ...userCrop\n }\n };\n\n // Verify that Fabric.js is present before any canvas work starts.\n this._fabricLoaded = !!ensureFabric();\n if (!this._fabricLoaded) {\n this._reportError('fabric.js is not loaded. Please include fabric.js first. Initialization will be aborted.');\n }\n\n // Runtime state owned by this editor instance.\n this.canvas = null;\n this.canvasElement = null;\n this.containerElement = null;\n this.placeholderElement = null;\n\n this.originalImage = null;\n this.baseImageScale = 1;\n this.currentScale = 1;\n this.currentRotation = 0;\n this.maskCounter = 0;\n this.isAnimating = false;\n this._isLoading = false;\n this._activeOperationName = null;\n this._activeOperationToken = null;\n this.elements = {};\n this.isImageLoadedToCanvas = false;\n this.maxHistorySize = 50;\n\n this._handlersByElementKey = {};\n this._elementCache = {};\n this._elementOriginalPointerEvents = new Map();\n\n this._lastMask = null;\n this._lastMaskInitialLeft = null;\n this._lastMaskInitialTop = null;\n this._lastMaskInitialWidth = null;\n this._lastSnapshot = null;\n\n this._cropMode = false;\n this._cropRect = null;\n this._cropHandlers = [];\n this._cropPrevEvented = null;\n this._prevSelectionSetting = undefined;\n this._containerOriginalOverflow = null;\n this._lastContainerViewportSize = null;\n this._canvasElementOriginalStyle = null;\n this._visibilityStateByElement = new WeakMap();\n this._scrollbarSizeCache = null;\n this._activeAnimationRejectors = new Set();\n this._disposed = false;\n this._initialized = false;\n\n this.onImageLoaded = typeof options.onImageLoaded === 'function' ? options.onImageLoaded : null;\n\n this.animationQueue = new AnimationQueue();\n this.historyManager = new HistoryManager(this.maxHistorySize);\n }\n\n /**\n * Backward-compatible alias for {@link ImageEditor#canvasElement}.\n *\n * @deprecated Use canvasElement instead. This alias will be removed in v2.0.0.\n * @returns {HTMLCanvasElement|null} The canvas element currently owned by the editor.\n */\n get canvasEl() {\n return this.canvasElement;\n }\n\n set canvasEl(value) {\n this.canvasElement = value;\n }\n\n /**\n * Backward-compatible alias for {@link ImageEditor#containerElement}.\n *\n * @deprecated Use containerElement instead. This alias will be removed in v2.0.0.\n * @returns {HTMLElement|null} The canvas viewport/container element.\n */\n get containerEl() {\n return this.containerElement;\n }\n\n set containerEl(value) {\n this.containerElement = value;\n }\n\n /**\n * Backward-compatible alias for {@link ImageEditor#placeholderElement}.\n *\n * @deprecated Use placeholderElement instead. This alias will be removed in v2.0.0.\n * @returns {HTMLElement|null} The placeholder element shown before an image loads.\n */\n get placeholderEl() {\n return this.placeholderElement;\n }\n\n set placeholderEl(value) {\n this.placeholderElement = value;\n }\n\n /**\n * Initializes the editor, binds to DOM elements, sets up event handlers,\n * and (optionally) loads an initial image.\n * Use this method to set up the editor UI before interacting with it.\n *\n * @param {Object} [idMap={}] - Optional mapping from logical element names to actual DOM element IDs.\n * Supported keys include: canvas, canvasContainer, imgPlaceholder, scaleRate, rotationLeftInput,\n * rotationRightInput, rotateLeftBtn, rotateRightBtn, addMaskBtn, removeMaskBtn, removeAllMasksBtn,\n * mergeBtn, downloadBtn, maskList, zoomInBtn, zoomOutBtn, resetBtn, undoBtn, redoBtn, imageInput,\n * uploadArea, cropBtn, applyCropBtn, and cancelCropBtn. Unknown keys are ignored.\n *\n * @returns {void}\n *\n * @public\n *\n * @example\n * editor.init({\n * canvas: 'myFabricCanvasId',\n * downloadBtn: 'myDownloadButtonId'\n * });\n */\n init(idMap = {}) {\n if (!this._fabricLoaded) return;\n if (this._initialized || this.canvas) this.dispose();\n this._disposed = false;\n this._initialized = true;\n this.animationQueue = new AnimationQueue();\n this.historyManager = new HistoryManager(this.maxHistorySize);\n this._visibilityStateByElement = new WeakMap();\n this._activeAnimationRejectors = new Set();\n this._isLoading = false;\n this._activeOperationName = null;\n this._activeOperationToken = null;\n this._elementOriginalPointerEvents = new Map();\n this._containerOriginalOverflow = null;\n this._lastContainerViewportSize = null;\n this._canvasElementOriginalStyle = null;\n\n const defaults = {\n canvas: 'fabricCanvas',\n canvasContainer: null, // Pass an ID here if you have a scrollable viewport container\n imgPlaceholder: 'imgPlaceholder',\n scaleRate: 'scaleRate',\n rotationLeftInput: 'rotationLeftInput',\n rotationRightInput: 'rotationRightInput',\n rotateLeftBtn: 'rotateLeftBtn',\n rotateRightBtn: 'rotateRightBtn',\n addMaskBtn: 'addMaskBtn',\n removeMaskBtn: 'removeMaskBtn',\n removeAllMasksBtn: 'removeAllMasksBtn',\n mergeBtn: 'mergeBtn',\n downloadBtn: 'downloadBtn',\n maskList: 'maskList',\n zoomInBtn: 'zoomInBtn',\n zoomOutBtn: 'zoomOutBtn',\n resetBtn: 'resetBtn',\n undoBtn: 'undoBtn',\n redoBtn: 'redoBtn',\n imageInput: 'imageInput',\n cropBtn: 'cropBtn',\n applyCropBtn: 'applyCropBtn',\n cancelCropBtn: 'cancelCropBtn'\n };\n\n this.elements = { ...defaults, ...idMap };\n this._elementCache = {};\n\n this._initCanvas();\n this._bindEvents();\n this._updateInputs();\n this._updateMaskList();\n this._updateUI();\n\n // Auto-load initial image if provided\n if (this.options.initialImageBase64) {\n this.loadImage(this.options.initialImageBase64);\n } else {\n this._updatePlaceholderStatus();\n }\n }\n\n _reportError(message, error = null) {\n const handler = this.options && this.options.onError;\n if (typeof handler !== 'function') return;\n\n try {\n handler(error, message);\n } catch {\n // Ignore observer failures so editor recovery paths remain stable.\n }\n }\n\n _reportWarning(message, error = null) {\n const handler = this.options && this.options.onWarning;\n if (typeof handler !== 'function') return;\n\n try {\n handler(error, message);\n } catch {\n // Ignore observer failures so editor recovery paths remain stable.\n }\n }\n\n /**\n * Initializes the Fabric canvas, viewport elements, and selection event handlers.\n *\n * @returns {void}\n * @private\n */\n _initCanvas() {\n const canvasElement = this._getElement('canvas');\n if (!canvasElement) throw new Error('Canvas is not found: ' + this.elements.canvas);\n this.canvasElement = canvasElement;\n this._canvasElementOriginalStyle = {\n display: canvasElement.style.display || '',\n width: canvasElement.style.width || '',\n height: canvasElement.style.height || '',\n maxWidth: canvasElement.style.maxWidth || ''\n };\n\n // Decide which element acts as the viewport for size fallback and scrolling.\n if (this.elements.canvasContainer) {\n const containerElement = this._getElement('canvasContainer');\n this.containerElement = containerElement || canvasElement.parentElement;\n } else {\n this.containerElement = canvasElement.parentElement;\n }\n\n this.placeholderElement = this._getElement('imgPlaceholder') || null;\n\n // Prefer a measured container size when it is available.\n let initialWidth = this.options.canvasWidth;\n let initialHeight = this.options.canvasHeight;\n if (this.containerElement) {\n const containerWidth = Math.floor(this.containerElement.clientWidth);\n const containerHeight = Math.floor(this.containerElement.clientHeight);\n if (containerWidth > 0 && containerHeight > 0) {\n initialWidth = containerWidth;\n initialHeight = containerHeight;\n\n this._lastContainerViewportSize = {\n width: containerWidth,\n height: containerHeight\n };\n }\n }\n\n this.canvas = new fabric.Canvas(canvasElement, {\n width: initialWidth,\n height: initialHeight,\n backgroundColor: this.options.backgroundColor,\n selection: this.options.groupSelection,\n preserveObjectStacking: true\n });\n\n // Fabric event wiring keeps selection, mask labels, and history in sync.\n this.canvas.on('selection:created', (event) => this._handleSelectionChanged(event.selected));\n this.canvas.on('selection:updated', (event) => this._handleSelectionChanged(event.selected));\n this.canvas.on('selection:cleared', () => this._handleSelectionChanged([]));\n this.canvas.on('object:moving', (event) => { if (event.target && event.target.maskId) this._syncMaskLabel(event.target); });\n this.canvas.on('object:scaling', (event) => { if (event.target && event.target.maskId) this._syncMaskLabel(event.target); });\n this.canvas.on('object:rotating', (event) => { if (event.target && event.target.maskId) this._syncMaskLabel(event.target); });\n this.canvas.on('object:modified', (event) => this._handleObjectModified(event.target));\n\n // Avoid inline-element whitespace artifacts around the canvas.\n this.canvasElement.style.display = 'block';\n }\n\n /**\n * Returns a configured DOM element and caches lookups for hot UI paths.\n *\n * @param {string} key - Key in the configured element map.\n * @returns {HTMLElement|null} The configured element, or null when missing.\n * @private\n */\n _getElement(key) {\n const id = this.elements && this.elements[key];\n if (!id) return null;\n if (this._elementCache && Object.prototype.hasOwnProperty.call(this._elementCache, key)) {\n return this._elementCache[key];\n }\n const element = document.getElementById(id);\n if (this._elementCache) this._elementCache[key] = element || null;\n return element || null;\n }\n\n /**\n * Records a history entry after Fabric finishes modifying one or more masks.\n *\n * @param {fabric.Object|fabric.ActiveSelection|null} target - Modified Fabric object or selection.\n * @returns {void}\n * @private\n */\n _handleObjectModified(target) {\n const masks = this._getModifiedMasks(target);\n if (!masks.length) return;\n masks.forEach(mask => {\n if (typeof mask.setCoords === 'function') mask.setCoords();\n this._syncMaskLabel(mask);\n });\n this._expandCanvasToFitObjects(masks);\n this.saveState();\n }\n\n /**\n * Extracts editable mask objects from a Fabric modification target.\n *\n * @param {fabric.Object|fabric.ActiveSelection|null} target - Fabric object or active selection.\n * @returns {Array<fabric.Object>} Modified mask objects.\n * @private\n */\n _getModifiedMasks(target) {\n if (!target) return [];\n if (target.maskId) return [target];\n\n const objects = typeof target.getObjects === 'function' ? target.getObjects() : [];\n\n return Array.isArray(objects) ? objects.filter(object => object && object.maskId) : [];\n }\n\n /**\n * Updates container overflow behavior for fit and cover image modes.\n *\n * @param {Object} [options={}] - Overflow update options.\n * @param {boolean} [options.preserveScroll=false] - If true, keeps the current scroll offsets.\n * @returns {void}\n * @private\n */\n _syncContainerOverflow(options = {}) {\n if (!this.containerElement || !this.containerElement.style) return;\n this._captureContainerOverflowState();\n\n const shouldPreserveScroll = options.preserveScroll === true;\n if (this.options.coverImageToCanvas) {\n this.containerElement.style.overflow = 'scroll';\n if (!shouldPreserveScroll) {\n this.containerElement.scrollLeft = 0;\n this.containerElement.scrollTop = 0;\n }\n } else if (this.options.fitImageToCanvas) {\n this.containerElement.style.overflow = 'auto';\n if (!shouldPreserveScroll) {\n this.containerElement.scrollLeft = 0;\n this.containerElement.scrollTop = 0;\n }\n } else {\n this._restoreContainerOverflowState();\n }\n }\n\n _captureContainerOverflowState() {\n if (!this.containerElement || !this.containerElement.style || this._containerOriginalOverflow) return;\n this._containerOriginalOverflow = {\n overflow: this.containerElement.style.overflow || '',\n overflowX: this.containerElement.style.overflowX || '',\n overflowY: this.containerElement.style.overflowY || ''\n };\n }\n\n _restoreContainerOverflowState() {\n if (!this.containerElement || !this.containerElement.style || !this._containerOriginalOverflow) return;\n this.containerElement.style.overflow = this._containerOriginalOverflow.overflow;\n this.containerElement.style.overflowX = this._containerOriginalOverflow.overflowX;\n this.containerElement.style.overflowY = this._containerOriginalOverflow.overflowY;\n }\n\n _restoreContainerOverflowSnapshot(snapshot) {\n if (!this.containerElement || !this.containerElement.style || !snapshot) return;\n this.containerElement.style.overflow = snapshot.overflow || '';\n this.containerElement.style.overflowX = snapshot.overflowX || '';\n this.containerElement.style.overflowY = snapshot.overflowY || '';\n }\n\n /** \n * DOM / UI bindings\n * @private\n */\n _bindEvents() {\n // Click anywhere on the upload area opens the native file dialog\n this._bindIfExists('uploadArea', 'click', () => {\n const uploadAreaElement = this._getElement('uploadArea');\n if (this._isElementDisabled(uploadAreaElement)) return;\n this._getElement('imageInput')?.click();\n });\n // File-input change\n this._bindIfExists('imageInput', 'change', (event) => {\n const file = event.target.files && event.target.files[0];\n if (file) {\n this._loadImageFile(file)\n .catch(error => this._reportError('Image file could not be loaded', error))\n .finally(() => {\n event.target.value = '';\n });\n }\n });\n // Zoom & reset\n this._bindIfExists('zoomInBtn', 'click', () => this.scaleImage(this.currentScale + this.options.scaleStep).catch(error => this._reportError('scaleImage failed', error)));\n this._bindIfExists('zoomOutBtn', 'click', () => this.scaleImage(this.currentScale - this.options.scaleStep).catch(error => this._reportError('scaleImage failed', error)));\n this._bindIfExists('resetBtn', 'click', () => { this.resetImageTransform().catch(error => this._reportError('resetImageTransform failed', error)); });\n // Mask management\n this._bindIfExists('addMaskBtn', 'click', () => this.createMask());\n this._bindIfExists('removeMaskBtn', 'click', () => this.removeSelectedMask());\n this._bindIfExists('removeAllMasksBtn', 'click', () => this.removeAllMasks());\n // Merge + download\n this._bindIfExists('mergeBtn', 'click', () => this.mergeMasks().catch(error => this._reportError('merge error', error)));\n this._bindIfExists('downloadBtn', 'click', () => this.downloadImage());\n // Undo + Redo\n this._bindIfExists('undoBtn', 'click', () => this.undo().catch(error => this._reportError('undo failed', error)));\n this._bindIfExists('redoBtn', 'click', () => this.redo().catch(error => this._reportError('redo failed', error)));\n\n // Rotation buttons (step can be overridden by two input fields)\n this._bindIfExists('rotateLeftBtn', 'click', () => {\n const rotationInputElement = this._getElement('rotationLeftInput');\n let step = this.options.rotationStep;\n if (rotationInputElement) {\n const parsedStep = parseFloat(rotationInputElement.value);\n if (!isNaN(parsedStep)) step = parsedStep;\n }\n this.rotateImage(this.currentRotation - step).catch(error => this._reportError('rotateImage failed', error));\n });\n this._bindIfExists('rotateRightBtn', 'click', () => {\n const rotationInputElement = this._getElement('rotationRightInput');\n let step = this.options.rotationStep;\n if (rotationInputElement) {\n const parsedStep = parseFloat(rotationInputElement.value);\n if (!isNaN(parsedStep)) step = parsedStep;\n }\n this.rotateImage(this.currentRotation + step).catch(error => this._reportError('rotateImage failed', error));\n });\n\n // Crop bindings (optional: bound only if element IDs exist in elements)\n this._bindIfExists('cropBtn', 'click', () => this.enterCropMode());\n this._bindIfExists('applyCropBtn', 'click', () => { this.applyCrop().catch(error => this._reportError('applyCrop failed', error)); });\n this._bindIfExists('cancelCropBtn', 'click', () => this.cancelCrop());\n this._bindIfExists('maskList', 'click', (event) => this._handleMaskListClick(event));\n }\n\n /**\n * Binds a DOM event listener when the configured element exists and records it for disposal.\n *\n * @param {string} key - Key in this.elements for the target DOM element.\n * @param {string} eventName - DOM event name to listen for.\n * @param {EventListener} handler - Event listener callback.\n * @private\n */\n _bindIfExists(key, eventName, handler) {\n const element = this._getElement(key);\n if (element) {\n element.addEventListener(eventName, handler);\n this._handlersByElementKey = this._handlersByElementKey || {};\n if (!this._handlersByElementKey[key]) this._handlersByElementKey[key] = [];\n this._handlersByElementKey[key].push({ eventName, handler });\n }\n }\n\n /**\n * Reads an image File as a data URL and loads it into the Fabric canvas.\n *\n * @param {File} file - Image file selected by the user.\n * @returns {Promise<void>} Resolves after the selected file is loaded.\n * @private\n */\n _loadImageFile(file) {\n if (!this._isSupportedImageFile(file)) {\n const error = new Error('Selected file is not a supported image');\n this._reportError('Selected file is not a supported image', error);\n return Promise.reject(error);\n }\n\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = (event) => {\n this.loadImage(event.target.result)\n .then(resolve)\n .catch(reject);\n };\n reader.onerror = (event) => {\n const error = new Error('Image file could not be read');\n this._reportError('Image file could not be read', event);\n reject(error);\n };\n reader.readAsDataURL(file);\n });\n }\n\n _isSupportedImageFile(file) {\n if (!file) return false;\n if (typeof file.type === 'string' && file.type.startsWith('image/')) return true;\n const fileName = String(file.name || '');\n return /\\.(avif|bmp|gif|jpe?g|png|webp)$/i.test(fileName);\n }\n\n /**\n * Warns when more than one mutually exclusive image layout mode is enabled.\n *\n * @returns {void}\n * @private\n */\n _warnOnImageLayoutOptionConflict() {\n const activeModes = [\n ['fitImageToCanvas', this.options.fitImageToCanvas],\n ['coverImageToCanvas', this.options.coverImageToCanvas],\n ['expandCanvasToImage', this.options.expandCanvasToImage]\n ].filter(([, isEnabled]) => !!isEnabled).map(([name]) => name);\n\n if (activeModes.length <= 1) return;\n this._reportWarning(\n `Only one image layout mode should be enabled. Active modes: ${activeModes.join(', ')}.`\n );\n }\n\n /**\n * Loads a base64 data URL into the Fabric canvas as the base image.\n *\n * @async\n * @param {string} imageBase64 - Image data URL beginning with `data:image/`.\n * @param {LoadImageOptions} [options={}] - Optional load behavior.\n * @returns {Promise<void>} Resolves after the Fabric image is added to the canvas.\n * @public\n */\n async loadImage(imageBase64, options = {}) {\n if (!this._fabricLoaded) return;\n if (!this.canvas || this._disposed) return;\n if (!imageBase64 || typeof imageBase64 !== 'string' || !imageBase64.startsWith('data:image/')) return;\n this._assertIdleForOperation('loadImage', options);\n\n this._isLoading = true;\n this._updateUI();\n this._warnOnImageLayoutOptionConflict();\n const transaction = this._captureLoadImageTransaction();\n\n try {\n const imageElement = await this._createImageElement(imageBase64);\n if (this._disposed || !this.canvas) throw new Error('Editor was disposed while loading image');\n\n let loadSource = imageBase64;\n const downsampleMaxWidth = Number(this.options.downsampleMaxWidth);\n const downsampleMaxHeight = Number(this.options.downsampleMaxHeight);\n if (this.options.downsampleOnLoad && downsampleMaxWidth > 0 && downsampleMaxHeight > 0) {\n const shouldResize =\n imageElement.naturalWidth > downsampleMaxWidth ||\n imageElement.naturalHeight > downsampleMaxHeight;\n if (shouldResize) {\n const ratio = Math.min(\n downsampleMaxWidth / imageElement.naturalWidth,\n downsampleMaxHeight / imageElement.naturalHeight\n );\n const targetWidth = Math.round(imageElement.naturalWidth * ratio);\n const targetHeight = Math.round(imageElement.naturalHeight * ratio);\n loadSource = this._resampleImageToDataURL(\n imageElement,\n targetWidth,\n targetHeight,\n this._normalizeQuality(this.options.downsampleQuality),\n imageBase64\n );\n }\n } else if (this.options.downsampleOnLoad) {\n this._reportWarning('loadImage: downsample limits must be positive numbers; using the original image');\n }\n\n const fabricImage = await this._createFabricImageFromURL(loadSource);\n if (this._disposed || !this.canvas) throw new Error('Editor was disposed while loading image');\n \n this.canvas.discardActiveObject();\n this._hideAllMaskLabels();\n this.canvas.clear();\n this.canvas.setBackgroundColor(this.options.backgroundColor, this.canvas.renderAll.bind(this.canvas));\n\n fabricImage.set({ originX: 'left', originY: 'top', selectable: false, evented: false });\n this._setPlaceholderVisible(false);\n this._syncContainerOverflow({ preserveScroll: options.preserveScroll === true });\n\n const imageWidth = fabricImage.width;\n const imageHeight = fabricImage.height;\n\n const viewport = this._getContainerViewportSize();\n const minWidth = viewport.width;\n const minHeight = viewport.height;\n\n if (this.options.fitImageToCanvas) {\n const canvasWidth = Math.max(1, minWidth - 1);\n const canvasHeight = Math.max(1, minHeight - 1);\n this._setCanvasSizeInt(canvasWidth, canvasHeight);\n const fitScale = Math.min(canvasWidth / imageWidth, canvasHeight / imageHeight, 1);\n fabricImage.set({ left: 0, top: 0 });\n fabricImage.scale(fitScale);\n this.baseImageScale = fabricImage.scaleX || 1;\n } else if (this.options.coverImageToCanvas) {\n const layout = this._calculateCoverCanvasLayout(imageWidth, imageHeight);\n this._setCanvasSizeInt(layout.canvasWidth, layout.canvasHeight);\n fabricImage.set({ left: 0, top: 0 });\n fabricImage.scale(layout.scale);\n this.baseImageScale = fabricImage.scaleX || 1;\n } else if (this.options.expandCanvasToImage) {\n const canvasWidth = Math.max(minWidth, Math.floor(imageWidth));\n const canvasHeight = Math.max(minHeight, Math.floor(imageHeight));\n this._setCanvasSizeInt(canvasWidth, canvasHeight);\n fabricImage.set({ left: 0, top: 0 });\n fabricImage.scale(1);\n this.baseImageScale = 1;\n } else {\n const canvasWidth = Math.max(this.options.canvasWidth, minWidth);\n const canvasHeight = Math.max(this.options.canvasHeight, minHeight);\n this._setCanvasSizeInt(canvasWidth, canvasHeight);\n const fitScale = Math.min(canvasWidth / imageWidth, canvasHeight / imageHeight, 1);\n fabricImage.set({ left: 0, top: 0 });\n fabricImage.scale(fitScale);\n this.baseImageScale = fabricImage.scaleX || 1;\n }\n\n this.originalImage = fabricImage;\n this.canvas.add(fabricImage);\n this.canvas.sendToBack(fabricImage);\n\n this._clearMaskPlacementMemory();\n if (options.resetMaskCounter !== false) this.maskCounter = 0;\n this.currentScale = 1;\n this.currentRotation = 0;\n\n // this._setPlaceholderVisible(false);\n this._updateInputs();\n this._updateMaskList();\n this.isImageLoadedToCanvas = true;\n this._updateUI();\n this.canvas.renderAll();\n this._lastSnapshot = this._captureCanvasStateOrThrow('loadImage');\n\n if (typeof this.onImageLoaded === 'function') {\n this.onImageLoaded();\n }\n } catch (error) {\n await this._rollbackLoadImageTransaction(transaction);\n throw error;\n } finally {\n this._isLoading = false;\n if (!this._disposed && this.canvas) this._updateUI();\n }\n }\n\n /**\n * Checks whether there is a loaded image on the current canvas.\n * @returns {boolean} true if loaded, false if not\n */\n isImageLoaded() {\n const fabricInstance = ensureFabric();\n return !!(\n this.originalImage &&\n fabricInstance &&\n this.originalImage instanceof fabricInstance.Image &&\n this.originalImage.width > 0 &&\n this.originalImage.height > 0\n );\n }\n\n /**\n * Checks whether the editor is in a temporary non-mutating state.\n *\n * @returns {boolean} True while loading, animating, cropping, or running a compound operation.\n * @public\n */\n isBusy() {\n return !!(\n this.isAnimating ||\n this._cropMode ||\n this._isLoading ||\n this._activeOperationToken ||\n (this.animationQueue && this.animationQueue.isBusy())\n );\n }\n\n /**\n * Creates an HTMLImageElement from a given data URL.\n * \n * @param {string} dataUrl - A data URL representing the image (e.g., \"data:image/png;base64,...\").\n * @param {number} [timeoutMs=this.options.imageLoadTimeoutMs] - Maximum decode time before rejecting.\n * @returns {Promise<HTMLImageElement>} A promise that resolves to the created image element when loaded, or rejects on error.\n * @private\n */\n _createImageElement(dataUrl, timeoutMs = this.options.imageLoadTimeoutMs) {\n return new Promise((resolve, reject) => {\n const imageElement = new Image();\n let isSettled = false;\n const safeTimeoutMs = Number.isFinite(Number(timeoutMs)) && Number(timeoutMs) > 0\n ? Number(timeoutMs)\n : 30000;\n let timerId;\n const settle = (callback) => {\n if (isSettled) return;\n isSettled = true;\n clearTimeout(timerId);\n imageElement.onload = null;\n imageElement.onerror = null;\n callback();\n };\n timerId = setTimeout(() => {\n settle(() => reject(new Error('Image load timed out')));\n try { imageElement.src = ''; } catch (error) { void error; }\n }, safeTimeoutMs);\n imageElement.onload = () => settle(() => resolve(imageElement));\n imageElement.onerror = (error) => settle(() => reject(error));\n imageElement.src = dataUrl;\n });\n }\n\n _createFabricImageFromURL(dataUrl, timeoutMs = this.options.imageLoadTimeoutMs) {\n return new Promise((resolve, reject) => {\n const safeTimeoutMs = this._getSafeTimeoutMs(timeoutMs);\n let isSettled = false;\n let timerId;\n const settle = (callback) => {\n if (isSettled) return;\n isSettled = true;\n clearTimeout(timerId);\n callback();\n };\n\n timerId = setTimeout(() => {\n settle(() => reject(new Error('Fabric image load timed out')));\n }, safeTimeoutMs);\n\n try {\n fabric.Image.fromURL(dataUrl, (fabricImage) => {\n settle(() => {\n if (!fabricImage) {\n reject(new Error('Image could not be loaded'));\n return;\n }\n resolve(fabricImage);\n });\n }, { crossOrigin: 'anonymous' });\n } catch (error) {\n settle(() => reject(error));\n }\n });\n }\n\n _getSafeTimeoutMs(timeoutMs) {\n const safeTimeoutMs = Number(timeoutMs);\n return Number.isFinite(safeTimeoutMs) && safeTimeoutMs > 0 ? safeTimeoutMs : 30000;\n }\n\n _captureLoadImageTransaction() {\n return {\n canvasState: this._serializeCanvasState(),\n baseImageScale: this.baseImageScale,\n currentScale: this.currentScale,\n currentRotation: this.currentRotation,\n maskCounter: this.maskCounter,\n isImageLoadedToCanvas: this.isImageLoadedToCanvas,\n lastSnapshot: this._lastSnapshot,\n lastMask: this._lastMask,\n lastMaskInitialLeft: this._lastMaskInitialLeft,\n lastMaskInitialTop: this._lastMaskInitialTop,\n lastMaskInitialWidth: this._lastMaskInitialWidth,\n containerOverflow: this.containerElement && this.containerElement.style ? {\n overflow: this.containerElement.style.overflow || '',\n overflowX: this.containerElement.style.overflowX || '',\n overflowY: this.containerElement.style.overflowY || ''\n } : null,\n scrollLeft: this.containerElement ? this.containerElement.scrollLeft : 0,\n scrollTop: this.containerElement ? this.containerElement.scrollTop : 0,\n placeholderVisibility: this._captureElementVisibility(this.placeholderElement),\n canvasVisibility: this._captureElementVisibility(this._getCanvasVisibilityElement())\n };\n }\n\n async _rollbackLoadImageTransaction(transaction) {\n if (!transaction || !this.canvas || this._disposed) return;\n let didRestoreCanvasState = false;\n try {\n if (transaction.canvasState) {\n await this.loadFromState(transaction.canvasState);\n didRestoreCanvasState = true;\n }\n } catch (error) {\n this._lastMask = null;\n this._reportError('loadImage rollback failed', error);\n }\n\n this.baseImageScale = transaction.baseImageScale;\n this.currentScale = transaction.currentScale;\n this.currentRotation = transaction.currentRotation;\n this.maskCounter = transaction.maskCounter;\n this.isImageLoadedToCanvas = transaction.isImageLoadedToCanvas;\n this._lastSnapshot = transaction.lastSnapshot;\n if (didRestoreCanvasState) {\n this._restoreLastMaskReference(transaction.lastMask);\n } else {\n this._lastMask = null;\n }\n this._lastMaskInitialLeft = transaction.lastMaskInitialLeft;\n this._lastMaskInitialTop = transaction.lastMaskInitialTop;\n this._lastMaskInitialWidth = transaction.lastMaskInitialWidth;\n this._restoreElementVisibility(this.placeholderElement, transaction.placeholderVisibility);\n this._restoreElementVisibility(this._getCanvasVisibilityElement(), transaction.canvasVisibility);\n if (this.containerElement) {\n this.containerElement.scrollLeft = transaction.scrollLeft;\n this.containerElement.scrollTop = transaction.scrollTop;\n this._restoreContainerOverflowSnapshot(transaction.containerOverflow);\n }\n this._updateInputs();\n this._updateMaskList();\n this._updateUI();\n if (this.canvas) this.canvas.renderAll();\n }\n\n _restoreLastMaskReference(previousLastMask) {\n if (!this.canvas) {\n this._lastMask = null;\n return;\n }\n\n const masks = this.canvas.getObjects().filter(object => object.maskId);\n const previousMaskId = previousLastMask && previousLastMask.maskId;\n this._lastMask = masks.find(mask => mask.maskId === previousMaskId) || masks[masks.length - 1] || null;\n if (!this._lastMask) {\n this._lastMaskInitialLeft = null;\n this._lastMaskInitialTop = null;\n this._lastMaskInitialWidth = null;\n }\n }\n\n /**\n * Resamples the given image element to a new width and height and returns the result as a data URL.\n * \n * @param {HTMLImageElement} imageElement - The image element to resample.\n * @param {number} targetWidth - Target width (in pixels) for the resampled image.\n * @param {number} targetHeight - Target height (in pixels) for the resampled image.\n * @param {number} [quality=0.92] - Image quality between 0 and 1 for lossy formats.\n * @param {string|null} [sourceDataUrl=null] - Source data URL used to preserve alpha-capable formats.\n * @returns {string} A data URL representing the resampled image.\n * @private\n */\n _resampleImageToDataURL(imageElement, targetWidth, targetHeight, quality = 0.92, sourceDataUrl = null) {\n const sourceWidth = Math.max(1, Number(imageElement && (imageElement.naturalWidth || imageElement.width)) || 0);\n const sourceHeight = Math.max(1, Number(imageElement && (imageElement.naturalHeight || imageElement.height)) || 0);\n const safeTargetWidth = Math.round(Number(targetWidth));\n const safeTargetHeight = Math.round(Number(targetHeight));\n if (!Number.isFinite(safeTargetWidth) || !Number.isFinite(safeTargetHeight) || safeTargetWidth <= 0 || safeTargetHeight <= 0) {\n throw new Error('Invalid image resample target dimensions');\n }\n\n const offscreenCanvas = document.createElement('canvas');\n offscreenCanvas.width = safeTargetWidth;\n offscreenCanvas.height = safeTargetHeight;\n const context = offscreenCanvas.getContext('2d');\n if (!context) throw new Error('2D canvas context is unavailable');\n context.drawImage(imageElement, 0, 0, sourceWidth, sourceHeight, 0, 0, safeTargetWidth, safeTargetHeight);\n return offscreenCanvas.toDataURL(this._getDownsampleMimeType(sourceDataUrl), quality);\n }\n\n _getDataUrlMimeType(dataUrl) {\n const match = String(dataUrl || '').match(/^data:([^;,]+)[;,]/i);\n return match ? match[1].toLowerCase() : '';\n }\n\n _getDownsampleMimeType(sourceDataUrl) {\n if (this.options.downsampleMimeType) {\n const requestedFormat = this._normalizeImageFormat(this.options.downsampleMimeType);\n return `image/${requestedFormat}`;\n }\n const sourceMimeType = this._getDataUrlMimeType(sourceDataUrl);\n if (this.options.preserveSourceFormat !== false && (sourceMimeType === 'image/png' || sourceMimeType === 'image/webp')) {\n return sourceMimeType;\n }\n return 'image/jpeg';\n }\n\n _captureCanvasStateOrThrow(context) {\n const snapshot = this._serializeCanvasState();\n if (!snapshot) throw new Error(`${context}: canvas state is unavailable`);\n return snapshot;\n }\n\n /** \n * Sets canvas size to integer width and height values to prevent scrollbars due to sub-pixel rendering.\n * Also updates the corresponding style attributes.\n * \n * @param {number} width - Canvas width in pixels.\n * @param {number} height - Canvas height in pixels.\n * @private\n */\n _setCanvasSizeInt(width, height) {\n const integerWidth = Math.max(1, Math.round(Number(width) || 1));\n const integerHeight = Math.max(1, Math.round(Number(height) || 1));\n // Set fabric internal and also style attributes to keep DOM consistent\n this.canvas.setWidth(integerWidth);\n this.canvas.setHeight(integerHeight);\n if (typeof this.canvas.calcOffset === 'function') this.canvas.calcOffset();\n // Keep DOM element in sync (avoid fractional CSS pixels)\n if (this.canvasElement) {\n this.canvasElement.style.width = integerWidth + 'px';\n this.canvasElement.style.height = integerHeight + 'px';\n }\n }\n\n _ceilCanvasDimension(value) {\n const numericValue = Number(value) || 0;\n const roundedValue = Math.round(numericValue);\n if (Math.abs(numericValue - roundedValue) < 0.01) return roundedValue;\n return Math.ceil(numericValue);\n }\n\n _getContainerViewportSize() {\n if (!this.containerElement) {\n return {\n width: Math.max(1, Math.floor(this.options.canvasWidth || 1)),\n height: Math.max(1, Math.floor(this.options.canvasHeight || 1))\n };\n }\n\n const measuredWidth = Math.floor(this.containerElement.clientWidth || 0);\n const measuredHeight = Math.floor(this.containerElement.clientHeight || 0);\n let width = Math.max(1, measuredWidth || this._lastContainerViewportSize?.width || this.options.canvasWidth || 1);\n let height = Math.max(1, measuredHeight || this._lastContainerViewportSize?.height || this.options.canvasHeight || 1);\n\n if (measuredWidth > 0 && measuredHeight > 0) {\n this._lastContainerViewportSize = { width: measuredWidth, height: measuredHeight };\n }\n\n if (this._hasFixedContainerScrollbars()) {\n return { width, height };\n }\n\n const overflow = this._getContainerOverflowValues();\n const canScrollX = overflow.x.some(value => value === 'auto' || value === 'scroll');\n const canScrollY = overflow.y.some(value => value === 'auto' || value === 'scroll');\n const hasHorizontalScrollbar = canScrollX && this.containerElement.scrollWidth > this.containerElement.clientWidth;\n const hasVerticalScrollbar = canScrollY && this.containerElement.scrollHeight > this.containerElement.clientHeight;\n\n if (hasHorizontalScrollbar || hasVerticalScrollbar) {\n const scrollbar = this._getScrollbarSize();\n if (hasVerticalScrollbar) width += scrollbar.width;\n if (hasHorizontalScrollbar) height += scrollbar.height;\n }\n\n return { width, height };\n }\n\n /**\n * Reads inline and computed overflow values for both scroll axes.\n *\n * @returns {{x:string[], y:string[]}} Overflow values grouped by axis.\n * @private\n */\n _getContainerOverflowValues() {\n if (!this.containerElement) return { x: [], y: [] };\n const inlineOverflow = this.containerElement.style.overflow;\n const inlineOverflowX = this.containerElement.style.overflowX;\n const inlineOverflowY = this.containerElement.style.overflowY;\n let computedOverflow = '';\n let computedOverflowX = '';\n let computedOverflowY = '';\n\n if (typeof window !== 'undefined' && typeof window.getComputedStyle === 'function') {\n const style = window.getComputedStyle(this.containerElement);\n computedOverflow = style.overflow;\n computedOverflowX = style.overflowX;\n computedOverflowY = style.overflowY;\n }\n\n return {\n x: [inlineOverflow, inlineOverflowX, computedOverflow, computedOverflowX],\n y: [inlineOverflow, inlineOverflowY, computedOverflow, computedOverflowY]\n };\n }\n\n _hasFixedContainerScrollbars() {\n if (!this.containerElement) return false;\n const overflow = this._getContainerOverflowValues();\n return [...overflow.x, ...overflow.y].some(value => value === 'scroll');\n }\n\n _getScrollbarSize() {\n if (this._scrollbarSizeCache) {\n return { ...this._scrollbarSizeCache };\n }\n if (typeof document === 'undefined' || !document.createElement || !document.body) {\n return { width: 0, height: 0 };\n }\n\n const probe = document.createElement('div');\n probe.style.position = 'absolute';\n probe.style.visibility = 'hidden';\n probe.style.overflow = 'scroll';\n probe.style.width = '100px';\n probe.style.height = '100px';\n probe.style.top = '-9999px';\n document.body.appendChild(probe);\n\n const width = Math.max(0, probe.offsetWidth - probe.clientWidth);\n const height = Math.max(0, probe.offsetHeight - probe.clientHeight);\n document.body.removeChild(probe);\n\n this._scrollbarSizeCache = { width, height };\n return { ...this._scrollbarSizeCache };\n }\n\n _getScrollSafetyMargin() {\n return 2;\n }\n\n _getScrollableCanvasSize(contentWidth, contentHeight, viewport = this._getContainerViewportSize()) {\n if (this._hasFixedContainerScrollbars()) {\n const safetyMargin = this._getScrollSafetyMargin();\n const safeWidth = Math.max(1, viewport.width - safetyMargin);\n const safeHeight = Math.max(1, viewport.height - safetyMargin);\n return {\n width: contentWidth > viewport.width + 0.5 ? this._ceilCanvasDimension(contentWidth) : safeWidth,\n height: contentHeight > viewport.height + 0.5 ? this._ceilCanvasDimension(contentHeight) : safeHeight,\n viewportWidth: viewport.width,\n viewportHeight: viewport.height,\n hasHorizontal: true,\n hasVertical: true\n };\n }\n\n const scrollbar = this._getScrollbarSize();\n let hasVertical = false;\n let hasHorizontal = false;\n let effectiveWidth;\n let effectiveHeight;\n\n for (let i = 0; i < 4; i += 1) {\n effectiveWidth = Math.max(1, viewport.width - (hasVertical ? scrollbar.width : 0));\n effectiveHeight = Math.max(1, viewport.height - (hasHorizontal ? scrollbar.height : 0));\n\n const nextHasVertical = contentHeight > effectiveHeight + 0.5;\n const nextHasHorizontal = contentWidth > effectiveWidth + 0.5;\n\n if (nextHasVertical === hasVertical && nextHasHorizontal === hasHorizontal) break;\n hasVertical = nextHasVertical;\n hasHorizontal = nextHasHorizontal;\n }\n\n effectiveWidth = Math.max(1, viewport.width - (hasVertical ? scrollbar.width : 0));\n effectiveHeight = Math.max(1, viewport.height - (hasHorizontal ? scrollbar.height : 0));\n\n return {\n width: hasHorizontal ? this._ceilCanvasDimension(contentWidth) : effectiveWidth,\n height: hasVertical ? this._ceilCanvasDimension(contentHeight) : effectiveHeight,\n viewportWidth: effectiveWidth,\n viewportHeight: effectiveHeight,\n hasHorizontal,\n hasVertical\n };\n }\n\n _calculateCoverCanvasLayout(imageWidth, imageHeight) {\n const viewport = this._getContainerViewportSize();\n\n if (this._hasFixedContainerScrollbars()) {\n const safetyMargin = this._getScrollSafetyMargin();\n const targetWidth = Math.max(1, viewport.width - safetyMargin);\n const targetHeight = Math.max(1, viewport.height - safetyMargin);\n const scale = Math.min(1, Math.max(targetWidth / imageWidth, targetHeight / imageHeight));\n const contentWidth = imageWidth * scale;\n const contentHeight = imageHeight * scale;\n const canvasSize = this._getScrollableCanvasSize(contentWidth, contentHeight, viewport);\n return {\n scale,\n canvasWidth: canvasSize.width,\n canvasHeight: canvasSize.height\n };\n }\n\n const scrollbar = this._getScrollbarSize();\n let hasVertical = false;\n let hasHorizontal = false;\n let scale = 1;\n let contentWidth = imageWidth;\n let contentHeight = imageHeight;\n let effectiveWidth;\n let effectiveHeight;\n\n for (let i = 0; i < 4; i += 1) {\n effectiveWidth = Math.max(1, viewport.width - (hasVertical ? scrollbar.width : 0));\n effectiveHeight = Math.max(1, viewport.height - (hasHorizontal ? scrollbar.height : 0));\n scale = Math.min(1, Math.max(effectiveWidth / imageWidth, effectiveHeight / imageHeight));\n contentWidth = imageWidth * scale;\n contentHeight = imageHeight * scale;\n\n const nextHasVertical = contentHeight > effectiveHeight + 0.5;\n const nextHasHorizontal = contentWidth > effectiveWidth + 0.5;\n\n if (nextHasVertical === hasVertical && nextHasHorizontal === hasHorizontal) break;\n hasVertical = nextHasVertical;\n hasHorizontal = nextHasHorizontal;\n }\n\n const canvasSize = this._getScrollableCanvasSize(contentWidth, contentHeight, viewport);\n return {\n scale,\n canvasWidth: canvasSize.width,\n canvasHeight: canvasSize.height\n };\n }\n\n _getStateProperties() {\n return [\n 'maskId',\n 'maskName',\n 'maskLabel',\n 'isCropRect',\n 'originalAlpha',\n 'originalStroke',\n 'originalStrokeWidth',\n 'selectable',\n 'evented',\n 'hasControls',\n 'lockRotation',\n 'borderColor',\n 'cornerColor',\n 'cornerSize',\n 'transparentCorners',\n 'strokeUniform',\n 'strokeDashArray'\n ];\n }\n\n _getMaskNormalStyle(mask) {\n const strokeWidth = Number(mask && mask.originalStrokeWidth);\n const opacity = Number(mask && mask.originalAlpha);\n const style = {\n stroke: (mask && mask.originalStroke) || '#ccc',\n strokeWidth: Number.isFinite(strokeWidth) ? strokeWidth : 1\n };\n if (Number.isFinite(opacity)) style.opacity = opacity;\n return style;\n }\n\n _withNormalizedMaskStyles(callback) {\n if (!this.canvas) return callback();\n const masks = this.canvas.getObjects().filter(object => object.maskId);\n const maskStyleBackups = [];\n\n try {\n masks.forEach(mask => {\n const normalStyle = this._getMaskNormalStyle(mask);\n const stylePatch = {};\n Object.keys(normalStyle).forEach(property => {\n if (mask[property] !== normalStyle[property]) {\n stylePatch[property] = normalStyle[property];\n }\n });\n const changedProperties = Object.keys(stylePatch);\n if (!changedProperties.length) return;\n\n const backup = { object: mask };\n changedProperties.forEach(property => {\n backup[property] = mask[property];\n });\n maskStyleBackups.push(backup);\n mask.set(stylePatch);\n });\n const result = callback();\n if (result && typeof result.then === 'function') {\n throw new Error('_withNormalizedMaskStyles callback must be synchronous');\n }\n return result;\n } finally {\n maskStyleBackups.forEach(backup => {\n try {\n const restorePatch = {};\n Object.keys(backup).forEach(property => {\n if (property !== 'object') restorePatch[property] = backup[property];\n });\n backup.object.set(restorePatch);\n } catch (error) { void error; }\n });\n }\n }\n\n _restoreMaskControls(mask) {\n if (!mask) return;\n\n const cornerSize = Number(mask.cornerSize);\n mask.set({\n selectable: mask.selectable !== false,\n evented: mask.evented !== false,\n hasControls: mask.hasControls !== false,\n lockRotation: typeof mask.lockRotation === 'boolean' ? mask.lockRotation : !this.options.maskRotatable,\n borderColor: mask.borderColor || 'red',\n cornerColor: mask.cornerColor || 'black',\n cornerSize: Number.isFinite(cornerSize) ? cornerSize : 8,\n transparentCorners: mask.transparentCorners === true,\n strokeUniform: mask.strokeUniform !== false\n });\n if (typeof mask.setCoords === 'function') mask.setCoords();\n }\n\n /**\n * Captures editor-owned runtime state that Fabric does not include in canvas JSON.\n *\n * @returns {{version:number, baseImageScale:number, currentScale:number, currentRotation:number, maskCounter:number}} Serializable editor metadata.\n * @private\n */\n _serializeEditorMetadata() {\n const baseImageScale = Number(this.baseImageScale);\n const currentScale = Number(this.currentScale);\n const currentRotation = Number(this.currentRotation);\n const maskCounter = Number(this.maskCounter);\n\n return {\n version: 1,\n baseImageScale: Number.isFinite(baseImageScale) && baseImageScale > 0 ? baseImageScale : 1,\n currentScale: Number.isFinite(currentScale) && currentScale > 0 ? currentScale : 1,\n currentRotation: Number.isFinite(currentRotation) ? currentRotation : 0,\n maskCounter: Number.isFinite(maskCounter) && maskCounter > 0 ? Math.floor(maskCounter) : 0\n };\n }\n\n _serializeCanvasState() {\n if (!this.canvas) return null;\n return this._withNormalizedMaskStyles(() => {\n const jsonObject = this.canvas.toJSON(this._getStateProperties());\n if (Array.isArray(jsonObject.objects)) {\n jsonObject.objects = jsonObject.objects.filter(object => !object.isCropRect && !object.maskLabel);\n }\n jsonObject.imageEditorMetadata = this._serializeEditorMetadata();\n return JSON.stringify(jsonObject);\n });\n }\n\n /**\n * Normalizes a lossy image quality value to Fabric/canvas's 0..1 range.\n *\n * @param {number} quality - Requested image quality.\n * @returns {number} A finite quality value between 0 and 1.\n * @private\n */\n _normalizeQuality(quality, fallback = undefined) {\n const fallbackQuality = fallback == null ? this.options.downsampleQuality : fallback;\n const numericFallback = fallbackQuality == null ? NaN : Number(fallbackQuality);\n const safeFallback = Number.isFinite(numericFallback)\n ? Math.max(0, Math.min(1, numericFallback))\n : 0.92;\n if (quality == null) return safeFallback;\n const numericQuality = Number(quality);\n if (!Number.isFinite(numericQuality)) return safeFallback;\n return Math.max(0, Math.min(1, numericQuality));\n }\n\n /**\n * Normalizes public image format aliases to canvas export format names.\n *\n * @param {string} format - Requested image format or MIME type.\n * @returns {'jpeg'|'png'|'webp'} Canvas-compatible image format.\n * @private\n */\n _normalizeImageFormat(format) {\n const typeMapping = {\n 'jpeg': 'jpeg',\n 'jpg': 'jpeg',\n 'image/jpeg': 'jpeg',\n 'png': 'png',\n 'image/png': 'png',\n 'webp': 'webp',\n 'image/webp': 'webp'\n };\n return typeMapping[String(format || 'jpeg').toLowerCase()] || 'jpeg';\n }\n\n /**\n * Converts a bounding rectangle into a canvas-safe integer source region.\n *\n * @param {{left:number, top:number, width:number, height:number}} bounds - Bounds in canvas coordinates.\n * @param {Object} [options={}] - Region rounding options.\n * @param {boolean} [options.includePartialPixels=true] - If false, excludes partially covered trailing pixels.\n * @returns {{sourceX:number, sourceY:number, sourceWidth:number, sourceHeight:number}} Clamped source region.\n * @private\n */\n _getClampedCanvasRegion(bounds, options = {}) {\n const canvasWidth = Math.max(1, Math.round(this.canvas.getWidth()));\n const canvasHeight = Math.max(1, Math.round(this.canvas.getHeight()));\n const left = Number(bounds.left) || 0;\n const top = Number(bounds.top) || 0;\n const width = Math.max(0, Number(bounds.width) || 0);\n const height = Math.max(0, Number(bounds.height) || 0);\n const includePartialPixels = options.includePartialPixels !== false;\n const roundEnd = includePartialPixels ? Math.ceil : Math.floor;\n const sourceX = Math.min(canvasWidth - 1, Math.max(0, Math.floor(left)));\n const sourceY = Math.min(canvasHeight - 1, Math.max(0, Math.floor(top)));\n const endX = Math.min(canvasWidth, Math.max(sourceX + 1, roundEnd(left + width)));\n const endY = Math.min(canvasHeight, Math.max(sourceY + 1, roundEnd(top + height)));\n\n return {\n sourceX,\n sourceY,\n sourceWidth: Math.max(1, endX - sourceX),\n sourceHeight: Math.max(1, endY - sourceY)\n };\n }\n\n _hasFractionalCanvasEdge(value) {\n const numericValue = Number(value);\n if (!Number.isFinite(numericValue)) return false;\n return Math.abs(numericValue - Math.round(numericValue)) > 0.01;\n }\n\n _getPartialExportEdges(bounds) {\n if (!bounds) return null;\n const angle = Math.abs((Number(this.originalImage && this.originalImage.angle) || 0) % 90);\n const isAxisAligned = angle < 0.01 || Math.abs(angle - 90) < 0.01;\n if (!isAxisAligned) return null;\n\n return {\n left: this._hasFractionalCanvasEdge(bounds.left),\n top: this._hasFractionalCanvasEdge(bounds.top),\n right: this._hasFractionalCanvasEdge((Number(bounds.left) || 0) + (Number(bounds.width) || 0)),\n bottom: this._hasFractionalCanvasEdge((Number(bounds.top) || 0) + (Number(bounds.height) || 0))\n };\n }\n\n async _sealPartialTransparentEdges(dataUrl, edges) {\n if (!edges || !Object.values(edges).some(Boolean)) return dataUrl;\n\n const imageElement = await this._createImageElement(dataUrl);\n const width = Math.max(1, imageElement.naturalWidth || imageElement.width || 1);\n const height = Math.max(1, imageElement.naturalHeight || imageElement.height || 1);\n const offscreenCanvas = document.createElement('canvas');\n offscreenCanvas.width = width;\n offscreenCanvas.height = height;\n const context = offscreenCanvas.getContext('2d');\n if (!context) throw new Error('2D canvas context is unavailable');\n context.drawImage(imageElement, 0, 0, width, height);\n\n const imageData = context.getImageData(0, 0, width, height);\n const pixels = imageData.data;\n const sealPixel = (x, y, fallbackX, fallbackY) => {\n const index = (y * width + x) * 4;\n const fallbackIndex = (fallbackY * width + fallbackX) * 4;\n if (pixels[index + 3] === 0 && pixels[fallbackIndex + 3] > 0) {\n pixels[index] = pixels[fallbackIndex];\n pixels[index + 1] = pixels[fallbackIndex + 1];\n pixels[index + 2] = pixels[fallbackIndex + 2];\n pixels[index + 3] = pixels[fallbackIndex + 3];\n }\n if (pixels[index + 3] > 0 && pixels[index + 3] < 255) {\n pixels[index + 3] = 255;\n }\n };\n\n if (edges.left && width > 1) {\n for (let y = 0; y < height; y += 1) sealPixel(0, y, 1, y);\n }\n if (edges.right && width > 1) {\n for (let y = 0; y < height; y += 1) sealPixel(width - 1, y, width - 2, y);\n }\n if (edges.top && height > 1) {\n for (let x = 0; x < width; x += 1) sealPixel(x, 0, x, 1);\n }\n if (edges.bottom && height > 1) {\n for (let x = 0; x < width; x += 1) sealPixel(x, height - 1, x, height - 2);\n }\n\n context.putImageData(imageData, 0, 0);\n return offscreenCanvas.toDataURL('image/png');\n }\n\n /**\n * Exports a source region directly through Fabric's region export options.\n *\n * @param {Object} region - Canvas source region and export options.\n * @param {number} region.sourceX - Source region x coordinate.\n * @param {number} region.sourceY - Source region y coordinate.\n * @param {number} region.sourceWidth - Source region width.\n * @param {number} region.sourceHeight - Source region height.\n * @param {number} [region.multiplier=1] - Export multiplier.\n * @param {number} [region.quality=0.92] - Output image quality for lossy formats.\n * @param {'jpeg'|'png'|'webp'} [region.format='jpeg'] - Output image format.\n * @param {Object|null} [region.sealPartialEdges=null] - Fractional canvas edges whose alpha should be sealed.\n * @returns {Promise<string>} Resolves with an image data URL for the cropped region.\n * @private\n */\n async _exportCanvasRegionToDataURL({ sourceX, sourceY, sourceWidth, sourceHeight, multiplier = 1, quality = 0.92, format = 'jpeg', sealPartialEdges = null }) {\n const safeMultiplier = Math.max(1, Number(multiplier) || 1);\n const safeFormat = this._normalizeImageFormat(format);\n const exportFormat = safeFormat === 'jpeg' ? 'png' : safeFormat;\n let regionDataUrl = this.canvas.toDataURL({\n format: exportFormat,\n quality,\n multiplier: safeMultiplier,\n left: sourceX,\n top: sourceY,\n width: sourceWidth,\n height: sourceHeight\n });\n\n regionDataUrl = await this._sealPartialTransparentEdges(regionDataUrl, sealPartialEdges);\n if (safeFormat !== 'jpeg') return regionDataUrl;\n return this._convertDataUrlToOpaqueJpeg(regionDataUrl, quality);\n }\n\n async _convertDataUrlToOpaqueJpeg(dataUrl, quality = 0.92) {\n const imageElement = await this._createImageElement(dataUrl);\n const width = Math.max(1, imageElement.naturalWidth || imageElement.width || 1);\n const height = Math.max(1, imageElement.naturalHeight || imageElement.height || 1);\n const offscreenCanvas = document.createElement('canvas');\n offscreenCanvas.width = width;\n offscreenCanvas.height = height;\n const context = offscreenCanvas.getContext('2d');\n if (!context) throw new Error('2D canvas context is unavailable');\n context.fillStyle = this._getJpegBackgroundColor();\n context.fillRect(0, 0, width, height);\n context.drawImage(imageElement, 0, 0, width, height);\n return offscreenCanvas.toDataURL('image/jpeg', this._normalizeQuality(quality));\n }\n\n _getJpegBackgroundColor() {\n const backgroundColor = String(this.options.backgroundColor || '').trim();\n if (!backgroundColor || this._isTransparentCssColor(backgroundColor)) return '#ffffff';\n return backgroundColor;\n }\n\n _isTransparentCssColor(color) {\n const normalizedColor = String(color || '').trim().toLowerCase();\n if (!normalizedColor || normalizedColor === 'transparent') return true;\n\n const hexAlphaMatch = normalizedColor.match(/^#(?:[0-9a-f]{3}([0-9a-f])|[0-9a-f]{6}([0-9a-f]{2}))$/i);\n if (hexAlphaMatch) {\n const alpha = hexAlphaMatch[1] || hexAlphaMatch[2];\n return alpha === '0' || alpha === '00';\n }\n\n const slashAlphaMatch = normalizedColor.match(/^(?:rgba?|hsla?)\\([^)]*\\/\\s*([^)]+)\\)$/i);\n if (slashAlphaMatch) return this._isZeroCssAlpha(slashAlphaMatch[1]);\n\n const commaAlphaMatch = normalizedColor.match(/^(?:rgba|hsla)\\((.*)\\)$/i);\n if (commaAlphaMatch) {\n const parts = commaAlphaMatch[1].split(',');\n if (parts.length >= 4) return this._isZeroCssAlpha(parts[parts.length - 1]);\n }\n\n return false;\n }\n\n _isZeroCssAlpha(alphaValue) {\n const normalizedAlpha = String(alphaValue || '').trim();\n if (!normalizedAlpha) return false;\n if (normalizedAlpha.endsWith('%')) return Number.parseFloat(normalizedAlpha) === 0;\n return Number(normalizedAlpha) === 0;\n }\n\n _decodeBase64Payload(base64Payload) {\n const payload = String(base64Payload || '');\n if (typeof atob === 'function') {\n return Uint8Array.from(atob(payload), char => char.charCodeAt(0));\n }\n if (typeof Buffer !== 'undefined' && typeof Buffer.from === 'function') {\n return new Uint8Array(Buffer.from(payload, 'base64'));\n }\n throw new Error('Base64 decoding is unavailable');\n }\n\n /** \n * Gets the top-left corner coordinates of the given object.\n * Used for geometry calculations (e.g., scale, rotate).\n * \n * @param {Object} fabricObject - The object for which to get the top-left coordinates. Should support setCoords and getCoords/getBoundingRect methods.\n * @returns {{x: number, y: number}} The top-left corner point as an object with x and y properties.\n * @private\n */\n _getObjectTopLeftPoint(fabricObject) {\n if (!fabricObject) return { x: 0, y: 0 };\n fabricObject.setCoords();\n const boundingRect = fabricObject.getBoundingRect(true, true);\n return { x: boundingRect.left, y: boundingRect.top };\n }\n\n _getObjectCoordinateTopLeftPoint(fabricObject) {\n if (!fabricObject) return { x: 0, y: 0 };\n fabricObject.setCoords();\n const coords = typeof fabricObject.getCoords === 'function' ? fabricObject.getCoords() : null;\n if (coords && coords.length) return coords[0];\n return this._getObjectTopLeftPoint(fabricObject);\n }\n\n _getObjectOriginPoint(fabricObject, originX, originY) {\n if (!fabricObject) return { x: 0, y: 0 };\n if (typeof fabricObject.getPointByOrigin === 'function') {\n return fabricObject.getPointByOrigin(originX, originY);\n }\n return this._getObjectTopLeftPoint(fabricObject);\n }\n\n _translateObjectByCanvasOffset(fabricObject, deltaX, deltaY) {\n if (!fabricObject) return;\n if (typeof fabricObject.getCenterPoint === 'function' && typeof fabricObject.setPositionByOrigin === 'function') {\n const center = fabricObject.getCenterPoint();\n const nextCenter = new fabric.Point(center.x + deltaX, center.y + deltaY);\n fabricObject.setPositionByOrigin(nextCenter, 'center', 'center');\n } else {\n fabricObject.set({\n left: (fabricObject.left || 0) + deltaX,\n top: (fabricObject.top || 0) + deltaY\n });\n }\n fabricObject.setCoords();\n }\n\n /**\n * Sets the object's origin at the specified origin point, keeping a reference point fixed in position.\n * \n * @param {Object} fabricObject - The object to modify. Should support set, setPositionByOrigin, and setCoords.\n * @param {string} originX - The new originX (\"left\", \"center\", \"right\", etc.).\n * @param {string} originY - The new originY (\"top\", \"center\", \"bottom\", etc.).\n * @param {{x: number, y: number}} refPoint - The point to keep fixed while setting the new origins.\n * @private\n */\n _setObjectOriginKeepingPosition(fabricObject, originX, originY, refPoint) {\n if (!fabricObject || !refPoint || !fabricObject.setPositionByOrigin) return;\n fabricObject.set({ originX, originY });\n fabricObject.setPositionByOrigin(refPoint, originX, originY);\n fabricObject.setCoords();\n }\n\n /**\n * Moves the object so its bounding box aligns with the canvas's top-left corner (0, 0).\n * \n * @param {Object} fabricObject - The object to align.\n * @private\n */\n _alignObjectBoundingBoxToCanvasTopLeft(fabricObject) {\n if (!fabricObject) return;\n fabricObject.setCoords();\n const boundingRect = fabricObject.getBoundingRect(true, true);\n const deltaX = boundingRect.left;\n const deltaY = boundingRect.top;\n fabricObject.set({ left: (fabricObject.left || 0) - deltaX, top: (fabricObject.top || 0) - deltaY });\n fabricObject.setCoords();\n this.canvas.renderAll();\n }\n\n /**\n * Updates the canvas size to match the bounding box of the original image,\n * ensuring that the canvas is always at least as large as its container.\n * @private\n */\n _updateCanvasSizeToImageBounds() {\n if (!this.originalImage) return;\n this.originalImage.setCoords();\n const imageBounds = this.originalImage.getBoundingRect(true, true);\n\n const size = this._getScrollableCanvasSize(imageBounds.width, imageBounds.height);\n this._setCanvasSizeInt(size.width, size.height);\n }\n\n /**\n * Whether post-load edits should resize the canvas to keep transformed content visible.\n *\n * @returns {boolean} True when canvas bounds should follow edited image or mask bounds.\n * @private\n */\n _shouldResizeCanvasToContentBounds() {\n return !!(this.options.expandCanvasToImage || this.options.coverImageToCanvas || this.options.fitImageToCanvas);\n }\n\n /**\n * Expands the canvas once so all provided objects remain visible after an edit.\n *\n * @param {Array<fabric.Object>} fabricObjects - Objects whose bounds should fit inside the canvas.\n * @param {number} [padding=10] - Extra canvas space after the farthest object edge.\n * @returns {void}\n * @private\n */\n _expandCanvasToFitObjects(fabricObjects, padding = 10) {\n if (!this.canvas || !Array.isArray(fabricObjects) || !fabricObjects.length || !this._shouldResizeCanvasToContentBounds()) return;\n try {\n const currentWidth = this.canvas.getWidth();\n const currentHeight = this.canvas.getHeight();\n let requiredWidth = currentWidth;\n let requiredHeight = currentHeight;\n fabricObjects.forEach(fabricObject => {\n if (!fabricObject) return;\n if (typeof fabricObject.setCoords === 'function') fabricObject.setCoords();\n const boundingRect = fabricObject.getBoundingRect(true, true);\n requiredWidth = Math.max(requiredWidth, Math.ceil(boundingRect.left + boundingRect.width + padding));\n requiredHeight = Math.max(requiredHeight, Math.ceil(boundingRect.top + boundingRect.height + padding));\n });\n const shouldUseScrollSafeViewport = this.options.fitImageToCanvas || this.options.coverImageToCanvas;\n\n let minWidth = 0;\n let minHeight = 0;\n if (shouldUseScrollSafeViewport) {\n const viewport = this._getContainerViewportSize();\n const safetyMargin = this._getScrollSafetyMargin();\n\n minWidth = Math.max(1, viewport.width - safetyMargin);\n minHeight = Math.max(1, viewport.height - safetyMargin);\n } else if (this.containerElement) {\n minWidth = Math.floor(this.containerElement.clientWidth || 0);\n minHeight = Math.floor(this.containerElement.clientHeight || 0);\n }\n const newWidth = Math.max(currentWidth, minWidth, requiredWidth);\n const newHeight = Math.max(currentHeight, minHeight, requiredHeight);\n if (newWidth !== currentWidth || newHeight !== currentHeight) {\n this._setCanvasSizeInt(newWidth, newHeight);\n }\n } catch (error) {\n this._reportWarning('expandCanvasToFitObjects: failed to expand canvas', error);\n }\n }\n\n /**\n * Expands the canvas so one object remains visible after an edit.\n *\n * @param {fabric.Object} fabricObject - Object whose bounds should fit inside the canvas.\n * @param {number} [padding=10] - Extra canvas space after the object edge.\n * @returns {void}\n * @private\n */\n _expandCanvasToFitObject(fabricObject, padding = 10) {\n this._expandCanvasToFitObjects([fabricObject], padding);\n }\n\n /** \n * Scales the original image by a given factor, with animation.\n * Returns a promise that resolves when the scale animation is complete.\n * @param {number} factor - The scaling factor (will be clamped between `options.minScale` and `options.maxScale`).\n * @returns {Promise<void>} Promise that resolves once the scaling animation finishes.\n * @public\n */\n scaleImage(factor, options = {}) {\n try {\n this._assertCanQueueAnimation('scaleImage', options);\n } catch (error) {\n return Promise.reject(error);\n }\n return this.animationQueue.add(() => this._scaleImageImpl(factor, options))\n .finally(() => {\n if (!this._disposed && this.canvas) this._updateUI();\n });\n }\n\n _getInternalOperationToken(options) {\n return options && options[INTERNAL_OPERATION_TOKEN];\n }\n\n _isOwnInternalOperation(options) {\n const token = this._getInternalOperationToken(options);\n return !!token && token === this._activeOperationToken;\n }\n\n _beginBusyOperation(operationName) {\n const token = Symbol(operationName);\n this._activeOperationName = operationName;\n this._activeOperationToken = token;\n this._updateUI();\n return token;\n }\n\n _endBusyOperation(token) {\n if (token && token === this._activeOperationToken) {\n this._activeOperationName = null;\n this._activeOperationToken = null;\n this._updateUI();\n }\n }\n\n _withInternalOperationOptions(token, options = {}) {\n return {\n ...options,\n [INTERNAL_OPERATION_TOKEN]: token\n };\n }\n\n _assertEditorAvailable(operationName) {\n if (this._disposed || !this.canvas) throw new Error(`${operationName} cannot run after the editor has been disposed`);\n }\n\n _assertIdleForOperation(operationName, options = {}) {\n this._assertEditorAvailable(operationName);\n const isOwnInternalOperation = this._isOwnInternalOperation(options);\n if (this.isAnimating || (this.animationQueue && this.animationQueue.isBusy())) {\n throw new Error(`${operationName} cannot run while an animation is running`);\n }\n if (this._isLoading && !isOwnInternalOperation) {\n throw new Error(`${operationName} cannot run while an image is loading`);\n }\n if (this._activeOperationToken && !isOwnInternalOperation) {\n throw new Error(`${operationName} cannot run while ${this._activeOperationName || 'another operation'} is running`);\n }\n }\n\n _assertCanQueueAnimation(operationName, options = {}) {\n this._assertEditorAvailable(operationName);\n if (this._isLoading && !this._isOwnInternalOperation(options)) {\n throw new Error(`${operationName} cannot run while an image is loading`);\n }\n if (this._activeOperationToken && !this._isOwnInternalOperation(options)) {\n throw new Error(`${operationName} cannot run while ${this._activeOperationName || 'another operation'} is running`);\n }\n }\n\n _canMutateNow(operationName, options = {}) {\n try {\n this._assertIdleForOperation(operationName, options);\n return true;\n } catch (error) {\n this._reportError(`${operationName} blocked`, error);\n return false;\n }\n }\n\n _rejectActiveAnimations(reason) {\n const error = reason instanceof Error ? reason : new Error(String(reason || 'Animation cancelled'));\n this._activeAnimationRejectors.forEach(reject => {\n try { reject(error); } catch (rejectError) { void rejectError; }\n });\n this._activeAnimationRejectors.clear();\n }\n\n _animateFabricProperty(fabricObject, property, value) {\n return new Promise((resolve, reject) => {\n if (this._disposed || !this.canvas || !fabricObject) {\n reject(new Error('Animation cannot start after editor disposal'));\n return;\n }\n\n let isSettled = false;\n const duration = Math.max(0, Number(this.options.animationDuration) || 0);\n const timeoutMs = Math.max(1000, duration + 1000);\n let timerId;\n const settle = (callback) => {\n if (isSettled) return;\n isSettled = true;\n clearTimeout(timerId);\n this._activeAnimationRejectors.delete(reject);\n callback();\n };\n\n this._activeAnimationRejectors.add(reject);\n timerId = setTimeout(() => {\n settle(() => reject(new Error(`Animation timed out while changing ${property}`)));\n }, timeoutMs);\n\n try {\n fabricObject.animate(property, value, {\n duration,\n onChange: () => {\n if (!this._disposed && this.canvas) this.canvas.renderAll();\n },\n onComplete: () => settle(resolve)\n });\n } catch (error) {\n settle(() => reject(error));\n }\n });\n }\n\n /** \n * Scales the original image by a given factor, with animation.\n * Returns a promise that resolves when the scale animation is complete.\n * @param {number} factor - The scaling factor (will be clamped between `options.minScale` and `options.maxScale`).\n * @returns {Promise<void>} Promise that resolves once the scaling animation finishes.\n * @private\n */\n async _scaleImageImpl(factor, options = {}) {\n if (!this.originalImage || this._disposed) return;\n if (this.isAnimating) return;\n const saveHistory = options.saveHistory !== false;\n let didStartAnimation = false;\n try {\n factor = Math.max(this.options.minScale, Math.min(this.options.maxScale, factor));\n this.currentScale = factor;\n this.isAnimating = true;\n didStartAnimation = true;\n this._updateUI();\n\n const targetScale = this.baseImageScale * factor;\n\n const topLeft = this._getObjectTopLeftPoint(this.originalImage);\n this._setObjectOriginKeepingPosition(this.originalImage, 'left', 'top', topLeft);\n\n await Promise.all([\n this._animateFabricProperty(this.originalImage, 'scaleX', targetScale),\n this._animateFabricProperty(this.originalImage, 'scaleY', targetScale)\n ]);\n if (this._disposed || !this.canvas || !this.originalImage) throw new Error('Editor was disposed during scale animation');\n\n this.originalImage.set({ scaleX: targetScale, scaleY: targetScale });\n this.originalImage.setCoords();\n\n if (this._shouldResizeCanvasToContentBounds()) {\n this._updateCanvasSizeToImageBounds();\n }\n\n this._alignObjectBoundingBoxToCanvasTopLeft(this.originalImage);\n\n this.canvas.getObjects().forEach(object => { if (object.maskId) this._syncMaskLabel(object); });\n\n this._updateInputs();\n if (saveHistory) this.saveState();\n } finally {\n if (didStartAnimation) {\n this.isAnimating = false;\n this._updateInputs();\n this._updateUI();\n }\n }\n }\n\n /** \n * Rotates the original image by a given number of degrees, with animation.\n * Returns a promise that resolves when the rotation animation is complete.\n * @param {number} degrees - The angle in degrees to rotate the image.\n * @returns {Promise<void>} Promise that resolves once the rotation animation finishes.\n * @public\n */\n rotateImage(degrees, options = {}) {\n try {\n this._assertCanQueueAnimation('rotateImage', options);\n } catch (error) {\n return Promise.reject(error);\n }\n return this.animationQueue.add(() => this._rotateImageImpl(degrees, options))\n .finally(() => {\n if (!this._disposed && this.canvas) this._updateUI();\n });\n }\n\n /** \n * Rotates the original image by a given number of degrees, with animation.\n * Returns a promise that resolves when the rotation animation is complete.\n * @param {number} degrees - The angle in degrees to rotate the image.\n * @returns {Promise<void>} Promise that resolves once the rotation animation finishes.\n * @private\n */\n async _rotateImageImpl(degrees, options = {}) {\n if (!this.originalImage || this._disposed) return;\n if (this.isAnimating) return;\n if (isNaN(degrees)) return;\n const saveHistory = options.saveHistory !== false;\n const image = this.originalImage;\n const previousOriginX = image.originX || 'left';\n const previousOriginY = image.originY || 'top';\n const previousOriginPoint = this._getObjectOriginPoint(image, previousOriginX, previousOriginY);\n let didStartAnimation = false;\n let didCompleteRotation = false;\n try {\n this.currentRotation = degrees;\n this.isAnimating = true;\n didStartAnimation = true;\n this._updateUI();\n\n const center = image.getCenterPoint();\n this._setObjectOriginKeepingPosition(image, 'center', 'center', center);\n\n await this._animateFabricProperty(image, 'angle', degrees);\n if (this._disposed || !this.canvas || !this.originalImage) throw new Error('Editor was disposed during rotation animation');\n\n this.originalImage.set('angle', degrees);\n this.originalImage.setCoords();\n\n if (this._shouldResizeCanvasToContentBounds()) {\n this._updateCanvasSizeToImageBounds();\n }\n\n this._alignObjectBoundingBoxToCanvasTopLeft(this.originalImage);\n\n const newTopLeft = this._getObjectCoordinateTopLeftPoint(this.originalImage);\n this._setObjectOriginKeepingPosition(this.originalImage, 'left', 'top', newTopLeft);\n\n this.canvas.getObjects().forEach(object => { if (object.maskId) this._syncMaskLabel(object); });\n\n this._updateInputs();\n if (saveHistory) this.saveState();\n didCompleteRotation = true;\n } finally {\n if (!didCompleteRotation && !this._disposed && image) {\n this._setObjectOriginKeepingPosition(image, previousOriginX, previousOriginY, previousOriginPoint);\n }\n if (didStartAnimation) {\n this.isAnimating = false;\n this._updateInputs();\n this._updateUI();\n }\n }\n }\n\n /**\n * Resets the image transform: scales to 1 and rotates to 0 degrees.\n *\n * @returns {Promise<void>} Resolves when the reset history transition has been recorded.\n * @public\n */\n resetImageTransform() {\n if (!this.originalImage) return Promise.resolve();\n try {\n this._assertCanQueueAnimation('resetImageTransform');\n } catch (error) {\n return Promise.reject(error);\n }\n\n return this.animationQueue.add(async () => {\n const before = this._lastSnapshot || this._captureCanvasStateOrThrow('resetImageTransform');\n await this._scaleImageImpl(1, { saveHistory: false });\n await this._rotateImageImpl(0, { saveHistory: false });\n const after = this._captureCanvasStateOrThrow('resetImageTransform');\n this._pushStateTransition(before, after);\n }).finally(() => {\n if (!this._disposed && this.canvas) this._updateUI();\n }).catch(error => {\n this._reportError('resetImageTransform() failed', error);\n throw error;\n });\n }\n\n /**\n * Backward-compatible alias for {@link ImageEditor#resetImageTransform}.\n *\n * @deprecated Use resetImageTransform() instead. This alias will be removed in v2.0.0.\n * @returns {Promise<void>} Resolves when the image transform reset is complete.\n */\n reset() {\n return this.resetImageTransform();\n }\n\n /**\n * Restores a serialized canvas state and rebinds editor-specific mask/image metadata.\n *\n * @param {string|Object} serializedState - State returned by `_serializeCanvasState()` as a JSON string or object.\n * @returns {Promise<void>} Resolves after Fabric has loaded the state and UI state has been refreshed.\n * @public\n */\n loadFromState(serializedState) {\n if (!serializedState || !this.canvas || this._disposed) return Promise.resolve();\n if (this._cropMode || this._cropRect) {\n this._removeCropRect();\n this._restoreCropObjectState();\n this._cropMode = false;\n if (this._prevSelectionSetting !== undefined && this.canvas) {\n this.canvas.selection = !!this._prevSelectionSetting;\n }\n this._prevSelectionSetting = undefined;\n }\n\n return new Promise((resolve, reject) => {\n try {\n const state = (typeof serializedState === 'string')\n ? JSON.parse(serializedState)\n : serializedState;\n const editorMetadata = state && state.imageEditorMetadata ? state.imageEditorMetadata : null;\n if (\n editorMetadata &&\n Object.prototype.hasOwnProperty.call(editorMetadata, 'version') &&\n Number(editorMetadata.version) !== 1\n ) {\n this._reportWarning(`loadFromState: unsupported editor metadata version ${editorMetadata.version}`);\n }\n\n this.canvas.loadFromJSON(state, async () => {\n try {\n if (this._disposed || !this.canvas) {\n reject(new Error('Editor was disposed while loading state'));\n return;\n }\n await this._waitForFabricImagesReady(this.canvas.getObjects());\n if (this._disposed || !this.canvas) {\n reject(new Error('Editor was disposed while loading state'));\n return;\n }\n this._hideAllMaskLabels();\n const canvasObjects = this.canvas.getObjects();\n this.originalImage = canvasObjects.find(object => object.type === 'image' && !object.maskId) || null;\n\n if (this.originalImage) {\n this.originalImage.set({ originX: 'left', originY: 'top', selectable: false, evented: false, hasControls: false, hoverCursor: 'default' });\n this.canvas.sendToBack(this.originalImage);\n const restoredBaseScale = Number(editorMetadata && editorMetadata.baseImageScale);\n const restoredCurrentScale = Number(editorMetadata && editorMetadata.currentScale);\n const restoredCurrentRotation = Number(editorMetadata && editorMetadata.currentRotation);\n\n if (Number.isFinite(restoredBaseScale) && restoredBaseScale > 0) {\n this.baseImageScale = restoredBaseScale;\n }\n\n if (Number.isFinite(restoredCurrentScale) && restoredCurrentScale > 0) {\n this.currentScale = restoredCurrentScale;\n } else {\n const baseScale = Number(this.baseImageScale) || 1;\n const imageScale = Number(this.originalImage.scaleX) || baseScale;\n this.currentScale = imageScale / baseScale;\n }\n\n this.currentRotation = Number.isFinite(restoredCurrentRotation)\n ? restoredCurrentRotation\n : (Number(this.originalImage.angle) || 0);\n } else {\n this.baseImageScale = 1;\n this.currentScale = 1;\n this.currentRotation = 0;\n }\n\n const masks = canvasObjects.filter(object => object.maskId);\n masks.forEach(mask => {\n this._restoreMaskControls(mask);\n this._rebindMaskEvents(mask);\n mask.set(this._getMaskNormalStyle(mask));\n });\n const restoredMaskCounter = Number(editorMetadata && editorMetadata.maskCounter);\n const maxMaskId = masks.reduce((max, mask) =>\n Math.max(max, mask.maskId), 0);\n this.maskCounter = Number.isFinite(restoredMaskCounter) && restoredMaskCounter >= maxMaskId\n ? Math.floor(restoredMaskCounter)\n : maxMaskId;\n this._lastMask = masks.length ? masks[masks.length - 1] : null;\n if (!this._lastMask) {\n this._lastMaskInitialLeft = null;\n this._lastMaskInitialTop = null;\n this._lastMaskInitialWidth = null;\n }\n this.isImageLoadedToCanvas = !!this.originalImage;\n\n this.canvas.renderAll();\n this._updateInputs();\n this._updateMaskList();\n this._updatePlaceholderStatus();\n this._lastSnapshot = this._serializeCanvasState();\n this._updateUI();\n resolve();\n } catch (callbackError) {\n this._reportError('loadFromState() failed', callbackError);\n reject(callbackError);\n }\n });\n\n } catch (error) {\n this._reportError('loadFromState() failed', error);\n reject(error);\n }\n });\n }\n\n async _waitForFabricImagesReady(canvasObjects) {\n const imageObjects = (canvasObjects || []).filter(object => object && object.type === 'image');\n await Promise.all(imageObjects.map(object => this._waitForImageElementReady(\n typeof object.getElement === 'function' ? object.getElement() : object._element\n )));\n }\n\n _waitForImageElementReady(imageElement) {\n if (!imageElement) return Promise.resolve();\n const hasLoadedDimensions = (Number(imageElement.naturalWidth) > 0 || Number(imageElement.width) > 0) &&\n (Number(imageElement.naturalHeight) > 0 || Number(imageElement.height) > 0);\n if (hasLoadedDimensions) return Promise.resolve();\n if (imageElement.complete) return Promise.reject(new Error('Image could not be loaded while restoring state'));\n return new Promise((resolve, reject) => {\n let isSettled = false;\n let timerId;\n const settle = (callback) => {\n if (isSettled) return;\n isSettled = true;\n clearTimeout(timerId);\n if (typeof imageElement.removeEventListener === 'function') {\n imageElement.removeEventListener('load', handleLoad);\n imageElement.removeEventListener('error', handleError);\n } else {\n imageElement.onload = null;\n imageElement.onerror = null;\n }\n callback();\n };\n const handleLoad = () => {\n const didLoad = (Number(imageElement.naturalWidth) > 0 || Number(imageElement.width) > 0) &&\n (Number(imageElement.naturalHeight) > 0 || Number(imageElement.height) > 0);\n settle(() => {\n if (didLoad) {\n resolve();\n } else {\n reject(new Error('Image could not be loaded while restoring state'));\n }\n });\n };\n const handleError = (error) => settle(() => reject(error instanceof Error ? error : new Error('Image could not be loaded while restoring state')));\n timerId = setTimeout(() => {\n settle(() => reject(new Error('Image load timed out while restoring state')));\n }, this._getSafeTimeoutMs(this.options.imageLoadTimeoutMs));\n if (typeof imageElement.addEventListener === 'function') {\n imageElement.addEventListener('load', handleLoad, { once: true });\n imageElement.addEventListener('error', handleError, { once: true });\n } else {\n imageElement.onload = handleLoad;\n imageElement.onerror = handleError;\n }\n });\n }\n\n /**\n * Saves the current editable canvas state as an undoable history transition.\n *\n * Labels are hidden before serialization because labels are UI overlays, while mask metadata is kept on\n * mask objects and restored by `loadFromState()`.\n *\n * @returns {void}\n * @public\n */\n saveState() {\n if (!this.canvas) return;\n\n try {\n const after = this._captureCanvasStateOrThrow('saveState');\n const before = this._lastSnapshot || after;\n if (after === before) return;\n let executedOnce = false;\n\n const command = new Command(\n () => {\n if (executedOnce) {\n return this.loadFromState(after);\n }\n executedOnce = true;\n return undefined;\n },\n () => this.loadFromState(before)\n );\n\n this.historyManager.execute(command);\n this._lastSnapshot = after;\n } catch (error) {\n this._reportWarning('saveState: failed to save canvas snapshot', error);\n } finally {\n this._updateUI();\n }\n }\n\n /**\n * Pushes a precomputed before/after state transition into history.\n *\n * Use this for operations such as crop and merge that build their snapshots around asynchronous image\n * loading, where the \"after\" state is already applied before the history command is recorded.\n *\n * @param {string} before - Serialized state before the operation.\n * @param {string} after - Serialized state after the operation.\n * @returns {void}\n * @private\n */\n _pushStateTransition(before, after) {\n if (!before || !after) {\n this._reportWarning('History transition skipped because a canvas snapshot is unavailable');\n return;\n }\n if (before === after) return;\n if (!this.historyManager) this.historyManager = new HistoryManager(this.maxHistorySize || 50);\n\n const command = new Command(\n () => this.loadFromState(after),\n () => this.loadFromState(before)\n );\n this.historyManager.push(command);\n this._lastSnapshot = after;\n this._updateUI();\n }\n\n /**\n * Undo the last state change, if possible.\n *\n * @returns {Promise<void>} Resolves after the history manager finishes the queued undo.\n * @public\n */\n undo() {\n return this.historyManager.undo()\n .then(() => { this._updateUI(); })\n .catch(error => {\n this._reportError('undo failed', error);\n throw error;\n });\n }\n\n /**\n * Redo the next state change, if possible.\n *\n * @returns {Promise<void>} Resolves after the history manager finishes the queued redo.\n * @public\n */\n redo() {\n return this.historyManager.redo()\n .then(() => { this._updateUI(); })\n .catch(error => {\n this._reportError('redo failed', error);\n throw error;\n });\n }\n\n _rebindMaskEvents(mask) {\n if (!mask) return;\n if (mask.__imageEditorMaskHandlers) {\n try {\n mask.off('mouseover', mask.__imageEditorMaskHandlers.mouseover);\n mask.off('mouseout', mask.__imageEditorMaskHandlers.mouseout);\n } catch (error) { void error; }\n }\n\n const metadata = {};\n if (!Number.isFinite(Number(mask.originalAlpha))) {\n metadata.originalAlpha = Number.isFinite(Number(mask.opacity)) ? Number(mask.opacity) : 0.5;\n }\n if (!mask.originalStroke) metadata.originalStroke = mask.stroke || '#ccc';\n if (!Number.isFinite(Number(mask.originalStrokeWidth))) {\n metadata.originalStrokeWidth = Number.isFinite(Number(mask.strokeWidth)) ? Number(mask.strokeWidth) : 1;\n }\n if (Object.keys(metadata).length) mask.set(metadata);\n\n const mouseover = () => {\n const opacity = Number(mask.originalAlpha);\n mask.set({\n stroke: '#ff5500',\n strokeWidth: 2,\n opacity: Math.min((Number.isFinite(opacity) ? opacity : 0.5) + 0.2, 1)\n });\n if (mask.canvas) mask.canvas.requestRenderAll();\n };\n const mouseout = () => {\n mask.set(this._getMaskNormalStyle(mask));\n if (mask.canvas) mask.canvas.requestRenderAll();\n };\n\n mask.on('mouseover', mouseover);\n mask.on('mouseout', mouseout);\n mask.__imageEditorMaskHandlers = { mouseover, mouseout };\n }\n\n /**\n * Creates a mask and adds it to the canvas.\n *\n * Placement is based on explicit `left`/`top` values when provided; otherwise each new mask is placed\n * after the previously created mask. Fabric object properties are applied through `set()` and `setCoords()`\n * so controls and hit testing stay in sync with Fabric 5.x behavior.\n *\n * @param {Object} [config={}] - Optional mask configuration overrides.\n * @param {string} [config.shape='rect'] - Mask shape: `rect`, `circle`, `ellipse`, `polygon`, or a custom shape handled by `fabricGenerator`.\n * @param {Array<{x:number,y:number}>|Array<Array<number>>} [config.points] - Polygon points.\n * @param {number|string|MaskValueResolver} [config.width] - Width in pixels, percentage string, or resolver callback.\n * @param {number|string|MaskValueResolver} [config.height] - Height in pixels, percentage string, or resolver callback.\n * @param {number|string|MaskValueResolver} [config.radius] - Circle radius in pixels, percentage string, or resolver callback.\n * @param {number|string|MaskValueResolver} [config.rx] - Ellipse horizontal radius or rectangle corner radius.\n * @param {number|string|MaskValueResolver} [config.ry] - Ellipse vertical radius or rectangle corner radius.\n * @param {number|string|MaskValueResolver} [config.left] - Left position in pixels, percentage string, or resolver callback.\n * @param {number|string|MaskValueResolver} [config.top] - Top position in pixels, percentage string, or resolver callback.\n * @param {number} [config.angle=0] - Rotation angle in degrees.\n * @param {string} [config.color='rgba(0,0,0,0.5)'] - Fill color.\n * @param {number} [config.alpha=0.5] - Opacity from 0 to 1.\n * @param {boolean} [config.selectable=true] - Whether the mask can be selected.\n * @param {boolean} [config.hasControls=true] - Whether Fabric transform controls are shown.\n * @param {Object} [config.styles] - Additional Fabric style properties, such as `stroke` or `strokeDashArray`.\n * @param {MaskFabricGenerator} [config.fabricGenerator] - Factory callback that returns a custom Fabric object.\n * @param {MaskCreateCallback} [config.onCreate] - Callback invoked after the mask is added to the canvas.\n * @returns {fabric.Object|null} The created mask object, or null if the canvas is not initialized.\n * @public\n */\n createMask(config = {}) {\n if (!this.canvas) return null;\n if (!this._canMutateNow('createMask')) return null;\n const shapeType = config.shape || 'rect';\n // Normalize mask defaults before applying caller-provided overrides.\n const maskConfig = {\n shape: shapeType,\n width: this.options.defaultMaskWidth,\n height: this.options.defaultMaskHeight,\n color: 'rgba(0,0,0,0.5)',\n alpha: 0.5,\n gap: 5,\n left: undefined,\n top: undefined,\n angle: 0,\n selectable: true,\n ...config\n };\n\n // Always start placement relative to canvas left/top.\n const firstOffset = 10;\n let left;\n let top;\n\n const getCanvasBasis = (axis) => {\n const canvasWidth = this.canvas ? this.canvas.getWidth() : 0;\n const canvasHeight = this.canvas ? this.canvas.getHeight() : 0;\n if (axis === 'height') return canvasHeight;\n if (axis === 'min') return Math.min(canvasWidth, canvasHeight);\n return canvasWidth;\n };\n\n const resolveValue = (value, fallback, axis = 'width') => {\n if (typeof value === 'function')\n return value(this.canvas, this.options);\n if (typeof value === 'string' && value.endsWith('%')) {\n const percent = Number.parseFloat(value) / 100;\n if (!Number.isFinite(percent)) return fallback;\n return Math.floor(getCanvasBasis(axis) * percent);\n }\n return value != null ? value : fallback;\n };\n\n if (maskConfig.left === undefined && this._lastMask) {\n const previousMask = this._lastMask;\n if (typeof previousMask.setCoords === 'function') previousMask.setCoords();\n const previousBounds = typeof previousMask.getBoundingRect === 'function'\n ? previousMask.getBoundingRect(true, true)\n : { left: previousMask.left || firstOffset, top: previousMask.top || firstOffset, width: previousMask.width || 0 };\n left = Math.round(previousBounds.left + previousBounds.width + maskConfig.gap);\n top = Math.round(previousBounds.top ?? firstOffset);\n } else {\n left = resolveValue(maskConfig.left, firstOffset, 'width');\n top = resolveValue(maskConfig.top, firstOffset, 'height');\n }\n\n maskConfig.width = resolveValue(maskConfig.width, this.options.defaultMaskWidth, 'width');\n maskConfig.height = resolveValue(maskConfig.height, this.options.defaultMaskHeight, 'height');\n maskConfig.left = left;\n maskConfig.top = top;\n\n let mask;\n if (typeof maskConfig.fabricGenerator === 'function') {\n mask = maskConfig.fabricGenerator(maskConfig, this.canvas, this.options);\n } else {\n switch (shapeType) {\n case 'circle':\n mask = new fabric.Circle({\n left, top,\n radius: resolveValue(maskConfig.radius, Math.min(maskConfig.width, maskConfig.height) / 2, 'min'),\n fill: maskConfig.color,\n opacity: maskConfig.alpha,\n angle: maskConfig.angle,\n ...maskConfig.styles\n });\n break;\n case 'ellipse':\n mask = new fabric.Ellipse({\n left, top,\n rx: resolveValue(maskConfig.rx, maskConfig.width / 2, 'width'),\n ry: resolveValue(maskConfig.ry, maskConfig.height / 2, 'height'),\n fill: maskConfig.color,\n opacity: maskConfig.alpha,\n angle: maskConfig.angle,\n ...maskConfig.styles\n });\n break;\n case 'polygon': {\n let polygonPoints = maskConfig.points || [];\n if (Array.isArray(polygonPoints) && polygonPoints.length) {\n // Ensure numeric {x,y} objects for fabric.Polygon.\n polygonPoints = polygonPoints.map(point => Array.isArray(point)\n ? { x: Number(point[0]), y: Number(point[1]) }\n : { x: Number(point.x), y: Number(point.y) });\n }\n mask = new fabric.Polygon(polygonPoints, {\n left, top,\n fill: maskConfig.color,\n opacity: maskConfig.alpha,\n angle: maskConfig.angle,\n ...maskConfig.styles\n });\n break;\n }\n case 'rect':\n default:\n mask = new fabric.Rect({\n left, top,\n width: resolveValue(maskConfig.width, this.options.defaultMaskWidth, 'width'),\n height: resolveValue(maskConfig.height, this.options.defaultMaskHeight, 'height'),\n fill: maskConfig.color,\n opacity: maskConfig.alpha,\n angle: maskConfig.angle,\n rx: maskConfig.rx,\n ry: maskConfig.ry,\n ...maskConfig.styles\n });\n }\n }\n\n if (!mask || typeof mask.set !== 'function' || typeof mask.setCoords !== 'function') {\n this._reportWarning('fabricGenerator returned an invalid Fabric object');\n return null;\n }\n\n const styles = maskConfig.styles || {};\n const hasStyle = property => Object.prototype.hasOwnProperty.call(styles, property);\n const maskSettings = {\n selectable: maskConfig.selectable !== false,\n hasControls: ('hasControls' in maskConfig) ? maskConfig.hasControls : true,\n lockRotation: !this.options.maskRotatable,\n borderColor: ('borderColor' in maskConfig) ? maskConfig.borderColor : 'red',\n cornerColor: ('cornerColor' in maskConfig) ? maskConfig.cornerColor : 'black',\n cornerSize: ('cornerSize' in maskConfig) ? maskConfig.cornerSize : 8,\n transparentCorners: ('transparentCorners' in maskConfig) ? maskConfig.transparentCorners : false,\n stroke: hasStyle('stroke') ? styles.stroke : '#ccc',\n strokeWidth: hasStyle('strokeWidth') ? styles.strokeWidth : 1,\n opacity: hasStyle('opacity') ? styles.opacity : maskConfig.alpha,\n strokeUniform: ('strokeUniform' in maskConfig) ? maskConfig.strokeUniform : (hasStyle('strokeUniform') ? styles.strokeUniform : true)\n };\n if (hasStyle('strokeDashArray')) maskSettings.strokeDashArray = styles.strokeDashArray;\n mask.set(maskSettings);\n mask.setCoords();\n\n mask.set({\n originalAlpha: Number.isFinite(Number(mask.opacity)) ? Number(mask.opacity) : maskConfig.alpha,\n originalStroke: mask.stroke || '#ccc',\n originalStrokeWidth: Number.isFinite(Number(mask.strokeWidth)) ? Number(mask.strokeWidth) : 1\n });\n this._rebindMaskEvents(mask);\n this._expandCanvasToFitObject(mask);\n\n // Store placement values so the next mask can be positioned beside this one.\n this._lastMaskInitialLeft = left;\n this._lastMaskInitialTop = top;\n this._lastMaskInitialWidth = resolveValue(maskConfig.width, this.options.defaultMaskWidth, 'width');\n\n const maskId = ++this.maskCounter;\n mask.set({\n maskId,\n maskName: `${this.options.maskName}${maskId}`\n });\n this._lastMask = mask;\n\n this.canvas.add(mask);\n this.canvas.bringToFront(mask);\n if (maskConfig.selectable) this.canvas.setActiveObject(mask);\n this._handleSelectionChanged([mask]);\n this._updateMaskList();\n this._updateUI();\n this.canvas.renderAll();\n this.saveState();\n\n if (typeof maskConfig.onCreate === 'function') maskConfig.onCreate(mask, this.canvas);\n return mask;\n }\n\n /**\n * Backward-compatible alias for {@link ImageEditor#createMask}.\n *\n * @deprecated Use createMask() instead. This alias will be removed in v2.0.0.\n * @param {Object} [config={}] - Mask configuration passed to createMask().\n * @returns {fabric.Object|null} The created mask object, or null if the canvas is not initialized.\n */\n addMask(config = {}) {\n return this.createMask(config);\n }\n\n /**\n * Removes the currently selected mask from the canvas, if any.\n * The associated label is also removed. UI and mask list are updated.\n */\n removeSelectedMask() {\n if (!this.canvas) return;\n if (!this._canMutateNow('removeSelectedMask')) return;\n const activeObject = this.canvas.getActiveObject();\n const selectedMasks = this._getModifiedMasks(activeObject);\n if (!selectedMasks.length) return;\n\n this.canvas.discardActiveObject();\n selectedMasks.forEach(mask => {\n this._removeLabelForMask(mask);\n this.canvas.remove(mask);\n });\n\n const masks = this.canvas.getObjects().filter(object => object.maskId);\n this._lastMask = masks.length ? masks[masks.length - 1] : null;\n if (!this._lastMask) {\n this._lastMaskInitialLeft = null;\n this._lastMaskInitialTop = null;\n this._lastMaskInitialWidth = null;\n }\n this._updateMaskList();\n this._updateUI();\n this.canvas.renderAll();\n this.saveState();\n }\n\n /**\n * Removes all masks from the canvas, including their labels.\n * UI and internal mask placement memory are reset.\n */\n removeAllMasks(options = {}) {\n if (!this.canvas) return;\n if (!this._canMutateNow('removeAllMasks', options)) return;\n const saveHistory = options.saveHistory !== false;\n const masks = this.canvas.getObjects().filter(object => object.maskId);\n masks.forEach(mask => this._removeLabelForMask(mask));\n masks.forEach(mask => this.canvas.remove(mask));\n this.canvas.discardActiveObject();\n this._lastMask = null;\n this._lastMaskInitialLeft = null;\n this._lastMaskInitialTop = null;\n this._lastMaskInitialWidth = null;\n this._updateMaskList();\n this._updateUI();\n this.canvas.renderAll();\n if (saveHistory) this.saveState();\n }\n\n /**\n * Removes the label associated with the specified mask object, if it exists.\n * \n * @param {fabric.Object} mask - The mask object whose label should be removed.\n * @private\n */\n _removeLabelForMask(mask) {\n if (!mask || !this.canvas) return;\n if (mask.__label) {\n try {\n const canvasObjects = this.canvas.getObjects();\n if (canvasObjects.includes(mask.__label)) {\n this.canvas.remove(mask.__label);\n }\n } catch (error) { void error; }\n try { delete mask.__label; } catch (error) { void error; }\n }\n }\n\n _captureMaskLabelBackups(masks) {\n if (!this.canvas) return [];\n const canvasObjects = new Set(this.canvas.getObjects());\n return (masks || []).map(mask => {\n const label = mask && mask.__label ? mask.__label : null;\n return {\n mask,\n label,\n hadLabel: !!label,\n labelInCanvas: !!label && canvasObjects.has(label),\n visible: label ? label.visible : undefined\n };\n });\n }\n\n _restoreMaskLabelBackups(labelBackups) {\n if (!this.canvas || !Array.isArray(labelBackups)) return;\n const canvasObjects = new Set(this.canvas.getObjects());\n labelBackups.forEach(backup => {\n if (!backup || !backup.mask) return;\n try {\n if (!backup.hadLabel) {\n if (backup.mask.__label) this._removeLabelForMask(backup.mask);\n return;\n }\n backup.mask.__label = backup.label;\n if (!backup.label) return;\n if (backup.labelInCanvas && !canvasObjects.has(backup.label)) {\n this.canvas.add(backup.label);\n canvasObjects.add(backup.label);\n }\n if (backup.visible !== undefined) backup.label.set({ visible: backup.visible });\n if (backup.labelInCanvas) this.canvas.bringToFront(backup.label);\n this._syncMaskLabel(backup.mask);\n } catch (error) { void error; }\n });\n }\n\n _captureActiveObjectBackup() {\n if (!this.canvas) return null;\n const activeObject = this.canvas.getActiveObject();\n if (!activeObject) return null;\n const selectedObjects = typeof activeObject.getObjects === 'function'\n ? activeObject.getObjects()\n : [activeObject];\n return { activeObject, selectedObjects };\n }\n\n _restoreActiveObjectBackup(activeObjectBackup) {\n if (!this.canvas || !activeObjectBackup || !activeObjectBackup.activeObject) return;\n const canvasObjects = this.canvas.getObjects();\n const selectedObjects = Array.isArray(activeObjectBackup.selectedObjects)\n ? activeObjectBackup.selectedObjects\n : [];\n const canRestore = selectedObjects.length\n ? selectedObjects.every(object => canvasObjects.includes(object))\n : canvasObjects.includes(activeObjectBackup.activeObject);\n if (!canRestore) return;\n try {\n this.canvas.setActiveObject(activeObjectBackup.activeObject);\n } catch (error) { void error; }\n }\n\n _captureMaskExportBackups(masks) {\n return (masks || []).map(mask => ({\n object: mask,\n visible: mask.visible,\n opacity: mask.opacity,\n fill: mask.fill,\n strokeWidth: mask.strokeWidth,\n stroke: mask.stroke,\n selectable: mask.selectable,\n lockRotation: mask.lockRotation\n }));\n }\n\n _restoreMaskExportBackups(maskBackups) {\n (maskBackups || []).forEach(backup => {\n try {\n backup.object.set({\n visible: backup.visible,\n opacity: backup.opacity,\n fill: backup.fill,\n strokeWidth: backup.strokeWidth,\n stroke: backup.stroke,\n selectable: backup.selectable,\n lockRotation: backup.lockRotation\n });\n backup.object.setCoords();\n } catch (error) { void error; }\n });\n }\n\n /**\n * Returns a stable zero-based creation index for label callbacks.\n *\n * Mask ids are one-based and are not renumbered after deletion, so this value remains stable for the\n * lifetime of a mask.\n *\n * @param {fabric.Object} mask - Mask object.\n * @returns {number} Stable zero-based creation index.\n * @private\n */\n _getMaskCreationIndex(mask) {\n const maskId = Number(mask && mask.maskId);\n if (Number.isFinite(maskId) && maskId > 0) return Math.floor(maskId) - 1;\n\n const masks = this.canvas ? this.canvas.getObjects().filter(object => object.maskId) : [];\n return Math.max(0, masks.indexOf(mask));\n }\n\n /**\n * Creates and adds a custom label (fabric.Text or fabric.IText) for the mask.\n * The label is default bound to the top-left of the mask and managed as a non-interactive overlay.\n * \n * @param {fabric.Object} mask - The mask to create a label for.\n * @private\n */\n _createLabelForMask(mask) {\n if (!mask || !this.options.maskLabelOnSelect) return;\n this._removeLabelForMask(mask);\n let textObject = null;\n if (this.options.label && typeof this.options.label.create === 'function') {\n textObject = this.options.label.create(mask, fabric);\n if (!textObject || typeof textObject.set !== 'function') {\n this._reportWarning('label.create() returned an invalid Fabric object; using the default label');\n textObject = null;\n }\n }\n if (!textObject) {\n let labelText = mask.maskName;\n let textOptions = {\n left: 0,\n top: 0,\n fontSize: 12,\n fill: '#fff',\n backgroundColor: 'rgba(0,0,0,0.7)',\n selectable: false,\n evented: false,\n padding: 2,\n originX: 'left',\n originY: 'top'\n };\n if (this.options.label) {\n if (typeof this.options.label.getText === 'function') {\n labelText = this.options.label.getText(mask, this._getMaskCreationIndex(mask));\n }\n // Merge external styles\n if (this.options.label.textOptions) {\n Object.assign(textOptions, this.options.label.textOptions);\n }\n }\n textObject = new fabric.Text(labelText, textOptions);\n }\n\n textObject.maskLabel = true;\n mask.__label = textObject;\n this.canvas.add(textObject);\n this.canvas.bringToFront(textObject);\n this._syncMaskLabel(mask);\n }\n\n /**\n * Hides (removes) all mask labels from the canvas.\n * Internal label references on mask objects are also deleted.\n * @private\n */\n _hideAllMaskLabels() {\n if (!this.canvas) return;\n const canvasObjects = this.canvas.getObjects();\n const canvasObjectSet = new Set(canvasObjects);\n const labels = canvasObjects.filter(object => object.maskLabel);\n labels.forEach(label => {\n try {\n if (canvasObjectSet.has(label)) {\n this.canvas.remove(label);\n canvasObjectSet.delete(label);\n }\n } catch (error) { void error; }\n });\n canvasObjects.forEach(object => {\n if (object.maskId && object.__label) {\n try { delete object.__label; } catch (error) { void error; }\n }\n });\n }\n\n /**\n * Synchronizes the position, angle, and visibility of the mask's label so that it appears properly above the mask.\n * \n * @param {fabric.Object} mask - The mask whose label should be repositioned.\n * @private\n */\n _syncMaskLabel(mask) {\n if (!mask) return;\n if (!this.options.maskLabelOnSelect) return;\n if (!mask.__label) return;\n\n if (typeof mask.setCoords === 'function') mask.setCoords();\n const bounds = mask.getBoundingRect ? mask.getBoundingRect(true, true) : null;\n if (!bounds) return;\n\n const tl = { x: bounds.left, y: bounds.top };\n const center = mask.getCenterPoint();\n\n const vx = center.x - tl.x;\n const vy = center.y - tl.y;\n const dist = Math.sqrt(vx * vx + vy * vy) || 1;\n const ux = vx / dist;\n const uy = vy / dist;\n\n const offset = Math.max(0, this.options.maskLabelOffset ?? 3);\n\n const px = tl.x + ux * offset;\n const py = tl.y + uy * offset;\n\n mask.__label.set({\n left: Math.round(px),\n top: Math.round(py),\n angle: mask.angle || 0,\n originX: 'left',\n originY: 'top',\n visible: true\n });\n mask.__label.setCoords();\n if (typeof this.canvas.requestRenderAll === 'function') {\n this.canvas.requestRenderAll();\n } else {\n this.canvas.renderAll();\n }\n }\n\n /**\n * Shows the label for the given mask, creating it if necessary and synchronizing its position.\n * \n * @param {fabric.Object} mask - The mask whose label should be shown.\n * @private\n */\n _showLabelForMask(mask) {\n if (!mask) return;\n if (!this.options.maskLabelOnSelect) return;\n if (!mask.__label) this._createLabelForMask(mask);\n mask.__label.set({ visible: true });\n this._syncMaskLabel(mask);\n }\n\n /**\n * Handles changes to the selection of canvas objects (masks),\n * updates mask stroke and label display, and syncs mask list selection.\n *\n * @param {Array<Object>} selected - The currently selected objects (e.g. [mask] or []).\n * @private\n */\n _handleSelectionChanged(selected) {\n const selectedMask = (selected || []).find(object => object.maskId);\n const masks = this.canvas.getObjects().filter(object => object.maskId);\n masks.forEach(mask => {\n if (mask !== selectedMask) {\n if (mask.__label) {\n try { this.canvas.remove(mask.__label); } catch (error) { void error; }\n delete mask.__label;\n }\n const originalStrokeWidth = Number(mask.originalStrokeWidth);\n mask.set({\n stroke: mask.originalStroke || '#ccc',\n strokeWidth: Number.isFinite(originalStrokeWidth) ? originalStrokeWidth : 1\n });\n } else {\n mask.set({ stroke: '#ff0000', strokeWidth: 1 });\n }\n });\n\n if (selectedMask) this._showLabelForMask(selectedMask);\n\n this._updateMaskListSelection(selectedMask);\n this.canvas.renderAll();\n this._updateUI();\n }\n\n /**\n * Updates the mask list in the DOM to reflect the current masks on the canvas.\n * Each list entry becomes a clickable element for mask selection.\n * @private\n */\n _updateMaskList() {\n const maskListElement = this._getElement('maskList');\n if (!maskListElement) return;\n maskListElement.innerHTML = '';\n const masks = this.canvas.getObjects().filter(object => object.maskId);\n masks.forEach(mask => {\n const listItemElement = document.createElement('li');\n listItemElement.className = 'list-group-item mask-item';\n listItemElement.textContent = mask.maskName;\n listItemElement.dataset.maskId = String(mask.maskId);\n maskListElement.appendChild(listItemElement);\n });\n }\n\n _handleMaskListClick(event) {\n if (!this.canvas) return;\n const itemElement = event.target && event.target.closest ? event.target.closest('.mask-item') : null;\n if (!itemElement || !itemElement.dataset) return;\n const maskId = Number(itemElement.dataset.maskId);\n const mask = this.canvas.getObjects().find(object => Number(object.maskId) === maskId);\n if (!mask) return;\n this.canvas.setActiveObject(mask);\n this._handleSelectionChanged([mask]);\n }\n\n /**\n * Updates the visual selection (CSS 'active') state for the mask list in the DOM.\n * \n * @param {Object|null} selectedMask - The currently selected mask, or null if none selected.\n * @private\n */\n _updateMaskListSelection(selectedMask) {\n const maskListElement = this._getElement('maskList');\n if (!maskListElement) return;\n const maskItems = maskListElement.querySelectorAll('.mask-item');\n maskItems.forEach(item => {\n const isSelected = !!selectedMask && Number(item.dataset.maskId) === Number(selectedMask.maskId);\n item.classList.toggle('active', isSelected);\n item.classList.toggle('selected', isSelected);\n });\n }\n\n /**\n * Flattens the current masks into the base image and reloads the flattened image.\n *\n * This removes editable mask objects after export and records the operation as one undoable history transition.\n * It does nothing when no base image or no masks exist.\n *\n * @async\n * @returns {Promise<void>} Resolves when the flattened image has been loaded.\n * @public\n */\n async mergeMasks() {\n if (!this.originalImage) return;\n this._assertIdleForOperation('mergeMasks');\n const masks = this.canvas.getObjects().filter(object => object.maskId);\n if (!masks.length) return;\n const beforeJson = this._serializeCanvasState();\n const operationToken = this._beginBusyOperation('mergeMasks');\n\n this.canvas.discardActiveObject();\n this.canvas.renderAll();\n\n try {\n const merged = await this.exportImageBase64(this._withInternalOperationOptions(operationToken, {\n exportImageArea: true,\n multiplier: this.options.exportMultiplier,\n fileType: 'png'\n }));\n this.removeAllMasks(this._withInternalOperationOptions(operationToken, { saveHistory: false }));\n if (this.canvas.getObjects().some(object => object.maskId)) {\n throw new Error('Masks could not be removed during merge');\n }\n await this.loadImage(merged, this._withInternalOperationOptions(operationToken, {\n preserveScroll: true,\n resetMaskCounter: false\n }));\n const afterJson = this._serializeCanvasState();\n this._pushStateTransition(beforeJson, afterJson);\n } catch (error) {\n this._reportError('merge error', error);\n try {\n await this.loadFromState(beforeJson);\n } catch (restoreError) {\n this._reportError('mergeMasks rollback failed', restoreError);\n }\n throw error;\n } finally {\n this._endBusyOperation(operationToken);\n }\n }\n\n /**\n * Backward-compatible alias for {@link ImageEditor#mergeMasks}.\n *\n * @deprecated Use mergeMasks() instead. This alias will be removed in v2.0.0.\n * @returns {Promise<void>} Resolves when mask flattening is complete.\n */\n async merge() {\n return this.mergeMasks();\n }\n\n /**\n * Triggers a JPEG image download of the current canvas.\n *\n * The image area and multiplier are controlled by options.\n * @param {string} [fileName=this.options.defaultDownloadFileName] - Desired download file name.\n * @returns {void}\n * @public\n */\n downloadImage(fileName = this.options.defaultDownloadFileName) {\n if (!this.originalImage) return;\n if (!this._canMutateNow('downloadImage')) return;\n const exportImageArea = this.options.exportImageAreaByDefault;\n this.exportImageBase64({ exportImageArea, multiplier: this.options.exportMultiplier })\n .then(imageBase64 => {\n const link = document.createElement('a');\n link.download = fileName;\n link.href = imageBase64;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n })\n .catch(error => this._reportError('download error', error));\n }\n\n /**\n * Exports the current image as a Base64-encoded data URL.\n *\n * When `exportImageArea` is false, the export omits masks and labels. When it is true, masks are\n * temporarily rendered as opaque export shapes and then restored, so editable mask state is not mutated.\n *\n * @async\n * @param {Object} [options={}] - Export options.\n * @param {boolean} [options.exportImageArea] - If true, exports only the image bounding area with masks cropped and blended.\n * @param {number} [options.multiplier=1] - Scaling multiplier for output (resolution).\n * @param {number} [options.quality=0.92] - Image quality between 0 and 1 for lossy formats.\n * @param {string} [options.fileType='jpeg'] - Output file type ('jpeg' | 'png' | 'webp').\n * @returns {Promise<string>} Resolves with an image data URL.\n * @throws {Error} If there is no image loaded.\n * @public\n */\n async exportImageBase64(options = {}) {\n if (!this.originalImage) throw new Error('No image loaded');\n this._assertIdleForOperation('exportImageBase64', options);\n const exportImageArea = typeof options.exportImageArea === 'boolean' ? options.exportImageArea : this.options.exportImageAreaByDefault;\n const multiplier = options.multiplier || this.options.exportMultiplier || 1;\n const quality = this._normalizeQuality(options.quality ?? this.options.downsampleQuality);\n const format = this._normalizeImageFormat(options.fileType || options.format);\n\n if (!exportImageArea) {\n const masks = this.canvas.getObjects().filter(object => object.maskId || object.maskLabel);\n const editableMasks = this.canvas.getObjects().filter(object => object.maskId);\n const maskVisibilityBackups = masks.map(mask => ({ object: mask, visible: mask.visible }));\n const maskStyleBackups = this._captureMaskExportBackups(editableMasks);\n const labelBackups = this._captureMaskLabelBackups(editableMasks);\n const activeObjectBackup = this._captureActiveObjectBackup();\n\n try {\n masks.forEach(mask => { mask.set({ visible: false }); });\n this.canvas.discardActiveObject();\n this.canvas.renderAll();\n\n this.originalImage.setCoords();\n const imageBounds = this.originalImage.getBoundingRect(true, true);\n const exportRegion = this._getClampedCanvasRegion(imageBounds);\n return await this._exportCanvasRegionToDataURL({\n ...exportRegion,\n multiplier,\n quality,\n format,\n sealPartialEdges: this._getPartialExportEdges(imageBounds)\n });\n } finally {\n maskVisibilityBackups.forEach(backup => {\n try { backup.object.set({ visible: backup.visible }); } catch (error) { void error; }\n });\n this._restoreMaskExportBackups(maskStyleBackups);\n this._restoreMaskLabelBackups(labelBackups);\n this._restoreActiveObjectBackup(activeObjectBackup);\n this.canvas.renderAll();\n }\n }\n\n // Render masks as export shapes without mutating their editable styles.\n const masks = this.canvas.getObjects().filter(object => object.maskId);\n const maskStyleBackups = this._captureMaskExportBackups(masks);\n const labelBackups = this._captureMaskLabelBackups(masks);\n const activeObjectBackup = this._captureActiveObjectBackup();\n\n let finalBase64;\n try {\n // Labels are UI overlays and should not be part of the flattened export.\n masks.forEach(mask => this._removeLabelForMask(mask));\n this.canvas.discardActiveObject();\n this.canvas.renderAll();\n\n // The export treats masks as opaque shapes with no editable border.\n masks.forEach(mask => {\n mask.set({ opacity: 1, fill: '#000000', strokeWidth: 0, stroke: null, selectable: false });\n mask.setCoords();\n });\n this.canvas.renderAll();\n\n // Compute an integer canvas region for the base image.\n this.originalImage.setCoords();\n const imageBounds = this.originalImage.getBoundingRect(true, true);\n const exportRegion = this._getClampedCanvasRegion(imageBounds);\n\n // Crop precisely in offscreen canvas\n finalBase64 = await this._exportCanvasRegionToDataURL({\n ...exportRegion,\n multiplier,\n quality,\n format,\n sealPartialEdges: this._getPartialExportEdges(imageBounds)\n });\n } finally {\n this._restoreMaskExportBackups(maskStyleBackups);\n this._restoreMaskLabelBackups(labelBackups);\n this._restoreActiveObjectBackup(activeObjectBackup);\n this.canvas.renderAll();\n }\n\n return finalBase64;\n }\n\n /**\n * Backward-compatible alias for {@link ImageEditor#exportImageBase64}.\n *\n * @deprecated Use exportImageBase64() instead. This alias will be removed in v2.0.0.\n * @param {Object} [options={}] - Export options passed to exportImageBase64().\n * @returns {Promise<string>} Resolves with an image data URL.\n */\n async getImageBase64(options = {}) {\n return this.exportImageBase64(options);\n }\n\n /**\n * Exports the current image as a File object.\n *\n * The export can include flattened masks (`mergeMask: true`) or only the plain base image (`mergeMask: false`).\n * Supported output formats are JPEG, PNG, and WebP.\n * \n * @async\n * @param {Object} [options={}] - Export options.\n * @param {boolean} [options.mergeMask=true] - If true, export image area with masks merged; if false, export the plain image without masks.\n * @param {string} [options.fileType='jpeg'] - Output file type ('jpeg' | 'png' | 'webp'). Defaults to 'jpeg' on invalid input.\n * @param {number} [options.quality=0.92] - Image quality for lossy types (0-1, default based on options.downsampleQuality).\n * @param {number} [options.multiplier=1] - Output resolution multiplier.\n * @param {string} [options.fileName] - Optional file name (only used for download).\n * @returns {Promise<File>} Resolves with the exported image as a File object.\n * \n * @example\n * const file = await this.exportImageFile({ mergeMask: false, fileType: 'png' });\n */\n async exportImageFile(options = {}) {\n if (!this.originalImage) throw new Error('No image loaded');\n this._assertIdleForOperation('exportImageFile');\n const {\n mergeMask = true,\n fileType = 'jpeg',\n quality = this.options.downsampleQuality ?? 0.92,\n multiplier = this.options.exportMultiplier ?? 1,\n fileName = this.options.defaultDownloadFileName ?? 'exported_image.jpg'\n } = options;\n\n const safeFileType = this._normalizeImageFormat(fileType);\n const normalizedQuality = this._normalizeQuality(quality);\n\n // Generate the data URL in the requested export mode.\n let imageBase64;\n if (mergeMask) {\n imageBase64 = await this.exportImageBase64({\n exportImageArea: true,\n multiplier,\n quality: normalizedQuality,\n fileType: safeFileType\n });\n } else {\n imageBase64 = await this.exportImageBase64({\n exportImageArea: false,\n multiplier,\n quality: normalizedQuality,\n fileType: safeFileType\n });\n }\n\n // Convert to the required image format\n let imageDataUrl = imageBase64;\n if (!imageDataUrl.startsWith(`data:image/${safeFileType}`)) {\n // Redraw the exported data URL when the browser returned a different image format.\n imageDataUrl = await new Promise((resolve, reject) => {\n const imageElement = new window.Image();\n imageElement.crossOrigin = \"Anonymous\";\n imageElement.onload = () => {\n try {\n const offscreenCanvas = document.createElement('canvas');\n offscreenCanvas.width = imageElement.width;\n offscreenCanvas.height = imageElement.height;\n const context = offscreenCanvas.getContext('2d');\n if (!context) throw new Error('Unable to create 2D canvas context for export conversion');\n context.drawImage(imageElement, 0, 0);\n const convertedDataUrl = offscreenCanvas.toDataURL(`image/${safeFileType}`, normalizedQuality);\n resolve(convertedDataUrl);\n } catch (error) { reject(error); }\n };\n imageElement.onerror = reject;\n imageElement.src = imageBase64;\n });\n }\n\n // Convert the final data URL to a File with the requested MIME type.\n const bytes = this._decodeBase64Payload(imageDataUrl.split(',')[1]);\n const mime = `image/${safeFileType}`;\n return new File([bytes], fileName, { type: mime });\n }\n\n _clearMaskPlacementMemory() {\n this._lastMask = null;\n this._lastMaskInitialLeft = null;\n this._lastMaskInitialTop = null;\n this._lastMaskInitialWidth = null;\n }\n\n async _restoreStateAfterCropFailure(beforeJson, message, error) {\n this._reportError(message, error);\n\n if (this._cropRect && this.canvas) this._removeCropRect();\n this._cropRect = null;\n this._cropMode = false;\n if (this.canvas && this._prevSelectionSetting !== undefined) {\n this.canvas.selection = !!this._prevSelectionSetting;\n }\n this._prevSelectionSetting = undefined;\n\n if (beforeJson) {\n try {\n await this.loadFromState(beforeJson);\n } catch (restoreError) {\n this._reportError('applyCrop: rollback failed', restoreError);\n }\n }\n\n this._updateUI();\n if (this.canvas) this.canvas.renderAll();\n }\n\n _restoreCropObjectState() {\n if (Array.isArray(this._cropPrevEvented)) {\n this._cropPrevEvented.forEach(state => {\n try {\n state.object.set({\n evented: state.evented,\n selectable: state.selectable,\n visible: state.visible\n });\n } catch (error) { void error; }\n });\n }\n this._cropPrevEvented = null;\n }\n\n _removeCropRect() {\n if (!this._cropRect) return;\n try {\n if (this._cropHandlers && this._cropHandlers.length) {\n this._cropHandlers.forEach(targetHandlers => {\n targetHandlers.handlers.forEach(handlerRecord => {\n if (targetHandlers.target && typeof targetHandlers.target.off === 'function') {\n targetHandlers.target.off(handlerRecord.eventName, handlerRecord.handler);\n }\n });\n });\n }\n } catch (error) { void error; }\n\n try { if (this.canvas) this.canvas.remove(this._cropRect); } catch (error) { void error; }\n this._cropRect = null;\n this._cropHandlers = [];\n }\n\n /**\n * Enters crop mode by creating a resizable crop rectangle above the base image.\n *\n * Other canvas objects are made non-interactive while crop mode is active. Masks can be hidden during\n * cropping when `crop.hideMasksDuringCrop` is enabled.\n *\n * @returns {void}\n * @public\n */\n enterCropMode() {\n if (!this.canvas || !this.originalImage || this._cropMode) return;\n if (!this._canMutateNow('enterCropMode')) return;\n if (!this.isImageLoaded()) return;\n this._removeCropRect();\n this._cropMode = true;\n\n // Disable group selection so only the crop rectangle can be manipulated.\n this._prevSelectionSetting = this.canvas.selection;\n this.canvas.selection = false;\n\n // Clear the current selection before activating the crop rectangle.\n this.canvas.discardActiveObject();\n\n // Create the initial crop rectangle inside the image bounds.\n this.originalImage.setCoords();\n const imageBounds = this.originalImage.getBoundingRect(true, true);\n // Use a small inset so the user can see the crop boundary.\n const padding = (this.options.crop && this.options.crop.padding) ? this.options.crop.padding : 10;\n const left = Math.max(0, Math.floor(imageBounds.left + padding));\n const top = Math.max(0, Math.floor(imageBounds.top + padding));\n const maxCropWidth = Math.max(1, Math.floor(imageBounds.width - padding * 2));\n const maxCropHeight = Math.max(1, Math.floor(imageBounds.height - padding * 2));\n const configuredMinWidth = Math.max(1, Number(this.options.crop.minWidth) || 50);\n const configuredMinHeight = Math.max(1, Number(this.options.crop.minHeight) || 50);\n const minCropWidth = Math.min(configuredMinWidth, maxCropWidth);\n const minCropHeight = Math.min(configuredMinHeight, maxCropHeight);\n const width = minCropWidth;\n const height = minCropHeight;\n\n // Visual style for the temporary crop rectangle.\n const cropRect = new fabric.Rect({\n left, top,\n width, height,\n fill: 'rgba(0,0,0,0.12)',\n stroke: '#00aaff',\n strokeDashArray: [6, 4],\n strokeWidth: 1,\n strokeUniform: true,\n selectable: true,\n hasRotatingPoint: !!(this.options.crop && this.options.crop.allowRotationOfCropRect),\n lockRotation: !(this.options.crop && this.options.crop.allowRotationOfCropRect),\n cornerSize: 8,\n objectCaching: false,\n originX: 'left',\n originY: 'top',\n lockScalingFlip: true\n });\n\n // Ensure the crop rect is above everything\n this.canvas.add(cropRect);\n cropRect.isCropRect = true;\n this.canvas.bringToFront(cropRect);\n this.canvas.setActiveObject(cropRect);\n\n // Store the crop rectangle so apply/cancel can clean it up.\n this._cropRect = cropRect;\n\n // Keep only the crop rectangle interactive while preserving each object's previous state.\n this._cropPrevEvented = [];\n const shouldHideMasks = !!(this.options.crop && this.options.crop.hideMasksDuringCrop);\n this.canvas.getObjects().forEach(object => {\n if (object !== cropRect) {\n this._cropPrevEvented.push({ object, evented: object.evented, selectable: object.selectable, visible: object.visible });\n try {\n const updates = {\n evented: false,\n selectable: false\n };\n if (shouldHideMasks && (object.maskId || object.maskLabel)) updates.visible = false;\n object.set(updates);\n } catch (error) { void error; }\n }\n });\n\n // Keep Fabric controls and configured size limits in sync as the crop rectangle changes.\n const handleCropRectModified = () => {\n try {\n const cropWidth = Math.max(1, Number(cropRect.width) || 1);\n const cropHeight = Math.max(1, Number(cropRect.height) || 1);\n const nextScaleX = Math.min(maxCropWidth / cropWidth, Math.max(minCropWidth / cropWidth, Number(cropRect.scaleX) || 1));\n const nextScaleY = Math.min(maxCropHeight / cropHeight, Math.max(minCropHeight / cropHeight, Number(cropRect.scaleY) || 1));\n cropRect.set({ scaleX: nextScaleX, scaleY: nextScaleY });\n cropRect.setCoords();\n const cropBounds = cropRect.getBoundingRect(true, true);\n const imageLeft = Number(imageBounds.left) || 0;\n const imageTop = Number(imageBounds.top) || 0;\n const imageRight = imageLeft + (Number(imageBounds.width) || 0);\n const imageBottom = imageTop + (Number(imageBounds.height) || 0);\n let deltaX = 0;\n let deltaY = 0;\n if (cropBounds.left < imageLeft) {\n deltaX = imageLeft - cropBounds.left;\n } else if (cropBounds.left + cropBounds.width > imageRight) {\n deltaX = imageRight - (cropBounds.left + cropBounds.width);\n }\n if (cropBounds.top < imageTop) {\n deltaY = imageTop - cropBounds.top;\n } else if (cropBounds.top + cropBounds.height > imageBottom) {\n deltaY = imageBottom - (cropBounds.top + cropBounds.height);\n }\n if (deltaX || deltaY) {\n cropRect.set({\n left: (Number(cropRect.left) || 0) + deltaX,\n top: (Number(cropRect.top) || 0) + deltaY\n });\n cropRect.setCoords();\n }\n this.canvas.requestRenderAll();\n } catch (error) { void error; }\n };\n cropRect.on('modified', handleCropRectModified);\n cropRect.on('moving', handleCropRectModified);\n cropRect.on('scaling', handleCropRectModified);\n\n // Store handlers so cancel/apply/dispose can unbind them.\n this._cropHandlers.push({\n target: cropRect,\n handlers: [\n { eventName: 'modified', handler: handleCropRectModified },\n { eventName: 'moving', handler: handleCropRectModified },\n { eventName: 'scaling', handler: handleCropRectModified }\n ]\n });\n\n this._updateUI();\n this.canvas.renderAll();\n }\n\n /**\n * Cancels crop mode and removes the temporary crop rectangle.\n *\n * @returns {void}\n * @public\n */\n cancelCrop() {\n if (!this.canvas || !this._cropMode) return;\n this._removeCropRect();\n this._restoreCropObjectState();\n this._cropMode = false;\n // Restore the canvas selection setting that was active before crop mode.\n this.canvas.selection = !!this._prevSelectionSetting;\n this._prevSelectionSetting = undefined;\n\n this.canvas.discardActiveObject();\n this._updateUI();\n this.canvas.renderAll();\n }\n\n /**\n * Applies the current crop rectangle to the base image.\n *\n * Masks are removed by default. When `crop.preserveMasksAfterCrop` is true, masks that intersect the crop\n * region are shifted into the cropped coordinate space and remain editable. The operation is recorded as a\n * single undoable history transition.\n *\n * @async\n * @returns {Promise<void>} Resolves after the cropped image has been loaded and history is updated.\n * @public\n */\n async applyCrop() {\n if (!this.canvas || !this._cropMode || !this._cropRect) return;\n this._assertIdleForOperation('applyCrop');\n\n // Fabric does not update control coordinates automatically after programmatic transforms.\n this._cropRect.setCoords();\n const rectBounds = this._cropRect.getBoundingRect(true, true);\n\n const cropRegion = this._getClampedCanvasRegion(rectBounds, { includePartialPixels: false });\n const shouldPreserveMasks = !!(this.options.crop && this.options.crop.preserveMasksAfterCrop);\n\n this._restoreCropObjectState();\n\n let beforeJson;\n try {\n beforeJson = this._serializeCanvasState();\n } catch (error) {\n this._reportWarning('applyCrop: could not serialize before state', error);\n beforeJson = null;\n }\n\n const preservedMasks = [];\n\n try {\n const masks = this.canvas.getObjects().filter(object => object.maskId);\n if (masks && masks.length) {\n masks.forEach(mask => {\n mask.setCoords();\n const maskBounds = mask.getBoundingRect(true, true);\n const intersectsCrop =\n maskBounds.left < cropRegion.sourceX + cropRegion.sourceWidth &&\n maskBounds.left + maskBounds.width > cropRegion.sourceX &&\n maskBounds.top < cropRegion.sourceY + cropRegion.sourceHeight &&\n maskBounds.top + maskBounds.height > cropRegion.sourceY;\n this._removeLabelForMask(mask);\n this.canvas.remove(mask);\n if (shouldPreserveMasks && intersectsCrop) {\n this._translateObjectByCanvasOffset(mask, -cropRegion.sourceX, -cropRegion.sourceY);\n mask.set({ visible: true });\n preservedMasks.push(mask);\n }\n });\n this._clearMaskPlacementMemory();\n this.canvas.discardActiveObject();\n this.canvas.renderAll();\n }\n } catch (error) {\n await this._restoreStateAfterCropFailure(beforeJson, 'applyCrop: failed to prepare masks', error);\n return;\n }\n\n this._removeCropRect();\n\n // End crop mode before loading the cropped image.\n this._cropMode = false;\n this.canvas.selection = !!this._prevSelectionSetting;\n this._prevSelectionSetting = undefined;\n\n // Export the crop region from the current canvas.\n let croppedBase64;\n try {\n croppedBase64 = await this._exportCanvasRegionToDataURL({\n ...cropRegion,\n multiplier: 1,\n quality: this._normalizeQuality(this.options.downsampleQuality),\n format: 'jpeg'\n });\n } catch (error) {\n await this._restoreStateAfterCropFailure(beforeJson, 'applyCrop: failed to create cropped image', error);\n return;\n }\n\n // Load the cropped image as the new base image.\n try {\n await this.loadImage(croppedBase64, { resetMaskCounter: false });\n if (preservedMasks.length) {\n preservedMasks.forEach(mask => {\n this._rebindMaskEvents(mask);\n this.canvas.add(mask);\n this.canvas.bringToFront(mask);\n });\n this._lastMask = preservedMasks[preservedMasks.length - 1];\n this.maskCounter = preservedMasks.reduce((max, mask) => Math.max(max, mask.maskId || 0), this.maskCounter);\n this._updateMaskList();\n this.canvas.renderAll();\n }\n } catch (error) {\n await this._restoreStateAfterCropFailure(beforeJson, 'applyCrop: loadImage(croppedBase64) failed', error);\n return;\n }\n\n // Create an after snapshot and push one history command for the crop operation.\n let afterJson;\n try {\n afterJson = preservedMasks.length ? this._serializeCanvasState() : this._lastSnapshot;\n } catch (error) {\n this._reportWarning('applyCrop: failed to serialize after state', error);\n afterJson = null;\n }\n\n try {\n this._pushStateTransition(beforeJson, afterJson);\n } catch (error) {\n this._reportWarning('applyCrop: failed to push history command', error);\n }\n\n // Refresh UI state after crop completion.\n this._updateUI();\n this.canvas.renderAll();\n }\n\n\n /* ---------- Misc / UI ---------- */\n\n /**\n * Updates the scale input field in the UI to reflect the current scale.\n * Sets the value (as percentage) if the element is present.\n * @private\n */\n _updateInputs() {\n const scaleInputElement = this._getElement('scaleRate');\n if (scaleInputElement) scaleInputElement.value = Math.round(this.currentScale * 100);\n }\n\n /**\n * Updates the enabled/disabled state of various UI controls (buttons)\n * based on the current application state (image/mask presence, animation, etc).\n * @private\n */\n _updateUI() {\n if (!this.canvas) return;\n const hasImage = !!this.originalImage;\n const masks = hasImage ? this.canvas.getObjects().filter(object => object.maskId) : [];\n const hasMasks = masks.length > 0;\n const activeObject = this.canvas.getActiveObject();\n const hasSelectedMask = activeObject && activeObject.maskId;\n const isDefaultTransform = this.currentScale === 1 && this.currentRotation === 0;\n const canUndo = this.historyManager?.canUndo();\n const canRedo = this.historyManager?.canRedo();\n const isInCropMode = !!this._cropMode;\n const isBusy = this.isBusy();\n\n if (isInCropMode) {\n // Disable all controls except the crop action buttons while crop mode is active.\n for (const key of Object.keys(this.elements || {})) {\n const element = this._getElement(key);\n if (!element) continue;\n if (key === 'applyCropBtn' || key === 'cancelCropBtn') {\n this._setDisabled(key, false);\n } else {\n this._setDisabled(key, true);\n }\n }\n return;\n }\n\n this._setDisabled('zoomInBtn', !hasImage || isBusy || this.currentScale >= this.options.maxScale);\n this._setDisabled('zoomOutBtn', !hasImage || isBusy || this.currentScale <= this.options.minScale);\n this._setDisabled('rotateLeftBtn', !hasImage || isBusy);\n this._setDisabled('rotateRightBtn', !hasImage || isBusy);\n this._setDisabled('addMaskBtn', !hasImage || isBusy);\n this._setDisabled('removeMaskBtn', !hasSelectedMask || isBusy);\n this._setDisabled('removeAllMasksBtn', !hasMasks || isBusy);\n this._setDisabled('mergeBtn', !hasImage || !hasMasks || isBusy);\n this._setDisabled('downloadBtn', !hasImage || isBusy);\n this._setDisabled('resetBtn', !hasImage || isDefaultTransform || isBusy);\n this._setDisabled('undoBtn', !hasImage || isBusy || !canUndo);\n this._setDisabled('redoBtn', !hasImage || isBusy || !canRedo);\n this._setDisabled('cropBtn', !hasImage || isBusy);\n this._setDisabled('applyCropBtn', true);\n this._setDisabled('cancelCropBtn', true);\n this._setDisabled('scaleRate', !hasImage || isBusy);\n this._setDisabled('rotationLeftInput', !hasImage || isBusy);\n this._setDisabled('rotationRightInput', !hasImage || isBusy);\n this._setDisabled('maskList', !hasImage || isBusy);\n this._setDisabled('imageInput', isBusy);\n this._setDisabled('uploadArea', isBusy);\n }\n\n /**\n * Enables or disables a specific UI element (typically a button) by its key.\n * \n * @param {string} key - Key of the element in this.elements (e.g. 'zoomInBtn').\n * @param {boolean} disabled - If true, disables the element; otherwise enables.\n * @private\n */\n _setDisabled(key, disabled) {\n const element = this._getElement(key);\n if (!element) return;\n if ('disabled' in element) {\n element.disabled = !!disabled;\n return;\n }\n if (!this._elementOriginalPointerEvents) this._elementOriginalPointerEvents = new Map();\n if (!this._elementOriginalPointerEvents.has(key)) {\n this._elementOriginalPointerEvents.set(key, element.style.pointerEvents || '');\n }\n\n if (disabled) {\n element.setAttribute('aria-disabled', 'true');\n element.style.pointerEvents = 'none';\n } else {\n element.removeAttribute('aria-disabled');\n element.style.pointerEvents = this._elementOriginalPointerEvents.get(key) ?? '';\n }\n }\n\n _isElementDisabled(element) {\n if (!element) return false;\n if ('disabled' in element) return !!element.disabled;\n return element.getAttribute('aria-disabled') === 'true';\n }\n\n /**\n * Updates placeholder and canvas container visibility based on whether an image is loaded.\n * @private\n */\n _updatePlaceholderStatus() {\n if (!this.options.showPlaceholder) return;\n this._setPlaceholderVisible(!this.originalImage);\n }\n\n /**\n * Shows or hides the placeholder and canvas container.\n *\n * @param {boolean} show - If true, displays the placeholder; otherwise displays the canvas container.\n * @private\n */\n _setPlaceholderVisible(show) {\n if (this.placeholderElement) this._setElementVisible(this.placeholderElement, show);\n const canvasVisibilityElement = this._getCanvasVisibilityElement();\n if (canvasVisibilityElement && canvasVisibilityElement !== this.placeholderElement) {\n this._setElementVisible(canvasVisibilityElement, !show);\n }\n }\n\n _getCanvasVisibilityElement() {\n const wrapperElement = this.canvas && this.canvas.wrapperEl ? this.canvas.wrapperEl : null;\n if (\n this.containerElement &&\n this.placeholderElement &&\n (this.containerElement === this.placeholderElement || this.containerElement.contains(this.placeholderElement))\n ) {\n return wrapperElement || this.canvasElement;\n }\n return this.containerElement || wrapperElement || this.canvasElement;\n }\n\n /**\n * Updates element visibility.\n *\n * @param {HTMLElement} element - Element whose visibility should be updated.\n * @param {boolean} isVisible - If true, removes the hidden state.\n * @returns {void}\n * @private\n */\n _setElementVisible(element, isVisible) {\n if (!element) return;\n this._rememberElementVisibility(element);\n element.hidden = !isVisible;\n element.setAttribute('aria-hidden', isVisible ? 'false' : 'true');\n if (element.classList) {\n element.classList.toggle('d-none', !isVisible);\n }\n }\n\n _rememberElementVisibility(element) {\n if (!element || this._visibilityStateByElement.has(element)) return;\n this._visibilityStateByElement.set(element, this._captureElementVisibility(element));\n }\n\n _captureElementVisibility(element) {\n if (!element) return null;\n return {\n hidden: element.hidden,\n ariaHidden: element.getAttribute('aria-hidden'),\n className: element.className\n };\n }\n\n _restoreElementVisibility(element, state) {\n if (!element || !state) return;\n element.hidden = !!state.hidden;\n if (state.ariaHidden === null) {\n element.removeAttribute('aria-hidden');\n } else {\n element.setAttribute('aria-hidden', state.ariaHidden);\n }\n element.className = state.className || '';\n }\n\n /**\n * Cleans up and disposes of the canvas and related references.\n * Call this method to free memory and remove canvas listeners when the editor is no longer needed.\n * @public\n */\n dispose() {\n this._disposed = true;\n this._rejectActiveAnimations(new Error('Editor disposed during animation'));\n if (this.animationQueue) {\n this.animationQueue.cancelAll(new Error('Editor disposed'));\n }\n this._isLoading = false;\n this._activeOperationName = null;\n this._activeOperationToken = null;\n\n // Remove bound DOM event listeners\n try {\n for (const [key, handlers] of Object.entries(this._handlersByElementKey || {})) {\n const element = this._getElement(key);\n if (!element) continue;\n handlers.forEach(handlerRecord => {\n try { element.removeEventListener(handlerRecord.eventName, handlerRecord.handler); } catch (error) { void error; }\n });\n }\n } catch (error) { void error; }\n\n if (this._cropRect) {\n try { this.canvas.remove(this._cropRect); } catch (error) { void error; }\n this._cropRect = null;\n }\n\n if (this.containerElement && this._containerOriginalOverflow) {\n try { this._restoreContainerOverflowState(); } catch (error) { void error; }\n }\n\n if (this._visibilityStateByElement) {\n try {\n [this.placeholderElement, this._getCanvasVisibilityElement()].forEach(element => {\n const state = element ? this._visibilityStateByElement.get(element) : null;\n if (state) this._restoreElementVisibility(element, state);\n });\n } catch (error) { void error; }\n }\n\n if (this.canvasElement && this._canvasElementOriginalStyle) {\n try {\n this.canvasElement.style.display = this._canvasElementOriginalStyle.display;\n this.canvasElement.style.width = this._canvasElementOriginalStyle.width;\n this.canvasElement.style.height = this._canvasElementOriginalStyle.height;\n } catch (error) { void error; }\n }\n\n if (this.canvas) {\n try { this.canvas.dispose(); } catch (error) { void error; }\n this.canvas = null;\n this.canvasElement = null;\n this.isImageLoadedToCanvas = false;\n }\n this._handlersByElementKey = {};\n this._elementCache = {};\n this._elementOriginalPointerEvents = new Map();\n this._clearMaskPlacementMemory();\n this.originalImage = null;\n this.baseImageScale = 1;\n this.currentScale = 1;\n this.currentRotation = 0;\n this.isAnimating = false;\n this._isLoading = false;\n this._cropMode = false;\n this._cropRect = null;\n this._cropHandlers = [];\n this._cropPrevEvented = null;\n this._prevSelectionSetting = undefined;\n this._lastContainerViewportSize = null;\n this._initialized = false;\n }\n }\n\n /**\n * @callback AnimationTaskCallback\n * @returns {unknown} Animation result or awaitable animation result.\n */\n\n /**\n * @callback PromiseResolveCallback\n * @param {unknown} value - Promise resolution value.\n * @returns {void}\n */\n\n /**\n * @callback PromiseRejectCallback\n * @param {unknown} reason - Promise rejection reason.\n * @returns {void}\n */\n\n /**\n * @typedef {Object} QueuedAnimationTask\n * @property {AnimationTaskCallback} animationFn - Queued animation function.\n * @property {PromiseResolveCallback} resolve - Promise resolver for the queued animation.\n * @property {PromiseRejectCallback} reject - Promise rejecter for the queued animation.\n */\n\n /**\n * @callback HistoryTaskCallback\n * @returns {void|Promise<void>} Result of a history operation.\n */\n\n /**\n * FIFO queue that serializes transform animations so Fabric state changes do not overlap.\n *\n * @private\n */\n class AnimationQueue {\n /**\n * Creates an empty animation queue.\n */\n constructor() {\n /**\n * Pending animation descriptors.\n * @type {Array<QueuedAnimationTask>}\n */\n this.animationTasks = [];\n /**\n * Whether an animation task is currently running.\n * @type {boolean}\n */\n this.isRunning = false;\n this.currentTask = null;\n this._generation = 0;\n }\n\n /**\n * Adds an animation function to the queue.\n *\n * @param {AnimationTaskCallback} animationFn - Function that returns a value, Promise, or awaitable animation result.\n * @returns {Promise<unknown>} Resolves or rejects with the queued animation result.\n */\n async add(animationFn) {\n return new Promise((resolve, reject) => {\n this.animationTasks.push({ animationFn, resolve, reject, isSettled: false });\n if (!this.isRunning) {\n this._drainQueue();\n }\n });\n }\n\n isBusy() {\n return this.isRunning || this.animationTasks.length > 0;\n }\n\n cancelAll(reason = new Error('Animation queue cancelled')) {\n this._generation += 1;\n const cancellationError = reason instanceof Error ? reason : new Error(String(reason));\n const tasks = [\n ...(this.currentTask ? [this.currentTask] : []),\n ...this.animationTasks.splice(0)\n ];\n tasks.forEach(task => {\n if (!task || task.isSettled) return;\n task.isSettled = true;\n task.reject(cancellationError);\n });\n this.isRunning = false;\n this.currentTask = null;\n }\n\n /**\n * Runs queued animation tasks sequentially until the queue is empty.\n *\n * @private\n * @returns {Promise<void>}\n */\n async _drainQueue() {\n if (this.isRunning) return;\n const generation = this._generation;\n this.isRunning = true;\n\n try {\n while (this.animationTasks.length > 0 && generation === this._generation) {\n const task = this.animationTasks.shift();\n this.currentTask = task;\n\n try {\n const result = await task.animationFn();\n if (generation === this._generation && !task.isSettled) {\n task.isSettled = true;\n task.resolve(result);\n }\n } catch (error) {\n if (generation === this._generation && !task.isSettled) {\n task.isSettled = true;\n task.reject(error);\n }\n } finally {\n if (generation === this._generation && this.currentTask === task) this.currentTask = null;\n }\n }\n } finally {\n if (generation === this._generation) {\n this.isRunning = false;\n this.currentTask = null;\n }\n }\n }\n }\n\n /**\n * Undoable command with paired execute and undo operations.\n *\n * @private\n */\n class Command {\n /**\n * @param {HistoryTaskCallback} execute - Function that performs the action.\n * @param {HistoryTaskCallback} undo - Function that reverts the action.\n */\n constructor(execute, undo) {\n /**\n * Executes the command.\n * @type {HistoryTaskCallback}\n */\n this.execute = execute;\n /**\n * Undoes the command.\n * @type {HistoryTaskCallback}\n */\n this.undo = undo;\n }\n }\n\n /**\n * Manages undo/redo history and serializes asynchronous history operations.\n *\n * @private\n */\n class HistoryManager {\n /**\n * @param {number} [maxSize=50] - Maximum number of commands to keep in history.\n */\n constructor(maxSize = 50) {\n /** @type {Array<Command>} */\n this.history = [];\n /** @type {number} */\n this.currentIndex = -1;\n /** @type {number} */\n this.maxSize = maxSize;\n /** @type {Promise<void>} */\n this.pending = Promise.resolve();\n }\n\n /**\n * Queues a history task after the previously queued undo/redo task completes.\n *\n * @param {HistoryTaskCallback} task - Task to run after earlier history work settles.\n * @returns {Promise<void>} Resolves or rejects with the queued task result.\n * @private\n */\n enqueue(task) {\n const nextTask = this.pending.then(() => Promise.resolve().then(task));\n this.pending = nextTask.catch(() => undefined);\n return nextTask;\n }\n\n /**\n * Executes a new command and pushes it onto the history stack.\n * Truncates any \"future\" history when branching.\n *\n * @param {Command} command The command to execute.\n * @returns {void}\n */\n execute(command) {\n const result = command.execute();\n if (result && typeof result.then === 'function') {\n return this.enqueue(() => Promise.resolve(result).then(() => {\n this.push(command);\n }));\n }\n this.push(command);\n return result;\n }\n\n /**\n * Pushes an already-applied command onto the history stack.\n * Truncates any \"future\" history when branching.\n *\n * @param {Command} command The command to push.\n * @returns {void}\n */\n push(command) {\n // Discard redo commands when a new branch is created.\n if (this.currentIndex < this.history.length - 1) {\n this.history = this.history.slice(0, this.currentIndex + 1);\n }\n\n this.history.push(command);\n\n if (this.history.length > this.maxSize) {\n this.history.shift();\n }\n this.currentIndex = this.history.length - 1;\n }\n\n /**\n * Checks whether an undo operation is possible.\n *\n * @returns {boolean} True if undo can be performed.\n */\n canUndo() {\n return this.currentIndex >= 0;\n }\n\n /**\n * Checks whether a redo operation is possible.\n *\n * @returns {boolean} True if redo can be performed.\n */\n canRedo() {\n return this.currentIndex < this.history.length - 1;\n }\n\n /**\n * Undoes the last executed command if possible.\n *\n * @returns {Promise<void>} Resolves after the undo task completes.\n */\n undo() {\n return this.enqueue(async () => {\n if (this.currentIndex >= 0) {\n const index = this.currentIndex;\n await this.history[index].undo();\n this.currentIndex = index - 1;\n }\n });\n }\n\n /**\n * Redoes the next command in history if possible.\n *\n * @returns {Promise<void>} Resolves after the redo task completes.\n */\n redo() {\n return this.enqueue(async () => {\n if (this.currentIndex < this.history.length - 1) {\n const index = this.currentIndex + 1;\n await this.history[index].execute();\n this.currentIndex = index;\n }\n });\n }\n }\n\nexport { ImageEditor };\nexport default ImageEditor;\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["fabricModule", "fabric", "INTERNAL_OPERATION_TOKEN", "getGlobalScope", "getGlobalFabric", "scope", "setFabric", "fabricInstance", "ensureFabric", "ImageEditor", "options", "defaultLabel", "mask", "defaultCrop", "userLabel", "userCrop", "AnimationQueue", "HistoryManager", "value", "idMap", "defaults", "message", "error", "handler", "canvasElement", "containerElement", "initialWidth", "initialHeight", "containerWidth", "containerHeight", "event", "key", "id", "element", "target", "masks", "objects", "object", "shouldPreserveScroll", "snapshot", "uploadAreaElement", "file", "rotationInputElement", "step", "parsedStep", "eventName", "resolve", "reject", "reader", "fileName", "activeModes", "isEnabled", "name", "imageBase64", "transaction", "imageElement", "loadSource", "downsampleMaxWidth", "downsampleMaxHeight", "ratio", "targetWidth", "targetHeight", "fabricImage", "imageWidth", "imageHeight", "viewport", "minWidth", "minHeight", "canvasWidth", "canvasHeight", "fitScale", "layout", "dataUrl", "timeoutMs", "isSettled", "safeTimeoutMs", "timerId", "settle", "callback", "didRestoreCanvasState", "previousLastMask", "previousMaskId", "quality", "sourceDataUrl", "sourceWidth", "sourceHeight", "safeTargetWidth", "safeTargetHeight", "offscreenCanvas", "context", "match", "sourceMimeType", "width", "height", "integerWidth", "integerHeight", "numericValue", "roundedValue", "measuredWidth", "measuredHeight", "overflow", "canScrollX", "canScrollY", "hasHorizontalScrollbar", "hasVerticalScrollbar", "scrollbar", "inlineOverflow", "inlineOverflowX", "inlineOverflowY", "computedOverflow", "computedOverflowX", "computedOverflowY", "style", "probe", "contentWidth", "contentHeight", "safetyMargin", "safeWidth", "safeHeight", "hasVertical", "hasHorizontal", "effectiveWidth", "effectiveHeight", "i", "nextHasVertical", "nextHasHorizontal", "scale", "canvasSize", "strokeWidth", "opacity", "maskStyleBackups", "normalStyle", "stylePatch", "property", "changedProperties", "backup", "result", "restorePatch", "cornerSize", "baseImageScale", "currentScale", "currentRotation", "maskCounter", "jsonObject", "fallback", "fallbackQuality", "numericFallback", "safeFallback", "numericQuality", "format", "bounds", "left", "top", "roundEnd", "sourceX", "sourceY", "endX", "endY", "angle", "edges", "imageData", "pixels", "sealPixel", "x", "y", "fallbackX", "fallbackY", "index", "fallbackIndex", "multiplier", "sealPartialEdges", "safeMultiplier", "safeFormat", "exportFormat", "regionDataUrl", "backgroundColor", "color", "normalizedColor", "hexAlphaMatch", "alpha", "slashAlphaMatch", "commaAlphaMatch", "parts", "alphaValue", "normalizedAlpha", "base64Payload", "payload", "char", "fabricObject", "boundingRect", "coords", "originX", "originY", "deltaX", "deltaY", "center", "nextCenter", "refPoint", "imageBounds", "size", "fabricObjects", "padding", "currentWidth", "currentHeight", "requiredWidth", "requiredHeight", "shouldUseScrollSafeViewport", "newWidth", "newHeight", "factor", "token", "operationName", "isOwnInternalOperation", "reason", "rejectError", "duration", "saveHistory", "didStartAnimation", "targetScale", "topLeft", "degrees", "image", "previousOriginX", "previousOriginY", "previousOriginPoint", "didCompleteRotation", "newTopLeft", "before", "after", "serializedState", "state", "editorMetadata", "canvasObjects", "restoredBaseScale", "restoredCurrentScale", "restoredCurrentRotation", "baseScale", "imageScale", "restoredMaskCounter", "maxMaskId", "max", "callbackError", "imageObjects", "handleLoad", "handleError", "didLoad", "executedOnce", "command", "Command", "metadata", "mouseover", "mouseout", "config", "shapeType", "maskConfig", "firstOffset", "getCanvasBasis", "axis", "resolveValue", "percent", "previousMask", "previousBounds", "polygonPoints", "point", "styles", "hasStyle", "maskSettings", "maskId", "activeObject", "selectedMasks", "label", "labelBackups", "selectedObjects", "activeObjectBackup", "maskBackups", "textObject", "labelText", "textOptions", "canvasObjectSet", "tl", "vx", "vy", "dist", "ux", "uy", "offset", "px", "py", "selected", "selectedMask", "originalStrokeWidth", "maskListElement", "listItemElement", "itemElement", "item", "isSelected", "beforeJson", "operationToken", "merged", "afterJson", "restoreError", "exportImageArea", "link", "editableMasks", "maskVisibilityBackups", "exportRegion", "finalBase64", "mergeMask", "fileType", "safeFileType", "normalizedQuality", "imageDataUrl", "convertedDataUrl", "bytes", "mime", "targetHandlers", "handlerRecord", "maxCropWidth", "maxCropHeight", "configuredMinWidth", "configuredMinHeight", "minCropWidth", "minCropHeight", "cropRect", "shouldHideMasks", "updates", "handleCropRectModified", "cropWidth", "cropHeight", "nextScaleX", "nextScaleY", "cropBounds", "imageLeft", "imageTop", "imageRight", "imageBottom", "rectBounds", "cropRegion", "shouldPreserveMasks", "preservedMasks", "maskBounds", "intersectsCrop", "croppedBase64", "scaleInputElement", "hasImage", "hasMasks", "hasSelectedMask", "isDefaultTransform", "canUndo", "canRedo", "isInCropMode", "isBusy", "disabled", "show", "canvasVisibilityElement", "wrapperElement", "isVisible", "handlers", "animationFn", "cancellationError", "task", "generation", "execute", "undo", "maxSize", "nextTask", "image_editor_default", "ImageEditor", "fabricInstance", "fabricModule", "setFabric", "esm_default", "image_editor_default"]
|
|
7
7
|
}
|