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.