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