(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.CropperUtils = {})); })(this, (function (exports) { 'use strict'; const IS_BROWSER = typeof window !== 'undefined' && typeof window.document !== 'undefined'; const WINDOW = IS_BROWSER ? window : {}; const IS_TOUCH_DEVICE = IS_BROWSER ? 'ontouchstart' in WINDOW.document.documentElement : false; const HAS_POINTER_EVENT = IS_BROWSER ? 'PointerEvent' in WINDOW : false; const NAMESPACE = 'cropper'; const CROPPER_CANVAS = `${NAMESPACE}-canvas`; const CROPPER_CROSSHAIR = `${NAMESPACE}-crosshair`; const CROPPER_GIRD = `${NAMESPACE}-grid`; const CROPPER_HANDLE = `${NAMESPACE}-handle`; const CROPPER_IMAGE = `${NAMESPACE}-image`; const CROPPER_SELECTION = `${NAMESPACE}-selection`; const CROPPER_SHADE = `${NAMESPACE}-shade`; const CROPPER_VIEWER = `${NAMESPACE}-viewer`; // Actions const ACTION_SELECT = 'select'; const ACTION_MOVE = 'move'; const ACTION_SCALE = 'scale'; const ACTION_ROTATE = 'rotate'; const ACTION_TRANSFORM = 'transform'; const ACTION_NONE = 'none'; const ACTION_RESIZE_NORTH = 'n-resize'; const ACTION_RESIZE_EAST = 'e-resize'; const ACTION_RESIZE_SOUTH = 's-resize'; const ACTION_RESIZE_WEST = 'w-resize'; const ACTION_RESIZE_NORTHEAST = 'ne-resize'; const ACTION_RESIZE_NORTHWEST = 'nw-resize'; const ACTION_RESIZE_SOUTHEAST = 'se-resize'; const ACTION_RESIZE_SOUTHWEST = 'sw-resize'; // Attributes const ATTRIBUTE_ACTION = 'action'; // Native events const EVENT_TOUCH_END = IS_TOUCH_DEVICE ? 'touchend touchcancel' : 'mouseup'; const EVENT_TOUCH_MOVE = IS_TOUCH_DEVICE ? 'touchmove' : 'mousemove'; const EVENT_TOUCH_START = IS_TOUCH_DEVICE ? 'touchstart' : 'mousedown'; const EVENT_POINTER_DOWN = HAS_POINTER_EVENT ? 'pointerdown' : EVENT_TOUCH_START; const EVENT_POINTER_MOVE = HAS_POINTER_EVENT ? 'pointermove' : EVENT_TOUCH_MOVE; const EVENT_POINTER_UP = HAS_POINTER_EVENT ? 'pointerup pointercancel' : EVENT_TOUCH_END; const EVENT_ERROR = 'error'; const EVENT_KEYDOWN = 'keydown'; const EVENT_LOAD = 'load'; const EVENT_RESIZE = 'resize'; const EVENT_WHEEL = 'wheel'; // Custom events const EVENT_ACTION = 'action'; const EVENT_ACTION_END = 'actionend'; const EVENT_ACTION_MOVE = 'actionmove'; const EVENT_ACTION_START = 'actionstart'; const EVENT_CHANGE = 'change'; const EVENT_TRANSFORM = 'transform'; /** * Check if the given value is a string. * @param {*} value The value to check. * @returns {boolean} Returns `true` if the given value is a string, else `false`. */ function isString(value) { return typeof value === 'string'; } /** * Check if the given value is not a number. */ const isNaN = Number.isNaN || WINDOW.isNaN; /** * Check if the given value is a number. * @param {*} value The value to check. * @returns {boolean} Returns `true` if the given value is a number, else `false`. */ function isNumber(value) { return typeof value === 'number' && !isNaN(value); } /** * Check if the given value is a positive number. * @param {*} value The value to check. * @returns {boolean} Returns `true` if the given value is a positive number, else `false`. */ function isPositiveNumber(value) { return isNumber(value) && value > 0 && value < Infinity; } /** * Check if the given value is undefined. * @param {*} value The value to check. * @returns {boolean} Returns `true` if the given value is undefined, else `false`. */ function isUndefined(value) { return typeof value === 'undefined'; } /** * Check if the given value is an object. * @param {*} value - The value to check. * @returns {boolean} Returns `true` if the given value is an object, else `false`. */ function isObject(value) { return typeof value === 'object' && value !== null; } const { hasOwnProperty } = Object.prototype; /** * Check if the given value is a plain object. * @param {*} value - The value to check. * @returns {boolean} Returns `true` if the given value is a plain object, else `false`. */ function isPlainObject(value) { if (!isObject(value)) { return false; } try { const { constructor } = value; const { prototype } = constructor; return constructor && prototype && hasOwnProperty.call(prototype, 'isPrototypeOf'); } catch (error) { return false; } } /** * Check if the given value is a function. * @param {*} value The value to check. * @returns {boolean} Returns `true` if the given value is a function, else `false`. */ function isFunction(value) { return typeof value === 'function'; } /** * Check if the given node is an element. * @param {*} node The node to check. * @returns {boolean} Returns `true` if the given node is an element; otherwise, `false`. */ function isElement(node) { return typeof node === 'object' && node !== null && node.nodeType === 1; } const REGEXP_CAMEL_CASE = /([a-z\d])([A-Z])/g; /** * Transform the given string from camelCase to kebab-case. * @param {string} value The value to transform. * @returns {string} Returns the transformed value. */ function toKebabCase(value) { return String(value).replace(REGEXP_CAMEL_CASE, '$1-$2').toLowerCase(); } const REGEXP_KEBAB_CASE = /-[A-z\d]/g; /** * Transform the given string from kebab-case to camelCase. * @param {string} value The value to transform. * @returns {string} Returns the transformed value. */ function toCamelCase(value) { return value.replace(REGEXP_KEBAB_CASE, (substring) => substring.slice(1).toUpperCase()); } const REGEXP_SPACES = /\s\s*/; /** * Remove event listener from the event target. * {@link https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener} * @param {EventTarget} target The target of the event. * @param {string} types The types of the event. * @param {EventListenerOrEventListenerObject} listener The listener of the event. * @param {EventListenerOptions} [options] The options specify characteristics about the event listener. */ function off(target, types, listener, options) { types.trim().split(REGEXP_SPACES).forEach((type) => { target.removeEventListener(type, listener, options); }); } /** * Add event listener to the event target. * {@link https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener} * @param {EventTarget} target The target of the event. * @param {string} types The types of the event. * @param {EventListenerOrEventListenerObject} listener The listener of the event. * @param {AddEventListenerOptions} [options] The options specify characteristics about the event listener. */ function on(target, types, listener, options) { types.trim().split(REGEXP_SPACES).forEach((type) => { target.addEventListener(type, listener, options); }); } /** * Add once event listener to the event target. * @param {EventTarget} target The target of the event. * @param {string} types The types of the event. * @param {EventListenerOrEventListenerObject} listener The listener of the event. * @param {AddEventListenerOptions} [options] The options specify characteristics about the event listener. */ function once(target, types, listener, options) { on(target, types, listener, Object.assign(Object.assign({}, options), { once: true })); } const defaultEventOptions = { bubbles: true, cancelable: true, composed: true, }; /** * Dispatch event on the event target. * {@link https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/dispatchEvent} * @param {EventTarget} target The target of the event. * @param {string} type The name of the event. * @param {*} [detail] The data passed when initializing the event. * @param {CustomEventInit} [options] The other event options. * @returns {boolean} Returns the result value. */ function emit(target, type, detail, options) { return target.dispatchEvent(new CustomEvent(type, Object.assign(Object.assign(Object.assign({}, defaultEventOptions), { detail }), options))); } const resolvedPromise = Promise.resolve(); /** * Defers the callback to be executed after the next DOM update cycle. * @param {*} [context] The `this` context. * @param {Function} [callback] The callback to execute after the next DOM update cycle. * @returns {Promise} A promise that resolves to nothing. */ function nextTick(context, callback) { return callback ? resolvedPromise.then(context ? callback.bind(context) : callback) : resolvedPromise; } /** * Get the offset base on the document. * @param {Element} element The target element. * @returns {object} The offset data. */ function getOffset(element) { const { documentElement } = element.ownerDocument; const box = element.getBoundingClientRect(); return { left: box.left + (WINDOW.pageXOffset - documentElement.clientLeft), top: box.top + (WINDOW.pageYOffset - documentElement.clientTop), }; } const REGEXP_ANGLE_UNIT = /deg|g?rad|turn$/i; /** * Convert an angle to a radian number. * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/angle} * @param {number|string} angle The angle to convert. * @returns {number} Returns the radian number. */ function toAngleInRadian(angle) { const value = parseFloat(angle) || 0; if (value !== 0) { const [unit = 'rad'] = String(angle).match(REGEXP_ANGLE_UNIT) || []; switch (unit.toLowerCase()) { case 'deg': return (value / 360) * (Math.PI * 2); case 'grad': return (value / 400) * (Math.PI * 2); case 'turn': return value * (Math.PI * 2); } } return value; } const SIZE_ADJUSTMENT_TYPE_CONTAIN = 'contain'; const SIZE_ADJUSTMENT_TYPE_COVER = 'cover'; /** * Get the max sizes in a rectangle under the given aspect ratio. * @param {object} data The original sizes. * @param {string} [type] The adjust type. * @returns {object} Returns the result sizes. */ function getAdjustedSizes(data, type = SIZE_ADJUSTMENT_TYPE_CONTAIN) { const { aspectRatio } = data; let { width, height } = data; const isValidWidth = isPositiveNumber(width); const isValidHeight = isPositiveNumber(height); if (isValidWidth && isValidHeight) { const adjustedWidth = height * aspectRatio; if ((type === SIZE_ADJUSTMENT_TYPE_CONTAIN && adjustedWidth > width) || (type === SIZE_ADJUSTMENT_TYPE_COVER && adjustedWidth < width)) { height = width / aspectRatio; } else { width = height * aspectRatio; } } else if (isValidWidth) { height = width / aspectRatio; } else if (isValidHeight) { width = height * aspectRatio; } return { width, height, }; } /** * Multiply multiple matrices. * @param {Array} matrix The first matrix. * @param {Array} args The rest matrices. * @returns {Array} Returns the result matrix. */ function multiplyMatrices(matrix, ...args) { if (args.length === 0) { return matrix; } const [a1, b1, c1, d1, e1, f1] = matrix; const [a2, b2, c2, d2, e2, f2] = args[0]; // ┌ a1 c1 e1 ┐ ┌ a2 c2 e2 ┐ // │ b1 d1 f1 │ × │ b2 d2 f2 │ // └ 0 0 1 ┘ └ 0 0 1 ┘ matrix = [ a1 * a2 + c1 * b2 /* + e1 * 0 */, b1 * a2 + d1 * b2 /* + f1 * 0 */, a1 * c2 + c1 * d2 /* + e1 * 0 */, b1 * c2 + d1 * d2 /* + f1 * 0 */, a1 * e2 + c1 * f2 + e1 /* * 1 */, b1 * e2 + d1 * f2 + f1 /* * 1 */, ]; return multiplyMatrices(matrix, ...args.slice(1)); } exports.ACTION_MOVE = ACTION_MOVE; exports.ACTION_NONE = ACTION_NONE; exports.ACTION_RESIZE_EAST = ACTION_RESIZE_EAST; exports.ACTION_RESIZE_NORTH = ACTION_RESIZE_NORTH; exports.ACTION_RESIZE_NORTHEAST = ACTION_RESIZE_NORTHEAST; exports.ACTION_RESIZE_NORTHWEST = ACTION_RESIZE_NORTHWEST; exports.ACTION_RESIZE_SOUTH = ACTION_RESIZE_SOUTH; exports.ACTION_RESIZE_SOUTHEAST = ACTION_RESIZE_SOUTHEAST; exports.ACTION_RESIZE_SOUTHWEST = ACTION_RESIZE_SOUTHWEST; exports.ACTION_RESIZE_WEST = ACTION_RESIZE_WEST; exports.ACTION_ROTATE = ACTION_ROTATE; exports.ACTION_SCALE = ACTION_SCALE; exports.ACTION_SELECT = ACTION_SELECT; exports.ACTION_TRANSFORM = ACTION_TRANSFORM; exports.ATTRIBUTE_ACTION = ATTRIBUTE_ACTION; exports.CROPPER_CANVAS = CROPPER_CANVAS; exports.CROPPER_CROSSHAIR = CROPPER_CROSSHAIR; exports.CROPPER_GIRD = CROPPER_GIRD; exports.CROPPER_HANDLE = CROPPER_HANDLE; exports.CROPPER_IMAGE = CROPPER_IMAGE; exports.CROPPER_SELECTION = CROPPER_SELECTION; exports.CROPPER_SHADE = CROPPER_SHADE; exports.CROPPER_VIEWER = CROPPER_VIEWER; exports.EVENT_ACTION = EVENT_ACTION; exports.EVENT_ACTION_END = EVENT_ACTION_END; exports.EVENT_ACTION_MOVE = EVENT_ACTION_MOVE; exports.EVENT_ACTION_START = EVENT_ACTION_START; exports.EVENT_CHANGE = EVENT_CHANGE; exports.EVENT_ERROR = EVENT_ERROR; exports.EVENT_KEYDOWN = EVENT_KEYDOWN; exports.EVENT_LOAD = EVENT_LOAD; exports.EVENT_POINTER_DOWN = EVENT_POINTER_DOWN; exports.EVENT_POINTER_MOVE = EVENT_POINTER_MOVE; exports.EVENT_POINTER_UP = EVENT_POINTER_UP; exports.EVENT_RESIZE = EVENT_RESIZE; exports.EVENT_TOUCH_END = EVENT_TOUCH_END; exports.EVENT_TOUCH_MOVE = EVENT_TOUCH_MOVE; exports.EVENT_TOUCH_START = EVENT_TOUCH_START; exports.EVENT_TRANSFORM = EVENT_TRANSFORM; exports.EVENT_WHEEL = EVENT_WHEEL; exports.HAS_POINTER_EVENT = HAS_POINTER_EVENT; exports.IS_BROWSER = IS_BROWSER; exports.IS_TOUCH_DEVICE = IS_TOUCH_DEVICE; exports.NAMESPACE = NAMESPACE; exports.WINDOW = WINDOW; exports.emit = emit; exports.getAdjustedSizes = getAdjustedSizes; exports.getOffset = getOffset; exports.isElement = isElement; exports.isFunction = isFunction; exports.isNaN = isNaN; exports.isNumber = isNumber; exports.isObject = isObject; exports.isPlainObject = isPlainObject; exports.isPositiveNumber = isPositiveNumber; exports.isString = isString; exports.isUndefined = isUndefined; exports.multiplyMatrices = multiplyMatrices; exports.nextTick = nextTick; exports.off = off; exports.on = on; exports.once = once; exports.toAngleInRadian = toAngleInRadian; exports.toCamelCase = toCamelCase; exports.toKebabCase = toKebabCase; Object.defineProperty(exports, '__esModule', { value: true }); }));