2018-05-12 08:43:32 +00:00
/ * !
2018-11-01 19:02:06 +00:00
* Cropper . js v1 . 4.3
* https : //fengyuanchen.github.io/cropperjs
2018-05-12 08:43:32 +00:00
*
2018-11-01 19:02:06 +00:00
* Copyright 2015 - present Chen Fengyuan
2018-05-12 08:43:32 +00:00
* Released under the MIT license
*
2018-11-01 19:02:06 +00:00
* Date : 2018 - 10 - 24 T13 : 07 : 15.032 Z
2018-05-12 08:43:32 +00:00
* /
2018-11-01 19:02:06 +00:00
( function ( global , factory ) {
typeof exports === 'object' && typeof module !== 'undefined' ? module . exports = factory ( ) :
typeof define === 'function' && define . amd ? define ( factory ) :
( global . Cropper = factory ( ) ) ;
} ( this , ( function ( ) { 'use strict' ;
function _typeof ( obj ) {
if ( typeof Symbol === "function" && typeof Symbol . iterator === "symbol" ) {
_typeof = function ( obj ) {
return typeof obj ;
} ;
} else {
_typeof = function ( obj ) {
return obj && typeof Symbol === "function" && obj . constructor === Symbol && obj !== Symbol . prototype ? "symbol" : typeof obj ;
} ;
}
return _typeof ( obj ) ;
}
function _classCallCheck ( instance , Constructor ) {
if ( ! ( instance instanceof Constructor ) ) {
throw new TypeError ( "Cannot call a class as a function" ) ;
}
}
function _defineProperties ( target , props ) {
for ( var i = 0 ; i < props . length ; i ++ ) {
var descriptor = props [ i ] ;
descriptor . enumerable = descriptor . enumerable || false ;
descriptor . configurable = true ;
if ( "value" in descriptor ) descriptor . writable = true ;
Object . defineProperty ( target , descriptor . key , descriptor ) ;
}
}
function _createClass ( Constructor , protoProps , staticProps ) {
if ( protoProps ) _defineProperties ( Constructor . prototype , protoProps ) ;
if ( staticProps ) _defineProperties ( Constructor , staticProps ) ;
return Constructor ;
}
function _toConsumableArray ( arr ) {
return _arrayWithoutHoles ( arr ) || _iterableToArray ( arr ) || _nonIterableSpread ( ) ;
}
function _arrayWithoutHoles ( arr ) {
if ( Array . isArray ( arr ) ) {
for ( var i = 0 , arr2 = new Array ( arr . length ) ; i < arr . length ; i ++ ) arr2 [ i ] = arr [ i ] ;
return arr2 ;
}
}
function _iterableToArray ( iter ) {
if ( Symbol . iterator in Object ( iter ) || Object . prototype . toString . call ( iter ) === "[object Arguments]" ) return Array . from ( iter ) ;
}
function _nonIterableSpread ( ) {
throw new TypeError ( "Invalid attempt to spread non-iterable instance" ) ;
}
var IN _BROWSER = typeof window !== 'undefined' ;
var WINDOW = IN _BROWSER ? window : { } ;
var NAMESPACE = 'cropper' ; // Actions
var ACTION _ALL = 'all' ;
var ACTION _CROP = 'crop' ;
var ACTION _MOVE = 'move' ;
var ACTION _ZOOM = 'zoom' ;
var ACTION _EAST = 'e' ;
var ACTION _WEST = 'w' ;
var ACTION _SOUTH = 's' ;
var ACTION _NORTH = 'n' ;
var ACTION _NORTH _EAST = 'ne' ;
var ACTION _NORTH _WEST = 'nw' ;
var ACTION _SOUTH _EAST = 'se' ;
var ACTION _SOUTH _WEST = 'sw' ; // Classes
var CLASS _CROP = "" . concat ( NAMESPACE , "-crop" ) ;
var CLASS _DISABLED = "" . concat ( NAMESPACE , "-disabled" ) ;
var CLASS _HIDDEN = "" . concat ( NAMESPACE , "-hidden" ) ;
var CLASS _HIDE = "" . concat ( NAMESPACE , "-hide" ) ;
var CLASS _INVISIBLE = "" . concat ( NAMESPACE , "-invisible" ) ;
var CLASS _MODAL = "" . concat ( NAMESPACE , "-modal" ) ;
var CLASS _MOVE = "" . concat ( NAMESPACE , "-move" ) ; // Data keys
var DATA _ACTION = "" . concat ( NAMESPACE , "Action" ) ;
var DATA _PREVIEW = "" . concat ( NAMESPACE , "Preview" ) ; // Drag modes
var DRAG _MODE _CROP = 'crop' ;
var DRAG _MODE _MOVE = 'move' ;
var DRAG _MODE _NONE = 'none' ; // Events
var EVENT _CROP = 'crop' ;
var EVENT _CROP _END = 'cropend' ;
var EVENT _CROP _MOVE = 'cropmove' ;
var EVENT _CROP _START = 'cropstart' ;
var EVENT _DBLCLICK = 'dblclick' ;
var EVENT _POINTER _DOWN = WINDOW . PointerEvent ? 'pointerdown' : 'touchstart mousedown' ;
var EVENT _POINTER _MOVE = WINDOW . PointerEvent ? 'pointermove' : 'touchmove mousemove' ;
var EVENT _POINTER _UP = WINDOW . PointerEvent ? 'pointerup pointercancel' : 'touchend touchcancel mouseup' ;
var EVENT _READY = 'ready' ;
var EVENT _RESIZE = 'resize' ;
var EVENT _WHEEL = 'wheel mousewheel DOMMouseScroll' ;
var EVENT _ZOOM = 'zoom' ; // Mime types
var MIME _TYPE _JPEG = 'image/jpeg' ; // RegExps
var REGEXP _ACTIONS = /^(?:e|w|s|n|se|sw|ne|nw|all|crop|move|zoom)$/ ;
var REGEXP _DATA _URL = /^data:/ ;
var REGEXP _DATA _URL _JPEG = /^data:image\/jpeg;base64,/ ;
var REGEXP _TAG _NAME = /^(?:img|canvas)$/i ;
var DEFAULTS = {
// Define the view mode of the cropper
viewMode : 0 ,
// 0, 1, 2, 3
// Define the dragging mode of the cropper
dragMode : DRAG _MODE _CROP ,
// 'crop', 'move' or 'none'
// Define the initial aspect ratio of the crop box
initialAspectRatio : NaN ,
// Define the aspect ratio of the crop box
aspectRatio : NaN ,
// An object with the previous cropping result data
data : null ,
// A selector for adding extra containers to preview
preview : '' ,
// Re-render the cropper when resize the window
responsive : true ,
// Restore the cropped area after resize the window
restore : true ,
// Check if the current image is a cross-origin image
checkCrossOrigin : true ,
// Check the current image's Exif Orientation information
checkOrientation : true ,
// Show the black modal
modal : true ,
// Show the dashed lines for guiding
guides : true ,
// Show the center indicator for guiding
center : true ,
// Show the white modal to highlight the crop box
highlight : true ,
// Show the grid background
background : true ,
// Enable to crop the image automatically when initialize
autoCrop : true ,
// Define the percentage of automatic cropping area when initializes
autoCropArea : 0.8 ,
// Enable to move the image
movable : true ,
// Enable to rotate the image
rotatable : true ,
// Enable to scale the image
scalable : true ,
// Enable to zoom the image
zoomable : true ,
// Enable to zoom the image by dragging touch
zoomOnTouch : true ,
// Enable to zoom the image by wheeling mouse
zoomOnWheel : true ,
// Define zoom ratio when zoom the image by wheeling mouse
wheelZoomRatio : 0.1 ,
// Enable to move the crop box
cropBoxMovable : true ,
// Enable to resize the crop box
cropBoxResizable : true ,
// Toggle drag mode between "crop" and "move" when click twice on the cropper
toggleDragModeOnDblclick : true ,
// Size limitation
minCanvasWidth : 0 ,
minCanvasHeight : 0 ,
minCropBoxWidth : 0 ,
minCropBoxHeight : 0 ,
minContainerWidth : 200 ,
minContainerHeight : 100 ,
// Shortcuts of events
ready : null ,
cropstart : null ,
cropmove : null ,
cropend : null ,
crop : null ,
zoom : null
} ;
2019-09-28 15:27:32 +00:00
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>' ;
2018-11-01 19:02:06 +00:00
/ * *
* Check if the given value is not a number .
* /
var isNaN = Number . isNaN || WINDOW . isNaN ;
/ * *
* Check if the given value is a number .
* @ param { * } value - The value to check .
* @ returns { boolean } Returns ` true ` if the given value is a number , else ` false ` .
* /
function isNumber ( value ) {
return typeof value === 'number' && ! isNaN ( value ) ;
}
/ * *
* Check if the given value is undefined .
* @ param { * } value - The value to check .
* @ returns { boolean } Returns ` true ` if the given value is undefined , else ` false ` .
* /
function isUndefined ( value ) {
return typeof value === 'undefined' ;
}
/ * *
* Check if the given value is an object .
* @ param { * } value - The value to check .
* @ returns { boolean } Returns ` true ` if the given value is an object , else ` false ` .
* /
function isObject ( value ) {
return _typeof ( value ) === 'object' && value !== null ;
}
var hasOwnProperty = Object . prototype . hasOwnProperty ;
/ * *
* Check if the given value is a plain object .
* @ param { * } value - The value to check .
* @ returns { boolean } Returns ` true ` if the given value is a plain object , else ` false ` .
* /
function isPlainObject ( value ) {
if ( ! isObject ( value ) ) {
return false ;
}
try {
var _constructor = value . constructor ;
var prototype = _constructor . prototype ;
return _constructor && prototype && hasOwnProperty . call ( prototype , 'isPrototypeOf' ) ;
} catch ( e ) {
return false ;
}
}
/ * *
* Check if the given value is a function .
* @ param { * } value - The value to check .
* @ returns { boolean } Returns ` true ` if the given value is a function , else ` false ` .
* /
function isFunction ( value ) {
return typeof value === 'function' ;
}
/ * *
* Iterate the given data .
* @ param { * } data - The data to iterate .
* @ param { Function } callback - The process function for each element .
* @ returns { * } The original data .
* /
function forEach ( data , callback ) {
if ( data && isFunction ( callback ) ) {
if ( Array . isArray ( data ) || isNumber ( data . length )
/* array-like */
) {
var length = data . length ;
var i ;
for ( i = 0 ; i < length ; i += 1 ) {
if ( callback . call ( data , data [ i ] , i , data ) === false ) {
break ;
}
}
} else if ( isObject ( data ) ) {
Object . keys ( data ) . forEach ( function ( key ) {
callback . call ( data , data [ key ] , key , data ) ;
} ) ;
}
}
return data ;
}
/ * *
* Extend the given object .
* @ param { * } obj - The object to be extended .
* @ param { * } args - The rest objects which will be merged to the first object .
* @ returns { Object } The extended object .
* /
var assign = Object . assign || function assign ( obj ) {
for ( var _len = arguments . length , args = new Array ( _len > 1 ? _len - 1 : 0 ) , _key = 1 ; _key < _len ; _key ++ ) {
args [ _key - 1 ] = arguments [ _key ] ;
}
if ( isObject ( obj ) && args . length > 0 ) {
args . forEach ( function ( arg ) {
if ( isObject ( arg ) ) {
Object . keys ( arg ) . forEach ( function ( key ) {
obj [ key ] = arg [ key ] ;
} ) ;
}
} ) ;
}
return obj ;
} ;
var REGEXP _DECIMALS = /\.\d*(?:0|9){12}\d*$/ ;
/ * *
* Normalize decimal number .
* Check out { @ link http : //0.30000000000000004.com/}
* @ param { number } value - The value to normalize .
* @ param { number } [ times = 100000000000 ] - The times for normalizing .
* @ returns { number } Returns the normalized number .
* /
function normalizeDecimalNumber ( value ) {
var times = arguments . length > 1 && arguments [ 1 ] !== undefined ? arguments [ 1 ] : 100000000000 ;
return REGEXP _DECIMALS . test ( value ) ? Math . round ( value * times ) / times : value ;
}
var REGEXP _SUFFIX = /^(?:width|height|left|top|marginLeft|marginTop)$/ ;
/ * *
* Apply styles to the given element .
* @ param { Element } element - The target element .
* @ param { Object } styles - The styles for applying .
* /
function setStyle ( element , styles ) {
var style = element . style ;
forEach ( styles , function ( value , property ) {
if ( REGEXP _SUFFIX . test ( property ) && isNumber ( value ) ) {
value += 'px' ;
}
style [ property ] = value ;
} ) ;
}
/ * *
* Check if the given element has a special class .
* @ param { Element } element - The element to check .
* @ param { string } value - The class to search .
* @ returns { boolean } Returns ` true ` if the special class was found .
* /
function hasClass ( element , value ) {
return element . classList ? element . classList . contains ( value ) : element . className . indexOf ( value ) > - 1 ;
}
/ * *
* Add classes to the given element .
* @ param { Element } element - The target element .
* @ param { string } value - The classes to be added .
* /
function addClass ( element , value ) {
if ( ! value ) {
return ;
}
if ( isNumber ( element . length ) ) {
forEach ( element , function ( elem ) {
addClass ( elem , value ) ;
} ) ;
return ;
}
if ( element . classList ) {
element . classList . add ( value ) ;
return ;
}
var className = element . className . trim ( ) ;
if ( ! className ) {
element . className = value ;
} else if ( className . indexOf ( value ) < 0 ) {
element . className = "" . concat ( className , " " ) . concat ( value ) ;
}
}
/ * *
* Remove classes from the given element .
* @ param { Element } element - The target element .
* @ param { string } value - The classes to be removed .
* /
function removeClass ( element , value ) {
if ( ! value ) {
return ;
}
if ( isNumber ( element . length ) ) {
forEach ( element , function ( elem ) {
removeClass ( elem , value ) ;
} ) ;
return ;
}
if ( element . classList ) {
element . classList . remove ( value ) ;
return ;
}
if ( element . className . indexOf ( value ) >= 0 ) {
element . className = element . className . replace ( value , '' ) ;
}
}
/ * *
* Add or remove classes from the given element .
* @ param { Element } element - The target element .
* @ param { string } value - The classes to be toggled .
* @ param { boolean } added - Add only .
* /
function toggleClass ( element , value , added ) {
if ( ! value ) {
return ;
}
if ( isNumber ( element . length ) ) {
forEach ( element , function ( elem ) {
toggleClass ( elem , value , added ) ;
} ) ;
return ;
} // IE10-11 doesn't support the second parameter of `classList.toggle`
if ( added ) {
addClass ( element , value ) ;
} else {
removeClass ( element , value ) ;
}
}
var REGEXP _HYPHENATE = /([a-z\d])([A-Z])/g ;
/ * *
* Transform the given string from camelCase to kebab - case
* @ param { string } value - The value to transform .
* @ returns { string } The transformed value .
* /
function hyphenate ( value ) {
return value . replace ( REGEXP _HYPHENATE , '$1-$2' ) . toLowerCase ( ) ;
}
/ * *
* Get data from the given element .
* @ param { Element } element - The target element .
* @ param { string } name - The data key to get .
* @ returns { string } The data value .
* /
function getData ( element , name ) {
if ( isObject ( element [ name ] ) ) {
return element [ name ] ;
}
if ( element . dataset ) {
return element . dataset [ name ] ;
}
return element . getAttribute ( "data-" . concat ( hyphenate ( name ) ) ) ;
}
/ * *
* Set data to the given element .
* @ param { Element } element - The target element .
* @ param { string } name - The data key to set .
* @ param { string } data - The data value .
* /
function setData ( element , name , data ) {
if ( isObject ( data ) ) {
element [ name ] = data ;
} else if ( element . dataset ) {
element . dataset [ name ] = data ;
} else {
element . setAttribute ( "data-" . concat ( hyphenate ( name ) ) , data ) ;
}
}
/ * *
* Remove data from the given element .
* @ param { Element } element - The target element .
* @ param { string } name - The data key to remove .
* /
function removeData ( element , name ) {
if ( isObject ( element [ name ] ) ) {
try {
delete element [ name ] ;
} catch ( e ) {
element [ name ] = undefined ;
}
} else if ( element . dataset ) {
// #128 Safari not allows to delete dataset property
try {
delete element . dataset [ name ] ;
} catch ( e ) {
element . dataset [ name ] = undefined ;
}
} else {
element . removeAttribute ( "data-" . concat ( hyphenate ( name ) ) ) ;
}
}
var REGEXP _SPACES = /\s\s*/ ;
var onceSupported = function ( ) {
var supported = false ;
if ( IN _BROWSER ) {
var once = false ;
var listener = function listener ( ) { } ;
var options = Object . defineProperty ( { } , 'once' , {
get : function get ( ) {
supported = true ;
return once ;
} ,
/ * *
* This setter can fix a ` TypeError ` in strict mode
* { @ link https : //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Getter_only}
* @ param { boolean } value - The value to set
* /
set : function set ( value ) {
once = value ;
}
} ) ;
WINDOW . addEventListener ( 'test' , listener , options ) ;
WINDOW . removeEventListener ( 'test' , listener , options ) ;
}
return supported ;
} ( ) ;
/ * *
* Remove event listener from the target element .
* @ param { Element } element - The event target .
* @ param { string } type - The event type ( s ) .
* @ param { Function } listener - The event listener .
* @ param { Object } options - The event options .
* /
function removeListener ( element , type , listener ) {
var options = arguments . length > 3 && arguments [ 3 ] !== undefined ? arguments [ 3 ] : { } ;
var handler = listener ;
type . trim ( ) . split ( REGEXP _SPACES ) . forEach ( function ( event ) {
if ( ! onceSupported ) {
var listeners = element . listeners ;
if ( listeners && listeners [ event ] && listeners [ event ] [ listener ] ) {
handler = listeners [ event ] [ listener ] ;
delete listeners [ event ] [ listener ] ;
if ( Object . keys ( listeners [ event ] ) . length === 0 ) {
delete listeners [ event ] ;
}
if ( Object . keys ( listeners ) . length === 0 ) {
delete element . listeners ;
}
}
}
element . removeEventListener ( event , handler , options ) ;
} ) ;
}
/ * *
* Add event listener to the target element .
* @ param { Element } element - The event target .
* @ param { string } type - The event type ( s ) .
* @ param { Function } listener - The event listener .
* @ param { Object } options - The event options .
* /
function addListener ( element , type , listener ) {
var options = arguments . length > 3 && arguments [ 3 ] !== undefined ? arguments [ 3 ] : { } ;
var _handler = listener ;
type . trim ( ) . split ( REGEXP _SPACES ) . forEach ( function ( event ) {
if ( options . once && ! onceSupported ) {
var _element$listeners = element . listeners ,
listeners = _element$listeners === void 0 ? { } : _element$listeners ;
_handler = function handler ( ) {
delete listeners [ event ] [ listener ] ;
element . removeEventListener ( event , _handler , options ) ;
for ( var _len2 = arguments . length , args = new Array ( _len2 ) , _key2 = 0 ; _key2 < _len2 ; _key2 ++ ) {
args [ _key2 ] = arguments [ _key2 ] ;
}
listener . apply ( element , args ) ;
} ;
if ( ! listeners [ event ] ) {
listeners [ event ] = { } ;
}
if ( listeners [ event ] [ listener ] ) {
element . removeEventListener ( event , listeners [ event ] [ listener ] , options ) ;
}
listeners [ event ] [ listener ] = _handler ;
element . listeners = listeners ;
}
element . addEventListener ( event , _handler , options ) ;
} ) ;
}
/ * *
* Dispatch event on the target element .
* @ param { Element } element - The event target .
* @ param { string } type - The event type ( s ) .
* @ param { Object } data - The additional event data .
* @ returns { boolean } Indicate if the event is default prevented or not .
* /
function dispatchEvent ( element , type , data ) {
var event ; // Event and CustomEvent on IE9-11 are global objects, not constructors
if ( isFunction ( Event ) && isFunction ( CustomEvent ) ) {
event = new CustomEvent ( type , {
detail : data ,
bubbles : true ,
cancelable : true
} ) ;
} else {
event = document . createEvent ( 'CustomEvent' ) ;
event . initCustomEvent ( type , true , true , data ) ;
}
return element . dispatchEvent ( event ) ;
}
/ * *
* Get the offset base on the document .
* @ param { Element } element - The t arget element .
* @ returns { Object } The offset data .
* /
function getOffset ( element ) {
var box = element . getBoundingClientRect ( ) ;
return {
left : box . left + ( window . pageXOffset - document . documentElement . clientLeft ) ,
top : box . top + ( window . pageYOffset - document . documentElement . clientTop )
} ;
}
var location = WINDOW . location ;
var REGEXP _ORIGINS = /^(https?:)\/\/([^:/?#]+):?(\d*)/i ;
/ * *
* Check if the given URL is a cross origin URL .
* @ param { string } url - The target URL .
* @ returns { boolean } Returns ` true ` if the given URL is a cross origin URL , else ` false ` .
* /
function isCrossOriginURL ( url ) {
var parts = url . match ( REGEXP _ORIGINS ) ;
return parts && ( parts [ 1 ] !== location . protocol || parts [ 2 ] !== location . hostname || parts [ 3 ] !== location . port ) ;
}
/ * *
* Add timestamp to the given URL .
* @ param { string } url - The target URL .
* @ returns { string } The result URL .
* /
function addTimestamp ( url ) {
var timestamp = "timestamp=" . concat ( new Date ( ) . getTime ( ) ) ;
return url + ( url . indexOf ( '?' ) === - 1 ? '?' : '&' ) + timestamp ;
}
/ * *
* Get transforms base on the given object .
* @ param { Object } obj - The target object .
* @ returns { string } A string contains transform values .
* /
function getTransforms ( _ref ) {
var rotate = _ref . rotate ,
scaleX = _ref . scaleX ,
scaleY = _ref . scaleY ,
translateX = _ref . translateX ,
translateY = _ref . translateY ;
var values = [ ] ;
if ( isNumber ( translateX ) && translateX !== 0 ) {
values . push ( "translateX(" . concat ( translateX , "px)" ) ) ;
}
if ( isNumber ( translateY ) && translateY !== 0 ) {
values . push ( "translateY(" . concat ( translateY , "px)" ) ) ;
} // Rotate should come first before scale to match orientation transform
if ( isNumber ( rotate ) && rotate !== 0 ) {
values . push ( "rotate(" . concat ( rotate , "deg)" ) ) ;
}
if ( isNumber ( scaleX ) && scaleX !== 1 ) {
values . push ( "scaleX(" . concat ( scaleX , ")" ) ) ;
}
if ( isNumber ( scaleY ) && scaleY !== 1 ) {
values . push ( "scaleY(" . concat ( scaleY , ")" ) ) ;
}
var transform = values . length ? values . join ( ' ' ) : 'none' ;
return {
WebkitTransform : transform ,
msTransform : transform ,
transform : transform
} ;
}
/ * *
* Get the max ratio of a group of pointers .
* @ param { string } pointers - The target pointers .
* @ returns { number } The result ratio .
* /
function getMaxZoomRatio ( pointers ) {
var pointers2 = assign ( { } , pointers ) ;
var ratios = [ ] ;
forEach ( pointers , function ( pointer , pointerId ) {
delete pointers2 [ pointerId ] ;
forEach ( pointers2 , function ( pointer2 ) {
var x1 = Math . abs ( pointer . startX - pointer2 . startX ) ;
var y1 = Math . abs ( pointer . startY - pointer2 . startY ) ;
var x2 = Math . abs ( pointer . endX - pointer2 . endX ) ;
var y2 = Math . abs ( pointer . endY - pointer2 . endY ) ;
var z1 = Math . sqrt ( x1 * x1 + y1 * y1 ) ;
var z2 = Math . sqrt ( x2 * x2 + y2 * y2 ) ;
var ratio = ( z2 - z1 ) / z1 ;
ratios . push ( ratio ) ;
} ) ;
} ) ;
ratios . sort ( function ( a , b ) {
return Math . abs ( a ) < Math . abs ( b ) ;
} ) ;
return ratios [ 0 ] ;
}
/ * *
* Get a pointer from an event object .
* @ param { Object } event - The target event object .
* @ param { boolean } endOnly - Indicates if only returns the end point coordinate or not .
* @ returns { Object } The result pointer contains start and / or end point coordinates .
* /
function getPointer ( _ref2 , endOnly ) {
var pageX = _ref2 . pageX ,
pageY = _ref2 . pageY ;
var end = {
endX : pageX ,
endY : pageY
} ;
return endOnly ? end : assign ( {
startX : pageX ,
startY : pageY
} , end ) ;
}
/ * *
* Get the center point coordinate of a group of pointers .
* @ param { Object } pointers - The target pointers .
* @ returns { Object } The center point coordinate .
* /
function getPointersCenter ( pointers ) {
var pageX = 0 ;
var pageY = 0 ;
var count = 0 ;
forEach ( pointers , function ( _ref3 ) {
var startX = _ref3 . startX ,
startY = _ref3 . startY ;
pageX += startX ;
pageY += startY ;
count += 1 ;
} ) ;
pageX /= count ;
pageY /= count ;
return {
pageX : pageX ,
pageY : pageY
} ;
}
/ * *
* Check if the given value is a finite number .
* /
var isFinite = Number . isFinite || WINDOW . isFinite ;
/ * *
* Get the max sizes in a rectangle under the given aspect ratio .
* @ param { Object } data - The original sizes .
* @ param { string } [ type = 'contain' ] - The adjust type .
* @ returns { Object } The result sizes .
* /
function getAdjustedSizes ( _ref4 ) // or 'cover'
{
var aspectRatio = _ref4 . aspectRatio ,
height = _ref4 . height ,
width = _ref4 . width ;
var type = arguments . length > 1 && arguments [ 1 ] !== undefined ? arguments [ 1 ] : 'contain' ;
var isValidNumber = function isValidNumber ( value ) {
return isFinite ( value ) && value > 0 ;
} ;
if ( isValidNumber ( width ) && isValidNumber ( height ) ) {
var adjustedWidth = height * aspectRatio ;
if ( type === 'contain' && adjustedWidth > width || type === 'cover' && adjustedWidth < width ) {
height = width / aspectRatio ;
} else {
width = height * aspectRatio ;
}
} else if ( isValidNumber ( width ) ) {
height = width / aspectRatio ;
} else if ( isValidNumber ( height ) ) {
width = height * aspectRatio ;
}
return {
width : width ,
height : height
} ;
}
/ * *
* Get the new sizes of a rectangle after rotated .
* @ param { Object } data - The original sizes .
* @ returns { Object } The result sizes .
* /
function getRotatedSizes ( _ref5 ) {
var width = _ref5 . width ,
height = _ref5 . height ,
degree = _ref5 . degree ;
degree = Math . abs ( degree ) % 180 ;
if ( degree === 90 ) {
return {
width : height ,
height : width
} ;
}
var arc = degree % 90 * Math . PI / 180 ;
var sinArc = Math . sin ( arc ) ;
var cosArc = Math . cos ( arc ) ;
var newWidth = width * cosArc + height * sinArc ;
var newHeight = width * sinArc + height * cosArc ;
return degree > 90 ? {
width : newHeight ,
height : newWidth
} : {
width : newWidth ,
height : newHeight
} ;
}
/ * *
* Get a canvas which drew the given image .
* @ param { HTMLImageElement } image - The image for drawing .
* @ param { Object } imageData - The image data .
* @ param { Object } canvasData - The canvas data .
* @ param { Object } options - The options .
* @ returns { HTMLCanvasElement } The result canvas .
* /
function getSourceCanvas ( image , _ref6 , _ref7 , _ref8 ) {
var imageAspectRatio = _ref6 . aspectRatio ,
imageNaturalWidth = _ref6 . naturalWidth ,
imageNaturalHeight = _ref6 . naturalHeight ,
_ref6$rotate = _ref6 . rotate ,
rotate = _ref6$rotate === void 0 ? 0 : _ref6$rotate ,
_ref6$scaleX = _ref6 . scaleX ,
scaleX = _ref6$scaleX === void 0 ? 1 : _ref6$scaleX ,
_ref6$scaleY = _ref6 . scaleY ,
scaleY = _ref6$scaleY === void 0 ? 1 : _ref6$scaleY ;
var aspectRatio = _ref7 . aspectRatio ,
naturalWidth = _ref7 . naturalWidth ,
naturalHeight = _ref7 . naturalHeight ;
var _ref8$fillColor = _ref8 . fillColor ,
fillColor = _ref8$fillColor === void 0 ? 'transparent' : _ref8$fillColor ,
_ref8$imageSmoothingE = _ref8 . imageSmoothingEnabled ,
imageSmoothingEnabled = _ref8$imageSmoothingE === void 0 ? true : _ref8$imageSmoothingE ,
_ref8$imageSmoothingQ = _ref8 . imageSmoothingQuality ,
imageSmoothingQuality = _ref8$imageSmoothingQ === void 0 ? 'low' : _ref8$imageSmoothingQ ,
_ref8$maxWidth = _ref8 . maxWidth ,
maxWidth = _ref8$maxWidth === void 0 ? Infinity : _ref8$maxWidth ,
_ref8$maxHeight = _ref8 . maxHeight ,
maxHeight = _ref8$maxHeight === void 0 ? Infinity : _ref8$maxHeight ,
_ref8$minWidth = _ref8 . minWidth ,
minWidth = _ref8$minWidth === void 0 ? 0 : _ref8$minWidth ,
_ref8$minHeight = _ref8 . minHeight ,
minHeight = _ref8$minHeight === void 0 ? 0 : _ref8$minHeight ;
var canvas = document . createElement ( 'canvas' ) ;
var context = canvas . getContext ( '2d' ) ;
var maxSizes = getAdjustedSizes ( {
aspectRatio : aspectRatio ,
width : maxWidth ,
height : maxHeight
} ) ;
var minSizes = getAdjustedSizes ( {
aspectRatio : aspectRatio ,
width : minWidth ,
height : minHeight
} , 'cover' ) ;
var width = Math . min ( maxSizes . width , Math . max ( minSizes . width , naturalWidth ) ) ;
var height = Math . min ( maxSizes . height , Math . max ( minSizes . height , naturalHeight ) ) ; // Note: should always use image's natural sizes for drawing as
// imageData.naturalWidth === canvasData.naturalHeight when rotate % 180 === 90
var destMaxSizes = getAdjustedSizes ( {
aspectRatio : imageAspectRatio ,
width : maxWidth ,
height : maxHeight
} ) ;
var destMinSizes = getAdjustedSizes ( {
aspectRatio : imageAspectRatio ,
width : minWidth ,
height : minHeight
} , 'cover' ) ;
var destWidth = Math . min ( destMaxSizes . width , Math . max ( destMinSizes . width , imageNaturalWidth ) ) ;
var destHeight = Math . min ( destMaxSizes . height , Math . max ( destMinSizes . height , imageNaturalHeight ) ) ;
var params = [ - destWidth / 2 , - destHeight / 2 , destWidth , destHeight ] ;
canvas . width = normalizeDecimalNumber ( width ) ;
canvas . height = normalizeDecimalNumber ( height ) ;
context . fillStyle = fillColor ;
context . fillRect ( 0 , 0 , width , height ) ;
context . save ( ) ;
context . translate ( width / 2 , height / 2 ) ;
context . rotate ( rotate * Math . PI / 180 ) ;
context . scale ( scaleX , scaleY ) ;
context . imageSmoothingEnabled = imageSmoothingEnabled ;
context . imageSmoothingQuality = imageSmoothingQuality ;
context . drawImage . apply ( context , [ image ] . concat ( _toConsumableArray ( params . map ( function ( param ) {
return Math . floor ( normalizeDecimalNumber ( param ) ) ;
} ) ) ) ) ;
context . restore ( ) ;
return canvas ;
}
var fromCharCode = String . fromCharCode ;
/ * *
* Get string from char code in data view .
* @ param { DataView } dataView - The data view for read .
* @ param { number } start - The start index .
* @ param { number } length - The read length .
* @ returns { string } The read result .
* /
function getStringFromCharCode ( dataView , start , length ) {
var str = '' ;
var i ;
length += start ;
for ( i = start ; i < length ; i += 1 ) {
str += fromCharCode ( dataView . getUint8 ( i ) ) ;
}
return str ;
}
var REGEXP _DATA _URL _HEAD = /^data:.*,/ ;
/ * *
* Transform Data URL to array buffer .
* @ param { string } dataURL - The Data URL to transform .
* @ returns { ArrayBuffer } The result array buffer .
* /
function dataURLToArrayBuffer ( dataURL ) {
var base64 = dataURL . replace ( REGEXP _DATA _URL _HEAD , '' ) ;
var binary = atob ( base64 ) ;
var arrayBuffer = new ArrayBuffer ( binary . length ) ;
var uint8 = new Uint8Array ( arrayBuffer ) ;
forEach ( uint8 , function ( value , i ) {
uint8 [ i ] = binary . charCodeAt ( i ) ;
} ) ;
return arrayBuffer ;
}
/ * *
* Transform array buffer to Data URL .
* @ param { ArrayBuffer } arrayBuffer - The array buffer to transform .
* @ param { string } mimeType - The mime type of the Data URL .
* @ returns { string } The result Data URL .
* /
function arrayBufferToDataURL ( arrayBuffer , mimeType ) {
var chunks = [ ] ;
var chunkSize = 8192 ;
var uint8 = new Uint8Array ( arrayBuffer ) ;
while ( uint8 . length > 0 ) {
chunks . push ( fromCharCode . apply ( void 0 , _toConsumableArray ( uint8 . subarray ( 0 , chunkSize ) ) ) ) ;
uint8 = uint8 . subarray ( chunkSize ) ;
}
return "data:" . concat ( mimeType , ";base64," ) . concat ( btoa ( chunks . join ( '' ) ) ) ;
}
/ * *
* Get orientation value from given array buffer .
* @ param { ArrayBuffer } arrayBuffer - The array buffer to read .
* @ returns { number } The read orientation value .
* /
function resetAndGetOrientation ( arrayBuffer ) {
var dataView = new DataView ( arrayBuffer ) ;
var orientation ; // Ignores range error when the image does not have correct Exif information
try {
var littleEndian ;
var app1Start ;
var ifdStart ; // Only handle JPEG image (start by 0xFFD8)
if ( dataView . getUint8 ( 0 ) === 0xFF && dataView . getUint8 ( 1 ) === 0xD8 ) {
var length = dataView . byteLength ;
var offset = 2 ;
while ( offset + 1 < length ) {
if ( dataView . getUint8 ( offset ) === 0xFF && dataView . getUint8 ( offset + 1 ) === 0xE1 ) {
app1Start = offset ;
break ;
}
offset += 1 ;
}
}
if ( app1Start ) {
var exifIDCode = app1Start + 4 ;
var tiffOffset = app1Start + 10 ;
if ( getStringFromCharCode ( dataView , exifIDCode , 4 ) === 'Exif' ) {
var endianness = dataView . getUint16 ( tiffOffset ) ;
littleEndian = endianness === 0x4949 ;
if ( littleEndian || endianness === 0x4D4D
/* bigEndian */
) {
if ( dataView . getUint16 ( tiffOffset + 2 , littleEndian ) === 0x002A ) {
var firstIFDOffset = dataView . getUint32 ( tiffOffset + 4 , littleEndian ) ;
if ( firstIFDOffset >= 0x00000008 ) {
ifdStart = tiffOffset + firstIFDOffset ;
}
}
}
}
}
if ( ifdStart ) {
var _length = dataView . getUint16 ( ifdStart , littleEndian ) ;
var _offset ;
var i ;
for ( i = 0 ; i < _length ; i += 1 ) {
_offset = ifdStart + i * 12 + 2 ;
if ( dataView . getUint16 ( _offset , littleEndian ) === 0x0112
/* Orientation */
) {
// 8 is the offset of the current tag's value
_offset += 8 ; // Get the original orientation value
orientation = dataView . getUint16 ( _offset , littleEndian ) ; // Override the orientation with its default value
dataView . setUint16 ( _offset , 1 , littleEndian ) ;
break ;
}
}
}
} catch ( e ) {
orientation = 1 ;
}
return orientation ;
}
/ * *
* Parse Exif Orientation value .
* @ param { number } orientation - The orientation to parse .
* @ returns { Object } The parsed result .
* /
function parseOrientation ( orientation ) {
var rotate = 0 ;
var scaleX = 1 ;
var scaleY = 1 ;
switch ( orientation ) {
// Flip horizontal
case 2 :
scaleX = - 1 ;
break ;
// Rotate left 180°
case 3 :
rotate = - 180 ;
break ;
// Flip vertical
case 4 :
scaleY = - 1 ;
break ;
// Flip vertical and rotate right 90°
case 5 :
rotate = 90 ;
scaleY = - 1 ;
break ;
// Rotate right 90°
case 6 :
rotate = 90 ;
break ;
// Flip horizontal and rotate right 90°
case 7 :
rotate = 90 ;
scaleX = - 1 ;
break ;
// Rotate left 90°
case 8 :
rotate = - 90 ;
break ;
default :
}
return {
rotate : rotate ,
scaleX : scaleX ,
scaleY : scaleY
} ;
}
var render = {
render : function render ( ) {
this . initContainer ( ) ;
this . initCanvas ( ) ;
this . initCropBox ( ) ;
this . renderCanvas ( ) ;
if ( this . cropped ) {
this . renderCropBox ( ) ;
}
} ,
initContainer : function initContainer ( ) {
var element = this . element ,
options = this . options ,
container = this . container ,
cropper = this . cropper ;
addClass ( cropper , CLASS _HIDDEN ) ;
removeClass ( element , CLASS _HIDDEN ) ;
var containerData = {
width : Math . max ( container . offsetWidth , Number ( options . minContainerWidth ) || 200 ) ,
height : Math . max ( container . offsetHeight , Number ( options . minContainerHeight ) || 100 )
} ;
this . containerData = containerData ;
setStyle ( cropper , {
width : containerData . width ,
height : containerData . height
} ) ;
addClass ( element , CLASS _HIDDEN ) ;
removeClass ( cropper , CLASS _HIDDEN ) ;
} ,
// Canvas (image wrapper)
initCanvas : function initCanvas ( ) {
var containerData = this . containerData ,
imageData = this . imageData ;
var viewMode = this . options . viewMode ;
var rotated = Math . abs ( imageData . rotate ) % 180 === 90 ;
var naturalWidth = rotated ? imageData . naturalHeight : imageData . naturalWidth ;
var naturalHeight = rotated ? imageData . naturalWidth : imageData . naturalHeight ;
var aspectRatio = naturalWidth / naturalHeight ;
var canvasWidth = containerData . width ;
var canvasHeight = containerData . height ;
if ( containerData . height * aspectRatio > containerData . width ) {
if ( viewMode === 3 ) {
canvasWidth = containerData . height * aspectRatio ;
} else {
canvasHeight = containerData . width / aspectRatio ;
}
} else if ( viewMode === 3 ) {
canvasHeight = containerData . width / aspectRatio ;
} else {
canvasWidth = containerData . height * aspectRatio ;
}
var canvasData = {
aspectRatio : aspectRatio ,
naturalWidth : naturalWidth ,
naturalHeight : naturalHeight ,
width : canvasWidth ,
height : canvasHeight
} ;
canvasData . left = ( containerData . width - canvasWidth ) / 2 ;
canvasData . top = ( containerData . height - canvasHeight ) / 2 ;
canvasData . oldLeft = canvasData . left ;
canvasData . oldTop = canvasData . top ;
this . canvasData = canvasData ;
this . limited = viewMode === 1 || viewMode === 2 ;
this . limitCanvas ( true , true ) ;
this . initialImageData = assign ( { } , imageData ) ;
this . initialCanvasData = assign ( { } , canvasData ) ;
} ,
limitCanvas : function limitCanvas ( sizeLimited , positionLimited ) {
var options = this . options ,
containerData = this . containerData ,
canvasData = this . canvasData ,
cropBoxData = this . cropBoxData ;
var viewMode = options . viewMode ;
var aspectRatio = canvasData . aspectRatio ;
var cropped = this . cropped && cropBoxData ;
if ( sizeLimited ) {
var minCanvasWidth = Number ( options . minCanvasWidth ) || 0 ;
var minCanvasHeight = Number ( options . minCanvasHeight ) || 0 ;
if ( viewMode > 1 ) {
minCanvasWidth = Math . max ( minCanvasWidth , containerData . width ) ;
minCanvasHeight = Math . max ( minCanvasHeight , containerData . height ) ;
if ( viewMode === 3 ) {
if ( minCanvasHeight * aspectRatio > minCanvasWidth ) {
minCanvasWidth = minCanvasHeight * aspectRatio ;
} else {
minCanvasHeight = minCanvasWidth / aspectRatio ;
}
}
} else if ( viewMode > 0 ) {
if ( minCanvasWidth ) {
minCanvasWidth = Math . max ( minCanvasWidth , cropped ? cropBoxData . width : 0 ) ;
} else if ( minCanvasHeight ) {
minCanvasHeight = Math . max ( minCanvasHeight , cropped ? cropBoxData . height : 0 ) ;
} else if ( cropped ) {
minCanvasWidth = cropBoxData . width ;
minCanvasHeight = cropBoxData . height ;
if ( minCanvasHeight * aspectRatio > minCanvasWidth ) {
minCanvasWidth = minCanvasHeight * aspectRatio ;
} else {
minCanvasHeight = minCanvasWidth / aspectRatio ;
}
}
}
var _getAdjustedSizes = getAdjustedSizes ( {
aspectRatio : aspectRatio ,
width : minCanvasWidth ,
height : minCanvasHeight
} ) ;
minCanvasWidth = _getAdjustedSizes . width ;
minCanvasHeight = _getAdjustedSizes . height ;
canvasData . minWidth = minCanvasWidth ;
canvasData . minHeight = minCanvasHeight ;
canvasData . maxWidth = Infinity ;
canvasData . maxHeight = Infinity ;
}
if ( positionLimited ) {
if ( viewMode > ( cropped ? 0 : 1 ) ) {
var newCanvasLeft = containerData . width - canvasData . width ;
var newCanvasTop = containerData . height - canvasData . height ;
canvasData . minLeft = Math . min ( 0 , newCanvasLeft ) ;
canvasData . minTop = Math . min ( 0 , newCanvasTop ) ;
canvasData . maxLeft = Math . max ( 0 , newCanvasLeft ) ;
canvasData . maxTop = Math . max ( 0 , newCanvasTop ) ;
if ( cropped && this . limited ) {
canvasData . minLeft = Math . min ( cropBoxData . left , cropBoxData . left + ( cropBoxData . width - canvasData . width ) ) ;
canvasData . minTop = Math . min ( cropBoxData . top , cropBoxData . top + ( cropBoxData . height - canvasData . height ) ) ;
canvasData . maxLeft = cropBoxData . left ;
canvasData . maxTop = cropBoxData . top ;
if ( viewMode === 2 ) {
if ( canvasData . width >= containerData . width ) {
canvasData . minLeft = Math . min ( 0 , newCanvasLeft ) ;
canvasData . maxLeft = Math . max ( 0 , newCanvasLeft ) ;
}
if ( canvasData . height >= containerData . height ) {
canvasData . minTop = Math . min ( 0 , newCanvasTop ) ;
canvasData . maxTop = Math . max ( 0 , newCanvasTop ) ;
}
}
}
} else {
canvasData . minLeft = - canvasData . width ;
canvasData . minTop = - canvasData . height ;
canvasData . maxLeft = containerData . width ;
canvasData . maxTop = containerData . height ;
}
}
} ,
renderCanvas : function renderCanvas ( changed , transformed ) {
var canvasData = this . canvasData ,
imageData = this . imageData ;
if ( transformed ) {
var _getRotatedSizes = getRotatedSizes ( {
width : imageData . naturalWidth * Math . abs ( imageData . scaleX || 1 ) ,
height : imageData . naturalHeight * Math . abs ( imageData . scaleY || 1 ) ,
degree : imageData . rotate || 0
} ) ,
naturalWidth = _getRotatedSizes . width ,
naturalHeight = _getRotatedSizes . height ;
var width = canvasData . width * ( naturalWidth / canvasData . naturalWidth ) ;
var height = canvasData . height * ( naturalHeight / canvasData . naturalHeight ) ;
canvasData . left -= ( width - canvasData . width ) / 2 ;
canvasData . top -= ( height - canvasData . height ) / 2 ;
canvasData . width = width ;
canvasData . height = height ;
canvasData . aspectRatio = naturalWidth / naturalHeight ;
canvasData . naturalWidth = naturalWidth ;
canvasData . naturalHeight = naturalHeight ;
this . limitCanvas ( true , false ) ;
}
if ( canvasData . width > canvasData . maxWidth || canvasData . width < canvasData . minWidth ) {
canvasData . left = canvasData . oldLeft ;
}
if ( canvasData . height > canvasData . maxHeight || canvasData . height < canvasData . minHeight ) {
canvasData . top = canvasData . oldTop ;
}
canvasData . width = Math . min ( Math . max ( canvasData . width , canvasData . minWidth ) , canvasData . maxWidth ) ;
canvasData . height = Math . min ( Math . max ( canvasData . height , canvasData . minHeight ) , canvasData . maxHeight ) ;
this . limitCanvas ( false , true ) ;
canvasData . left = Math . min ( Math . max ( canvasData . left , canvasData . minLeft ) , canvasData . maxLeft ) ;
canvasData . top = Math . min ( Math . max ( canvasData . top , canvasData . minTop ) , canvasData . maxTop ) ;
canvasData . oldLeft = canvasData . left ;
canvasData . oldTop = canvasData . top ;
setStyle ( this . canvas , assign ( {
width : canvasData . width ,
height : canvasData . height
} , getTransforms ( {
translateX : canvasData . left ,
translateY : canvasData . top
} ) ) ) ;
this . renderImage ( changed ) ;
if ( this . cropped && this . limited ) {
this . limitCropBox ( true , true ) ;
}
} ,
renderImage : function renderImage ( changed ) {
var canvasData = this . canvasData ,
imageData = this . imageData ;
var width = imageData . naturalWidth * ( canvasData . width / canvasData . naturalWidth ) ;
var height = imageData . naturalHeight * ( canvasData . height / canvasData . naturalHeight ) ;
assign ( imageData , {
width : width ,
height : height ,
left : ( canvasData . width - width ) / 2 ,
top : ( canvasData . height - height ) / 2
} ) ;
setStyle ( this . image , assign ( {
width : imageData . width ,
height : imageData . height
} , getTransforms ( assign ( {
translateX : imageData . left ,
translateY : imageData . top
} , imageData ) ) ) ) ;
if ( changed ) {
this . output ( ) ;
}
} ,
initCropBox : function initCropBox ( ) {
var options = this . options ,
canvasData = this . canvasData ;
var aspectRatio = options . aspectRatio || options . initialAspectRatio ;
var autoCropArea = Number ( options . autoCropArea ) || 0.8 ;
var cropBoxData = {
width : canvasData . width ,
height : canvasData . height
} ;
if ( aspectRatio ) {
if ( canvasData . height * aspectRatio > canvasData . width ) {
cropBoxData . height = cropBoxData . width / aspectRatio ;
} else {
cropBoxData . width = cropBoxData . height * aspectRatio ;
}
}
this . cropBoxData = cropBoxData ;
this . limitCropBox ( true , true ) ; // Initialize auto crop area
cropBoxData . width = Math . min ( Math . max ( cropBoxData . width , cropBoxData . minWidth ) , cropBoxData . maxWidth ) ;
cropBoxData . height = Math . min ( Math . max ( cropBoxData . height , cropBoxData . minHeight ) , cropBoxData . maxHeight ) ; // The width/height of auto crop area must large than "minWidth/Height"
cropBoxData . width = Math . max ( cropBoxData . minWidth , cropBoxData . width * autoCropArea ) ;
cropBoxData . height = Math . max ( cropBoxData . minHeight , cropBoxData . height * autoCropArea ) ;
cropBoxData . left = canvasData . left + ( canvasData . width - cropBoxData . width ) / 2 ;
cropBoxData . top = canvasData . top + ( canvasData . height - cropBoxData . height ) / 2 ;
cropBoxData . oldLeft = cropBoxData . left ;
cropBoxData . oldTop = cropBoxData . top ;
this . initialCropBoxData = assign ( { } , cropBoxData ) ;
} ,
limitCropBox : function limitCropBox ( sizeLimited , positionLimited ) {
var options = this . options ,
containerData = this . containerData ,
canvasData = this . canvasData ,
cropBoxData = this . cropBoxData ,
limited = this . limited ;
var aspectRatio = options . aspectRatio ;
if ( sizeLimited ) {
var minCropBoxWidth = Number ( options . minCropBoxWidth ) || 0 ;
var minCropBoxHeight = Number ( options . minCropBoxHeight ) || 0 ;
var maxCropBoxWidth = limited ? Math . min ( containerData . width , canvasData . width , canvasData . width + canvasData . left , containerData . width - canvasData . left ) : containerData . width ;
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
minCropBoxWidth = Math . min ( minCropBoxWidth , containerData . width ) ;
minCropBoxHeight = Math . min ( minCropBoxHeight , containerData . height ) ;
if ( aspectRatio ) {
if ( minCropBoxWidth && minCropBoxHeight ) {
if ( minCropBoxHeight * aspectRatio > minCropBoxWidth ) {
minCropBoxHeight = minCropBoxWidth / aspectRatio ;
} else {
minCropBoxWidth = minCropBoxHeight * aspectRatio ;
}
} else if ( minCropBoxWidth ) {
minCropBoxHeight = minCropBoxWidth / aspectRatio ;
} else if ( minCropBoxHeight ) {
minCropBoxWidth = minCropBoxHeight * aspectRatio ;
}
if ( maxCropBoxHeight * aspectRatio > maxCropBoxWidth ) {
maxCropBoxHeight = maxCropBoxWidth / aspectRatio ;
} else {
maxCropBoxWidth = maxCropBoxHeight * aspectRatio ;
}
} // The minWidth/Height must be less than maxWidth/Height
cropBoxData . minWidth = Math . min ( minCropBoxWidth , maxCropBoxWidth ) ;
cropBoxData . minHeight = Math . min ( minCropBoxHeight , maxCropBoxHeight ) ;
cropBoxData . maxWidth = maxCropBoxWidth ;
cropBoxData . maxHeight = maxCropBoxHeight ;
}
if ( positionLimited ) {
if ( limited ) {
cropBoxData . minLeft = Math . max ( 0 , canvasData . left ) ;
cropBoxData . minTop = Math . max ( 0 , canvasData . top ) ;
cropBoxData . maxLeft = Math . min ( containerData . width , canvasData . left + canvasData . width ) - cropBoxData . width ;
cropBoxData . maxTop = Math . min ( containerData . height , canvasData . top + canvasData . height ) - cropBoxData . height ;
} else {
cropBoxData . minLeft = 0 ;
cropBoxData . minTop = 0 ;
cropBoxData . maxLeft = containerData . width - cropBoxData . width ;
cropBoxData . maxTop = containerData . height - cropBoxData . height ;
}
}
} ,
renderCropBox : function renderCropBox ( ) {
var options = this . options ,
containerData = this . containerData ,
cropBoxData = this . cropBoxData ;
if ( cropBoxData . width > cropBoxData . maxWidth || cropBoxData . width < cropBoxData . minWidth ) {
cropBoxData . left = cropBoxData . oldLeft ;
}
if ( cropBoxData . height > cropBoxData . maxHeight || cropBoxData . height < cropBoxData . minHeight ) {
cropBoxData . top = cropBoxData . oldTop ;
}
cropBoxData . width = Math . min ( Math . max ( cropBoxData . width , cropBoxData . minWidth ) , cropBoxData . maxWidth ) ;
cropBoxData . height = Math . min ( Math . max ( cropBoxData . height , cropBoxData . minHeight ) , cropBoxData . maxHeight ) ;
this . limitCropBox ( false , true ) ;
cropBoxData . left = Math . min ( Math . max ( cropBoxData . left , cropBoxData . minLeft ) , cropBoxData . maxLeft ) ;
cropBoxData . top = Math . min ( Math . max ( cropBoxData . top , cropBoxData . minTop ) , cropBoxData . maxTop ) ;
cropBoxData . oldLeft = cropBoxData . left ;
cropBoxData . oldTop = cropBoxData . top ;
if ( options . movable && options . cropBoxMovable ) {
// Turn to move the canvas when the crop box is equal to the container
setData ( this . face , DATA _ACTION , cropBoxData . width >= containerData . width && cropBoxData . height >= containerData . height ? ACTION _MOVE : ACTION _ALL ) ;
}
setStyle ( this . cropBox , assign ( {
width : cropBoxData . width ,
height : cropBoxData . height
} , getTransforms ( {
translateX : cropBoxData . left ,
translateY : cropBoxData . top
} ) ) ) ;
if ( this . cropped && this . limited ) {
this . limitCanvas ( true , true ) ;
}
if ( ! this . disabled ) {
this . output ( ) ;
}
} ,
output : function output ( ) {
this . preview ( ) ;
dispatchEvent ( this . element , EVENT _CROP , this . getData ( ) ) ;
}
} ;
var preview = {
initPreview : function initPreview ( ) {
var crossOrigin = this . crossOrigin ;
var preview = this . options . preview ;
var url = crossOrigin ? this . crossOriginUrl : this . url ;
var image = document . createElement ( 'img' ) ;
if ( crossOrigin ) {
image . crossOrigin = crossOrigin ;
}
image . src = url ;
this . viewBox . appendChild ( image ) ;
this . viewBoxImage = image ;
if ( ! preview ) {
return ;
}
var previews = preview ;
if ( typeof preview === 'string' ) {
previews = this . element . ownerDocument . querySelectorAll ( preview ) ;
} else if ( preview . querySelector ) {
previews = [ preview ] ;
}
this . previews = previews ;
forEach ( previews , function ( el ) {
var img = document . createElement ( 'img' ) ; // Save the original size for recover
setData ( el , DATA _PREVIEW , {
width : el . offsetWidth ,
height : el . offsetHeight ,
html : el . innerHTML
} ) ;
if ( crossOrigin ) {
img . crossOrigin = crossOrigin ;
}
img . src = url ;
/ * *
* Override img element styles
* Add ` display:block ` to avoid margin top issue
* Add ` height:auto ` to override ` height ` attribute on IE8
* ( Occur only when margin - top <= - height )
* /
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;"' ;
el . innerHTML = '' ;
el . appendChild ( img ) ;
} ) ;
} ,
resetPreview : function resetPreview ( ) {
forEach ( this . previews , function ( element ) {
var data = getData ( element , DATA _PREVIEW ) ;
setStyle ( element , {
width : data . width ,
height : data . height
} ) ;
element . innerHTML = data . html ;
removeData ( element , DATA _PREVIEW ) ;
} ) ;
} ,
preview : function preview ( ) {
var imageData = this . imageData ,
canvasData = this . canvasData ,
cropBoxData = this . cropBoxData ;
var cropBoxWidth = cropBoxData . width ,
cropBoxHeight = cropBoxData . height ;
var width = imageData . width ,
height = imageData . height ;
var left = cropBoxData . left - canvasData . left - imageData . left ;
var top = cropBoxData . top - canvasData . top - imageData . top ;
if ( ! this . cropped || this . disabled ) {
return ;
}
setStyle ( this . viewBoxImage , assign ( {
width : width ,
height : height
} , getTransforms ( assign ( {
translateX : - left ,
translateY : - top
} , imageData ) ) ) ) ;
forEach ( this . previews , function ( element ) {
var data = getData ( element , DATA _PREVIEW ) ;
var originalWidth = data . width ;
var originalHeight = data . height ;
var newWidth = originalWidth ;
var newHeight = originalHeight ;
var ratio = 1 ;
if ( cropBoxWidth ) {
ratio = originalWidth / cropBoxWidth ;
newHeight = cropBoxHeight * ratio ;
}
if ( cropBoxHeight && newHeight > originalHeight ) {
ratio = originalHeight / cropBoxHeight ;
newWidth = cropBoxWidth * ratio ;
newHeight = originalHeight ;
}
setStyle ( element , {
width : newWidth ,
height : newHeight
} ) ;
setStyle ( element . getElementsByTagName ( 'img' ) [ 0 ] , assign ( {
width : width * ratio ,
height : height * ratio
} , getTransforms ( assign ( {
translateX : - left * ratio ,
translateY : - top * ratio
} , imageData ) ) ) ) ;
} ) ;
}
} ;
var events = {
bind : function bind ( ) {
var element = this . element ,
options = this . options ,
cropper = this . cropper ;
if ( isFunction ( options . cropstart ) ) {
addListener ( element , EVENT _CROP _START , options . cropstart ) ;
}
if ( isFunction ( options . cropmove ) ) {
addListener ( element , EVENT _CROP _MOVE , options . cropmove ) ;
}
if ( isFunction ( options . cropend ) ) {
addListener ( element , EVENT _CROP _END , options . cropend ) ;
}
if ( isFunction ( options . crop ) ) {
addListener ( element , EVENT _CROP , options . crop ) ;
}
if ( isFunction ( options . zoom ) ) {
addListener ( element , EVENT _ZOOM , options . zoom ) ;
}
addListener ( cropper , EVENT _POINTER _DOWN , this . onCropStart = this . cropStart . bind ( this ) ) ;
if ( options . zoomable && options . zoomOnWheel ) {
addListener ( cropper , EVENT _WHEEL , this . onWheel = this . wheel . bind ( this ) ) ;
}
if ( options . toggleDragModeOnDblclick ) {
addListener ( cropper , EVENT _DBLCLICK , this . onDblclick = this . dblclick . bind ( this ) ) ;
}
addListener ( element . ownerDocument , EVENT _POINTER _MOVE , this . onCropMove = this . cropMove . bind ( this ) ) ;
addListener ( element . ownerDocument , EVENT _POINTER _UP , this . onCropEnd = this . cropEnd . bind ( this ) ) ;
if ( options . responsive ) {
addListener ( window , EVENT _RESIZE , this . onResize = this . resize . bind ( this ) ) ;
}
} ,
unbind : function unbind ( ) {
var element = this . element ,
options = this . options ,
cropper = this . cropper ;
if ( isFunction ( options . cropstart ) ) {
removeListener ( element , EVENT _CROP _START , options . cropstart ) ;
}
if ( isFunction ( options . cropmove ) ) {
removeListener ( element , EVENT _CROP _MOVE , options . cropmove ) ;
}
if ( isFunction ( options . cropend ) ) {
removeListener ( element , EVENT _CROP _END , options . cropend ) ;
}
if ( isFunction ( options . crop ) ) {
removeListener ( element , EVENT _CROP , options . crop ) ;
}
if ( isFunction ( options . zoom ) ) {
removeListener ( element , EVENT _ZOOM , options . zoom ) ;
}
removeListener ( cropper , EVENT _POINTER _DOWN , this . onCropStart ) ;
if ( options . zoomable && options . zoomOnWheel ) {
removeListener ( cropper , EVENT _WHEEL , this . onWheel ) ;
}
if ( options . toggleDragModeOnDblclick ) {
removeListener ( cropper , EVENT _DBLCLICK , this . onDblclick ) ;
}
removeListener ( element . ownerDocument , EVENT _POINTER _MOVE , this . onCropMove ) ;
removeListener ( element . ownerDocument , EVENT _POINTER _UP , this . onCropEnd ) ;
if ( options . responsive ) {
removeListener ( window , EVENT _RESIZE , this . onResize ) ;
}
}
} ;
var handlers = {
resize : function resize ( ) {
var options = this . options ,
container = this . container ,
containerData = this . containerData ;
var minContainerWidth = Number ( options . minContainerWidth ) || 200 ;
var minContainerHeight = Number ( options . minContainerHeight ) || 100 ;
if ( this . disabled || containerData . width <= minContainerWidth || containerData . height <= minContainerHeight ) {
return ;
}
var ratio = container . offsetWidth / containerData . width ; // Resize when width changed or height changed
if ( ratio !== 1 || container . offsetHeight !== containerData . height ) {
var canvasData ;
var cropBoxData ;
if ( options . restore ) {
canvasData = this . getCanvasData ( ) ;
cropBoxData = this . getCropBoxData ( ) ;
}
this . render ( ) ;
if ( options . restore ) {
this . setCanvasData ( forEach ( canvasData , function ( n , i ) {
canvasData [ i ] = n * ratio ;
} ) ) ;
this . setCropBoxData ( forEach ( cropBoxData , function ( n , i ) {
cropBoxData [ i ] = n * ratio ;
} ) ) ;
}
}
} ,
dblclick : function dblclick ( ) {
if ( this . disabled || this . options . dragMode === DRAG _MODE _NONE ) {
return ;
}
this . setDragMode ( hasClass ( this . dragBox , CLASS _CROP ) ? DRAG _MODE _MOVE : DRAG _MODE _CROP ) ;
} ,
wheel : function wheel ( e ) {
var _this = this ;
var ratio = Number ( this . options . wheelZoomRatio ) || 0.1 ;
var delta = 1 ;
if ( this . disabled ) {
return ;
}
e . preventDefault ( ) ; // Limit wheel speed to prevent zoom too fast (#21)
if ( this . wheeling ) {
return ;
}
this . wheeling = true ;
setTimeout ( function ( ) {
_this . wheeling = false ;
} , 50 ) ;
if ( e . deltaY ) {
delta = e . deltaY > 0 ? 1 : - 1 ;
} else if ( e . wheelDelta ) {
delta = - e . wheelDelta / 120 ;
} else if ( e . detail ) {
delta = e . detail > 0 ? 1 : - 1 ;
}
this . zoom ( - delta * ratio , e ) ;
} ,
cropStart : function cropStart ( e ) {
if ( this . disabled ) {
return ;
}
var options = this . options ,
pointers = this . pointers ;
var action ;
if ( e . changedTouches ) {
// Handle touch event
forEach ( e . changedTouches , function ( touch ) {
pointers [ touch . identifier ] = getPointer ( touch ) ;
} ) ;
} else {
// Handle mouse event and pointer event
pointers [ e . pointerId || 0 ] = getPointer ( e ) ;
}
if ( Object . keys ( pointers ) . length > 1 && options . zoomable && options . zoomOnTouch ) {
action = ACTION _ZOOM ;
} else {
action = getData ( e . target , DATA _ACTION ) ;
}
if ( ! REGEXP _ACTIONS . test ( action ) ) {
return ;
}
if ( dispatchEvent ( this . element , EVENT _CROP _START , {
originalEvent : e ,
action : action
} ) === false ) {
return ;
} // This line is required for preventing page zooming in iOS browsers
e . preventDefault ( ) ;
this . action = action ;
this . cropping = false ;
if ( action === ACTION _CROP ) {
this . cropping = true ;
addClass ( this . dragBox , CLASS _MODAL ) ;
}
} ,
cropMove : function cropMove ( e ) {
var action = this . action ;
if ( this . disabled || ! action ) {
return ;
}
var pointers = this . pointers ;
e . preventDefault ( ) ;
if ( dispatchEvent ( this . element , EVENT _CROP _MOVE , {
originalEvent : e ,
action : action
} ) === false ) {
return ;
}
if ( e . changedTouches ) {
forEach ( e . changedTouches , function ( touch ) {
// The first parameter should not be undefined (#432)
assign ( pointers [ touch . identifier ] || { } , getPointer ( touch , true ) ) ;
} ) ;
} else {
assign ( pointers [ e . pointerId || 0 ] || { } , getPointer ( e , true ) ) ;
}
this . change ( e ) ;
} ,
cropEnd : function cropEnd ( e ) {
if ( this . disabled ) {
return ;
}
var action = this . action ,
pointers = this . pointers ;
if ( e . changedTouches ) {
forEach ( e . changedTouches , function ( touch ) {
delete pointers [ touch . identifier ] ;
} ) ;
} else {
delete pointers [ e . pointerId || 0 ] ;
}
if ( ! action ) {
return ;
}
e . preventDefault ( ) ;
if ( ! Object . keys ( pointers ) . length ) {
this . action = '' ;
}
if ( this . cropping ) {
this . cropping = false ;
toggleClass ( this . dragBox , CLASS _MODAL , this . cropped && this . options . modal ) ;
}
dispatchEvent ( this . element , EVENT _CROP _END , {
originalEvent : e ,
action : action
} ) ;
}
} ;
var change = {
change : function change ( e ) {
var options = this . options ,
canvasData = this . canvasData ,
containerData = this . containerData ,
cropBoxData = this . cropBoxData ,
pointers = this . pointers ;
var action = this . action ;
var aspectRatio = options . aspectRatio ;
var left = cropBoxData . left ,
top = cropBoxData . top ,
width = cropBoxData . width ,
height = cropBoxData . height ;
var right = left + width ;
var bottom = top + height ;
var minLeft = 0 ;
var minTop = 0 ;
var maxWidth = containerData . width ;
var maxHeight = containerData . height ;
var renderable = true ;
var offset ; // Locking aspect ratio in "free mode" by holding shift key
if ( ! aspectRatio && e . shiftKey ) {
aspectRatio = width && height ? width / height : 1 ;
}
if ( this . limited ) {
minLeft = cropBoxData . minLeft ;
minTop = cropBoxData . minTop ;
maxWidth = minLeft + Math . min ( containerData . width , canvasData . width , canvasData . left + canvasData . width ) ;
maxHeight = minTop + Math . min ( containerData . height , canvasData . height , canvasData . top + canvasData . height ) ;
}
var pointer = pointers [ Object . keys ( pointers ) [ 0 ] ] ;
var range = {
x : pointer . endX - pointer . startX ,
y : pointer . endY - pointer . startY
} ;
var check = function check ( side ) {
switch ( side ) {
case ACTION _EAST :
if ( right + range . x > maxWidth ) {
range . x = maxWidth - right ;
}
break ;
case ACTION _WEST :
if ( left + range . x < minLeft ) {
range . x = minLeft - left ;
}
break ;
case ACTION _NORTH :
if ( top + range . y < minTop ) {
range . y = minTop - top ;
}
break ;
case ACTION _SOUTH :
if ( bottom + range . y > maxHeight ) {
range . y = maxHeight - bottom ;
}
break ;
default :
}
} ;
switch ( action ) {
// Move crop box
case ACTION _ALL :
left += range . x ;
top += range . y ;
break ;
// Resize crop box
case ACTION _EAST :
if ( range . x >= 0 && ( right >= maxWidth || aspectRatio && ( top <= minTop || bottom >= maxHeight ) ) ) {
renderable = false ;
break ;
}
check ( ACTION _EAST ) ;
width += range . x ;
if ( width < 0 ) {
action = ACTION _WEST ;
width = - width ;
left -= width ;
}
if ( aspectRatio ) {
height = width / aspectRatio ;
top += ( cropBoxData . height - height ) / 2 ;
}
break ;
case ACTION _NORTH :
if ( range . y <= 0 && ( top <= minTop || aspectRatio && ( left <= minLeft || right >= maxWidth ) ) ) {
renderable = false ;
break ;
}
check ( ACTION _NORTH ) ;
height -= range . y ;
top += range . y ;
if ( height < 0 ) {
action = ACTION _SOUTH ;
height = - height ;
top -= height ;
}
if ( aspectRatio ) {
width = height * aspectRatio ;
left += ( cropBoxData . width - width ) / 2 ;
}
break ;
case ACTION _WEST :
if ( range . x <= 0 && ( left <= minLeft || aspectRatio && ( top <= minTop || bottom >= maxHeight ) ) ) {
renderable = false ;
break ;
}
check ( ACTION _WEST ) ;
width -= range . x ;
left += range . x ;
if ( width < 0 ) {
action = ACTION _EAST ;
width = - width ;
left -= width ;
}
if ( aspectRatio ) {
height = width / aspectRatio ;
top += ( cropBoxData . height - height ) / 2 ;
}
break ;
case ACTION _SOUTH :
if ( range . y >= 0 && ( bottom >= maxHeight || aspectRatio && ( left <= minLeft || right >= maxWidth ) ) ) {
renderable = false ;
break ;
}
check ( ACTION _SOUTH ) ;
height += range . y ;
if ( height < 0 ) {
action = ACTION _NORTH ;
height = - height ;
top -= height ;
}
if ( aspectRatio ) {
width = height * aspectRatio ;
left += ( cropBoxData . width - width ) / 2 ;
}
break ;
case ACTION _NORTH _EAST :
if ( aspectRatio ) {
if ( range . y <= 0 && ( top <= minTop || right >= maxWidth ) ) {
renderable = false ;
break ;
}
check ( ACTION _NORTH ) ;
height -= range . y ;
top += range . y ;
width = height * aspectRatio ;
} else {
check ( ACTION _NORTH ) ;
check ( ACTION _EAST ) ;
if ( range . x >= 0 ) {
if ( right < maxWidth ) {
width += range . x ;
} else if ( range . y <= 0 && top <= minTop ) {
renderable = false ;
}
} else {
width += range . x ;
}
if ( range . y <= 0 ) {
if ( top > minTop ) {
height -= range . y ;
top += range . y ;
}
} else {
height -= range . y ;
top += range . y ;
}
}
if ( width < 0 && height < 0 ) {
action = ACTION _SOUTH _WEST ;
height = - height ;
width = - width ;
top -= height ;
left -= width ;
} else if ( width < 0 ) {
action = ACTION _NORTH _WEST ;
width = - width ;
left -= width ;
} else if ( height < 0 ) {
action = ACTION _SOUTH _EAST ;
height = - height ;
top -= height ;
}
break ;
case ACTION _NORTH _WEST :
if ( aspectRatio ) {
if ( range . y <= 0 && ( top <= minTop || left <= minLeft ) ) {
renderable = false ;
break ;
}
check ( ACTION _NORTH ) ;
height -= range . y ;
top += range . y ;
width = height * aspectRatio ;
left += cropBoxData . width - width ;
} else {
check ( ACTION _NORTH ) ;
check ( ACTION _WEST ) ;
if ( range . x <= 0 ) {
if ( left > minLeft ) {
width -= range . x ;
left += range . x ;
} else if ( range . y <= 0 && top <= minTop ) {
renderable = false ;
}
} else {
width -= range . x ;
left += range . x ;
}
if ( range . y <= 0 ) {
if ( top > minTop ) {
height -= range . y ;
top += range . y ;
}
} else {
height -= range . y ;
top += range . y ;
}
}
if ( width < 0 && height < 0 ) {
action = ACTION _SOUTH _EAST ;
height = - height ;
width = - width ;
top -= height ;
left -= width ;
} else if ( width < 0 ) {
action = ACTION _NORTH _EAST ;
width = - width ;
left -= width ;
} else if ( height < 0 ) {
action = ACTION _SOUTH _WEST ;
height = - height ;
top -= height ;
}
break ;
case ACTION _SOUTH _WEST :
if ( aspectRatio ) {
if ( range . x <= 0 && ( left <= minLeft || bottom >= maxHeight ) ) {
renderable = false ;
break ;
}
check ( ACTION _WEST ) ;
width -= range . x ;
left += range . x ;
height = width / aspectRatio ;
} else {
check ( ACTION _SOUTH ) ;
check ( ACTION _WEST ) ;
if ( range . x <= 0 ) {
if ( left > minLeft ) {
width -= range . x ;
left += range . x ;
} else if ( range . y >= 0 && bottom >= maxHeight ) {
renderable = false ;
}
} else {
width -= range . x ;
left += range . x ;
}
if ( range . y >= 0 ) {
if ( bottom < maxHeight ) {
height += range . y ;
}
} else {
height += range . y ;
}
}
if ( width < 0 && height < 0 ) {
action = ACTION _NORTH _EAST ;
height = - height ;
width = - width ;
top -= height ;
left -= width ;
} else if ( width < 0 ) {
action = ACTION _SOUTH _EAST ;
width = - width ;
left -= width ;
} else if ( height < 0 ) {
action = ACTION _NORTH _WEST ;
height = - height ;
top -= height ;
}
break ;
case ACTION _SOUTH _EAST :
if ( aspectRatio ) {
if ( range . x >= 0 && ( right >= maxWidth || bottom >= maxHeight ) ) {
renderable = false ;
break ;
}
check ( ACTION _EAST ) ;
width += range . x ;
height = width / aspectRatio ;
} else {
check ( ACTION _SOUTH ) ;
check ( ACTION _EAST ) ;
if ( range . x >= 0 ) {
if ( right < maxWidth ) {
width += range . x ;
} else if ( range . y >= 0 && bottom >= maxHeight ) {
renderable = false ;
}
} else {
width += range . x ;
}
if ( range . y >= 0 ) {
if ( bottom < maxHeight ) {
height += range . y ;
}
} else {
height += range . y ;
}
}
if ( width < 0 && height < 0 ) {
action = ACTION _NORTH _WEST ;
height = - height ;
width = - width ;
top -= height ;
left -= width ;
} else if ( width < 0 ) {
action = ACTION _SOUTH _WEST ;
width = - width ;
left -= width ;
} else if ( height < 0 ) {
action = ACTION _NORTH _EAST ;
height = - height ;
top -= height ;
}
break ;
// Move canvas
case ACTION _MOVE :
this . move ( range . x , range . y ) ;
renderable = false ;
break ;
// Zoom canvas
case ACTION _ZOOM :
this . zoom ( getMaxZoomRatio ( pointers ) , e ) ;
renderable = false ;
break ;
// Create crop box
case ACTION _CROP :
if ( ! range . x || ! range . y ) {
renderable = false ;
break ;
}
offset = getOffset ( this . cropper ) ;
left = pointer . startX - offset . left ;
top = pointer . startY - offset . top ;
width = cropBoxData . minWidth ;
height = cropBoxData . minHeight ;
if ( range . x > 0 ) {
action = range . y > 0 ? ACTION _SOUTH _EAST : ACTION _NORTH _EAST ;
} else if ( range . x < 0 ) {
left -= width ;
action = range . y > 0 ? ACTION _SOUTH _WEST : ACTION _NORTH _WEST ;
}
if ( range . y < 0 ) {
top -= height ;
} // Show the crop box if is hidden
if ( ! this . cropped ) {
removeClass ( this . cropBox , CLASS _HIDDEN ) ;
this . cropped = true ;
if ( this . limited ) {
this . limitCropBox ( true , true ) ;
}
}
break ;
default :
}
if ( renderable ) {
cropBoxData . width = width ;
cropBoxData . height = height ;
cropBoxData . left = left ;
cropBoxData . top = top ;
this . action = action ;
this . renderCropBox ( ) ;
} // Override
forEach ( pointers , function ( p ) {
p . startX = p . endX ;
p . startY = p . endY ;
} ) ;
}
} ;
var methods = {
// Show the crop box manually
crop : function crop ( ) {
if ( this . ready && ! this . cropped && ! this . disabled ) {
this . cropped = true ;
this . limitCropBox ( true , true ) ;
if ( this . options . modal ) {
addClass ( this . dragBox , CLASS _MODAL ) ;
}
removeClass ( this . cropBox , CLASS _HIDDEN ) ;
this . setCropBoxData ( this . initialCropBoxData ) ;
}
return this ;
} ,
// Reset the image and crop box to their initial states
reset : function reset ( ) {
if ( this . ready && ! this . disabled ) {
this . imageData = assign ( { } , this . initialImageData ) ;
this . canvasData = assign ( { } , this . initialCanvasData ) ;
this . cropBoxData = assign ( { } , this . initialCropBoxData ) ;
this . renderCanvas ( ) ;
if ( this . cropped ) {
this . renderCropBox ( ) ;
}
}
return this ;
} ,
// Clear the crop box
clear : function clear ( ) {
if ( this . cropped && ! this . disabled ) {
assign ( this . cropBoxData , {
left : 0 ,
top : 0 ,
width : 0 ,
height : 0
} ) ;
this . cropped = false ;
this . renderCropBox ( ) ;
this . limitCanvas ( true , true ) ; // Render canvas after crop box rendered
this . renderCanvas ( ) ;
removeClass ( this . dragBox , CLASS _MODAL ) ;
addClass ( this . cropBox , CLASS _HIDDEN ) ;
}
return this ;
} ,
/ * *
* Replace the image ' s src and rebuild the cropper
* @ param { string } url - The new URL .
* @ param { boolean } [ hasSameSize ] - Indicate if the new image has the same size as the old one .
* @ returns { Cropper } this
* /
replace : function replace ( url ) {
var hasSameSize = arguments . length > 1 && arguments [ 1 ] !== undefined ? arguments [ 1 ] : false ;
if ( ! this . disabled && url ) {
if ( this . isImg ) {
this . element . src = url ;
}
if ( hasSameSize ) {
this . url = url ;
this . image . src = url ;
if ( this . ready ) {
this . viewBoxImage . src = url ;
forEach ( this . previews , function ( element ) {
element . getElementsByTagName ( 'img' ) [ 0 ] . src = url ;
} ) ;
}
} else {
if ( this . isImg ) {
this . replaced = true ;
}
this . options . data = null ;
this . uncreate ( ) ;
this . load ( url ) ;
}
}
return this ;
} ,
// Enable (unfreeze) the cropper
enable : function enable ( ) {
if ( this . ready && this . disabled ) {
this . disabled = false ;
removeClass ( this . cropper , CLASS _DISABLED ) ;
}
return this ;
} ,
// Disable (freeze) the cropper
disable : function disable ( ) {
if ( this . ready && ! this . disabled ) {
this . disabled = true ;
addClass ( this . cropper , CLASS _DISABLED ) ;
}
return this ;
} ,
/ * *
* Destroy the cropper and remove the instance from the image
* @ returns { Cropper } this
* /
destroy : function destroy ( ) {
var element = this . element ;
if ( ! element [ NAMESPACE ] ) {
return this ;
}
element [ NAMESPACE ] = undefined ;
if ( this . isImg && this . replaced ) {
element . src = this . originalUrl ;
}
this . uncreate ( ) ;
return this ;
} ,
/ * *
* Move the canvas with relative offsets
* @ param { number } offsetX - The relative offset distance on the x - axis .
* @ param { number } [ offsetY = offsetX ] - The relative offset distance on the y - axis .
* @ returns { Cropper } this
* /
move : function move ( offsetX ) {
var offsetY = arguments . length > 1 && arguments [ 1 ] !== undefined ? arguments [ 1 ] : offsetX ;
var _this$canvasData = this . canvasData ,
left = _this$canvasData . left ,
top = _this$canvasData . top ;
return this . moveTo ( isUndefined ( offsetX ) ? offsetX : left + Number ( offsetX ) , isUndefined ( offsetY ) ? offsetY : top + Number ( offsetY ) ) ;
} ,
/ * *
* Move the canvas to an absolute point
* @ param { number } x - The x - axis coordinate .
* @ param { number } [ y = x ] - The y - axis coordinate .
* @ returns { Cropper } this
* /
moveTo : function moveTo ( x ) {
var y = arguments . length > 1 && arguments [ 1 ] !== undefined ? arguments [ 1 ] : x ;
var canvasData = this . canvasData ;
var changed = false ;
x = Number ( x ) ;
y = Number ( y ) ;
if ( this . ready && ! this . disabled && this . options . movable ) {
if ( isNumber ( x ) ) {
canvasData . left = x ;
changed = true ;
}
if ( isNumber ( y ) ) {
canvasData . top = y ;
changed = true ;
}
if ( changed ) {
this . renderCanvas ( true ) ;
}
}
return this ;
} ,
/ * *
* Zoom the canvas with a relative ratio
* @ param { number } ratio - The target ratio .
* @ param { Event } _originalEvent - The original event if any .
* @ returns { Cropper } this
* /
zoom : function zoom ( ratio , _originalEvent ) {
var canvasData = this . canvasData ;
ratio = Number ( ratio ) ;
if ( ratio < 0 ) {
ratio = 1 / ( 1 - ratio ) ;
} else {
ratio = 1 + ratio ;
}
return this . zoomTo ( canvasData . width * ratio / canvasData . naturalWidth , null , _originalEvent ) ;
} ,
/ * *
* Zoom the canvas to an absolute ratio
* @ param { number } ratio - The target ratio .
* @ param { Object } pivot - The zoom pivot point coordinate .
* @ param { Event } _originalEvent - The original event if any .
* @ returns { Cropper } this
* /
zoomTo : function zoomTo ( ratio , pivot , _originalEvent ) {
var options = this . options ,
canvasData = this . canvasData ;
var width = canvasData . width ,
height = canvasData . height ,
naturalWidth = canvasData . naturalWidth ,
naturalHeight = canvasData . naturalHeight ;
ratio = Number ( ratio ) ;
if ( ratio >= 0 && this . ready && ! this . disabled && options . zoomable ) {
var newWidth = naturalWidth * ratio ;
var newHeight = naturalHeight * ratio ;
if ( dispatchEvent ( this . element , EVENT _ZOOM , {
ratio : ratio ,
oldRatio : width / naturalWidth ,
originalEvent : _originalEvent
} ) === false ) {
return this ;
}
if ( _originalEvent ) {
var pointers = this . pointers ;
var offset = getOffset ( this . cropper ) ;
var center = pointers && Object . keys ( pointers ) . length ? getPointersCenter ( pointers ) : {
pageX : _originalEvent . pageX ,
pageY : _originalEvent . pageY
} ; // Zoom from the triggering point of the event
canvasData . left -= ( newWidth - width ) * ( ( center . pageX - offset . left - canvasData . left ) / width ) ;
canvasData . top -= ( newHeight - height ) * ( ( center . pageY - offset . top - canvasData . top ) / height ) ;
} else if ( isPlainObject ( pivot ) && isNumber ( pivot . x ) && isNumber ( pivot . y ) ) {
canvasData . left -= ( newWidth - width ) * ( ( pivot . x - canvasData . left ) / width ) ;
canvasData . top -= ( newHeight - height ) * ( ( pivot . y - canvasData . top ) / height ) ;
} else {
// Zoom from the center of the canvas
canvasData . left -= ( newWidth - width ) / 2 ;
canvasData . top -= ( newHeight - height ) / 2 ;
}
canvasData . width = newWidth ;
canvasData . height = newHeight ;
this . renderCanvas ( true ) ;
}
return this ;
} ,
/ * *
* Rotate the canvas with a relative degree
* @ param { number } degree - The rotate degree .
* @ returns { Cropper } this
* /
rotate : function rotate ( degree ) {
return this . rotateTo ( ( this . imageData . rotate || 0 ) + Number ( degree ) ) ;
} ,
/ * *
* Rotate the canvas to an absolute degree
* @ param { number } degree - The rotate degree .
* @ returns { Cropper } this
* /
rotateTo : function rotateTo ( degree ) {
degree = Number ( degree ) ;
if ( isNumber ( degree ) && this . ready && ! this . disabled && this . options . rotatable ) {
this . imageData . rotate = degree % 360 ;
this . renderCanvas ( true , true ) ;
}
return this ;
} ,
/ * *
* Scale the image on the x - axis .
* @ param { number } scaleX - The scale ratio on the x - axis .
* @ returns { Cropper } this
* /
scaleX : function scaleX ( _scaleX ) {
var scaleY = this . imageData . scaleY ;
return this . scale ( _scaleX , isNumber ( scaleY ) ? scaleY : 1 ) ;
} ,
/ * *
* Scale the image on the y - axis .
* @ param { number } scaleY - The scale ratio on the y - axis .
* @ returns { Cropper } this
* /
scaleY : function scaleY ( _scaleY ) {
var scaleX = this . imageData . scaleX ;
return this . scale ( isNumber ( scaleX ) ? scaleX : 1 , _scaleY ) ;
} ,
/ * *
* Scale the image
* @ param { number } scaleX - The scale ratio on the x - axis .
* @ param { number } [ scaleY = scaleX ] - The scale ratio on the y - axis .
* @ returns { Cropper } this
* /
scale : function scale ( scaleX ) {
var scaleY = arguments . length > 1 && arguments [ 1 ] !== undefined ? arguments [ 1 ] : scaleX ;
var imageData = this . imageData ;
var transformed = false ;
scaleX = Number ( scaleX ) ;
scaleY = Number ( scaleY ) ;
if ( this . ready && ! this . disabled && this . options . scalable ) {
if ( isNumber ( scaleX ) ) {
imageData . scaleX = scaleX ;
transformed = true ;
}
if ( isNumber ( scaleY ) ) {
imageData . scaleY = scaleY ;
transformed = true ;
}
if ( transformed ) {
this . renderCanvas ( true , true ) ;
}
}
return this ;
} ,
/ * *
* Get the cropped area position and size data ( base on the original image )
* @ param { boolean } [ rounded = false ] - Indicate if round the data values or not .
* @ returns { Object } The result cropped data .
* /
getData : function getData$$1 ( ) {
var rounded = arguments . length > 0 && arguments [ 0 ] !== undefined ? arguments [ 0 ] : false ;
var options = this . options ,
imageData = this . imageData ,
canvasData = this . canvasData ,
cropBoxData = this . cropBoxData ;
var data ;
if ( this . ready && this . cropped ) {
data = {
x : cropBoxData . left - canvasData . left ,
y : cropBoxData . top - canvasData . top ,
width : cropBoxData . width ,
height : cropBoxData . height
} ;
var ratio = imageData . width / imageData . naturalWidth ;
forEach ( data , function ( n , i ) {
data [ i ] = n / ratio ;
} ) ;
if ( rounded ) {
// In case rounding off leads to extra 1px in right or bottom border
// we should round the top-left corner and the dimension (#343).
var bottom = Math . round ( data . y + data . height ) ;
var right = Math . round ( data . x + data . width ) ;
data . x = Math . round ( data . x ) ;
data . y = Math . round ( data . y ) ;
data . width = right - data . x ;
data . height = bottom - data . y ;
}
} else {
data = {
x : 0 ,
y : 0 ,
width : 0 ,
height : 0
} ;
}
if ( options . rotatable ) {
data . rotate = imageData . rotate || 0 ;
}
if ( options . scalable ) {
data . scaleX = imageData . scaleX || 1 ;
data . scaleY = imageData . scaleY || 1 ;
}
return data ;
} ,
/ * *
* Set the cropped area position and size with new data
* @ param { Object } data - The new data .
* @ returns { Cropper } this
* /
setData : function setData$$1 ( data ) {
var options = this . options ,
imageData = this . imageData ,
canvasData = this . canvasData ;
var cropBoxData = { } ;
if ( this . ready && ! this . disabled && isPlainObject ( data ) ) {
var transformed = false ;
if ( options . rotatable ) {
if ( isNumber ( data . rotate ) && data . rotate !== imageData . rotate ) {
imageData . rotate = data . rotate ;
transformed = true ;
}
}
if ( options . scalable ) {
if ( isNumber ( data . scaleX ) && data . scaleX !== imageData . scaleX ) {
imageData . scaleX = data . scaleX ;
transformed = true ;
}
if ( isNumber ( data . scaleY ) && data . scaleY !== imageData . scaleY ) {
imageData . scaleY = data . scaleY ;
transformed = true ;
}
}
if ( transformed ) {
this . renderCanvas ( true , true ) ;
}
var ratio = imageData . width / imageData . naturalWidth ;
if ( isNumber ( data . x ) ) {
cropBoxData . left = data . x * ratio + canvasData . left ;
}
if ( isNumber ( data . y ) ) {
cropBoxData . top = data . y * ratio + canvasData . top ;
}
if ( isNumber ( data . width ) ) {
cropBoxData . width = data . width * ratio ;
}
if ( isNumber ( data . height ) ) {
cropBoxData . height = data . height * ratio ;
}
this . setCropBoxData ( cropBoxData ) ;
}
return this ;
} ,
/ * *
* Get the container size data .
* @ returns { Object } The result container data .
* /
getContainerData : function getContainerData ( ) {
return this . ready ? assign ( { } , this . containerData ) : { } ;
} ,
/ * *
* Get the image position and size data .
* @ returns { Object } The result image data .
* /
getImageData : function getImageData ( ) {
return this . sized ? assign ( { } , this . imageData ) : { } ;
} ,
/ * *
* Get the canvas position and size data .
* @ returns { Object } The result canvas data .
* /
getCanvasData : function getCanvasData ( ) {
var canvasData = this . canvasData ;
var data = { } ;
if ( this . ready ) {
forEach ( [ 'left' , 'top' , 'width' , 'height' , 'naturalWidth' , 'naturalHeight' ] , function ( n ) {
data [ n ] = canvasData [ n ] ;
} ) ;
}
return data ;
} ,
/ * *
* Set the canvas position and size with new data .
* @ param { Object } data - The new canvas data .
* @ returns { Cropper } this
* /
setCanvasData : function setCanvasData ( data ) {
var canvasData = this . canvasData ;
var aspectRatio = canvasData . aspectRatio ;
if ( this . ready && ! this . disabled && isPlainObject ( data ) ) {
if ( isNumber ( data . left ) ) {
canvasData . left = data . left ;
}
if ( isNumber ( data . top ) ) {
canvasData . top = data . top ;
}
if ( isNumber ( data . width ) ) {
canvasData . width = data . width ;
canvasData . height = data . width / aspectRatio ;
} else if ( isNumber ( data . height ) ) {
canvasData . height = data . height ;
canvasData . width = data . height * aspectRatio ;
}
this . renderCanvas ( true ) ;
}
return this ;
} ,
/ * *
* Get the crop box position and size data .
* @ returns { Object } The result crop box data .
* /
getCropBoxData : function getCropBoxData ( ) {
var cropBoxData = this . cropBoxData ;
var data ;
if ( this . ready && this . cropped ) {
data = {
left : cropBoxData . left ,
top : cropBoxData . top ,
width : cropBoxData . width ,
height : cropBoxData . height
} ;
}
return data || { } ;
} ,
/ * *
* Set the crop box position and size with new data .
* @ param { Object } data - The new crop box data .
* @ returns { Cropper } this
* /
setCropBoxData : function setCropBoxData ( data ) {
var cropBoxData = this . cropBoxData ;
var aspectRatio = this . options . aspectRatio ;
var widthChanged ;
var heightChanged ;
if ( this . ready && this . cropped && ! this . disabled && isPlainObject ( data ) ) {
if ( isNumber ( data . left ) ) {
cropBoxData . left = data . left ;
}
if ( isNumber ( data . top ) ) {
cropBoxData . top = data . top ;
}
if ( isNumber ( data . width ) && data . width !== cropBoxData . width ) {
widthChanged = true ;
cropBoxData . width = data . width ;
}
if ( isNumber ( data . height ) && data . height !== cropBoxData . height ) {
heightChanged = true ;
cropBoxData . height = data . height ;
}
if ( aspectRatio ) {
if ( widthChanged ) {
cropBoxData . height = cropBoxData . width / aspectRatio ;
} else if ( heightChanged ) {
cropBoxData . width = cropBoxData . height * aspectRatio ;
}
}
this . renderCropBox ( ) ;
}
return this ;
} ,
/ * *
* Get a canvas drawn the cropped image .
* @ param { Object } [ options = { } ] - The config options .
* @ returns { HTMLCanvasElement } - The result canvas .
* /
getCroppedCanvas : function getCroppedCanvas ( ) {
var options = arguments . length > 0 && arguments [ 0 ] !== undefined ? arguments [ 0 ] : { } ;
if ( ! this . ready || ! window . HTMLCanvasElement ) {
return null ;
}
var canvasData = this . canvasData ;
var source = getSourceCanvas ( this . image , this . imageData , canvasData , options ) ; // Returns the source canvas if it is not cropped.
if ( ! this . cropped ) {
return source ;
}
var _this$getData = this . getData ( ) ,
initialX = _this$getData . x ,
initialY = _this$getData . y ,
initialWidth = _this$getData . width ,
initialHeight = _this$getData . height ;
var ratio = source . width / Math . floor ( canvasData . naturalWidth ) ;
if ( ratio !== 1 ) {
initialX *= ratio ;
initialY *= ratio ;
initialWidth *= ratio ;
initialHeight *= ratio ;
}
var aspectRatio = initialWidth / initialHeight ;
var maxSizes = getAdjustedSizes ( {
aspectRatio : aspectRatio ,
width : options . maxWidth || Infinity ,
height : options . maxHeight || Infinity
} ) ;
var minSizes = getAdjustedSizes ( {
aspectRatio : aspectRatio ,
width : options . minWidth || 0 ,
height : options . minHeight || 0
} , 'cover' ) ;
var _getAdjustedSizes = getAdjustedSizes ( {
aspectRatio : aspectRatio ,
width : options . width || ( ratio !== 1 ? source . width : initialWidth ) ,
height : options . height || ( ratio !== 1 ? source . height : initialHeight )
} ) ,
width = _getAdjustedSizes . width ,
height = _getAdjustedSizes . height ;
width = Math . min ( maxSizes . width , Math . max ( minSizes . width , width ) ) ;
height = Math . min ( maxSizes . height , Math . max ( minSizes . height , height ) ) ;
var canvas = document . createElement ( 'canvas' ) ;
var context = canvas . getContext ( '2d' ) ;
canvas . width = normalizeDecimalNumber ( width ) ;
canvas . height = normalizeDecimalNumber ( height ) ;
context . fillStyle = options . fillColor || 'transparent' ;
context . fillRect ( 0 , 0 , width , height ) ;
var _options$imageSmoothi = options . imageSmoothingEnabled ,
imageSmoothingEnabled = _options$imageSmoothi === void 0 ? true : _options$imageSmoothi ,
imageSmoothingQuality = options . imageSmoothingQuality ;
context . imageSmoothingEnabled = imageSmoothingEnabled ;
if ( imageSmoothingQuality ) {
context . imageSmoothingQuality = imageSmoothingQuality ;
} // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D.drawImage
var sourceWidth = source . width ;
var sourceHeight = source . height ; // Source canvas parameters
var srcX = initialX ;
var srcY = initialY ;
var srcWidth ;
var srcHeight ; // Destination canvas parameters
var dstX ;
var dstY ;
var dstWidth ;
var dstHeight ;
if ( srcX <= - initialWidth || srcX > sourceWidth ) {
srcX = 0 ;
srcWidth = 0 ;
dstX = 0 ;
dstWidth = 0 ;
} else if ( srcX <= 0 ) {
dstX = - srcX ;
srcX = 0 ;
srcWidth = Math . min ( sourceWidth , initialWidth + srcX ) ;
dstWidth = srcWidth ;
} else if ( srcX <= sourceWidth ) {
dstX = 0 ;
srcWidth = Math . min ( initialWidth , sourceWidth - srcX ) ;
dstWidth = srcWidth ;
}
if ( srcWidth <= 0 || srcY <= - initialHeight || srcY > sourceHeight ) {
srcY = 0 ;
srcHeight = 0 ;
dstY = 0 ;
dstHeight = 0 ;
} else if ( srcY <= 0 ) {
dstY = - srcY ;
srcY = 0 ;
srcHeight = Math . min ( sourceHeight , initialHeight + srcY ) ;
dstHeight = srcHeight ;
} else if ( srcY <= sourceHeight ) {
dstY = 0 ;
srcHeight = Math . min ( initialHeight , sourceHeight - srcY ) ;
dstHeight = srcHeight ;
}
var params = [ srcX , srcY , srcWidth , srcHeight ] ; // Avoid "IndexSizeError"
if ( dstWidth > 0 && dstHeight > 0 ) {
var scale = width / initialWidth ;
params . push ( dstX * scale , dstY * scale , dstWidth * scale , dstHeight * scale ) ;
} // All the numerical parameters should be integer for `drawImage`
// https://github.com/fengyuanchen/cropper/issues/476
context . drawImage . apply ( context , [ source ] . concat ( _toConsumableArray ( params . map ( function ( param ) {
return Math . floor ( normalizeDecimalNumber ( param ) ) ;
} ) ) ) ) ;
return canvas ;
} ,
/ * *
* Change the aspect ratio of the crop box .
* @ param { number } aspectRatio - The new aspect ratio .
* @ returns { Cropper } this
* /
setAspectRatio : function setAspectRatio ( aspectRatio ) {
var options = this . options ;
if ( ! this . disabled && ! isUndefined ( aspectRatio ) ) {
// 0 -> NaN
options . aspectRatio = Math . max ( 0 , aspectRatio ) || NaN ;
if ( this . ready ) {
this . initCropBox ( ) ;
if ( this . cropped ) {
this . renderCropBox ( ) ;
}
}
}
return this ;
} ,
/ * *
* Change the drag mode .
* @ param { string } mode - The new drag mode .
* @ returns { Cropper } this
* /
setDragMode : function setDragMode ( mode ) {
var options = this . options ,
dragBox = this . dragBox ,
face = this . face ;
if ( this . ready && ! this . disabled ) {
var croppable = mode === DRAG _MODE _CROP ;
var movable = options . movable && mode === DRAG _MODE _MOVE ;
mode = croppable || movable ? mode : DRAG _MODE _NONE ;
options . dragMode = mode ;
setData ( dragBox , DATA _ACTION , mode ) ;
toggleClass ( dragBox , CLASS _CROP , croppable ) ;
toggleClass ( dragBox , CLASS _MOVE , movable ) ;
if ( ! options . cropBoxMovable ) {
// Sync drag mode to crop box when it is not movable
setData ( face , DATA _ACTION , mode ) ;
toggleClass ( face , CLASS _CROP , croppable ) ;
toggleClass ( face , CLASS _MOVE , movable ) ;
}
}
return this ;
}
} ;
var AnotherCropper = WINDOW . Cropper ;
var Cropper =
/*#__PURE__*/
function ( ) {
/ * *
* Create a new Cropper .
* @ param { Element } element - The target element for cropping .
* @ param { Object } [ options = { } ] - The configuration options .
* /
function Cropper ( element ) {
var options = arguments . length > 1 && arguments [ 1 ] !== undefined ? arguments [ 1 ] : { } ;
_classCallCheck ( this , Cropper ) ;
if ( ! element || ! REGEXP _TAG _NAME . test ( element . tagName ) ) {
throw new Error ( 'The first argument is required and must be an <img> or <canvas> element.' ) ;
}
this . element = element ;
this . options = assign ( { } , DEFAULTS , isPlainObject ( options ) && options ) ;
this . cropped = false ;
this . disabled = false ;
this . pointers = { } ;
this . ready = false ;
this . reloading = false ;
this . replaced = false ;
this . sized = false ;
this . sizing = false ;
this . init ( ) ;
}
_createClass ( Cropper , [ {
key : "init" ,
value : function init ( ) {
var element = this . element ;
var tagName = element . tagName . toLowerCase ( ) ;
var url ;
if ( element [ NAMESPACE ] ) {
return ;
}
element [ NAMESPACE ] = this ;
if ( tagName === 'img' ) {
this . isImg = true ; // e.g.: "img/picture.jpg"
url = element . getAttribute ( 'src' ) || '' ;
this . originalUrl = url ; // Stop when it's a blank image
if ( ! url ) {
return ;
} // e.g.: "http://example.com/img/picture.jpg"
url = element . src ;
} else if ( tagName === 'canvas' && window . HTMLCanvasElement ) {
url = element . toDataURL ( ) ;
}
this . load ( url ) ;
}
} , {
key : "load" ,
value : function load ( url ) {
var _this = this ;
if ( ! url ) {
return ;
}
this . url = url ;
this . imageData = { } ;
var element = this . element ,
options = this . options ;
if ( ! options . rotatable && ! options . scalable ) {
options . checkOrientation = false ;
} // Only IE10+ supports Typed Arrays
if ( ! options . checkOrientation || ! window . ArrayBuffer ) {
this . clone ( ) ;
return ;
} // XMLHttpRequest disallows to open a Data URL in some browsers like IE11 and Safari
if ( REGEXP _DATA _URL . test ( url ) ) {
if ( REGEXP _DATA _URL _JPEG . test ( url ) ) {
this . read ( dataURLToArrayBuffer ( url ) ) ;
} else {
this . clone ( ) ;
}
return ;
}
var xhr = new XMLHttpRequest ( ) ;
var clone = this . clone . bind ( this ) ;
this . reloading = true ;
this . xhr = xhr ;
xhr . ontimeout = clone ;
xhr . onabort = clone ;
xhr . onerror = clone ;
xhr . onprogress = function ( ) {
if ( xhr . getResponseHeader ( 'content-type' ) !== MIME _TYPE _JPEG ) {
xhr . abort ( ) ;
}
} ;
xhr . onload = function ( ) {
_this . read ( xhr . response ) ;
} ;
xhr . onloadend = function ( ) {
_this . reloading = false ;
_this . xhr = null ;
} ; // Bust cache when there is a "crossOrigin" property to avoid browser cache error
if ( options . checkCrossOrigin && isCrossOriginURL ( url ) && element . crossOrigin ) {
url = addTimestamp ( url ) ;
}
xhr . open ( 'GET' , url ) ;
xhr . responseType = 'arraybuffer' ;
xhr . withCredentials = element . crossOrigin === 'use-credentials' ;
xhr . send ( ) ;
}
} , {
key : "read" ,
value : function read ( arrayBuffer ) {
var options = this . options ,
imageData = this . imageData ;
var orientation = resetAndGetOrientation ( arrayBuffer ) ;
var rotate = 0 ;
var scaleX = 1 ;
var scaleY = 1 ;
if ( orientation > 1 ) {
// Generate a new Data URL with the orientation value set to 1
// as some iOS browsers will render image with its orientation
this . url = arrayBufferToDataURL ( arrayBuffer , MIME _TYPE _JPEG ) ;
var _parseOrientation = parseOrientation ( orientation ) ;
rotate = _parseOrientation . rotate ;
scaleX = _parseOrientation . scaleX ;
scaleY = _parseOrientation . scaleY ;
}
if ( options . rotatable ) {
imageData . rotate = rotate ;
}
if ( options . scalable ) {
imageData . scaleX = scaleX ;
imageData . scaleY = scaleY ;
}
this . clone ( ) ;
}
} , {
key : "clone" ,
value : function clone ( ) {
var element = this . element ,
url = this . url ;
var crossOrigin ;
var crossOriginUrl ;
if ( this . options . checkCrossOrigin && isCrossOriginURL ( url ) ) {
crossOrigin = element . crossOrigin ;
if ( crossOrigin ) {
crossOriginUrl = url ;
} else {
crossOrigin = 'anonymous' ; // Bust cache when there is not a "crossOrigin" property
crossOriginUrl = addTimestamp ( url ) ;
}
}
this . crossOrigin = crossOrigin ;
this . crossOriginUrl = crossOriginUrl ;
var image = document . createElement ( 'img' ) ;
if ( crossOrigin ) {
image . crossOrigin = crossOrigin ;
}
image . src = crossOriginUrl || url ;
this . image = image ;
image . onload = this . start . bind ( this ) ;
image . onerror = this . stop . bind ( this ) ;
addClass ( image , CLASS _HIDE ) ;
element . parentNode . insertBefore ( image , element . nextSibling ) ;
}
} , {
key : "start" ,
value : function start ( ) {
var _this2 = this ;
var image = this . isImg ? this . element : this . image ;
image . onload = null ;
image . onerror = null ;
this . sizing = true ;
var IS _SAFARI = WINDOW . navigator && /(Macintosh|iPhone|iPod|iPad).*AppleWebKit/i . test ( WINDOW . navigator . userAgent ) ;
var done = function done ( naturalWidth , naturalHeight ) {
assign ( _this2 . imageData , {
naturalWidth : naturalWidth ,
naturalHeight : naturalHeight ,
aspectRatio : naturalWidth / naturalHeight
} ) ;
_this2 . sizing = false ;
_this2 . sized = true ;
_this2 . build ( ) ;
} ; // Modern browsers (except Safari)
if ( image . naturalWidth && ! IS _SAFARI ) {
done ( image . naturalWidth , image . naturalHeight ) ;
return ;
}
var sizingImage = document . createElement ( 'img' ) ;
var body = document . body || document . documentElement ;
this . sizingImage = sizingImage ;
sizingImage . onload = function ( ) {
done ( sizingImage . width , sizingImage . height ) ;
if ( ! IS _SAFARI ) {
body . removeChild ( sizingImage ) ;
}
} ;
sizingImage . src = image . src ; // iOS Safari will convert the image automatically
// with its orientation once append it into DOM (#279)
if ( ! IS _SAFARI ) {
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;' ;
body . appendChild ( sizingImage ) ;
}
}
} , {
key : "stop" ,
value : function stop ( ) {
var image = this . image ;
image . onload = null ;
image . onerror = null ;
image . parentNode . removeChild ( image ) ;
this . image = null ;
}
} , {
key : "build" ,
value : function build ( ) {
if ( ! this . sized || this . ready ) {
return ;
}
var element = this . element ,
options = this . options ,
image = this . image ; // Create cropper elements
var container = element . parentNode ;
var template = document . createElement ( 'div' ) ;
template . innerHTML = TEMPLATE ;
var cropper = template . querySelector ( "." . concat ( NAMESPACE , "-container" ) ) ;
var canvas = cropper . querySelector ( "." . concat ( NAMESPACE , "-canvas" ) ) ;
var dragBox = cropper . querySelector ( "." . concat ( NAMESPACE , "-drag-box" ) ) ;
var cropBox = cropper . querySelector ( "." . concat ( NAMESPACE , "-crop-box" ) ) ;
var face = cropBox . querySelector ( "." . concat ( NAMESPACE , "-face" ) ) ;
this . container = container ;
this . cropper = cropper ;
this . canvas = canvas ;
this . dragBox = dragBox ;
this . cropBox = cropBox ;
this . viewBox = cropper . querySelector ( "." . concat ( NAMESPACE , "-view-box" ) ) ;
this . face = face ;
canvas . appendChild ( image ) ; // Hide the original image
addClass ( element , CLASS _HIDDEN ) ; // Inserts the cropper after to the current image
container . insertBefore ( cropper , element . nextSibling ) ; // Show the image if is hidden
if ( ! this . isImg ) {
removeClass ( image , CLASS _HIDE ) ;
}
this . initPreview ( ) ;
this . bind ( ) ;
options . initialAspectRatio = Math . max ( 0 , options . initialAspectRatio ) || NaN ;
options . aspectRatio = Math . max ( 0 , options . aspectRatio ) || NaN ;
options . viewMode = Math . max ( 0 , Math . min ( 3 , Math . round ( options . viewMode ) ) ) || 0 ;
addClass ( cropBox , CLASS _HIDDEN ) ;
if ( ! options . guides ) {
addClass ( cropBox . getElementsByClassName ( "" . concat ( NAMESPACE , "-dashed" ) ) , CLASS _HIDDEN ) ;
}
if ( ! options . center ) {
addClass ( cropBox . getElementsByClassName ( "" . concat ( NAMESPACE , "-center" ) ) , CLASS _HIDDEN ) ;
}
if ( options . background ) {
addClass ( cropper , "" . concat ( NAMESPACE , "-bg" ) ) ;
}
if ( ! options . highlight ) {
addClass ( face , CLASS _INVISIBLE ) ;
}
if ( options . cropBoxMovable ) {
addClass ( face , CLASS _MOVE ) ;
setData ( face , DATA _ACTION , ACTION _ALL ) ;
}
if ( ! options . cropBoxResizable ) {
addClass ( cropBox . getElementsByClassName ( "" . concat ( NAMESPACE , "-line" ) ) , CLASS _HIDDEN ) ;
addClass ( cropBox . getElementsByClassName ( "" . concat ( NAMESPACE , "-point" ) ) , CLASS _HIDDEN ) ;
}
this . render ( ) ;
this . ready = true ;
this . setDragMode ( options . dragMode ) ;
if ( options . autoCrop ) {
this . crop ( ) ;
}
this . setData ( options . data ) ;
if ( isFunction ( options . ready ) ) {
addListener ( element , EVENT _READY , options . ready , {
once : true
} ) ;
}
dispatchEvent ( element , EVENT _READY ) ;
}
} , {
key : "unbuild" ,
value : function unbuild ( ) {
if ( ! this . ready ) {
return ;
}
this . ready = false ;
this . unbind ( ) ;
this . resetPreview ( ) ;
this . cropper . parentNode . removeChild ( this . cropper ) ;
removeClass ( this . element , CLASS _HIDDEN ) ;
}
} , {
key : "uncreate" ,
value : function uncreate ( ) {
if ( this . ready ) {
this . unbuild ( ) ;
this . ready = false ;
this . cropped = false ;
} else if ( this . sizing ) {
this . sizingImage . onload = null ;
this . sizing = false ;
this . sized = false ;
} else if ( this . reloading ) {
this . xhr . onabort = null ;
this . xhr . abort ( ) ;
} else if ( this . image ) {
this . stop ( ) ;
}
}
/ * *
* Get the no conflict cropper class .
* @ returns { Cropper } The cropper class .
* /
} ] , [ {
key : "noConflict" ,
value : function noConflict ( ) {
window . Cropper = AnotherCropper ;
return Cropper ;
}
/ * *
* Change the default options .
* @ param { Object } options - The new default options .
* /
} , {
key : "setDefaults" ,
value : function setDefaults ( options ) {
assign ( DEFAULTS , isPlainObject ( options ) && options ) ;
}
} ] ) ;
return Cropper ;
} ( ) ;
assign ( Cropper . prototype , render , preview , events , handlers , change , methods ) ;
return Cropper ;
} ) ) ) ;