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