PageSourceSearch

https://www.lcdpu.fr/libs/jquery-cropper/js/cropper.js

js lcdpu.fr collected 2026-09-24 09:14:11 UTC 117,328 bytes, 3,624 lines download raw bytes

vendor: 4,429 bytes, lines 1-136
1/*!
2 * Cropper.js v1.5.9
3 * https://fengyuanchen.github.io/cropperjs
4 *
5 * Copyright 2015-present Chen Fengyuan
6 * Released under the MIT license
7 *
8 * Date: 2020-09-10T13:16:26.743Z
9 */
10
11(function (global, factory) {
12  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
13  typeof define === 'function' && define.amd ? define(factory) :
14  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Cropper = factory());
15}(this, (function () { 'use strict';
16
17  function _typeof(obj) {
18    "@babel/helpers - typeof";
19
20    if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
21      _typeof = function (obj) {
22        return typeof obj;
23      };
24    } else {
25      _typeof = function (obj) {
26        return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
27      };
28    }
29
30    return _typeof(obj);
31  }
32
33  function _classCallCheck(instance, Constructor) {
34    if (!(instance instanceof Constructor)) {
35      throw new TypeError("Cannot call a class as a function");
36    }
37  }
38
39  function _defineProperties(target, props) {
40    for (var i = 0; i < props.length; i++) {
41      var descriptor = props[i];
42      descriptor.enumerable = descriptor.enumerable || false;
43      descriptor.configurable = true;
44      if ("value" in descriptor) descriptor.writable = true;
45      Object.defineProperty(target, descriptor.key, descriptor);
46    }
47  }
48
49  function _createClass(Constructor, protoProps, staticProps) {
50    if (protoProps) _defineProperties(Constructor.prototype, protoProps);
51    if (staticProps) _defineProperties(Constructor, staticProps);
52    return Constructor;
53  }
54
55  function _defineProperty(obj, key, value) {
56    if (key in obj) {
57      Object.defineProperty(obj, key, {
58        value: value,
59        enumerable: true,
60        configurable: true,
61        writable: true
62      });
63    } else {
64      obj[key] = value;
65    }
66
67    return obj;
68  }
69
70  function ownKeys(object, enumerableOnly) {
71    var keys = Object.keys(object);
72
73    if (Object.getOwnPropertySymbols) {
74      var symbols = Object.getOwnPropertySymbols(object);
75      if (enumerableOnly) symbols = symbols.filter(function (sym) {
76        return Object.getOwnPropertyDescriptor(object, sym).enumerable;
77      });
78      keys.push.apply(keys, symbols);
79    }
80
81    return keys;
82  }
83
84  function _objectSpread2(target) {
85    for (var i = 1; i < arguments.length; i++) {
86      var source = arguments[i] != null ? arguments[i] : {};
87
88      if (i % 2) {
89        ownKeys(Object(source), true).forEach(function (key) {
90          _defineProperty(target, key, source[key]);
91        });
92      } else if (Object.getOwnPropertyDescriptors) {
93        Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
94      } else {
95        ownKeys(Object(source)).forEach(function (key) {
96          Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
97        });
98      }
99    }
100
101    return target;
102  }
103
104  function _toConsumableArray(arr) {
105    return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
106  }
107
108  function _arrayWithoutHoles(arr) {
109    if (Array.isArray(arr)) return _arrayLikeToArray(arr);
110  }
111
112  function _iterableToArray(iter) {
113    if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter);
114  }
115
116  function _unsupportedIterableToArray(o, minLen) {
117    if (!o) return;
118    if (typeof o === "string") return _arrayLikeToArray(o, minLen);
119    var n = Object.prototype.toString.call(o).slice(8, -1);
120    if (n === "Object" && o.constructor) n = o.constructor.name;
121    if (n === "Map" || n === "Set") return Array.from(o);
122    if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
123  }
124
125  function _arrayLikeToArray(arr, len) {
126    if (len == null || len > arr.length) len = arr.length;
127
128    for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
129
130    return arr2;
131  }
132
133  function _nonIterableSpread() {
134    throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
135  }
136
vendor: 112,899 bytes, lines 137-3624
137  var IS_BROWSER = typeof window !== 'undefined' && typeof window.document !== 'undefined';
138  var WINDOW = IS_BROWSER ? window : {};
139  var IS_TOUCH_DEVICE = IS_BROWSER && WINDOW.document.documentElement ? 'ontouchstart' in WINDOW.document.documentElement : false;
140  var HAS_POINTER_EVENT = IS_BROWSER ? 'PointerEvent' in WINDOW : false;
141  var NAMESPACE = 'cropper'; // Actions
142
143  var ACTION_ALL = 'all';
144  var ACTION_CROP = 'crop';
145  var ACTION_MOVE = 'move';
146  var ACTION_ZOOM = 'zoom';
147  var ACTION_EAST = 'e';
148  var ACTION_WEST = 'w';
149  var ACTION_SOUTH = 's';
150  var ACTION_NORTH = 'n';
151  var ACTION_NORTH_EAST = 'ne';
152  var ACTION_NORTH_WEST = 'nw';
153  var ACTION_SOUTH_EAST = 'se';
154  var ACTION_SOUTH_WEST = 'sw'; // Classes
155
156  var CLASS_CROP = "".concat(NAMESPACE, "-crop");
157  var CLASS_DISABLED = "".concat(NAMESPACE, "-disabled");
158  var CLASS_HIDDEN = "".concat(NAMESPACE, "-hidden");
159  var CLASS_HIDE = "".concat(NAMESPACE, "-hide");
160  var CLASS_INVISIBLE = "".concat(NAMESPACE, "-invisible");
161  var CLASS_MODAL = "".concat(NAMESPACE, "-modal");
162  var CLASS_MOVE = "".concat(NAMESPACE, "-move"); // Data keys
163
164  var DATA_ACTION = "".concat(NAMESPACE, "Action");
165  var DATA_PREVIEW = "".concat(NAMESPACE, "Preview"); // Drag modes
166
167  var DRAG_MODE_CROP = 'crop';
168  var DRAG_MODE_MOVE = 'move';
169  var DRAG_MODE_NONE = 'none'; // Events
170
171  var EVENT_CROP = 'crop';
172  var EVENT_CROP_END = 'cropend';
173  var EVENT_CROP_MOVE = 'cropmove';
174  var EVENT_CROP_START = 'cropstart';
175  var EVENT_DBLCLICK = 'dblclick';
176  var EVENT_TOUCH_START = IS_TOUCH_DEVICE ? 'touchstart' : 'mousedown';
177  var EVENT_TOUCH_MOVE = IS_TOUCH_DEVICE ? 'touchmove' : 'mousemove';
178  var EVENT_TOUCH_END = IS_TOUCH_DEVICE ? 'touchend touchcancel' : 'mouseup';
179  var EVENT_POINTER_DOWN = HAS_POINTER_EVENT ? 'pointerdown' : EVENT_TOUCH_START;
180  var EVENT_POINTER_MOVE = HAS_POINTER_EVENT ? 'pointermove' : EVENT_TOUCH_MOVE;
181  var EVENT_POINTER_UP = HAS_POINTER_EVENT ? 'pointerup pointercancel' : EVENT_TOUCH_END;
182  var EVENT_READY = 'ready';
183  var EVENT_RESIZE = 'resize';
184  var EVENT_WHEEL = 'wheel';
185  var EVENT_ZOOM = 'zoom'; // Mime types
186
187  var MIME_TYPE_JPEG = 'image/jpeg'; // RegExps
188
189  var REGEXP_ACTIONS = /^e|w|s|n|se|sw|ne|nw|all|crop|move|zoom$/;
190  var REGEXP_DATA_URL = /^data:/;
191  var REGEXP_DATA_URL_JPEG = /^data:image\/jpeg;base64,/;
192  var REGEXP_TAG_NAME = /^img|canvas$/i; // Misc
193  // Inspired by the default width and height of a canvas element.
194
195  var MIN_CONTAINER_WIDTH = 200;
196  var MIN_CONTAINER_HEIGHT = 100;
197
198  var DEFAULTS = {
199    // Define the view mode of the cropper
200    viewMode: 0,
201    // 0, 1, 2, 3
202    // Define the dragging mode of the cropper
203    dragMode: DRAG_MODE_CROP,
204    // 'crop', 'move' or 'none'
205    // Define the initial aspect ratio of the crop box
206    initialAspectRatio: NaN,
207    // Define the aspect ratio of the crop box
208    aspectRatio: NaN,
209    // An object with the previous cropping result data
210    data: null,
211    // A selector for adding extra containers to preview
212    preview: '',
213    // Re-render the cropper when resize the window
214    responsive: true,
215    // Restore the cropped area after resize the window
216    restore: true,
217    // Check if the current image is a cross-origin image
218    checkCrossOrigin: true,
219    // Check the current image's Exif Orientation information
220    checkOrientation: true,
221    // Show the black modal
222    modal: true,
223    // Show the dashed lines for guiding
224    guides: true,
225    // Show the center indicator for guiding
226    center: true,
227    // Show the white modal to highlight the crop box
228    highlight: true,
229    // Show the grid background
230    background: true,
231    // Enable to crop the image automatically when initialize
232    autoCrop: true,
233    // Define the percentage of automatic cropping area when initializes
234    autoCropArea: 0.8,
235    // Enable to move the image
236    movable: true,
237    // Enable to rotate the image
238    rotatable: true,
239    // Enable to scale the image
240    scalable: true,
241    // Enable to zoom the image
242    zoomable: true,
243    // Enable to zoom the image by dragging touch
244    zoomOnTouch: true,
245    // Enable to zoom the image by wheeling mouse
246    zoomOnWheel: true,
247    // Define zoom ratio when zoom the image by wheeling mouse
248    wheelZoomRatio: 0.1,
249    // Enable to move the crop box
250    cropBoxMovable: true,
251    // Enable to resize the crop box
252    cropBoxResizable: true,
253    // Toggle drag mode between "crop" and "move" when click twice on the cropper
254    toggleDragModeOnDblclick: true,
255    // Size limitation
256    minCanvasWidth: 0,
257    minCanvasHeight: 0,
258    minCropBoxWidth: 0,
259    minCropBoxHeight: 0,
260    minContainerWidth: MIN_CONTAINER_WIDTH,
261    minContainerHeight: MIN_CONTAINER_HEIGHT,
262    // Shortcuts of events
263    ready: null,
264    cropstart: null,
265    cropmove: null,
266    cropend: null,
267    crop: null,
268    zoom: null
269  };
270
271  var TEMPLATE = '<div class="cropper-container" touch-action="none">' + '<div class="cropper-wrap-box">' + '<div class="cropper-canvas"></div>' + '</div>' + '<div class="cropper-drag-box"></div>' + '<div class="cropper-crop-box">' + '<span class="cropper-view-box"></span>' + '<span class="cropper-dashed dashed-h"></span>' + '<span class="cropper-dashed dashed-v"></span>' + '<span class="cropper-center"></span>' + '<span class="cropper-face"></span>' + '<span class="cropper-line line-e" data-cropper-action="e"></span>' + '<span class="cropper-line line-n" data-cropper-action="n"></span>' + '<span class="cropper-line line-w" data-cropper-action="w"></span>' + '<span class="cropper-line line-s" data-cropper-action="s"></span>' + '<span class="cropper-point point-e" data-cropper-action="e"></span>' + '<span class="cropper-point point-n" data-cropper-action="n"></span>' + '<span class="cropper-point point-w" data-cropper-action="w"></span>' + '<span class="cropper-point point-s" data-cropper-action="s"></span>' + '<span class="cropper-point point-ne" data-cropper-action="ne"></span>' + '<span class="cropper-point point-nw" data-cropper-action="nw"></span>' + '<span class="cropper-point point-sw" data-cropper-action="sw"></span>' + '<span class="cropper-point point-se" data-cropper-action="se"></span>' + '</div>' + '</div>';
272
273  /**
274   * Check if the given value is not a number.
275   */
276
277  var isNaN = Number.isNaN || WINDOW.isNaN;
278  /**
279   * Check if the given value is a number.
280   * @param {*} value - The value to check.
281   * @returns {boolean} Returns `true` if the given value is a number, else `false`.
282   */
283
284  function isNumber(value) {
285    return typeof value === 'number' && !isNaN(value);
286  }
287  /**
288   * Check if the given value is a positive number.
289   * @param {*} value - The value to check.
290   * @returns {boolean} Returns `true` if the given value is a positive number, else `false`.
291   */
292
293  var isPositiveNumber = function isPositiveNumber(value) {
294    return value > 0 && value < Infinity;
295  };
296  /**
297   * Check if the given value is undefined.
298   * @param {*} value - The value to check.
299   * @returns {boolean} Returns `true` if the given value is undefined, else `false`.
300   */
301
302  function isUndefined(value) {
303    return typeof value === 'undefined';
304  }
305  /**
306   * Check if the given value is an object.
307   * @param {*} value - The value to check.
308   * @returns {boolean} Returns `true` if the given value is an object, else `false`.
309   */
310
311  function isObject(value) {
312    return _typeof(value) === 'object' && value !== null;
313  }
314  var hasOwnProperty = Object.prototype.hasOwnProperty;
315  /**
316   * Check if the given value is a plain object.
317   * @param {*} value - The value to check.
318   * @returns {boolean} Returns `true` if the given value is a plain object, else `false`.
319   */
320
321  function isPlainObject(value) {
322    if (!isObject(value)) {
323      return false;
324    }
325
326    try {
327      var _constructor = value.constructor;
328      var prototype = _constructor.prototype;
329      return _constructor && prototype && hasOwnProperty.call(prototype, 'isPrototypeOf');
330    } catch (error) {
331      return false;
332    }
333  }
334  /**
335   * Check if the given value is a function.
336   * @param {*} value - The value to check.
337   * @returns {boolean} Returns `true` if the given value is a function, else `false`.
338   */
339
340  function isFunction(value) {
341    return typeof value === 'function';
342  }
343  var slice = Array.prototype.slice;
344  /**
345   * Convert array-like or iterable object to an array.
346   * @param {*} value - The value to convert.
347   * @returns {Array} Returns a new array.
348   */
349
350  function toArray(value) {
351    return Array.from ? Array.from(value) : slice.call(value);
352  }
353  /**
354   * Iterate the given data.
355   * @param {*} data - The data to iterate.
356   * @param {Function} callback - The process function for each element.
357   * @returns {*} The original data.
358   */
359
360  function forEach(data, callback) {
361    if (data && isFunction(callback)) {
362      if (Array.isArray(data) || isNumber(data.length)
363      /* array-like */
364      ) {
365          toArray(data).forEach(function (value, key) {
366            callback.call(data, value, key, data);
367          });
368        } else if (isObject(data)) {
369        Object.keys(data).forEach(function (key) {
370          callback.call(data, data[key], key, data);
371        });
372      }
373    }
374
375    return data;
376  }
377  /**
378   * Extend the given object.
379   * @param {*} target - The target object to extend.
380   * @param {*} args - The rest objects for merging to the target object.
381   * @returns {Object} The extended object.
382   */
383
384  var assign = Object.assign || function assign(target) {
385    for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
386      args[_key - 1] = arguments[_key];
387    }
388
389    if (isObject(target) && args.length > 0) {
390      args.forEach(function (arg) {
391        if (isObject(arg)) {
392          Object.keys(arg).forEach(function (key) {
393            target[key] = arg[key];
394          });
395        }
396      });
397    }
398
399    return target;
400  };
401  var REGEXP_DECIMALS = /\.\d*(?:0|9){12}\d*$/;
402  /**
403   * Normalize decimal number.
404   * Check out {@link https://0.30000000000000004.com/}
405   * @param {number} value - The value to normalize.
406   * @param {number} [times=100000000000] - The times for normalizing.
407   * @returns {number} Returns the normalized number.
408   */
409
410  function normalizeDecimalNumber(value) {
411    var times = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 100000000000;
412    return REGEXP_DECIMALS.test(value) ? Math.round(value * times) / times : value;
413  }
414  var REGEXP_SUFFIX = /^width|height|left|top|marginLeft|marginTop$/;
415  /**
416   * Apply styles to the given element.
417   * @param {Element} element - The target element.
418   * @param {Object} styles - The styles for applying.
419   */
420
421  function setStyle(element, styles) {
422    var style = element.style;
423    forEach(styles, function (value, property) {
424      if (REGEXP_SUFFIX.test(property) && isNumber(value)) {
425        value = "".concat(value, "px");
426      }
427
428      style[property] = value;
429    });
430  }
431  /**
432   * Check if the given element has a special class.
433   * @param {Element} element - The element to check.
434   * @param {string} value - The class to search.
435   * @returns {boolean} Returns `true` if the special class was found.
436   */
437
438  function hasClass(element, value) {
439    return element.classList ? element.classList.contains(value) : element.className.indexOf(value) > -1;
440  }
441  /**
442   * Add classes to the given element.
443   * @param {Element} element - The target element.
444   * @param {string} value - The classes to be added.
445   */
446
447  function addClass(element, value) {
448    if (!value) {
449      return;
450    }
451
452    if (isNumber(element.length)) {
453      forEach(element, function (elem) {
454        addClass(elem, value);
455      });
456      return;
457    }
458
459    if (element.classList) {
460      element.classList.add(value);
461      return;
462    }
463
464    var className = element.className.trim();
465
466    if (!className) {
467      element.className = value;
468    } else if (className.indexOf(value) < 0) {
469      element.className = "".concat(className, " ").concat(value);
470    }
471  }
472  /**
473   * Remove classes from the given element.
474   * @param {Element} element - The target element.
475   * @param {string} value - The classes to be removed.
476   */
477
478  function removeClass(element, value) {
479    if (!value) {
480      return;
481    }
482
483    if (isNumber(element.length)) {
484      forEach(element, function (elem) {
485        removeClass(elem, value);
486      });
487      return;
488    }
489
490    if (element.classList) {
491      element.classList.remove(value);
492      return;
493    }
494
495    if (element.className.indexOf(value) >= 0) {
496      element.className = element.className.replace(value, '');
497    }
498  }
499  /**
500   * Add or remove classes from the given element.
501   * @param {Element} element - The target element.
502   * @param {string} value - The classes to be toggled.
503   * @param {boolean} added - Add only.
504   */
505
506  function toggleClass(element, value, added) {
507    if (!value) {
508      return;
509    }
510
511    if (isNumber(element.length)) {
512      forEach(element, function (elem) {
513        toggleClass(elem, value, added);
514      });
515      return;
516    } // IE10-11 doesn't support the second parameter of `classList.toggle`
517
518
519    if (added) {
520      addClass(element, value);
521    } else {
522      removeClass(element, value);
523    }
524  }
525  var REGEXP_CAMEL_CASE = /([a-z\d])([A-Z])/g;
526  /**
527   * Transform the given string from camelCase to kebab-case
528   * @param {string} value - The value to transform.
529   * @returns {string} The transformed value.
530   */
531
532  function toParamCase(value) {
533    return value.replace(REGEXP_CAMEL_CASE, '$1-$2').toLowerCase();
534  }
535  /**
536   * Get data from the given element.
537   * @param {Element} element - The target element.
538   * @param {string} name - The data key to get.
539   * @returns {string} The data value.
540   */
541
542  function getData(element, name) {
543    if (isObject(element[name])) {
544      return element[name];
545    }
546
547    if (element.dataset) {
548      return element.dataset[name];
549    }
550
551    return element.getAttribute("data-".concat(toParamCase(name)));
552  }
553  /**
554   * Set data to the given element.
555   * @param {Element} element - The target element.
556   * @param {string} name - The data key to set.
557   * @param {string} data - The data value.
558   */
559
560  function setData(element, name, data) {
561    if (isObject(data)) {
562      element[name] = data;
563    } else if (element.dataset) {
564      element.dataset[name] = data;
565    } else {
566      element.setAttribute("data-".concat(toParamCase(name)), data);
567    }
568  }
569  /**
570   * Remove data from the given element.
571   * @param {Element} element - The target element.
572   * @param {string} name - The data key to remove.
573   */
574
575  function removeData(element, name) {
576    if (isObject(element[name])) {
577      try {
578        delete element[name];
579      } catch (error) {
580        element[name] = undefined;
581      }
582    } else if (element.dataset) {
583      // #128 Safari not allows to delete dataset property
584      try {
585        delete element.dataset[name];
586      } catch (error) {
587        element.dataset[name] = undefined;
588      }
589    } else {
590      element.removeAttribute("data-".concat(toParamCase(name)));
591    }
592  }
593  var REGEXP_SPACES = /\s\s*/;
594
595  var onceSupported = function () {
596    var supported = false;
597
598    if (IS_BROWSER) {
599      var once = false;
600
601      var listener = function listener() {};
602
603      var options = Object.defineProperty({}, 'once', {
604        get: function get() {
605          supported = true;
606          return once;
607        },
608
609        /**
610         * This setter can fix a `TypeError` in strict mode
611         * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Getter_only}
612         * @param {boolean} value - The value to set
613         */
614        set: function set(value) {
615          once = value;
616        }
617      });
618      WINDOW.addEventListener('test', listener, options);
619      WINDOW.removeEventListener('test', listener, options);
620    }
621
622    return supported;
623  }();
624  /**
625   * Remove event listener from the target element.
626   * @param {Element} element - The event target.
627   * @param {string} type - The event type(s).
628   * @param {Function} listener - The event listener.
629   * @param {Object} options - The event options.
630   */
631
632
633  function removeListener(element, type, listener) {
634    var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
635    var handler = listener;
636    type.trim().split(REGEXP_SPACES).forEach(function (event) {
637      if (!onceSupported) {
638        var listeners = element.listeners;
639
640        if (listeners && listeners[event] && listeners[event][listener]) {
641          handler = listeners[event][listener];
642          delete listeners[event][listener];
643
644          if (Object.keys(listeners[event]).length === 0) {
645            delete listeners[event];
646          }
647
648          if (Object.keys(listeners).length === 0) {
649            delete element.listeners;
650          }
651        }
652      }
653
654      element.removeEventListener(event, handler, options);
655    });
656  }
657  /**
658   * Add event listener to the target element.
659   * @param {Element} element - The event target.
660   * @param {string} type - The event type(s).
661   * @param {Function} listener - The event listener.
662   * @param {Object} options - The event options.
663   */
664
665  function addListener(element, type, listener) {
666    var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
667    var _handler = listener;
668    type.trim().split(REGEXP_SPACES).forEach(function (event) {
669      if (options.once && !onceSupported) {
670        var _element$listeners = element.listeners,
671            listeners = _element$listeners === void 0 ? {} : _element$listeners;
672
673        _handler = function handler() {
674          delete listeners[event][listener];
675          element.removeEventListener(event, _handler, options);
676
677          for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
678            args[_key2] = arguments[_key2];
679          }
680
681          listener.apply(element, args);
682        };
683
684        if (!listeners[event]) {
685          listeners[event] = {};
686        }
687
688        if (listeners[event][listener]) {
689          element.removeEventListener(event, listeners[event][listener], options);
690        }
691
692        listeners[event][listener] = _handler;
693        element.listeners = listeners;
694      }
695
696      element.addEventListener(event, _handler, options);
697    });
698  }
699  /**
700   * Dispatch event on the target element.
701   * @param {Element} element - The event target.
702   * @param {string} type - The event type(s).
703   * @param {Object} data - The additional event data.
704   * @returns {boolean} Indicate if the event is default prevented or not.
705   */
706
707  function dispatchEvent(element, type, data) {
708    var event; // Event and CustomEvent on IE9-11 are global objects, not constructors
709
710    if (isFunction(Event) && isFunction(CustomEvent)) {
711      event = new CustomEvent(type, {
712        detail: data,
713        bubbles: true,
714        cancelable: true
715      });
716    } else {
717      event = document.createEvent('CustomEvent');
718      event.initCustomEvent(type, true, true, data);
719    }
720
721    return element.dispatchEvent(event);
722  }
723  /**
724   * Get the offset base on the document.
725   * @param {Element} element - The target element.
726   * @returns {Object} The offset data.
727   */
728
729  function getOffset(element) {
730    var box = element.getBoundingClientRect();
731    return {
732      left: box.left + (window.pageXOffset - document.documentElement.clientLeft),
733      top: box.top + (window.pageYOffset - document.documentElement.clientTop)
734    };
735  }
736  var location = WINDOW.location;
737  var REGEXP_ORIGINS = /^(\w+:)\/\/([^:/?#]*):?(\d*)/i;
738  /**
739   * Check if the given URL is a cross origin URL.
740   * @param {string} url - The target URL.
741   * @returns {boolean} Returns `true` if the given URL is a cross origin URL, else `false`.
742   */
743
744  function isCrossOriginURL(url) {
745    var parts = url.match(REGEXP_ORIGINS);
746    return parts !== null && (parts[1] !== location.protocol || parts[2] !== location.hostname || parts[3] !== location.port);
747  }
748  /**
749   * Add timestamp to the given URL.
750   * @param {string} url - The target URL.
751   * @returns {string} The result URL.
752   */
753
754  function addTimestamp(url) {
755    var timestamp = "timestamp=".concat(new Date().getTime());
756    return url + (url.indexOf('?') === -1 ? '?' : '&') + timestamp;
757  }
758  /**
759   * Get transforms base on the given object.
760   * @param {Object} obj - The target object.
761   * @returns {string} A string contains transform values.
762   */
763
764  function getTransforms(_ref) {
765    var rotate = _ref.rotate,
766        scaleX = _ref.scaleX,
767        scaleY = _ref.scaleY,
768        translateX = _ref.translateX,
769        translateY = _ref.translateY;
770    var values = [];
771
772    if (isNumber(translateX) && translateX !== 0) {
773      values.push("translateX(".concat(translateX, "px)"));
774    }
775
776    if (isNumber(translateY) && translateY !== 0) {
777      values.push("translateY(".concat(translateY, "px)"));
778    } // Rotate should come first before scale to match orientation transform
779
780
781    if (isNumber(rotate) && rotate !== 0) {
782      values.push("rotate(".concat(rotate, "deg)"));
783    }
784
785    if (isNumber(scaleX) && scaleX !== 1) {
786      values.push("scaleX(".concat(scaleX, ")"));
787    }
788
789    if (isNumber(scaleY) && scaleY !== 1) {
790      values.push("scaleY(".concat(scaleY, ")"));
791    }
792
793    var transform = values.length ? values.join(' ') : 'none';
794    return {
795      WebkitTransform: transform,
796      msTransform: transform,
797      transform: transform
798    };
799  }
800  /**
801   * Get the max ratio of a group of pointers.
802   * @param {string} pointers - The target pointers.
803   * @returns {number} The result ratio.
804   */
805
806  function getMaxZoomRatio(pointers) {
807    var pointers2 = _objectSpread2({}, pointers);
808
809    var maxRatio = 0;
810    forEach(pointers, function (pointer, pointerId) {
811      delete pointers2[pointerId];
812      forEach(pointers2, function (pointer2) {
813        var x1 = Math.abs(pointer.startX - pointer2.startX);
814        var y1 = Math.abs(pointer.startY - pointer2.startY);
815        var x2 = Math.abs(pointer.endX - pointer2.endX);
816        var y2 = Math.abs(pointer.endY - pointer2.endY);
817        var z1 = Math.sqrt(x1 * x1 + y1 * y1);
818        var z2 = Math.sqrt(x2 * x2 + y2 * y2);
819        var ratio = (z2 - z1) / z1;
820
821        if (Math.abs(ratio) > Math.abs(maxRatio)) {
822          maxRatio = ratio;
823        }
824      });
825    });
826    return maxRatio;
827  }
828  /**
829   * Get a pointer from an event object.
830   * @param {Object} event - The target event object.
831   * @param {boolean} endOnly - Indicates if only returns the end point coordinate or not.
832   * @returns {Object} The result pointer contains start and/or end point coordinates.
833   */
834
835  function getPointer(_ref2, endOnly) {
836    var pageX = _ref2.pageX,
837        pageY = _ref2.pageY;
838    var end = {
839      endX: pageX,
840      endY: pageY
841    };
842    return endOnly ? end : _objectSpread2({
843      startX: pageX,
844      startY: pageY
845    }, end);
846  }
847  /**
848   * Get the center point coordinate of a group of pointers.
849   * @param {Object} pointers - The target pointers.
850   * @returns {Object} The center point coordinate.
851   */
852
853  function getPointersCenter(pointers) {
854    var pageX = 0;
855    var pageY = 0;
856    var count = 0;
857    forEach(pointers, function (_ref3) {
858      var startX = _ref3.startX,
859          startY = _ref3.startY;
860      pageX += startX;
861      pageY += startY;
862      count += 1;
863    });
864    pageX /= count;
865    pageY /= count;
866    return {
867      pageX: pageX,
868      pageY: pageY
869    };
870  }
871  /**
872   * Get the max sizes in a rectangle under the given aspect ratio.
873   * @param {Object} data - The original sizes.
874   * @param {string} [type='contain'] - The adjust type.
875   * @returns {Object} The result sizes.
876   */
877
878  function getAdjustedSizes(_ref4) // or 'cover'
879  {
880    var aspectRatio = _ref4.aspectRatio,
881        height = _ref4.height,
882        width = _ref4.width;
883    var type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'contain';
884    var isValidWidth = isPositiveNumber(width);
885    var isValidHeight = isPositiveNumber(height);
886
887    if (isValidWidth && isValidHeight) {
888      var adjustedWidth = height * aspectRatio;
889
890      if (type === 'contain' && adjustedWidth > width || type === 'cover' && adjustedWidth < width) {
891        height = width / aspectRatio;
892      } else {
893        width = height * aspectRatio;
894      }
895    } else if (isValidWidth) {
896      height = width / aspectRatio;
897    } else if (isValidHeight) {
898      width = height * aspectRatio;
899    }
900
901    return {
902      width: width,
903      height: height
904    };
905  }
906  /**
907   * Get the new sizes of a rectangle after rotated.
908   * @param {Object} data - The original sizes.
909   * @returns {Object} The result sizes.
910   */
911
912  function getRotatedSizes(_ref5) {
913    var width = _ref5.width,
914        height = _ref5.height,
915        degree = _ref5.degree;
916    degree = Math.abs(degree) % 180;
917
918    if (degree === 90) {
919      return {
920        width: height,
921        height: width
922      };
923    }
924
925    var arc = degree % 90 * Math.PI / 180;
926    var sinArc = Math.sin(arc);
927    var cosArc = Math.cos(arc);
928    var newWidth = width * cosArc + height * sinArc;
929    var newHeight = width * sinArc + height * cosArc;
930    return degree > 90 ? {
931      width: newHeight,
932      height: newWidth
933    } : {
934      width: newWidth,
935      height: newHeight
936    };
937  }
938  /**
939   * Get a canvas which drew the given image.
940   * @param {HTMLImageElement} image - The image for drawing.
941   * @param {Object} imageData - The image data.
942   * @param {Object} canvasData - The canvas data.
943   * @param {Object} options - The options.
944   * @returns {HTMLCanvasElement} The result canvas.
945   */
946
947  function getSourceCanvas(image, _ref6, _ref7, _ref8) {
948    var imageAspectRatio = _ref6.aspectRatio,
949        imageNaturalWidth = _ref6.naturalWidth,
950        imageNaturalHeight = _ref6.naturalHeight,
951        _ref6$rotate = _ref6.rotate,
952        rotate = _ref6$rotate === void 0 ? 0 : _ref6$rotate,
953        _ref6$scaleX = _ref6.scaleX,
954        scaleX = _ref6$scaleX === void 0 ? 1 : _ref6$scaleX,
955        _ref6$scaleY = _ref6.scaleY,
956        scaleY = _ref6$scaleY === void 0 ? 1 : _ref6$scaleY;
957    var aspectRatio = _ref7.aspectRatio,
958        naturalWidth = _ref7.naturalWidth,
959        naturalHeight = _ref7.naturalHeight;
960    var _ref8$fillColor = _ref8.fillColor,
961        fillColor = _ref8$fillColor === void 0 ? 'transparent' : _ref8$fillColor,
962        _ref8$imageSmoothingE = _ref8.imageSmoothingEnabled,
963        imageSmoothingEnabled = _ref8$imageSmoothingE === void 0 ? true : _ref8$imageSmoothingE,
964        _ref8$imageSmoothingQ = _ref8.imageSmoothingQuality,
965        imageSmoothingQuality = _ref8$imageSmoothingQ === void 0 ? 'low' : _ref8$imageSmoothingQ,
966        _ref8$maxWidth = _ref8.maxWidth,
967        maxWidth = _ref8$maxWidth === void 0 ? Infinity : _ref8$maxWidth,
968        _ref8$maxHeight = _ref8.maxHeight,
969        maxHeight = _ref8$maxHeight === void 0 ? Infinity : _ref8$maxHeight,
970        _ref8$minWidth = _ref8.minWidth,
971        minWidth = _ref8$minWidth === void 0 ? 0 : _ref8$minWidth,
972        _ref8$minHeight = _ref8.minHeight,
973        minHeight = _ref8$minHeight === void 0 ? 0 : _ref8$minHeight;
974    var canvas = document.createElement('canvas');
975    var context = canvas.getContext('2d');
976    var maxSizes = getAdjustedSizes({
977      aspectRatio: aspectRatio,
978      width: maxWidth,
979      height: maxHeight
980    });
981    var minSizes = getAdjustedSizes({
982      aspectRatio: aspectRatio,
983      width: minWidth,
984      height: minHeight
985    }, 'cover');
986    var width = Math.min(maxSizes.width, Math.max(minSizes.width, naturalWidth));
987    var height = Math.min(maxSizes.height, Math.max(minSizes.height, naturalHeight)); // Note: should always use image's natural sizes for drawing as
988    // imageData.naturalWidth === canvasData.naturalHeight when rotate % 180 === 90
989
990    var destMaxSizes = getAdjustedSizes({
991      aspectRatio: imageAspectRatio,
992      width: maxWidth,
993      height: maxHeight
994    });
995    var destMinSizes = getAdjustedSizes({
996      aspectRatio: imageAspectRatio,
997      width: minWidth,
998      height: minHeight
999    }, 'cover');
1000    var destWidth = Math.min(destMaxSizes.width, Math.max(destMinSizes.width, imageNaturalWidth));
1001    var destHeight = Math.min(destMaxSizes.height, Math.max(destMinSizes.height, imageNaturalHeight));
1002    var params = [-destWidth / 2, -destHeight / 2, destWidth, destHeight];
1003    canvas.width = normalizeDecimalNumber(width);
1004    canvas.height = normalizeDecimalNumber(height);
1005    context.fillStyle = fillColor;
1006    context.fillRect(0, 0, width, height);
1007    context.save();
1008    context.translate(width / 2, height / 2);
1009    context.rotate(rotate * Math.PI / 180);
1010    context.scale(scaleX, scaleY);
1011    context.imageSmoothingEnabled = imageSmoothingEnabled;
1012    context.imageSmoothingQuality = imageSmoothingQuality;
1013    context.drawImage.apply(context, [image].concat(_toConsumableArray(params.map(function (param) {
1014      return Math.floor(normalizeDecimalNumber(param));
1015    }))));
1016    context.restore();
1017    return canvas;
1018  }
1019  var fromCharCode = String.fromCharCode;
1020  /**
1021   * Get string from char code in data view.
1022   * @param {DataView} dataView - The data view for read.
1023   * @param {number} start - The start index.
1024   * @param {number} length - The read length.
1025   * @returns {string} The read result.
1026   */
1027
1028  function getStringFromCharCode(dataView, start, length) {
1029    var str = '';
1030    length += start;
1031
1032    for (var i = start; i < length; i += 1) {
1033      str += fromCharCode(dataView.getUint8(i));
1034    }
1035
1036    return str;
1037  }
1038  var REGEXP_DATA_URL_HEAD = /^data:.*,/;
1039  /**
1040   * Transform Data URL to array buffer.
1041   * @param {string} dataURL - The Data URL to transform.
1042   * @returns {ArrayBuffer} The result array buffer.
1043   */
1044
1045  function dataURLToArrayBuffer(dataURL) {
1046    var base64 = dataURL.replace(REGEXP_DATA_URL_HEAD, '');
1047    var binary = atob(base64);
1048    var arrayBuffer = new ArrayBuffer(binary.length);
1049    var uint8 = new Uint8Array(arrayBuffer);
1050    forEach(uint8, function (value, i) {
1051      uint8[i] = binary.charCodeAt(i);
1052    });
1053    return arrayBuffer;
1054  }
1055  /**
1056   * Transform array buffer to Data URL.
1057   * @param {ArrayBuffer} arrayBuffer - The array buffer to transform.
1058   * @param {string} mimeType - The mime type of the Data URL.
1059   * @returns {string} The result Data URL.
1060   */
1061
1062  function arrayBufferToDataURL(arrayBuffer, mimeType) {
1063    var chunks = []; // Chunk Typed Array for better performance (#435)
1064
1065    var chunkSize = 8192;
1066    var uint8 = new Uint8Array(arrayBuffer);
1067
1068    while (uint8.length > 0) {
1069      // XXX: Babel's `toConsumableArray` helper will throw error in IE or Safari 9
1070      // eslint-disable-next-line prefer-spread
1071      chunks.push(fromCharCode.apply(null, toArray(uint8.subarray(0, chunkSize))));
1072      uint8 = uint8.subarray(chunkSize);
1073    }
1074
1075    return "data:".concat(mimeType, ";base64,").concat(btoa(chunks.join('')));
1076  }
1077  /**
1078   * Get orientation value from given array buffer.
1079   * @param {ArrayBuffer} arrayBuffer - The array buffer to read.
1080   * @returns {number} The read orientation value.
1081   */
1082
1083  function resetAndGetOrientation(arrayBuffer) {
1084    var dataView = new DataView(arrayBuffer);
1085    var orientation; // Ignores range error when the image does not have correct Exif information
1086
1087    try {
1088      var littleEndian;
1089      var app1Start;
1090      var ifdStart; // Only handle JPEG image (start by 0xFFD8)
1091
1092      if (dataView.getUint8(0) === 0xFF && dataView.getUint8(1) === 0xD8) {
1093        var length = dataView.byteLength;
1094        var offset = 2;
1095
1096        while (offset + 1 < length) {
1097          if (dataView.getUint8(offset) === 0xFF && dataView.getUint8(offset + 1) === 0xE1) {
1098            app1Start = offset;
1099            break;
1100          }
1101
1102          offset += 1;
1103        }
1104      }
1105
1106      if (app1Start) {
1107        var exifIDCode = app1Start + 4;
1108        var tiffOffset = app1Start + 10;
1109
1110        if (getStringFromCharCode(dataView, exifIDCode, 4) === 'Exif') {
1111          var endianness = dataView.getUint16(tiffOffset);
1112          littleEndian = endianness === 0x4949;
1113
1114          if (littleEndian || endianness === 0x4D4D
1115          /* bigEndian */
1116          ) {
1117              if (dataView.getUint16(tiffOffset + 2, littleEndian) === 0x002A) {
1118                var firstIFDOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
1119
1120                if (firstIFDOffset >= 0x00000008) {
1121                  ifdStart = tiffOffset + firstIFDOffset;
1122                }
1123              }
1124            }
1125        }
1126      }
1127
1128      if (ifdStart) {
1129        var _length = dataView.getUint16(ifdStart, littleEndian);
1130
1131        var _offset;
1132
1133        var i;
1134
1135        for (i = 0; i < _length; i += 1) {
1136          _offset = ifdStart + i * 12 + 2;
1137
1138          if (dataView.getUint16(_offset, littleEndian) === 0x0112
1139          /* Orientation */
1140          ) {
1141              // 8 is the offset of the current tag's value
1142              _offset += 8; // Get the original orientation value
1143
1144              orientation = dataView.getUint16(_offset, littleEndian); // Override the orientation with its default value
1145
1146              dataView.setUint16(_offset, 1, littleEndian);
1147              break;
1148            }
1149        }
1150      }
1151    } catch (error) {
1152      orientation = 1;
1153    }
1154
1155    return orientation;
1156  }
1157  /**
1158   * Parse Exif Orientation value.
1159   * @param {number} orientation - The orientation to parse.
1160   * @returns {Object} The parsed result.
1161   */
1162
1163  function parseOrientation(orientation) {
1164    var rotate = 0;
1165    var scaleX = 1;
1166    var scaleY = 1;
1167
1168    switch (orientation) {
1169      // Flip horizontal
1170      case 2:
1171        scaleX = -1;
1172        break;
1173      // Rotate left 180°
1174
1175      case 3:
1176        rotate = -180;
1177        break;
1178      // Flip vertical
1179
1180      case 4:
1181        scaleY = -1;
1182        break;
1183      // Flip vertical and rotate right 90°
1184
1185      case 5:
1186        rotate = 90;
1187        scaleY = -1;
1188        break;
1189      // Rotate right 90°
1190
1191      case 6:
1192        rotate = 90;
1193        break;
1194      // Flip horizontal and rotate right 90°
1195
1196      case 7:
1197        rotate = 90;
1198        scaleX = -1;
1199        break;
1200      // Rotate left 90°
1201
1202      case 8:
1203        rotate = -90;
1204        break;
1205    }
1206
1207    return {
1208      rotate: rotate,
1209      scaleX: scaleX,
1210      scaleY: scaleY
1211    };
1212  }
1213
1214  var render = {
1215    render: function render() {
1216      this.initContainer();
1217      this.initCanvas();
1218      this.initCropBox();
1219      this.renderCanvas();
1220
1221      if (this.cropped) {
1222        this.renderCropBox();
1223      }
1224    },
1225    initContainer: function initContainer() {
1226      var element = this.element,
1227          options = this.options,
1228          container = this.container,
1229          cropper = this.cropper;
1230      var minWidth = Number(options.minContainerWidth);
1231      var minHeight = Number(options.minContainerHeight);
1232      addClass(cropper, CLASS_HIDDEN);
1233      removeClass(element, CLASS_HIDDEN);
1234      var containerData = {
1235        width: Math.max(container.offsetWidth, minWidth >= 0 ? minWidth : MIN_CONTAINER_WIDTH),
1236        height: Math.max(container.offsetHeight, minHeight >= 0 ? minHeight : MIN_CONTAINER_HEIGHT)
1237      };
1238      this.containerData = containerData;
1239      setStyle(cropper, {
1240        width: containerData.width,
1241        height: containerData.height
1242      });
1243      addClass(element, CLASS_HIDDEN);
1244      removeClass(cropper, CLASS_HIDDEN);
1245    },
1246    // Canvas (image wrapper)
1247    initCanvas: function initCanvas() {
1248      var containerData = this.containerData,
1249          imageData = this.imageData;
1250      var viewMode = this.options.viewMode;
1251      var rotated = Math.abs(imageData.rotate) % 180 === 90;
1252      var naturalWidth = rotated ? imageData.naturalHeight : imageData.naturalWidth;
1253      var naturalHeight = rotated ? imageData.naturalWidth : imageData.naturalHeight;
1254      var aspectRatio = naturalWidth / naturalHeight;
1255      var canvasWidth = containerData.width;
1256      var canvasHeight = containerData.height;
1257
1258      if (containerData.height * aspectRatio > containerData.width) {
1259        if (viewMode === 3) {
1260          canvasWidth = containerData.height * aspectRatio;
1261        } else {
1262          canvasHeight = containerData.width / aspectRatio;
1263        }
1264      } else if (viewMode === 3) {
1265        canvasHeight = containerData.width / aspectRatio;
1266      } else {
1267        canvasWidth = containerData.height * aspectRatio;
1268      }
1269
1270      var canvasData = {
1271        aspectRatio: aspectRatio,
1272        naturalWidth: naturalWidth,
1273        naturalHeight: naturalHeight,
1274        width: canvasWidth,
1275        height: canvasHeight
1276      };
1277      this.canvasData = canvasData;
1278      this.limited = viewMode === 1 || viewMode === 2;
1279      this.limitCanvas(true, true);
1280      canvasData.width = Math.min(Math.max(canvasData.width, canvasData.minWidth), canvasData.maxWidth);
1281      canvasData.height = Math.min(Math.max(canvasData.height, canvasData.minHeight), canvasData.maxHeight);
1282      canvasData.left = (containerData.width - canvasData.width) / 2;
1283      canvasData.top = (containerData.height - canvasData.height) / 2;
1284      canvasData.oldLeft = canvasData.left;
1285      canvasData.oldTop = canvasData.top;
1286      this.initialCanvasData = assign({}, canvasData);
1287    },
1288    limitCanvas: function limitCanvas(sizeLimited, positionLimited) {
1289      var options = this.options,
1290          containerData = this.containerData,
1291          canvasData = this.canvasData,
1292          cropBoxData = this.cropBoxData;
1293      var viewMode = options.viewMode;
1294      var aspectRatio = canvasData.aspectRatio;
1295      var cropped = this.cropped && cropBoxData;
1296
1297      if (sizeLimited) {
1298        var minCanvasWidth = Number(options.minCanvasWidth) || 0;
1299        var minCanvasHeight = Number(options.minCanvasHeight) || 0;
1300
1301        if (viewMode > 1) {
1302          minCanvasWidth = Math.max(minCanvasWidth, containerData.width);
1303          minCanvasHeight = Math.max(minCanvasHeight, containerData.height);
1304
1305          if (viewMode === 3) {
1306            if (minCanvasHeight * aspectRatio > minCanvasWidth) {
1307              minCanvasWidth = minCanvasHeight * aspectRatio;
1308            } else {
1309              minCanvasHeight = minCanvasWidth / aspectRatio;
1310            }
1311          }
1312        } else if (viewMode > 0) {
1313          if (minCanvasWidth) {
1314            minCanvasWidth = Math.max(minCanvasWidth, cropped ? cropBoxData.width : 0);
1315          } else if (minCanvasHeight) {
1316            minCanvasHeight = Math.max(minCanvasHeight, cropped ? cropBoxData.height : 0);
1317          } else if (cropped) {
1318            minCanvasWidth = cropBoxData.width;
1319            minCanvasHeight = cropBoxData.height;
1320
1321            if (minCanvasHeight * aspectRatio > minCanvasWidth) {
1322              minCanvasWidth = minCanvasHeight * aspectRatio;
1323            } else {
1324              minCanvasHeight = minCanvasWidth / aspectRatio;
1325            }
1326          }
1327        }
1328
1329        var _getAdjustedSizes = getAdjustedSizes({
1330          aspectRatio: aspectRatio,
1331          width: minCanvasWidth,
1332          height: minCanvasHeight
1333        });
1334
1335        minCanvasWidth = _getAdjustedSizes.width;
1336        minCanvasHeight = _getAdjustedSizes.height;
1337        canvasData.minWidth = minCanvasWidth;
1338        canvasData.minHeight = minCanvasHeight;
1339        canvasData.maxWidth = Infinity;
1340        canvasData.maxHeight = Infinity;
1341      }
1342
1343      if (positionLimited) {
1344        if (viewMode > (cropped ? 0 : 1)) {
1345          var newCanvasLeft = containerData.width - canvasData.width;
1346          var newCanvasTop = containerData.height - canvasData.height;
1347          canvasData.minLeft = Math.min(0, newCanvasLeft);
1348          canvasData.minTop = Math.min(0, newCanvasTop);
1349          canvasData.maxLeft = Math.max(0, newCanvasLeft);
1350          canvasData.maxTop = Math.max(0, newCanvasTop);
1351
1352          if (cropped && this.limited) {
1353            canvasData.minLeft = Math.min(cropBoxData.left, cropBoxData.left + (cropBoxData.width - canvasData.width));
1354            canvasData.minTop = Math.min(cropBoxData.top, cropBoxData.top + (cropBoxData.height - canvasData.height));
1355            canvasData.maxLeft = cropBoxData.left;
1356            canvasData.maxTop = cropBoxData.top;
1357
1358            if (viewMode === 2) {
1359              if (canvasData.width >= containerData.width) {
1360                canvasData.minLeft = Math.min(0, newCanvasLeft);
1361                canvasData.maxLeft = Math.max(0, newCanvasLeft);
1362              }
1363
1364              if (canvasData.height >= containerData.height) {
1365                canvasData.minTop = Math.min(0, newCanvasTop);
1366                canvasData.maxTop = Math.max(0, newCanvasTop);
1367              }
1368            }
1369          }
1370        } else {
1371          canvasData.minLeft = -canvasData.width;
1372          canvasData.minTop = -canvasData.height;
1373          canvasData.maxLeft = containerData.width;
1374          canvasData.maxTop = containerData.height;
1375        }
1376      }
1377    },
1378    renderCanvas: function renderCanvas(changed, transformed) {
1379      var canvasData = this.canvasData,
1380          imageData = this.imageData;
1381
1382      if (transformed) {
1383        var _getRotatedSizes = getRotatedSizes({
1384          width: imageData.naturalWidth * Math.abs(imageData.scaleX || 1),
1385          height: imageData.naturalHeight * Math.abs(imageData.scaleY || 1),
1386          degree: imageData.rotate || 0
1387        }),
1388            naturalWidth = _getRotatedSizes.width,
1389            naturalHeight = _getRotatedSizes.height;
1390
1391        var width = canvasData.width * (naturalWidth / canvasData.naturalWidth);
1392        var height = canvasData.height * (naturalHeight / canvasData.naturalHeight);
1393        canvasData.left -= (width - canvasData.width) / 2;
1394        canvasData.top -= (height - canvasData.height) / 2;
1395        canvasData.width = width;
1396        canvasData.height = height;
1397        canvasData.aspectRatio = naturalWidth / naturalHeight;
1398        canvasData.naturalWidth = naturalWidth;
1399        canvasData.naturalHeight = naturalHeight;
1400        this.limitCanvas(true, false);
1401      }
1402
1403      if (canvasData.width > canvasData.maxWidth || canvasData.width < canvasData.minWidth) {
1404        canvasData.left = canvasData.oldLeft;
1405      }
1406
1407      if (canvasData.height > canvasData.maxHeight || canvasData.height < canvasData.minHeight) {
1408        canvasData.top = canvasData.oldTop;
1409      }
1410
1411      canvasData.width = Math.min(Math.max(canvasData.width, canvasData.minWidth), canvasData.maxWidth);
1412      canvasData.height = Math.min(Math.max(canvasData.height, canvasData.minHeight), canvasData.maxHeight);
1413      this.limitCanvas(false, true);
1414      canvasData.left = Math.min(Math.max(canvasData.left, canvasData.minLeft), canvasData.maxLeft);
1415      canvasData.top = Math.min(Math.max(canvasData.top, canvasData.minTop), canvasData.maxTop);
1416      canvasData.oldLeft = canvasData.left;
1417      canvasData.oldTop = canvasData.top;
1418      setStyle(this.canvas, assign({
1419        width: canvasData.width,
1420        height: canvasData.height
1421      }, getTransforms({
1422        translateX: canvasData.left,
1423        translateY: canvasData.top
1424      })));
1425      this.renderImage(changed);
1426
1427      if (this.cropped && this.limited) {
1428        this.limitCropBox(true, true);
1429      }
1430    },
1431    renderImage: function renderImage(changed) {
1432      var canvasData = this.canvasData,
1433          imageData = this.imageData;
1434      var width = imageData.naturalWidth * (canvasData.width / canvasData.naturalWidth);
1435      var height = imageData.naturalHeight * (canvasData.height / canvasData.naturalHeight);
1436      assign(imageData, {
1437        width: width,
1438        height: height,
1439        left: (canvasData.width - width) / 2,
1440        top: (canvasData.height - height) / 2
1441      });
1442      setStyle(this.image, assign({
1443        width: imageData.width,
1444        height: imageData.height
1445      }, getTransforms(assign({
1446        translateX: imageData.left,
1447        translateY: imageData.top
1448      }, imageData))));
1449
1450      if (changed) {
1451        this.output();
1452      }
1453    },
1454    initCropBox: function initCropBox() {
1455      var options = this.options,
1456          canvasData = this.canvasData;
1457      var aspectRatio = options.aspectRatio || options.initialAspectRatio;
1458      var autoCropArea = Number(options.autoCropArea) || 0.8;
1459      var cropBoxData = {
1460        width: canvasData.width,
1461        height: canvasData.height
1462      };
1463
1464      if (aspectRatio) {
1465        if (canvasData.height * aspectRatio > canvasData.width) {
1466          cropBoxData.height = cropBoxData.width / aspectRatio;
1467        } else {
1468          cropBoxData.width = cropBoxData.height * aspectRatio;
1469        }
1470      }
1471
1472      this.cropBoxData = cropBoxData;
1473      this.limitCropBox(true, true); // Initialize auto crop area
1474
1475      cropBoxData.width = Math.min(Math.max(cropBoxData.width, cropBoxData.minWidth), cropBoxData.maxWidth);
1476      cropBoxData.height = Math.min(Math.max(cropBoxData.height, cropBoxData.minHeight), cropBoxData.maxHeight); // The width/height of auto crop area must large than "minWidth/Height"
1477
1478      cropBoxData.width = Math.max(cropBoxData.minWidth, cropBoxData.width * autoCropArea);
1479      cropBoxData.height = Math.max(cropBoxData.minHeight, cropBoxData.height * autoCropArea);
1480      cropBoxData.left = canvasData.left + (canvasData.width - cropBoxData.width) / 2;
1481      cropBoxData.top = canvasData.top + (canvasData.height - cropBoxData.height) / 2;
1482      cropBoxData.oldLeft = cropBoxData.left;
1483      cropBoxData.oldTop = cropBoxData.top;
1484      this.initialCropBoxData = assign({}, cropBoxData);
1485    },
1486    limitCropBox: function limitCropBox(sizeLimited, positionLimited) {
1487      var options = this.options,
1488          containerData = this.containerData,
1489          canvasData = this.canvasData,
1490          cropBoxData = this.cropBoxData,
1491          limited = this.limited;
1492      var aspectRatio = options.aspectRatio;
1493
1494      if (sizeLimited) {
1495        var minCropBoxWidth = Number(options.minCropBoxWidth) || 0;
1496        var minCropBoxHeight = Number(options.minCropBoxHeight) || 0;
1497        var maxCropBoxWidth = limited ? Math.min(containerData.width, canvasData.width, canvasData.width + canvasData.left, containerData.width - canvasData.left) : containerData.width;
1498        var maxCropBoxHeight = limited ? Math.min(containerData.height, canvasData.height, canvasData.height + canvasData.top, containerData.height - canvasData.top) : containerData.height; // The min/maxCropBoxWidth/Height must be less than container's width/height
1499
1500        minCropBoxWidth = Math.min(minCropBoxWidth, containerData.width);
1501        minCropBoxHeight = Math.min(minCropBoxHeight, containerData.height);
1502
1503        if (aspectRatio) {
1504          if (minCropBoxWidth && minCropBoxHeight) {
1505            if (minCropBoxHeight * aspectRatio > minCropBoxWidth) {
1506              minCropBoxHeight = minCropBoxWidth / aspectRatio;
1507            } else {
1508              minCropBoxWidth = minCropBoxHeight * aspectRatio;
1509            }
1510          } else if (minCropBoxWidth) {
1511            minCropBoxHeight = minCropBoxWidth / aspectRatio;
1512          } else if (minCropBoxHeight) {
1513            minCropBoxWidth = minCropBoxHeight * aspectRatio;
1514          }
1515
1516          if (maxCropBoxHeight * aspectRatio > maxCropBoxWidth) {
1517            maxCropBoxHeight = maxCropBoxWidth / aspectRatio;
1518          } else {
1519            maxCropBoxWidth = maxCropBoxHeight * aspectRatio;
1520          }
1521        } // The minWidth/Height must be less than maxWidth/Height
1522
1523
1524        cropBoxData.minWidth = Math.min(minCropBoxWidth, maxCropBoxWidth);
1525        cropBoxData.minHeight = Math.min(minCropBoxHeight, maxCropBoxHeight);
1526        cropBoxData.maxWidth = maxCropBoxWidth;
1527        cropBoxData.maxHeight = maxCropBoxHeight;
1528      }
1529
1530      if (positionLimited) {
1531        if (limited) {
1532          cropBoxData.minLeft = Math.max(0, canvasData.left);
1533          cropBoxData.minTop = Math.max(0, canvasData.top);
1534          cropBoxData.maxLeft = Math.min(containerData.width, canvasData.left + canvasData.width) - cropBoxData.width;
1535          cropBoxData.maxTop = Math.min(containerData.height, canvasData.top + canvasData.height) - cropBoxData.height;
1536        } else {
1537          cropBoxData.minLeft = 0;
1538          cropBoxData.minTop = 0;
1539          cropBoxData.maxLeft = containerData.width - cropBoxData.width;
1540          cropBoxData.maxTop = containerData.height - cropBoxData.height;
1541        }
1542      }
1543    },
1544    renderCropBox: function renderCropBox() {
1545      var options = this.options,
1546          containerData = this.containerData,
1547          cropBoxData = this.cropBoxData;
1548
1549      if (cropBoxData.width > cropBoxData.maxWidth || cropBoxData.width < cropBoxData.minWidth) {
1550        cropBoxData.left = cropBoxData.oldLeft;
1551      }
1552
1553      if (cropBoxData.height > cropBoxData.maxHeight || cropBoxData.height < cropBoxData.minHeight) {
1554        cropBoxData.top = cropBoxData.oldTop;
1555      }
1556
1557      cropBoxData.width = Math.min(Math.max(cropBoxData.width, cropBoxData.minWidth), cropBoxData.maxWidth);
1558      cropBoxData.height = Math.min(Math.max(cropBoxData.height, cropBoxData.minHeight), cropBoxData.maxHeight);
1559      this.limitCropBox(false, true);
1560      cropBoxData.left = Math.min(Math.max(cropBoxData.left, cropBoxData.minLeft), cropBoxData.maxLeft);
1561      cropBoxData.top = Math.min(Math.max(cropBoxData.top, cropBoxData.minTop), cropBoxData.maxTop);
1562      cropBoxData.oldLeft = cropBoxData.left;
1563      cropBoxData.oldTop = cropBoxData.top;
1564
1565      if (options.movable && options.cropBoxMovable) {
1566        // Turn to move the canvas when the crop box is equal to the container
1567        setData(this.face, DATA_ACTION, cropBoxData.width >= containerData.width && cropBoxData.height >= containerData.height ? ACTION_MOVE : ACTION_ALL);
1568      }
1569
1570      setStyle(this.cropBox, assign({
1571        width: cropBoxData.width,
1572        height: cropBoxData.height
1573      }, getTransforms({
1574        translateX: cropBoxData.left,
1575        translateY: cropBoxData.top
1576      })));
1577
1578      if (this.cropped && this.limited) {
1579        this.limitCanvas(true, true);
1580      }
1581
1582      if (!this.disabled) {
1583        this.output();
1584      }
1585    },
1586    output: function output() {
1587      this.preview();
1588      dispatchEvent(this.element, EVENT_CROP, this.getData());
1589    }
1590  };
1591
1592  var preview = {
1593    initPreview: function initPreview() {
1594      var element = this.element,
1595          crossOrigin = this.crossOrigin;
1596      var preview = this.options.preview;
1597      var url = crossOrigin ? this.crossOriginUrl : this.url;
1598      var alt = element.alt || 'The image to preview';
1599      var image = document.createElement('img');
1600
1601      if (crossOrigin) {
1602        image.crossOrigin = crossOrigin;
1603      }
1604
1605      image.src = url;
1606      image.alt = alt;
1607      this.viewBox.appendChild(image);
1608      this.viewBoxImage = image;
1609
1610      if (!preview) {
1611        return;
1612      }
1613
1614      var previews = preview;
1615
1616      if (typeof preview === 'string') {
1617        previews = element.ownerDocument.querySelectorAll(preview);
1618      } else if (preview.querySelector) {
1619        previews = [preview];
1620      }
1621
1622      this.previews = previews;
1623      forEach(previews, function (el) {
1624        var img = document.createElement('img'); // Save the original size for recover
1625
1626        setData(el, DATA_PREVIEW, {
1627          width: el.offsetWidth,
1628          height: el.offsetHeight,
1629          html: el.innerHTML
1630        });
1631
1632        if (crossOrigin) {
1633          img.crossOrigin = crossOrigin;
1634        }
1635
1636        img.src = url;
1637        img.alt = alt;
1638        /**
1639         * Override img element styles
1640         * Add `display:block` to avoid margin top issue
1641         * Add `height:auto` to override `height` attribute on IE8
1642         * (Occur only when margin-top <= -height)
1643         */
1644
1645        img.style.cssText = 'display:block;' + 'width:100%;' + 'height:auto;' + 'min-width:0!important;' + 'min-height:0!important;' + 'max-width:none!important;' + 'max-height:none!important;' + 'image-orientation:0deg!important;"';
1646        el.innerHTML = '';
1647        el.appendChild(img);
1648      });
1649    },
1650    resetPreview: function resetPreview() {
1651      forEach(this.previews, function (element) {
1652        var data = getData(element, DATA_PREVIEW);
1653        setStyle(element, {
1654          width: data.width,
1655          height: data.height
1656        });
1657        element.innerHTML = data.html;
1658        removeData(element, DATA_PREVIEW);
1659      });
1660    },
1661    preview: function preview() {
1662      var imageData = this.imageData,
1663          canvasData = this.canvasData,
1664          cropBoxData = this.cropBoxData;
1665      var cropBoxWidth = cropBoxData.width,
1666          cropBoxHeight = cropBoxData.height;
1667      var width = imageData.width,
1668          height = imageData.height;
1669      var left = cropBoxData.left - canvasData.left - imageData.left;
1670      var top = cropBoxData.top - canvasData.top - imageData.top;
1671
1672      if (!this.cropped || this.disabled) {
1673        return;
1674      }
1675
1676      setStyle(this.viewBoxImage, assign({
1677        width: width,
1678        height: height
1679      }, getTransforms(assign({
1680        translateX: -left,
1681        translateY: -top
1682      }, imageData))));
1683      forEach(this.previews, function (element) {
1684        var data = getData(element, DATA_PREVIEW);
1685        var originalWidth = data.width;
1686        var originalHeight = data.height;
1687        var newWidth = originalWidth;
1688        var newHeight = originalHeight;
1689        var ratio = 1;
1690
1691        if (cropBoxWidth) {
1692          ratio = originalWidth / cropBoxWidth;
1693          newHeight = cropBoxHeight * ratio;
1694        }
1695
1696        if (cropBoxHeight && newHeight > originalHeight) {
1697          ratio = originalHeight / cropBoxHeight;
1698          newWidth = cropBoxWidth * ratio;
1699          newHeight = originalHeight;
1700        }
1701
1702        setStyle(element, {
1703          width: newWidth,
1704          height: newHeight
1705        });
1706        setStyle(element.getElementsByTagName('img')[0], assign({
1707          width: width * ratio,
1708          height: height * ratio
1709        }, getTransforms(assign({
1710          translateX: -left * ratio,
1711          translateY: -top * ratio
1712        }, imageData))));
1713      });
1714    }
1715  };
1716
1717  var events = {
1718    bind: function bind() {
1719      var element = this.element,
1720          options = this.options,
1721          cropper = this.cropper;
1722
1723      if (isFunction(options.cropstart)) {
1724        addListener(element, EVENT_CROP_START, options.cropstart);
1725      }
1726
1727      if (isFunction(options.cropmove)) {
1728        addListener(element, EVENT_CROP_MOVE, options.cropmove);
1729      }
1730
1731      if (isFunction(options.cropend)) {
1732        addListener(element, EVENT_CROP_END, options.cropend);
1733      }
1734
1735      if (isFunction(options.crop)) {
1736        addListener(element, EVENT_CROP, options.crop);
1737      }
1738
1739      if (isFunction(options.zoom)) {
1740        addListener(element, EVENT_ZOOM, options.zoom);
1741      }
1742
1743      addListener(cropper, EVENT_POINTER_DOWN, this.onCropStart = this.cropStart.bind(this));
1744
1745      if (options.zoomable && options.zoomOnWheel) {
1746        addListener(cropper, EVENT_WHEEL, this.onWheel = this.wheel.bind(this), {
1747          passive: false,
1748          capture: true
1749        });
1750      }
1751
1752      if (options.toggleDragModeOnDblclick) {
1753        addListener(cropper, EVENT_DBLCLICK, this.onDblclick = this.dblclick.bind(this));
1754      }
1755
1756      addListener(element.ownerDocument, EVENT_POINTER_MOVE, this.onCropMove = this.cropMove.bind(this));
1757      addListener(element.ownerDocument, EVENT_POINTER_UP, this.onCropEnd = this.cropEnd.bind(this));
1758
1759      if (options.responsive) {
1760        addListener(window, EVENT_RESIZE, this.onResize = this.resize.bind(this));
1761      }
1762    },
1763    unbind: function unbind() {
1764      var element = this.element,
1765          options = this.options,
1766          cropper = this.cropper;
1767
1768      if (isFunction(options.cropstart)) {
1769        removeListener(element, EVENT_CROP_START, options.cropstart);
1770      }
1771
1772      if (isFunction(options.cropmove)) {
1773        removeListener(element, EVENT_CROP_MOVE, options.cropmove);
1774      }
1775
1776      if (isFunction(options.cropend)) {
1777        removeListener(element, EVENT_CROP_END, options.cropend);
1778      }
1779
1780      if (isFunction(options.crop)) {
1781        removeListener(element, EVENT_CROP, options.crop);
1782      }
1783
1784      if (isFunction(options.zoom)) {
1785        removeListener(element, EVENT_ZOOM, options.zoom);
1786      }
1787
1788      removeListener(cropper, EVENT_POINTER_DOWN, this.onCropStart);
1789
1790      if (options.zoomable && options.zoomOnWheel) {
1791        removeListener(cropper, EVENT_WHEEL, this.onWheel, {
1792          passive: false,
1793          capture: true
1794        });
1795      }
1796
1797      if (options.toggleDragModeOnDblclick) {
1798        removeListener(cropper, EVENT_DBLCLICK, this.onDblclick);
1799      }
1800
1801      removeListener(element.ownerDocument, EVENT_POINTER_MOVE, this.onCropMove);
1802      removeListener(element.ownerDocument, EVENT_POINTER_UP, this.onCropEnd);
1803
1804      if (options.responsive) {
1805        removeListener(window, EVENT_RESIZE, this.onResize);
1806      }
1807    }
1808  };
1809
1810  var handlers = {
1811    resize: function resize() {
1812      if (this.disabled) {
1813        return;
1814      }
1815
1816      var options = this.options,
1817          container = this.container,
1818          containerData = this.containerData;
1819      var ratio = container.offsetWidth / containerData.width; // Resize when width changed or height changed
1820
1821      if (ratio !== 1 || container.offsetHeight !== containerData.height) {
1822        var canvasData;
1823        var cropBoxData;
1824
1825        if (options.restore) {
1826          canvasData = this.getCanvasData();
1827          cropBoxData = this.getCropBoxData();
1828        }
1829
1830        this.render();
1831
1832        if (options.restore) {
1833          this.setCanvasData(forEach(canvasData, function (n, i) {
1834            canvasData[i] = n * ratio;
1835          }));
1836          this.setCropBoxData(forEach(cropBoxData, function (n, i) {
1837            cropBoxData[i] = n * ratio;
1838          }));
1839        }
1840      }
1841    },
1842    dblclick: function dblclick() {
1843      if (this.disabled || this.options.dragMode === DRAG_MODE_NONE) {
1844        return;
1845      }
1846
1847      this.setDragMode(hasClass(this.dragBox, CLASS_CROP) ? DRAG_MODE_MOVE : DRAG_MODE_CROP);
1848    },
1849    wheel: function wheel(event) {
1850      var _this = this;
1851
1852      var ratio = Number(this.options.wheelZoomRatio) || 0.1;
1853      var delta = 1;
1854
1855      if (this.disabled) {
1856        return;
1857      }
1858
1859      event.preventDefault(); // Limit wheel speed to prevent zoom too fast (#21)
1860
1861      if (this.wheeling) {
1862        return;
1863      }
1864
1865      this.wheeling = true;
1866      setTimeout(function () {
1867        _this.wheeling = false;
1868      }, 50);
1869
1870      if (event.deltaY) {
1871        delta = event.deltaY > 0 ? 1 : -1;
1872      } else if (event.wheelDelta) {
1873        delta = -event.wheelDelta / 120;
1874      } else if (event.detail) {
1875        delta = event.detail > 0 ? 1 : -1;
1876      }
1877
1878      this.zoom(-delta * ratio, event);
1879    },
1880    cropStart: function cropStart(event) {
1881      var buttons = event.buttons,
1882          button = event.button;
1883
1884      if (this.disabled // Handle mouse event and pointer event and ignore touch event
1885      || (event.type === 'mousedown' || event.type === 'pointerdown' && event.pointerType === 'mouse') && ( // No primary button (Usually the left button)
1886      isNumber(buttons) && buttons !== 1 || isNumber(button) && button !== 0 // Open context menu
1887      || event.ctrlKey)) {
1888        return;
1889      }
1890
1891      var options = this.options,
1892          pointers = this.pointers;
1893      var action;
1894
1895      if (event.changedTouches) {
1896        // Handle touch event
1897        forEach(event.changedTouches, function (touch) {
1898          pointers[touch.identifier] = getPointer(touch);
1899        });
1900      } else {
1901        // Handle mouse event and pointer event
1902        pointers[event.pointerId || 0] = getPointer(event);
1903      }
1904
1905      if (Object.keys(pointers).length > 1 && options.zoomable && options.zoomOnTouch) {
1906        action = ACTION_ZOOM;
1907      } else {
1908        action = getData(event.target, DATA_ACTION);
1909      }
1910
1911      if (!REGEXP_ACTIONS.test(action)) {
1912        return;
1913      }
1914
1915      if (dispatchEvent(this.element, EVENT_CROP_START, {
1916        originalEvent: event,
1917        action: action
1918      }) === false) {
1919        return;
1920      } // This line is required for preventing page zooming in iOS browsers
1921
1922
1923      event.preventDefault();
1924      this.action = action;
1925      this.cropping = false;
1926
1927      if (action === ACTION_CROP) {
1928        this.cropping = true;
1929        addClass(this.dragBox, CLASS_MODAL);
1930      }
1931    },
1932    cropMove: function cropMove(event) {
1933      var action = this.action;
1934
1935      if (this.disabled || !action) {
1936        return;
1937      }
1938
1939      var pointers = this.pointers;
1940      event.preventDefault();
1941
1942      if (dispatchEvent(this.element, EVENT_CROP_MOVE, {
1943        originalEvent: event,
1944        action: action
1945      }) === false) {
1946        return;
1947      }
1948
1949      if (event.changedTouches) {
1950        forEach(event.changedTouches, function (touch) {
1951          // The first parameter should not be undefined (#432)
1952          assign(pointers[touch.identifier] || {}, getPointer(touch, true));
1953        });
1954      } else {
1955        assign(pointers[event.pointerId || 0] || {}, getPointer(event, true));
1956      }
1957
1958      this.change(event);
1959    },
1960    cropEnd: function cropEnd(event) {
1961      if (this.disabled) {
1962        return;
1963      }
1964
1965      var action = this.action,
1966          pointers = this.pointers;
1967
1968      if (event.changedTouches) {
1969        forEach(event.changedTouches, function (touch) {
1970          delete pointers[touch.identifier];
1971        });
1972      } else {
1973        delete pointers[event.pointerId || 0];
1974      }
1975
1976      if (!action) {
1977        return;
1978      }
1979
1980      event.preventDefault();
1981
1982      if (!Object.keys(pointers).length) {
1983        this.action = '';
1984      }
1985
1986      if (this.cropping) {
1987        this.cropping = false;
1988        toggleClass(this.dragBox, CLASS_MODAL, this.cropped && this.options.modal);
1989      }
1990
1991      dispatchEvent(this.element, EVENT_CROP_END, {
1992        originalEvent: event,
1993        action: action
1994      });
1995    }
1996  };
1997
1998  var change = {
1999    change: function change(event) {
2000      var options = this.options,
2001          canvasData = this.canvasData,
2002          containerData = this.containerData,
2003          cropBoxData = this.cropBoxData,
2004          pointers = this.pointers;
2005      var action = this.action;
2006      var aspectRatio = options.aspectRatio;
2007      var left = cropBoxData.left,
2008          top = cropBoxData.top,
2009          width = cropBoxData.width,
2010          height = cropBoxData.height;
2011      var right = left + width;
2012      var bottom = top + height;
2013      var minLeft = 0;
2014      var minTop = 0;
2015      var maxWidth = containerData.width;
2016      var maxHeight = containerData.height;
2017      var renderable = true;
2018      var offset; // Locking aspect ratio in "free mode" by holding shift key
2019
2020      if (!aspectRatio && event.shiftKey) {
2021        aspectRatio = width && height ? width / height : 1;
2022      }
2023
2024      if (this.limited) {
2025        minLeft = cropBoxData.minLeft;
2026        minTop = cropBoxData.minTop;
2027        maxWidth = minLeft + Math.min(containerData.width, canvasData.width, canvasData.left + canvasData.width);
2028        maxHeight = minTop + Math.min(containerData.height, canvasData.height, canvasData.top + canvasData.height);
2029      }
2030
2031      var pointer = pointers[Object.keys(pointers)[0]];
2032      var range = {
2033        x: pointer.endX - pointer.startX,
2034        y: pointer.endY - pointer.startY
2035      };
2036
2037      var check = function check(side) {
2038        switch (side) {
2039          case ACTION_EAST:
2040            if (right + range.x > maxWidth) {
2041              range.x = maxWidth - right;
2042            }
2043
2044            break;
2045
2046          case ACTION_WEST:
2047            if (left + range.x < minLeft) {
2048              range.x = minLeft - left;
2049            }
2050
2051            break;
2052
2053          case ACTION_NORTH:
2054            if (top + range.y < minTop) {
2055              range.y = minTop - top;
2056            }
2057
2058            break;
2059
2060          case ACTION_SOUTH:
2061            if (bottom + range.y > maxHeight) {
2062              range.y = maxHeight - bottom;
2063            }
2064
2065            break;
2066        }
2067      };
2068
2069      switch (action) {
2070        // Move crop box
2071        case ACTION_ALL:
2072          left += range.x;
2073          top += range.y;
2074          break;
2075        // Resize crop box
2076
2077        case ACTION_EAST:
2078          if (range.x >= 0 && (right >= maxWidth || aspectRatio && (top <= minTop || bottom >= maxHeight))) {
2079            renderable = false;
2080            break;
2081          }
2082
2083          check(ACTION_EAST);
2084          width += range.x;
2085
2086          if (width < 0) {
2087            action = ACTION_WEST;
2088            width = -width;
2089            left -= width;
2090          }
2091
2092          if (aspectRatio) {
2093            height = width / aspectRatio;
2094            top += (cropBoxData.height - height) / 2;
2095          }
2096
2097          break;
2098
2099        case ACTION_NORTH:
2100          if (range.y <= 0 && (top <= minTop || aspectRatio && (left <= minLeft || right >= maxWidth))) {
2101            renderable = false;
2102            break;
2103          }
2104
2105          check(ACTION_NORTH);
2106          height -= range.y;
2107          top += range.y;
2108
2109          if (height < 0) {
2110            action = ACTION_SOUTH;
2111            height = -height;
2112            top -= height;
2113          }
2114
2115          if (aspectRatio) {
2116            width = height * aspectRatio;
2117            left += (cropBoxData.width - width) / 2;
2118          }
2119
2120          break;
2121
2122        case ACTION_WEST:
2123          if (range.x <= 0 && (left <= minLeft || aspectRatio && (top <= minTop || bottom >= maxHeight))) {
2124            renderable = false;
2125            break;
2126          }
2127
2128          check(ACTION_WEST);
2129          width -= range.x;
2130          left += range.x;
2131
2132          if (width < 0) {
2133            action = ACTION_EAST;
2134            width = -width;
2135            left -= width;
2136          }
2137
2138          if (aspectRatio) {
2139            height = width / aspectRatio;
2140            top += (cropBoxData.height - height) / 2;
2141          }
2142
2143          break;
2144
2145        case ACTION_SOUTH:
2146          if (range.y >= 0 && (bottom >= maxHeight || aspectRatio && (left <= minLeft || right >= maxWidth))) {
2147            renderable = false;
2148            break;
2149          }
2150
2151          check(ACTION_SOUTH);
2152          height += range.y;
2153
2154          if (height < 0) {
2155            action = ACTION_NORTH;
2156            height = -height;
2157            top -= height;
2158          }
2159
2160          if (aspectRatio) {
2161            width = height * aspectRatio;
2162            left += (cropBoxData.width - width) / 2;
2163          }
2164
2165          break;
2166
2167        case ACTION_NORTH_EAST:
2168          if (aspectRatio) {
2169            if (range.y <= 0 && (top <= minTop || right >= maxWidth)) {
2170              renderable = false;
2171              break;
2172            }
2173
2174            check(ACTION_NORTH);
2175            height -= range.y;
2176            top += range.y;
2177            width = height * aspectRatio;
2178          } else {
2179            check(ACTION_NORTH);
2180            check(ACTION_EAST);
2181
2182            if (range.x >= 0) {
2183              if (right < maxWidth) {
2184                width += range.x;
2185              } else if (range.y <= 0 && top <= minTop) {
2186                renderable = false;
2187              }
2188            } else {
2189              width += range.x;
2190            }
2191
2192            if (range.y <= 0) {
2193              if (top > minTop) {
2194                height -= range.y;
2195                top += range.y;
2196              }
2197            } else {
2198              height -= range.y;
2199              top += range.y;
2200            }
2201          }
2202
2203          if (width < 0 && height < 0) {
2204            action = ACTION_SOUTH_WEST;
2205            height = -height;
2206            width = -width;
2207            top -= height;
2208            left -= width;
2209          } else if (width < 0) {
2210            action = ACTION_NORTH_WEST;
2211            width = -width;
2212            left -= width;
2213          } else if (height < 0) {
2214            action = ACTION_SOUTH_EAST;
2215            height = -height;
2216            top -= height;
2217          }
2218
2219          break;
2220
2221        case ACTION_NORTH_WEST:
2222          if (aspectRatio) {
2223            if (range.y <= 0 && (top <= minTop || left <= minLeft)) {
2224              renderable = false;
2225              break;
2226            }
2227
2228            check(ACTION_NORTH);
2229            height -= range.y;
2230            top += range.y;
2231            width = height * aspectRatio;
2232            left += cropBoxData.width - width;
2233          } else {
2234            check(ACTION_NORTH);
2235            check(ACTION_WEST);
2236
2237            if (range.x <= 0) {
2238              if (left > minLeft) {
2239                width -= range.x;
2240                left += range.x;
2241              } else if (range.y <= 0 && top <= minTop) {
2242                renderable = false;
2243              }
2244            } else {
2245              width -= range.x;
2246              left += range.x;
2247            }
2248
2249            if (range.y <= 0) {
2250              if (top > minTop) {
2251                height -= range.y;
2252                top += range.y;
2253              }
2254            } else {
2255              height -= range.y;
2256              top += range.y;
2257            }
2258          }
2259
2260          if (width < 0 && height < 0) {
2261            action = ACTION_SOUTH_EAST;
2262            height = -height;
2263            width = -width;
2264            top -= height;
2265            left -= width;
2266          } else if (width < 0) {
2267            action = ACTION_NORTH_EAST;
2268            width = -width;
2269            left -= width;
2270          } else if (height < 0) {
2271            action = ACTION_SOUTH_WEST;
2272            height = -height;
2273            top -= height;
2274          }
2275
2276          break;
2277
2278        case ACTION_SOUTH_WEST:
2279          if (aspectRatio) {
2280            if (range.x <= 0 && (left <= minLeft || bottom >= maxHeight)) {
2281              renderable = false;
2282              break;
2283            }
2284
2285            check(ACTION_WEST);
2286            width -= range.x;
2287            left += range.x;
2288            height = width / aspectRatio;
2289          } else {
2290            check(ACTION_SOUTH);
2291            check(ACTION_WEST);
2292
2293            if (range.x <= 0) {
2294              if (left > minLeft) {
2295                width -= range.x;
2296                left += range.x;
2297              } else if (range.y >= 0 && bottom >= maxHeight) {
2298                renderable = false;
2299              }
2300            } else {
2301              width -= range.x;
2302              left += range.x;
2303            }
2304
2305            if (range.y >= 0) {
2306              if (bottom < maxHeight) {
2307                height += range.y;
2308              }
2309            } else {
2310              height += range.y;
2311            }
2312          }
2313
2314          if (width < 0 && height < 0) {
2315            action = ACTION_NORTH_EAST;
2316            height = -height;
2317            width = -width;
2318            top -= height;
2319            left -= width;
2320          } else if (width < 0) {
2321            action = ACTION_SOUTH_EAST;
2322            width = -width;
2323            left -= width;
2324          } else if (height < 0) {
2325            action = ACTION_NORTH_WEST;
2326            height = -height;
2327            top -= height;
2328          }
2329
2330          break;
2331
2332        case ACTION_SOUTH_EAST:
2333          if (aspectRatio) {
2334            if (range.x >= 0 && (right >= maxWidth || bottom >= maxHeight)) {
2335              renderable = false;
2336              break;
2337            }
2338
2339            check(ACTION_EAST);
2340            width += range.x;
2341            height = width / aspectRatio;
2342          } else {
2343            check(ACTION_SOUTH);
2344            check(ACTION_EAST);
2345
2346            if (range.x >= 0) {
2347              if (right < maxWidth) {
2348                width += range.x;
2349              } else if (range.y >= 0 && bottom >= maxHeight) {
2350                renderable = false;
2351              }
2352            } else {
2353              width += range.x;
2354            }
2355
2356            if (range.y >= 0) {
2357              if (bottom < maxHeight) {
2358                height += range.y;
2359              }
2360            } else {
2361              height += range.y;
2362            }
2363          }
2364
2365          if (width < 0 && height < 0) {
2366            action = ACTION_NORTH_WEST;
2367            height = -height;
2368            width = -width;
2369            top -= height;
2370            left -= width;
2371          } else if (width < 0) {
2372            action = ACTION_SOUTH_WEST;
2373            width = -width;
2374            left -= width;
2375          } else if (height < 0) {
2376            action = ACTION_NORTH_EAST;
2377            height = -height;
2378            top -= height;
2379          }
2380
2381          break;
2382        // Move canvas
2383
2384        case ACTION_MOVE:
2385          this.move(range.x, range.y);
2386          renderable = false;
2387          break;
2388        // Zoom canvas
2389
2390        case ACTION_ZOOM:
2391          this.zoom(getMaxZoomRatio(pointers), event);
2392          renderable = false;
2393          break;
2394        // Create crop box
2395
2396        case ACTION_CROP:
2397          if (!range.x || !range.y) {
2398            renderable = false;
2399            break;
2400          }
2401
2402          offset = getOffset(this.cropper);
2403          left = pointer.startX - offset.left;
2404          top = pointer.startY - offset.top;
2405          width = cropBoxData.minWidth;
2406          height = cropBoxData.minHeight;
2407
2408          if (range.x > 0) {
2409            action = range.y > 0 ? ACTION_SOUTH_EAST : ACTION_NORTH_EAST;
2410          } else if (range.x < 0) {
2411            left -= width;
2412            action = range.y > 0 ? ACTION_SOUTH_WEST : ACTION_NORTH_WEST;
2413          }
2414
2415          if (range.y < 0) {
2416            top -= height;
2417          } // Show the crop box if is hidden
2418
2419
2420          if (!this.cropped) {
2421            removeClass(this.cropBox, CLASS_HIDDEN);
2422            this.cropped = true;
2423
2424            if (this.limited) {
2425              this.limitCropBox(true, true);
2426            }
2427          }
2428
2429          break;
2430      }
2431
2432      if (renderable) {
2433        cropBoxData.width = width;
2434        cropBoxData.height = height;
2435        cropBoxData.left = left;
2436        cropBoxData.top = top;
2437        this.action = action;
2438        this.renderCropBox();
2439      } // Override
2440
2441
2442      forEach(pointers, function (p) {
2443        p.startX = p.endX;
2444        p.startY = p.endY;
2445      });
2446    }
2447  };
2448
2449  var methods = {
2450    // Show the crop box manually
2451    crop: function crop() {
2452      if (this.ready && !this.cropped && !this.disabled) {
2453        this.cropped = true;
2454        this.limitCropBox(true, true);
2455
2456        if (this.options.modal) {
2457          addClass(this.dragBox, CLASS_MODAL);
2458        }
2459
2460        removeClass(this.cropBox, CLASS_HIDDEN);
2461        this.setCropBoxData(this.initialCropBoxData);
2462      }
2463
2464      return this;
2465    },
2466    // Reset the image and crop box to their initial states
2467    reset: function reset() {
2468      if (this.ready && !this.disabled) {
2469        this.imageData = assign({}, this.initialImageData);
2470        this.canvasData = assign({}, this.initialCanvasData);
2471        this.cropBoxData = assign({}, this.initialCropBoxData);
2472        this.renderCanvas();
2473
2474        if (this.cropped) {
2475          this.renderCropBox();
2476        }
2477      }
2478
2479      return this;
2480    },
2481    // Clear the crop box
2482    clear: function clear() {
2483      if (this.cropped && !this.disabled) {
2484        assign(this.cropBoxData, {
2485          left: 0,
2486          top: 0,
2487          width: 0,
2488          height: 0
2489        });
2490        this.cropped = false;
2491        this.renderCropBox();
2492        this.limitCanvas(true, true); // Render canvas after crop box rendered
2493
2494        this.renderCanvas();
2495        removeClass(this.dragBox, CLASS_MODAL);
2496        addClass(this.cropBox, CLASS_HIDDEN);
2497      }
2498
2499      return this;
2500    },
2501
2502    /**
2503     * Replace the image's src and rebuild the cropper
2504     * @param {string} url - The new URL.
2505     * @param {boolean} [hasSameSize] - Indicate if the new image has the same size as the old one.
2506     * @returns {Cropper} this
2507     */
2508    replace: function replace(url) {
2509      var hasSameSize = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
2510
2511      if (!this.disabled && url) {
2512        if (this.isImg) {
2513          this.element.src = url;
2514        }
2515
2516        if (hasSameSize) {
2517          this.url = url;
2518          this.image.src = url;
2519
2520          if (this.ready) {
2521            this.viewBoxImage.src = url;
2522            forEach(this.previews, function (element) {
2523              element.getElementsByTagName('img')[0].src = url;
2524            });
2525          }
2526        } else {
2527          if (this.isImg) {
2528            this.replaced = true;
2529          }
2530
2531          this.options.data = null;
2532          this.uncreate();
2533          this.load(url);
2534        }
2535      }
2536
2537      return this;
2538    },
2539    // Enable (unfreeze) the cropper
2540    enable: function enable() {
2541      if (this.ready && this.disabled) {
2542        this.disabled = false;
2543        removeClass(this.cropper, CLASS_DISABLED);
2544      }
2545
2546      return this;
2547    },
2548    // Disable (freeze) the cropper
2549    disable: function disable() {
2550      if (this.ready && !this.disabled) {
2551        this.disabled = true;
2552        addClass(this.cropper, CLASS_DISABLED);
2553      }
2554
2555      return this;
2556    },
2557
2558    /**
2559     * Destroy the cropper and remove the instance from the image
2560     * @returns {Cropper} this
2561     */
2562    destroy: function destroy() {
2563      var element = this.element;
2564
2565      if (!element[NAMESPACE]) {
2566        return this;
2567      }
2568
2569      element[NAMESPACE] = undefined;
2570
2571      if (this.isImg && this.replaced) {
2572        element.src = this.originalUrl;
2573      }
2574
2575      this.uncreate();
2576      return this;
2577    },
2578
2579    /**
2580     * Move the canvas with relative offsets
2581     * @param {number} offsetX - The relative offset distance on the x-axis.
2582     * @param {number} [offsetY=offsetX] - The relative offset distance on the y-axis.
2583     * @returns {Cropper} this
2584     */
2585    move: function move(offsetX) {
2586      var offsetY = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : offsetX;
2587      var _this$canvasData = this.canvasData,
2588          left = _this$canvasData.left,
2589          top = _this$canvasData.top;
2590      return this.moveTo(isUndefined(offsetX) ? offsetX : left + Number(offsetX), isUndefined(offsetY) ? offsetY : top + Number(offsetY));
2591    },
2592
2593    /**
2594     * Move the canvas to an absolute point
2595     * @param {number} x - The x-axis coordinate.
2596     * @param {number} [y=x] - The y-axis coordinate.
2597     * @returns {Cropper} this
2598     */
2599    moveTo: function moveTo(x) {
2600      var y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : x;
2601      var canvasData = this.canvasData;
2602      var changed = false;
2603      x = Number(x);
2604      y = Number(y);
2605
2606      if (this.ready && !this.disabled && this.options.movable) {
2607        if (isNumber(x)) {
2608          canvasData.left = x;
2609          changed = true;
2610        }
2611
2612        if (isNumber(y)) {
2613          canvasData.top = y;
2614          changed = true;
2615        }
2616
2617        if (changed) {
2618          this.renderCanvas(true);
2619        }
2620      }
2621
2622      return this;
2623    },
2624
2625    /**
2626     * Zoom the canvas with a relative ratio
2627     * @param {number} ratio - The target ratio.
2628     * @param {Event} _originalEvent - The original event if any.
2629     * @returns {Cropper} this
2630     */
2631    zoom: function zoom(ratio, _originalEvent) {
2632      var canvasData = this.canvasData;
2633      ratio = Number(ratio);
2634
2635      if (ratio < 0) {
2636        ratio = 1 / (1 - ratio);
2637      } else {
2638        ratio = 1 + ratio;
2639      }
2640
2641      return this.zoomTo(canvasData.width * ratio / canvasData.naturalWidth, null, _originalEvent);
2642    },
2643
2644    /**
2645     * Zoom the canvas to an absolute ratio
2646     * @param {number} ratio - The target ratio.
2647     * @param {Object} pivot - The zoom pivot point coordinate.
2648     * @param {Event} _originalEvent - The original event if any.
2649     * @returns {Cropper} this
2650     */
2651    zoomTo: function zoomTo(ratio, pivot, _originalEvent) {
2652      var options = this.options,
2653          canvasData = this.canvasData;
2654      var width = canvasData.width,
2655          height = canvasData.height,
2656          naturalWidth = canvasData.naturalWidth,
2657          naturalHeight = canvasData.naturalHeight;
2658      ratio = Number(ratio);
2659
2660      if (ratio >= 0 && this.ready && !this.disabled && options.zoomable) {
2661        var newWidth = naturalWidth * ratio;
2662        var newHeight = naturalHeight * ratio;
2663
2664        if (dispatchEvent(this.element, EVENT_ZOOM, {
2665          ratio: ratio,
2666          oldRatio: width / naturalWidth,
2667          originalEvent: _originalEvent
2668        }) === false) {
2669          return this;
2670        }
2671
2672        if (_originalEvent) {
2673          var pointers = this.pointers;
2674          var offset = getOffset(this.cropper);
2675          var center = pointers && Object.keys(pointers).length ? getPointersCenter(pointers) : {
2676            pageX: _originalEvent.pageX,
2677            pageY: _originalEvent.pageY
2678          }; // Zoom from the triggering point of the event
2679
2680          canvasData.left -= (newWidth - width) * ((center.pageX - offset.left - canvasData.left) / width);
2681          canvasData.top -= (newHeight - height) * ((center.pageY - offset.top - canvasData.top) / height);
2682        } else if (isPlainObject(pivot) && isNumber(pivot.x) && isNumber(pivot.y)) {
2683          canvasData.left -= (newWidth - width) * ((pivot.x - canvasData.left) / width);
2684          canvasData.top -= (newHeight - height) * ((pivot.y - canvasData.top) / height);
2685        } else {
2686          // Zoom from the center of the canvas
2687          canvasData.left -= (newWidth - width) / 2;
2688          canvasData.top -= (newHeight - height) / 2;
2689        }
2690
2691        canvasData.width = newWidth;
2692        canvasData.height = newHeight;
2693        this.renderCanvas(true);
2694      }
2695
2696      return this;
2697    },
2698
2699    /**
2700     * Rotate the canvas with a relative degree
2701     * @param {number} degree - The rotate degree.
2702     * @returns {Cropper} this
2703     */
2704    rotate: function rotate(degree) {
2705      return this.rotateTo((this.imageData.rotate || 0) + Number(degree));
2706    },
2707
2708    /**
2709     * Rotate the canvas to an absolute degree
2710     * @param {number} degree - The rotate degree.
2711     * @returns {Cropper} this
2712     */
2713    rotateTo: function rotateTo(degree) {
2714      degree = Number(degree);
2715
2716      if (isNumber(degree) && this.ready && !this.disabled && this.options.rotatable) {
2717        this.imageData.rotate = degree % 360;
2718        this.renderCanvas(true, true);
2719      }
2720
2721      return this;
2722    },
2723
2724    /**
2725     * Scale the image on the x-axis.
2726     * @param {number} scaleX - The scale ratio on the x-axis.
2727     * @returns {Cropper} this
2728     */
2729    scaleX: function scaleX(_scaleX) {
2730      var scaleY = this.imageData.scaleY;
2731      return this.scale(_scaleX, isNumber(scaleY) ? scaleY : 1);
2732    },
2733
2734    /**
2735     * Scale the image on the y-axis.
2736     * @param {number} scaleY - The scale ratio on the y-axis.
2737     * @returns {Cropper} this
2738     */
2739    scaleY: function scaleY(_scaleY) {
2740      var scaleX = this.imageData.scaleX;
2741      return this.scale(isNumber(scaleX) ? scaleX : 1, _scaleY);
2742    },
2743
2744    /**
2745     * Scale the image
2746     * @param {number} scaleX - The scale ratio on the x-axis.
2747     * @param {number} [scaleY=scaleX] - The scale ratio on the y-axis.
2748     * @returns {Cropper} this
2749     */
2750    scale: function scale(scaleX) {
2751      var scaleY = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : scaleX;
2752      var imageData = this.imageData;
2753      var transformed = false;
2754      scaleX = Number(scaleX);
2755      scaleY = Number(scaleY);
2756
2757      if (this.ready && !this.disabled && this.options.scalable) {
2758        if (isNumber(scaleX)) {
2759          imageData.scaleX = scaleX;
2760          transformed = true;
2761        }
2762
2763        if (isNumber(scaleY)) {
2764          imageData.scaleY = scaleY;
2765          transformed = true;
2766        }
2767
2768        if (transformed) {
2769          this.renderCanvas(true, true);
2770        }
2771      }
2772
2773      return this;
2774    },
2775
2776    /**
2777     * Get the cropped area position and size data (base on the original image)
2778     * @param {boolean} [rounded=false] - Indicate if round the data values or not.
2779     * @returns {Object} The result cropped data.
2780     */
2781    getData: function getData() {
2782      var rounded = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
2783      var options = this.options,
2784          imageData = this.imageData,
2785          canvasData = this.canvasData,
2786          cropBoxData = this.cropBoxData;
2787      var data;
2788
2789      if (this.ready && this.cropped) {
2790        data = {
2791          x: cropBoxData.left - canvasData.left,
2792          y: cropBoxData.top - canvasData.top,
2793          width: cropBoxData.width,
2794          height: cropBoxData.height
2795        };
2796        var ratio = imageData.width / imageData.naturalWidth;
2797        forEach(data, function (n, i) {
2798          data[i] = n / ratio;
2799        });
2800
2801        if (rounded) {
2802          // In case rounding off leads to extra 1px in right or bottom border
2803          // we should round the top-left corner and the dimension (#343).
2804          var bottom = Math.round(data.y + data.height);
2805          var right = Math.round(data.x + data.width);
2806          data.x = Math.round(data.x);
2807          data.y = Math.round(data.y);
2808          data.width = right - data.x;
2809          data.height = bottom - data.y;
2810        }
2811      } else {
2812        data = {
2813          x: 0,
2814          y: 0,
2815          width: 0,
2816          height: 0
2817        };
2818      }
2819
2820      if (options.rotatable) {
2821        data.rotate = imageData.rotate || 0;
2822      }
2823
2824      if (options.scalable) {
2825        data.scaleX = imageData.scaleX || 1;
2826        data.scaleY = imageData.scaleY || 1;
2827      }
2828
2829      return data;
2830    },
2831
2832    /**
2833     * Set the cropped area position and size with new data
2834     * @param {Object} data - The new data.
2835     * @returns {Cropper} this
2836     */
2837    setData: function setData(data) {
2838      var options = this.options,
2839          imageData = this.imageData,
2840          canvasData = this.canvasData;
2841      var cropBoxData = {};
2842
2843      if (this.ready && !this.disabled && isPlainObject(data)) {
2844        var transformed = false;
2845
2846        if (options.rotatable) {
2847          if (isNumber(data.rotate) && data.rotate !== imageData.rotate) {
2848            imageData.rotate = data.rotate;
2849            transformed = true;
2850          }
2851        }
2852
2853        if (options.scalable) {
2854          if (isNumber(data.scaleX) && data.scaleX !== imageData.scaleX) {
2855            imageData.scaleX = data.scaleX;
2856            transformed = true;
2857          }
2858
2859          if (isNumber(data.scaleY) && data.scaleY !== imageData.scaleY) {
2860            imageData.scaleY = data.scaleY;
2861            transformed = true;
2862          }
2863        }
2864
2865        if (transformed) {
2866          this.renderCanvas(true, true);
2867        }
2868
2869        var ratio = imageData.width / imageData.naturalWidth;
2870
2871        if (isNumber(data.x)) {
2872          cropBoxData.left = data.x * ratio + canvasData.left;
2873        }
2874
2875        if (isNumber(data.y)) {
2876          cropBoxData.top = data.y * ratio + canvasData.top;
2877        }
2878
2879        if (isNumber(data.width)) {
2880          cropBoxData.width = data.width * ratio;
2881        }
2882
2883        if (isNumber(data.height)) {
2884          cropBoxData.height = data.height * ratio;
2885        }
2886
2887        this.setCropBoxData(cropBoxData);
2888      }
2889
2890      return this;
2891    },
2892
2893    /**
2894     * Get the container size data.
2895     * @returns {Object} The result container data.
2896     */
2897    getContainerData: function getContainerData() {
2898      return this.ready ? assign({}, this.containerData) : {};
2899    },
2900
2901    /**
2902     * Get the image position and size data.
2903     * @returns {Object} The result image data.
2904     */
2905    getImageData: function getImageData() {
2906      return this.sized ? assign({}, this.imageData) : {};
2907    },
2908
2909    /**
2910     * Get the canvas position and size data.
2911     * @returns {Object} The result canvas data.
2912     */
2913    getCanvasData: function getCanvasData() {
2914      var canvasData = this.canvasData;
2915      var data = {};
2916
2917      if (this.ready) {
2918        forEach(['left', 'top', 'width', 'height', 'naturalWidth', 'naturalHeight'], function (n) {
2919          data[n] = canvasData[n];
2920        });
2921      }
2922
2923      return data;
2924    },
2925
2926    /**
2927     * Set the canvas position and size with new data.
2928     * @param {Object} data - The new canvas data.
2929     * @returns {Cropper} this
2930     */
2931    setCanvasData: function setCanvasData(data) {
2932      var canvasData = this.canvasData;
2933      var aspectRatio = canvasData.aspectRatio;
2934
2935      if (this.ready && !this.disabled && isPlainObject(data)) {
2936        if (isNumber(data.left)) {
2937          canvasData.left = data.left;
2938        }
2939
2940        if (isNumber(data.top)) {
2941          canvasData.top = data.top;
2942        }
2943
2944        if (isNumber(data.width)) {
2945          canvasData.width = data.width;
2946          canvasData.height = data.width / aspectRatio;
2947        } else if (isNumber(data.height)) {
2948          canvasData.height = data.height;
2949          canvasData.width = data.height * aspectRatio;
2950        }
2951
2952        this.renderCanvas(true);
2953      }
2954
2955      return this;
2956    },
2957
2958    /**
2959     * Get the crop box position and size data.
2960     * @returns {Object} The result crop box data.
2961     */
2962    getCropBoxData: function getCropBoxData() {
2963      var cropBoxData = this.cropBoxData;
2964      var data;
2965
2966      if (this.ready && this.cropped) {
2967        data = {
2968          left: cropBoxData.left,
2969          top: cropBoxData.top,
2970          width: cropBoxData.width,
2971          height: cropBoxData.height
2972        };
2973      }
2974
2975      return data || {};
2976    },
2977
2978    /**
2979     * Set the crop box position and size with new data.
2980     * @param {Object} data - The new crop box data.
2981     * @returns {Cropper} this
2982     */
2983    setCropBoxData: function setCropBoxData(data) {
2984      var cropBoxData = this.cropBoxData;
2985      var aspectRatio = this.options.aspectRatio;
2986      var widthChanged;
2987      var heightChanged;
2988
2989      if (this.ready && this.cropped && !this.disabled && isPlainObject(data)) {
2990        if (isNumber(data.left)) {
2991          cropBoxData.left = data.left;
2992        }
2993
2994        if (isNumber(data.top)) {
2995          cropBoxData.top = data.top;
2996        }
2997
2998        if (isNumber(data.width) && data.width !== cropBoxData.width) {
2999          widthChanged = true;
3000          cropBoxData.width = data.width;
3001        }
3002
3003        if (isNumber(data.height) && data.height !== cropBoxData.height) {
3004          heightChanged = true;
3005          cropBoxData.height = data.height;
3006        }
3007
3008        if (aspectRatio) {
3009          if (widthChanged) {
3010            cropBoxData.height = cropBoxData.width / aspectRatio;
3011          } else if (heightChanged) {
3012            cropBoxData.width = cropBoxData.height * aspectRatio;
3013          }
3014        }
3015
3016        this.renderCropBox();
3017      }
3018
3019      return this;
3020    },
3021
3022    /**
3023     * Get a canvas drawn the cropped image.
3024     * @param {Object} [options={}] - The config options.
3025     * @returns {HTMLCanvasElement} - The result canvas.
3026     */
3027    getCroppedCanvas: function getCroppedCanvas() {
3028      var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
3029
3030      if (!this.ready || !window.HTMLCanvasElement) {
3031        return null;
3032      }
3033
3034      var canvasData = this.canvasData;
3035      var source = getSourceCanvas(this.image, this.imageData, canvasData, options); // Returns the source canvas if it is not cropped.
3036
3037      if (!this.cropped) {
3038        return source;
3039      }
3040
3041      var _this$getData = this.getData(),
3042          initialX = _this$getData.x,
3043          initialY = _this$getData.y,
3044          initialWidth = _this$getData.width,
3045          initialHeight = _this$getData.height;
3046
3047      var ratio = source.width / Math.floor(canvasData.naturalWidth);
3048
3049      if (ratio !== 1) {
3050        initialX *= ratio;
3051        initialY *= ratio;
3052        initialWidth *= ratio;
3053        initialHeight *= ratio;
3054      }
3055
3056      var aspectRatio = initialWidth / initialHeight;
3057      var maxSizes = getAdjustedSizes({
3058        aspectRatio: aspectRatio,
3059        width: options.maxWidth || Infinity,
3060        height: options.maxHeight || Infinity
3061      });
3062      var minSizes = getAdjustedSizes({
3063        aspectRatio: aspectRatio,
3064        width: options.minWidth || 0,
3065        height: options.minHeight || 0
3066      }, 'cover');
3067
3068      var _getAdjustedSizes = getAdjustedSizes({
3069        aspectRatio: aspectRatio,
3070        width: options.width || (ratio !== 1 ? source.width : initialWidth),
3071        height: options.height || (ratio !== 1 ? source.height : initialHeight)
3072      }),
3073          width = _getAdjustedSizes.width,
3074          height = _getAdjustedSizes.height;
3075
3076      width = Math.min(maxSizes.width, Math.max(minSizes.width, width));
3077      height = Math.min(maxSizes.height, Math.max(minSizes.height, height));
3078      var canvas = document.createElement('canvas');
3079      var context = canvas.getContext('2d');
3080      canvas.width = normalizeDecimalNumber(width);
3081      canvas.height = normalizeDecimalNumber(height);
3082      context.fillStyle = options.fillColor || 'transparent';
3083      context.fillRect(0, 0, width, height);
3084      var _options$imageSmoothi = options.imageSmoothingEnabled,
3085          imageSmoothingEnabled = _options$imageSmoothi === void 0 ? true : _options$imageSmoothi,
3086          imageSmoothingQuality = options.imageSmoothingQuality;
3087      context.imageSmoothingEnabled = imageSmoothingEnabled;
3088
3089      if (imageSmoothingQuality) {
3090        context.imageSmoothingQuality = imageSmoothingQuality;
3091      } // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D.drawImage
3092
3093
3094      var sourceWidth = source.width;
3095      var sourceHeight = source.height; // Source canvas parameters
3096
3097      var srcX = initialX;
3098      var srcY = initialY;
3099      var srcWidth;
3100      var srcHeight; // Destination canvas parameters
3101
3102      var dstX;
3103      var dstY;
3104      var dstWidth;
3105      var dstHeight;
3106
3107      if (srcX <= -initialWidth || srcX > sourceWidth) {
3108        srcX = 0;
3109        srcWidth = 0;
3110        dstX = 0;
3111        dstWidth = 0;
3112      } else if (srcX <= 0) {
3113        dstX = -srcX;
3114        srcX = 0;
3115        srcWidth = Math.min(sourceWidth, initialWidth + srcX);
3116        dstWidth = srcWidth;
3117      } else if (srcX <= sourceWidth) {
3118        dstX = 0;
3119        srcWidth = Math.min(initialWidth, sourceWidth - srcX);
3120        dstWidth = srcWidth;
3121      }
3122
3123      if (srcWidth <= 0 || srcY <= -initialHeight || srcY > sourceHeight) {
3124        srcY = 0;
3125        srcHeight = 0;
3126        dstY = 0;
3127        dstHeight = 0;
3128      } else if (srcY <= 0) {
3129        dstY = -srcY;
3130        srcY = 0;
3131        srcHeight = Math.min(sourceHeight, initialHeight + srcY);
3132        dstHeight = srcHeight;
3133      } else if (srcY <= sourceHeight) {
3134        dstY = 0;
3135        srcHeight = Math.min(initialHeight, sourceHeight - srcY);
3136        dstHeight = srcHeight;
3137      }
3138
3139      var params = [srcX, srcY, srcWidth, srcHeight]; // Avoid "IndexSizeError"
3140
3141      if (dstWidth > 0 && dstHeight > 0) {
3142        var scale = width / initialWidth;
3143        params.push(dstX * scale, dstY * scale, dstWidth * scale, dstHeight * scale);
3144      } // All the numerical parameters should be integer for `drawImage`
3145      // https://github.com/fengyuanchen/cropper/issues/476
3146
3147
3148      context.drawImage.apply(context, [source].concat(_toConsumableArray(params.map(function (param) {
3149        return Math.floor(normalizeDecimalNumber(param));
3150      }))));
3151      return canvas;
3152    },
3153
3154    /**
3155     * Change the aspect ratio of the crop box.
3156     * @param {number} aspectRatio - The new aspect ratio.
3157     * @returns {Cropper} this
3158     */
3159    setAspectRatio: function setAspectRatio(aspectRatio) {
3160      var options = this.options;
3161
3162      if (!this.disabled && !isUndefined(aspectRatio)) {
3163        // 0 -> NaN
3164        options.aspectRatio = Math.max(0, aspectRatio) || NaN;
3165
3166        if (this.ready) {
3167          this.initCropBox();
3168
3169          if (this.cropped) {
3170            this.renderCropBox();
3171          }
3172        }
3173      }
3174
3175      return this;
3176    },
3177
3178    /**
3179     * Change the drag mode.
3180     * @param {string} mode - The new drag mode.
3181     * @returns {Cropper} this
3182     */
3183    setDragMode: function setDragMode(mode) {
3184      var options = this.options,
3185          dragBox = this.dragBox,
3186          face = this.face;
3187
3188      if (this.ready && !this.disabled) {
3189        var croppable = mode === DRAG_MODE_CROP;
3190        var movable = options.movable && mode === DRAG_MODE_MOVE;
3191        mode = croppable || movable ? mode : DRAG_MODE_NONE;
3192        options.dragMode = mode;
3193        setData(dragBox, DATA_ACTION, mode);
3194        toggleClass(dragBox, CLASS_CROP, croppable);
3195        toggleClass(dragBox, CLASS_MOVE, movable);
3196
3197        if (!options.cropBoxMovable) {
3198          // Sync drag mode to crop box when it is not movable
3199          setData(face, DATA_ACTION, mode);
3200          toggleClass(face, CLASS_CROP, croppable);
3201          toggleClass(face, CLASS_MOVE, movable);
3202        }
3203      }
3204
3205      return this;
3206    }
3207  };
3208
3209  var AnotherCropper = WINDOW.Cropper;
3210
3211  var Cropper = /*#__PURE__*/function () {
3212    /**
3213     * Create a new Cropper.
3214     * @param {Element} element - The target element for cropping.
3215     * @param {Object} [options={}] - The configuration options.
3216     */
3217    function Cropper(element) {
3218      var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
3219
3220      _classCallCheck(this, Cropper);
3221
3222      if (!element || !REGEXP_TAG_NAME.test(element.tagName)) {
3223        throw new Error('The first argument is required and must be an <img> or <canvas> element.');
3224      }
3225
3226      this.element = element;
3227      this.options = assign({}, DEFAULTS, isPlainObject(options) && options);
3228      this.cropped = false;
3229      this.disabled = false;
3230      this.pointers = {};
3231      this.ready = false;
3232      this.reloading = false;
3233      this.replaced = false;
3234      this.sized = false;
3235      this.sizing = false;
3236      this.init();
3237    }
3238
3239    _createClass(Cropper, [{
3240      key: "init",
3241      value: function init() {
3242        var element = this.element;
3243        var tagName = element.tagName.toLowerCase();
3244        var url;
3245
3246        if (element[NAMESPACE]) {
3247          return;
3248        }
3249
3250        element[NAMESPACE] = this;
3251
3252        if (tagName === 'img') {
3253          this.isImg = true; // e.g.: "img/picture.jpg"
3254
3255          url = element.getAttribute('src') || '';
3256          this.originalUrl = url; // Stop when it's a blank image
3257
3258          if (!url) {
3259            return;
3260          } // e.g.: "https://example.com/img/picture.jpg"
3261
3262
3263          url = element.src;
3264        } else if (tagName === 'canvas' && window.HTMLCanvasElement) {
3265          url = element.toDataURL();
3266        }
3267
3268        this.load(url);
3269      }
3270    }, {
3271      key: "load",
3272      value: function load(url) {
3273        var _this = this;
3274
3275        if (!url) {
3276          return;
3277        }
3278
3279        this.url = url;
3280        this.imageData = {};
3281        var element = this.element,
3282            options = this.options;
3283
3284        if (!options.rotatable && !options.scalable) {
3285          options.checkOrientation = false;
3286        } // Only IE10+ supports Typed Arrays
3287
3288
3289        if (!options.checkOrientation || !window.ArrayBuffer) {
3290          this.clone();
3291          return;
3292        } // Detect the mime type of the image directly if it is a Data URL
3293
3294
3295        if (REGEXP_DATA_URL.test(url)) {
3296          // Read ArrayBuffer from Data URL of JPEG images directly for better performance
3297          if (REGEXP_DATA_URL_JPEG.test(url)) {
3298            this.read(dataURLToArrayBuffer(url));
3299          } else {
3300            // Only a JPEG image may contains Exif Orientation information,
3301            // the rest types of Data URLs are not necessary to check orientation at all.
3302            this.clone();
3303          }
3304
3305          return;
3306        } // 1. Detect the mime type of the image by a XMLHttpRequest.
3307        // 2. Load the image as ArrayBuffer for reading orientation if its a JPEG image.
3308
3309
3310        var xhr = new XMLHttpRequest();
3311        var clone = this.clone.bind(this);
3312        this.reloading = true;
3313        this.xhr = xhr; // 1. Cross origin requests are only supported for protocol schemes:
3314        // http, https, data, chrome, chrome-extension.
3315        // 2. Access to XMLHttpRequest from a Data URL will be blocked by CORS policy
3316        // in some browsers as IE11 and Safari.
3317
3318        xhr.onabort = clone;
3319        xhr.onerror = clone;
3320        xhr.ontimeout = clone;
3321
3322        xhr.onprogress = function () {
3323          // Abort the request directly if it not a JPEG image for better performance
3324          if (xhr.getResponseHeader('content-type') !== MIME_TYPE_JPEG) {
3325            xhr.abort();
3326          }
3327        };
3328
3329        xhr.onload = function () {
3330          _this.read(xhr.response);
3331        };
3332
3333        xhr.onloadend = function () {
3334          _this.reloading = false;
3335          _this.xhr = null;
3336        }; // Bust cache when there is a "crossOrigin" property to avoid browser cache error
3337
3338
3339        if (options.checkCrossOrigin && isCrossOriginURL(url) && element.crossOrigin) {
3340          url = addTimestamp(url);
3341        }
3342
3343        xhr.open('GET', url);
3344        xhr.responseType = 'arraybuffer';
3345        xhr.withCredentials = element.crossOrigin === 'use-credentials';
3346        xhr.send();
3347      }
3348    }, {
3349      key: "read",
3350      value: function read(arrayBuffer) {
3351        var options = this.options,
3352            imageData = this.imageData; // Reset the orientation value to its default value 1
3353        // as some iOS browsers will render image with its orientation
3354
3355        var orientation = resetAndGetOrientation(arrayBuffer);
3356        var rotate = 0;
3357        var scaleX = 1;
3358        var scaleY = 1;
3359
3360        if (orientation > 1) {
3361          // Generate a new URL which has the default orientation value
3362          this.url = arrayBufferToDataURL(arrayBuffer, MIME_TYPE_JPEG);
3363
3364          var _parseOrientation = parseOrientation(orientation);
3365
3366          rotate = _parseOrientation.rotate;
3367          scaleX = _parseOrientation.scaleX;
3368          scaleY = _parseOrientation.scaleY;
3369        }
3370
3371        if (options.rotatable) {
3372          imageData.rotate = rotate;
3373        }
3374
3375        if (options.scalable) {
3376          imageData.scaleX = scaleX;
3377          imageData.scaleY = scaleY;
3378        }
3379
3380        this.clone();
3381      }
3382    }, {
3383      key: "clone",
3384      value: function clone() {
3385        var element = this.element,
3386            url = this.url;
3387        var crossOrigin = element.crossOrigin;
3388        var crossOriginUrl = url;
3389
3390        if (this.options.checkCrossOrigin && isCrossOriginURL(url)) {
3391          if (!crossOrigin) {
3392            crossOrigin = 'anonymous';
3393          } // Bust cache when there is not a "crossOrigin" property (#519)
3394
3395
3396          crossOriginUrl = addTimestamp(url);
3397        }
3398
3399        this.crossOrigin = crossOrigin;
3400        this.crossOriginUrl = crossOriginUrl;
3401        var image = document.createElement('img');
3402
3403        if (crossOrigin) {
3404          image.crossOrigin = crossOrigin;
3405        }
3406
3407        image.src = crossOriginUrl || url;
3408        image.alt = element.alt || 'The image to crop';
3409        this.image = image;
3410        image.onload = this.start.bind(this);
3411        image.onerror = this.stop.bind(this);
3412        addClass(image, CLASS_HIDE);
3413        element.parentNode.insertBefore(image, element.nextSibling);
3414      }
3415    }, {
3416      key: "start",
3417      value: function start() {
3418        var _this2 = this;
3419
3420        var image = this.image;
3421        image.onload = null;
3422        image.onerror = null;
3423        this.sizing = true; // Match all browsers that use WebKit as the layout engine in iOS devices,
3424        // such as Safari for iOS, Chrome for iOS, and in-app browsers.
3425
3426        var isIOSWebKit = WINDOW.navigator && /(?:iPad|iPhone|iPod).*?AppleWebKit/i.test(WINDOW.navigator.userAgent);
3427
3428        var done = function done(naturalWidth, naturalHeight) {
3429          assign(_this2.imageData, {
3430            naturalWidth: naturalWidth,
3431            naturalHeight: naturalHeight,
3432            aspectRatio: naturalWidth / naturalHeight
3433          });
3434          _this2.initialImageData = assign({}, _this2.imageData);
3435          _this2.sizing = false;
3436          _this2.sized = true;
3437
3438          _this2.build();
3439        }; // Most modern browsers (excepts iOS WebKit)
3440
3441
3442        if (image.naturalWidth && !isIOSWebKit) {
3443          done(image.naturalWidth, image.naturalHeight);
3444          return;
3445        }
3446
3447        var sizingImage = document.createElement('img');
3448        var body = document.body || document.documentElement;
3449        this.sizingImage = sizingImage;
3450
3451        sizingImage.onload = function () {
3452          done(sizingImage.width, sizingImage.height);
3453
3454          if (!isIOSWebKit) {
3455            body.removeChild(sizingImage);
3456          }
3457        };
3458
3459        sizingImage.src = image.src; // iOS WebKit will convert the image automatically
3460        // with its orientation once append it into DOM (#279)
3461
3462        if (!isIOSWebKit) {
3463          sizingImage.style.cssText = 'left:0;' + 'max-height:none!important;' + 'max-width:none!important;' + 'min-height:0!important;' + 'min-width:0!important;' + 'opacity:0;' + 'position:absolute;' + 'top:0;' + 'z-index:-1;';
3464          body.appendChild(sizingImage);
3465        }
3466      }
3467    }, {
3468      key: "stop",
3469      value: function stop() {
3470        var image = this.image;
3471        image.onload = null;
3472        image.onerror = null;
3473        image.parentNode.removeChild(image);
3474        this.image = null;
3475      }
3476    }, {
3477      key: "build",
3478      value: function build() {
3479        if (!this.sized || this.ready) {
3480          return;
3481        }
3482
3483        var element = this.element,
3484            options = this.options,
3485            image = this.image; // Create cropper elements
3486
3487        var container = element.parentNode;
3488        var template = document.createElement('div');
3489        template.innerHTML = TEMPLATE;
3490        var cropper = template.querySelector(".".concat(NAMESPACE, "-container"));
3491        var canvas = cropper.querySelector(".".concat(NAMESPACE, "-canvas"));
3492        var dragBox = cropper.querySelector(".".concat(NAMESPACE, "-drag-box"));
3493        var cropBox = cropper.querySelector(".".concat(NAMESPACE, "-crop-box"));
3494        var face = cropBox.querySelector(".".concat(NAMESPACE, "-face"));
3495        this.container = container;
3496        this.cropper = cropper;
3497        this.canvas = canvas;
3498        this.dragBox = dragBox;
3499        this.cropBox = cropBox;
3500        this.viewBox = cropper.querySelector(".".concat(NAMESPACE, "-view-box"));
3501        this.face = face;
3502        canvas.appendChild(image); // Hide the original image
3503
3504        addClass(element, CLASS_HIDDEN); // Inserts the cropper after to the current image
3505
3506        container.insertBefore(cropper, element.nextSibling); // Show the image if is hidden
3507
3508        if (!this.isImg) {
3509          removeClass(image, CLASS_HIDE);
3510        }
3511
3512        this.initPreview();
3513        this.bind();
3514        options.initialAspectRatio = Math.max(0, options.initialAspectRatio) || NaN;
3515        options.aspectRatio = Math.max(0, options.aspectRatio) || NaN;
3516        options.viewMode = Math.max(0, Math.min(3, Math.round(options.viewMode))) || 0;
3517        addClass(cropBox, CLASS_HIDDEN);
3518
3519        if (!options.guides) {
3520          addClass(cropBox.getElementsByClassName("".concat(NAMESPACE, "-dashed")), CLASS_HIDDEN);
3521        }
3522
3523        if (!options.center) {
3524          addClass(cropBox.getElementsByClassName("".concat(NAMESPACE, "-center")), CLASS_HIDDEN);
3525        }
3526
3527        if (options.background) {
3528          addClass(cropper, "".concat(NAMESPACE, "-bg"));
3529        }
3530
3531        if (!options.highlight) {
3532          addClass(face, CLASS_INVISIBLE);
3533        }
3534
3535        if (options.cropBoxMovable) {
3536          addClass(face, CLASS_MOVE);
3537          setData(face, DATA_ACTION, ACTION_ALL);
3538        }
3539
3540        if (!options.cropBoxResizable) {
3541          addClass(cropBox.getElementsByClassName("".concat(NAMESPACE, "-line")), CLASS_HIDDEN);
3542          addClass(cropBox.getElementsByClassName("".concat(NAMESPACE, "-point")), CLASS_HIDDEN);
3543        }
3544
3545        this.render();
3546        this.ready = true;
3547        this.setDragMode(options.dragMode);
3548
3549        if (options.autoCrop) {
3550          this.crop();
3551        }
3552
3553        this.setData(options.data);
3554
3555        if (isFunction(options.ready)) {
3556          addListener(element, EVENT_READY, options.ready, {
3557            once: true
3558          });
3559        }
3560
3561        dispatchEvent(element, EVENT_READY);
3562      }
3563    }, {
3564      key: "unbuild",
3565      value: function unbuild() {
3566        if (!this.ready) {
3567          return;
3568        }
3569
3570        this.ready = false;
3571        this.unbind();
3572        this.resetPreview();
3573        this.cropper.parentNode.removeChild(this.cropper);
3574        removeClass(this.element, CLASS_HIDDEN);
3575      }
3576    }, {
3577      key: "uncreate",
3578      value: function uncreate() {
3579        if (this.ready) {
3580          this.unbuild();
3581          this.ready = false;
3582          this.cropped = false;
3583        } else if (this.sizing) {
3584          this.sizingImage.onload = null;
3585          this.sizing = false;
3586          this.sized = false;
3587        } else if (this.reloading) {
3588          this.xhr.onabort = null;
3589          this.xhr.abort();
3590        } else if (this.image) {
3591          this.stop();
3592        }
3593      }
3594      /**
3595       * Get the no conflict cropper class.
3596       * @returns {Cropper} The cropper class.
3597       */
3598
3599    }], [{
3600      key: "noConflict",
3601      value: function noConflict() {
3602        window.Cropper = AnotherCropper;
3603        return Cropper;
3604      }
3605      /**
3606       * Change the default options.
3607       * @param {Object} options - The new default options.
3608       */
3609
3610    }, {
3611      key: "setDefaults",
3612      value: function setDefaults(options) {
3613        assign(DEFAULTS, isPlainObject(options) && options);
3614      }
3615    }]);
3616
3617    return Cropper;
3618  }();
3619
3620  assign(Cropper.prototype, render, preview, events, handlers, change, methods);
3621
3622  return Cropper;
3623
3624})));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.