Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Boxiang Sun
gitlab-ce
Commits
78530162
Commit
78530162
authored
Mar 17, 2016
by
Alfredo Sumaran
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add cropper library
parent
9392e137
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
3373 additions
and
0 deletions
+3373
-0
app/assets/javascripts/lib/cropper.js
app/assets/javascripts/lib/cropper.js
+2993
-0
app/assets/stylesheets/application.scss
app/assets/stylesheets/application.scss
+1
-0
app/assets/stylesheets/cropper.css
app/assets/stylesheets/cropper.css
+379
-0
No files found.
app/assets/javascripts/lib/cropper.js
0 → 100644
View file @
78530162
/*!
* Cropper v2.3.0
* https://github.com/fengyuanchen/cropper
*
* Copyright (c) 2014-2016 Fengyuan Chen and contributors
* Released under the MIT license
*
* Date: 2016-02-22T02:13:13.332Z
*/
(
function
(
factory
)
{
if
(
typeof
define
===
'
function
'
&&
define
.
amd
)
{
// AMD. Register as anonymous module.
define
([
'
jquery
'
],
factory
);
}
else
if
(
typeof
exports
===
'
object
'
)
{
// Node / CommonJS
factory
(
require
(
'
jquery
'
));
}
else
{
// Browser globals.
factory
(
jQuery
);
}
})(
function
(
$
)
{
'
use strict
'
;
// Globals
var
$window
=
$
(
window
);
var
$document
=
$
(
document
);
var
location
=
window
.
location
;
var
navigator
=
window
.
navigator
;
var
ArrayBuffer
=
window
.
ArrayBuffer
;
var
Uint8Array
=
window
.
Uint8Array
;
var
DataView
=
window
.
DataView
;
var
btoa
=
window
.
btoa
;
// Constants
var
NAMESPACE
=
'
cropper
'
;
// Classes
var
CLASS_MODAL
=
'
cropper-modal
'
;
var
CLASS_HIDE
=
'
cropper-hide
'
;
var
CLASS_HIDDEN
=
'
cropper-hidden
'
;
var
CLASS_INVISIBLE
=
'
cropper-invisible
'
;
var
CLASS_MOVE
=
'
cropper-move
'
;
var
CLASS_CROP
=
'
cropper-crop
'
;
var
CLASS_DISABLED
=
'
cropper-disabled
'
;
var
CLASS_BG
=
'
cropper-bg
'
;
// Events
var
EVENT_MOUSE_DOWN
=
'
mousedown touchstart pointerdown MSPointerDown
'
;
var
EVENT_MOUSE_MOVE
=
'
mousemove touchmove pointermove MSPointerMove
'
;
var
EVENT_MOUSE_UP
=
'
mouseup touchend touchcancel pointerup pointercancel MSPointerUp MSPointerCancel
'
;
var
EVENT_WHEEL
=
'
wheel mousewheel DOMMouseScroll
'
;
var
EVENT_DBLCLICK
=
'
dblclick
'
;
var
EVENT_LOAD
=
'
load.
'
+
NAMESPACE
;
var
EVENT_ERROR
=
'
error.
'
+
NAMESPACE
;
var
EVENT_RESIZE
=
'
resize.
'
+
NAMESPACE
;
// Bind to window with namespace
var
EVENT_BUILD
=
'
build.
'
+
NAMESPACE
;
var
EVENT_BUILT
=
'
built.
'
+
NAMESPACE
;
var
EVENT_CROP_START
=
'
cropstart.
'
+
NAMESPACE
;
var
EVENT_CROP_MOVE
=
'
cropmove.
'
+
NAMESPACE
;
var
EVENT_CROP_END
=
'
cropend.
'
+
NAMESPACE
;
var
EVENT_CROP
=
'
crop.
'
+
NAMESPACE
;
var
EVENT_ZOOM
=
'
zoom.
'
+
NAMESPACE
;
// 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_HEAD
=
/^data
\:([^\;]
+
)\;
base64,/
;
var
REGEXP_DATA_URL_JPEG
=
/^data
\:
image
\/
jpeg.*;base64,/
;
// Data keys
var
DATA_PREVIEW
=
'
preview
'
;
var
DATA_ACTION
=
'
action
'
;
// Actions
var
ACTION_EAST
=
'
e
'
;
var
ACTION_WEST
=
'
w
'
;
var
ACTION_SOUTH
=
'
s
'
;
var
ACTION_NORTH
=
'
n
'
;
var
ACTION_SOUTH_EAST
=
'
se
'
;
var
ACTION_SOUTH_WEST
=
'
sw
'
;
var
ACTION_NORTH_EAST
=
'
ne
'
;
var
ACTION_NORTH_WEST
=
'
nw
'
;
var
ACTION_ALL
=
'
all
'
;
var
ACTION_CROP
=
'
crop
'
;
var
ACTION_MOVE
=
'
move
'
;
var
ACTION_ZOOM
=
'
zoom
'
;
var
ACTION_NONE
=
'
none
'
;
// Supports
var
SUPPORT_CANVAS
=
$
.
isFunction
(
$
(
'
<canvas>
'
)[
0
].
getContext
);
var
IS_SAFARI
=
navigator
&&
/safari/i
.
test
(
navigator
.
userAgent
)
&&
/apple computer/i
.
test
(
navigator
.
vendor
);
// Maths
var
num
=
Number
;
var
min
=
Math
.
min
;
var
max
=
Math
.
max
;
var
abs
=
Math
.
abs
;
var
sin
=
Math
.
sin
;
var
cos
=
Math
.
cos
;
var
sqrt
=
Math
.
sqrt
;
var
round
=
Math
.
round
;
var
floor
=
Math
.
floor
;
// Utilities
var
fromCharCode
=
String
.
fromCharCode
;
function
isNumber
(
n
)
{
return
typeof
n
===
'
number
'
&&
!
isNaN
(
n
);
}
function
isUndefined
(
n
)
{
return
typeof
n
===
'
undefined
'
;
}
function
toArray
(
obj
,
offset
)
{
var
args
=
[];
// This is necessary for IE8
if
(
isNumber
(
offset
))
{
args
.
push
(
offset
);
}
return
args
.
slice
.
apply
(
obj
,
args
);
}
// Custom proxy to avoid jQuery's guid
function
proxy
(
fn
,
context
)
{
var
args
=
toArray
(
arguments
,
2
);
return
function
()
{
return
fn
.
apply
(
context
,
args
.
concat
(
toArray
(
arguments
)));
};
}
function
isCrossOriginURL
(
url
)
{
var
parts
=
url
.
match
(
/^
(
https
?
:
)\/\/([^\:\/\?
#
]
+
)
:
?(\d
*
)
/i
);
return
parts
&&
(
parts
[
1
]
!==
location
.
protocol
||
parts
[
2
]
!==
location
.
hostname
||
parts
[
3
]
!==
location
.
port
);
}
function
addTimestamp
(
url
)
{
var
timestamp
=
'
timestamp=
'
+
(
new
Date
()).
getTime
();
return
(
url
+
(
url
.
indexOf
(
'
?
'
)
===
-
1
?
'
?
'
:
'
&
'
)
+
timestamp
);
}
function
getCrossOrigin
(
crossOrigin
)
{
return
crossOrigin
?
'
crossOrigin="
'
+
crossOrigin
+
'
"
'
:
''
;
}
function
getImageSize
(
image
,
callback
)
{
var
newImage
;
// Modern browsers (ignore Safari, #120 & #509)
if
(
image
.
naturalWidth
&&
!
IS_SAFARI
)
{
return
callback
(
image
.
naturalWidth
,
image
.
naturalHeight
);
}
// IE8: Don't use `new Image()` here (#319)
newImage
=
document
.
createElement
(
'
img
'
);
newImage
.
onload
=
function
()
{
callback
(
this
.
width
,
this
.
height
);
};
newImage
.
src
=
image
.
src
;
}
function
getTransform
(
options
)
{
var
transforms
=
[];
var
rotate
=
options
.
rotate
;
var
scaleX
=
options
.
scaleX
;
var
scaleY
=
options
.
scaleY
;
if
(
isNumber
(
rotate
))
{
transforms
.
push
(
'
rotate(
'
+
rotate
+
'
deg)
'
);
}
if
(
isNumber
(
scaleX
)
&&
isNumber
(
scaleY
))
{
transforms
.
push
(
'
scale(
'
+
scaleX
+
'
,
'
+
scaleY
+
'
)
'
);
}
return
transforms
.
length
?
transforms
.
join
(
'
'
)
:
'
none
'
;
}
function
getRotatedSizes
(
data
,
isReversed
)
{
var
deg
=
abs
(
data
.
degree
)
%
180
;
var
arc
=
(
deg
>
90
?
(
180
-
deg
)
:
deg
)
*
Math
.
PI
/
180
;
var
sinArc
=
sin
(
arc
);
var
cosArc
=
cos
(
arc
);
var
width
=
data
.
width
;
var
height
=
data
.
height
;
var
aspectRatio
=
data
.
aspectRatio
;
var
newWidth
;
var
newHeight
;
if
(
!
isReversed
)
{
newWidth
=
width
*
cosArc
+
height
*
sinArc
;
newHeight
=
width
*
sinArc
+
height
*
cosArc
;
}
else
{
newWidth
=
width
/
(
cosArc
+
sinArc
/
aspectRatio
);
newHeight
=
newWidth
/
aspectRatio
;
}
return
{
width
:
newWidth
,
height
:
newHeight
};
}
function
getSourceCanvas
(
image
,
data
)
{
var
canvas
=
$
(
'
<canvas>
'
)[
0
];
var
context
=
canvas
.
getContext
(
'
2d
'
);
var
dstX
=
0
;
var
dstY
=
0
;
var
dstWidth
=
data
.
naturalWidth
;
var
dstHeight
=
data
.
naturalHeight
;
var
rotate
=
data
.
rotate
;
var
scaleX
=
data
.
scaleX
;
var
scaleY
=
data
.
scaleY
;
var
scalable
=
isNumber
(
scaleX
)
&&
isNumber
(
scaleY
)
&&
(
scaleX
!==
1
||
scaleY
!==
1
);
var
rotatable
=
isNumber
(
rotate
)
&&
rotate
!==
0
;
var
advanced
=
rotatable
||
scalable
;
var
canvasWidth
=
dstWidth
*
abs
(
scaleX
||
1
);
var
canvasHeight
=
dstHeight
*
abs
(
scaleY
||
1
);
var
translateX
;
var
translateY
;
var
rotated
;
if
(
scalable
)
{
translateX
=
canvasWidth
/
2
;
translateY
=
canvasHeight
/
2
;
}
if
(
rotatable
)
{
rotated
=
getRotatedSizes
({
width
:
canvasWidth
,
height
:
canvasHeight
,
degree
:
rotate
});
canvasWidth
=
rotated
.
width
;
canvasHeight
=
rotated
.
height
;
translateX
=
canvasWidth
/
2
;
translateY
=
canvasHeight
/
2
;
}
canvas
.
width
=
canvasWidth
;
canvas
.
height
=
canvasHeight
;
if
(
advanced
)
{
dstX
=
-
dstWidth
/
2
;
dstY
=
-
dstHeight
/
2
;
context
.
save
();
context
.
translate
(
translateX
,
translateY
);
}
if
(
rotatable
)
{
context
.
rotate
(
rotate
*
Math
.
PI
/
180
);
}
// Should call `scale` after rotated
if
(
scalable
)
{
context
.
scale
(
scaleX
,
scaleY
);
}
context
.
drawImage
(
image
,
floor
(
dstX
),
floor
(
dstY
),
floor
(
dstWidth
),
floor
(
dstHeight
));
if
(
advanced
)
{
context
.
restore
();
}
return
canvas
;
}
function
getTouchesCenter
(
touches
)
{
var
length
=
touches
.
length
;
var
pageX
=
0
;
var
pageY
=
0
;
if
(
length
)
{
$
.
each
(
touches
,
function
(
i
,
touch
)
{
pageX
+=
touch
.
pageX
;
pageY
+=
touch
.
pageY
;
});
pageX
/=
length
;
pageY
/=
length
;
}
return
{
pageX
:
pageX
,
pageY
:
pageY
};
}
function
getStringFromCharCode
(
dataView
,
start
,
length
)
{
var
str
=
''
;
var
i
;
for
(
i
=
start
,
length
+=
start
;
i
<
length
;
i
++
)
{
str
+=
fromCharCode
(
dataView
.
getUint8
(
i
));
}
return
str
;
}
function
getOrientation
(
arrayBuffer
)
{
var
dataView
=
new
DataView
(
arrayBuffer
);
var
length
=
dataView
.
byteLength
;
var
orientation
;
var
exifIDCode
;
var
tiffOffset
;
var
firstIFDOffset
;
var
littleEndian
;
var
endianness
;
var
app1Start
;
var
ifdStart
;
var
offset
;
var
i
;
// Only handle JPEG image (start by 0xFFD8)
if
(
dataView
.
getUint8
(
0
)
===
0xFF
&&
dataView
.
getUint8
(
1
)
===
0xD8
)
{
offset
=
2
;
while
(
offset
<
length
)
{
if
(
dataView
.
getUint8
(
offset
)
===
0xFF
&&
dataView
.
getUint8
(
offset
+
1
)
===
0xE1
)
{
app1Start
=
offset
;
break
;
}
offset
++
;
}
}
if
(
app1Start
)
{
exifIDCode
=
app1Start
+
4
;
tiffOffset
=
app1Start
+
10
;
if
(
getStringFromCharCode
(
dataView
,
exifIDCode
,
4
)
===
'
Exif
'
)
{
endianness
=
dataView
.
getUint16
(
tiffOffset
);
littleEndian
=
endianness
===
0x4949
;
if
(
littleEndian
||
endianness
===
0x4D4D
/* bigEndian */
)
{
if
(
dataView
.
getUint16
(
tiffOffset
+
2
,
littleEndian
)
===
0x002A
)
{
firstIFDOffset
=
dataView
.
getUint32
(
tiffOffset
+
4
,
littleEndian
);
if
(
firstIFDOffset
>=
0x00000008
)
{
ifdStart
=
tiffOffset
+
firstIFDOffset
;
}
}
}
}
}
if
(
ifdStart
)
{
length
=
dataView
.
getUint16
(
ifdStart
,
littleEndian
);
for
(
i
=
0
;
i
<
length
;
i
++
)
{
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 for Safari (#120)
if
(
IS_SAFARI
)
{
dataView
.
setUint16
(
offset
,
1
,
littleEndian
);
}
break
;
}
}
}
return
orientation
;
}
function
dataURLToArrayBuffer
(
dataURL
)
{
var
base64
=
dataURL
.
replace
(
REGEXP_DATA_URL_HEAD
,
''
);
var
binary
=
atob
(
base64
);
var
length
=
binary
.
length
;
var
arrayBuffer
=
new
ArrayBuffer
(
length
);
var
dataView
=
new
Uint8Array
(
arrayBuffer
);
var
i
;
for
(
i
=
0
;
i
<
length
;
i
++
)
{
dataView
[
i
]
=
binary
.
charCodeAt
(
i
);
}
return
arrayBuffer
;
}
// Only available for JPEG image
function
arrayBufferToDataURL
(
arrayBuffer
)
{
var
dataView
=
new
Uint8Array
(
arrayBuffer
);
var
length
=
dataView
.
length
;
var
base64
=
''
;
var
i
;
for
(
i
=
0
;
i
<
length
;
i
++
)
{
base64
+=
fromCharCode
(
dataView
[
i
]);
}
return
'
data:image/jpeg;base64,
'
+
btoa
(
base64
);
}
function
Cropper
(
element
,
options
)
{
this
.
$element
=
$
(
element
);
this
.
options
=
$
.
extend
({},
Cropper
.
DEFAULTS
,
$
.
isPlainObject
(
options
)
&&
options
);
this
.
isLoaded
=
false
;
this
.
isBuilt
=
false
;
this
.
isCompleted
=
false
;
this
.
isRotated
=
false
;
this
.
isCropped
=
false
;
this
.
isDisabled
=
false
;
this
.
isReplaced
=
false
;
this
.
isLimited
=
false
;
this
.
wheeling
=
false
;
this
.
isImg
=
false
;
this
.
originalUrl
=
''
;
this
.
canvas
=
null
;
this
.
cropBox
=
null
;
this
.
init
();
}
Cropper
.
prototype
=
{
constructor
:
Cropper
,
init
:
function
()
{
var
$this
=
this
.
$element
;
var
url
;
if
(
$this
.
is
(
'
img
'
))
{
this
.
isImg
=
true
;
// Should use `$.fn.attr` here. e.g.: "img/picture.jpg"
this
.
originalUrl
=
url
=
$this
.
attr
(
'
src
'
);
// Stop when it's a blank image
if
(
!
url
)
{
return
;
}
// Should use `$.fn.prop` here. e.g.: "http://example.com/img/picture.jpg"
url
=
$this
.
prop
(
'
src
'
);
}
else
if
(
$this
.
is
(
'
canvas
'
)
&&
SUPPORT_CANVAS
)
{
url
=
$this
[
0
].
toDataURL
();
}
this
.
load
(
url
);
},
// A shortcut for triggering custom events
trigger
:
function
(
type
,
data
)
{
var
e
=
$
.
Event
(
type
,
data
);
this
.
$element
.
trigger
(
e
);
return
e
;
},
load
:
function
(
url
)
{
var
options
=
this
.
options
;
var
$this
=
this
.
$element
;
var
read
;
var
xhr
;
if
(
!
url
)
{
return
;
}
// Trigger build event first
$this
.
one
(
EVENT_BUILD
,
options
.
build
);
if
(
this
.
trigger
(
EVENT_BUILD
).
isDefaultPrevented
())
{
return
;
}
this
.
url
=
url
;
this
.
image
=
{};
if
(
!
options
.
checkOrientation
||
!
ArrayBuffer
)
{
return
this
.
clone
();
}
read
=
$
.
proxy
(
this
.
read
,
this
);
// XMLHttpRequest disallows to open a Data URL in some browsers like IE11 and Safari
if
(
REGEXP_DATA_URL
.
test
(
url
))
{
return
REGEXP_DATA_URL_JPEG
.
test
(
url
)
?
read
(
dataURLToArrayBuffer
(
url
))
:
this
.
clone
();
}
xhr
=
new
XMLHttpRequest
();
xhr
.
onerror
=
xhr
.
onabort
=
$
.
proxy
(
function
()
{
this
.
clone
();
},
this
);
xhr
.
onload
=
function
()
{
read
(
this
.
response
);
};
xhr
.
open
(
'
get
'
,
url
);
xhr
.
responseType
=
'
arraybuffer
'
;
xhr
.
send
();
},
read
:
function
(
arrayBuffer
)
{
var
options
=
this
.
options
;
var
orientation
=
getOrientation
(
arrayBuffer
);
var
image
=
this
.
image
;
var
rotate
;
var
scaleX
;
var
scaleY
;
if
(
orientation
>
1
)
{
this
.
url
=
arrayBufferToDataURL
(
arrayBuffer
);
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 + rotate right 90°
case
5
:
rotate
=
90
;
scaleY
=
-
1
;
break
;
// rotate right 90°
case
6
:
rotate
=
90
;
break
;
// flip horizontal + rotate right 90°
case
7
:
rotate
=
90
;
scaleX
=
-
1
;
break
;
// rotate left 90°
case
8
:
rotate
=
-
90
;
break
;
}
}
if
(
options
.
rotatable
)
{
image
.
rotate
=
rotate
;
}
if
(
options
.
scalable
)
{
image
.
scaleX
=
scaleX
;
image
.
scaleY
=
scaleY
;
}
this
.
clone
();
},
clone
:
function
()
{
var
options
=
this
.
options
;
var
$this
=
this
.
$element
;
var
url
=
this
.
url
;
var
crossOrigin
=
''
;
var
crossOriginUrl
;
var
$clone
;
if
(
options
.
checkCrossOrigin
&&
isCrossOriginURL
(
url
))
{
crossOrigin
=
$this
.
prop
(
'
crossOrigin
'
);
if
(
crossOrigin
)
{
crossOriginUrl
=
url
;
}
else
{
crossOrigin
=
'
anonymous
'
;
// Bust cache (#148) when there is not a "crossOrigin" property
crossOriginUrl
=
addTimestamp
(
url
);
}
}
this
.
crossOrigin
=
crossOrigin
;
this
.
crossOriginUrl
=
crossOriginUrl
;
this
.
$clone
=
$clone
=
$
(
'
<img
'
+
getCrossOrigin
(
crossOrigin
)
+
'
src="
'
+
(
crossOriginUrl
||
url
)
+
'
">
'
);
if
(
this
.
isImg
)
{
if
(
$this
[
0
].
complete
)
{
this
.
start
();
}
else
{
$this
.
one
(
EVENT_LOAD
,
$
.
proxy
(
this
.
start
,
this
));
}
}
else
{
$clone
.
one
(
EVENT_LOAD
,
$
.
proxy
(
this
.
start
,
this
)).
one
(
EVENT_ERROR
,
$
.
proxy
(
this
.
stop
,
this
)).
addClass
(
CLASS_HIDE
).
insertAfter
(
$this
);
}
},
start
:
function
()
{
var
$image
=
this
.
$element
;
var
$clone
=
this
.
$clone
;
if
(
!
this
.
isImg
)
{
$clone
.
off
(
EVENT_ERROR
,
this
.
stop
);
$image
=
$clone
;
}
getImageSize
(
$image
[
0
],
$
.
proxy
(
function
(
naturalWidth
,
naturalHeight
)
{
$
.
extend
(
this
.
image
,
{
naturalWidth
:
naturalWidth
,
naturalHeight
:
naturalHeight
,
aspectRatio
:
naturalWidth
/
naturalHeight
});
this
.
isLoaded
=
true
;
this
.
build
();
},
this
));
},
stop
:
function
()
{
this
.
$clone
.
remove
();
this
.
$clone
=
null
;
},
build
:
function
()
{
var
options
=
this
.
options
;
var
$this
=
this
.
$element
;
var
$clone
=
this
.
$clone
;
var
$cropper
;
var
$cropBox
;
var
$face
;
if
(
!
this
.
isLoaded
)
{
return
;
}
// Unbuild first when replace
if
(
this
.
isBuilt
)
{
this
.
unbuild
();
}
// Create cropper elements
this
.
$container
=
$this
.
parent
();
this
.
$cropper
=
$cropper
=
$
(
Cropper
.
TEMPLATE
);
this
.
$canvas
=
$cropper
.
find
(
'
.cropper-canvas
'
).
append
(
$clone
);
this
.
$dragBox
=
$cropper
.
find
(
'
.cropper-drag-box
'
);
this
.
$cropBox
=
$cropBox
=
$cropper
.
find
(
'
.cropper-crop-box
'
);
this
.
$viewBox
=
$cropper
.
find
(
'
.cropper-view-box
'
);
this
.
$face
=
$face
=
$cropBox
.
find
(
'
.cropper-face
'
);
// Hide the original image
$this
.
addClass
(
CLASS_HIDDEN
).
after
(
$cropper
);
// Show the clone image if is hidden
if
(
!
this
.
isImg
)
{
$clone
.
removeClass
(
CLASS_HIDE
);
}
this
.
initPreview
();
this
.
bind
();
options
.
aspectRatio
=
max
(
0
,
options
.
aspectRatio
)
||
NaN
;
options
.
viewMode
=
max
(
0
,
min
(
3
,
round
(
options
.
viewMode
)))
||
0
;
if
(
options
.
autoCrop
)
{
this
.
isCropped
=
true
;
if
(
options
.
modal
)
{
this
.
$dragBox
.
addClass
(
CLASS_MODAL
);
}
}
else
{
$cropBox
.
addClass
(
CLASS_HIDDEN
);
}
if
(
!
options
.
guides
)
{
$cropBox
.
find
(
'
.cropper-dashed
'
).
addClass
(
CLASS_HIDDEN
);
}
if
(
!
options
.
center
)
{
$cropBox
.
find
(
'
.cropper-center
'
).
addClass
(
CLASS_HIDDEN
);
}
if
(
options
.
cropBoxMovable
)
{
$face
.
addClass
(
CLASS_MOVE
).
data
(
DATA_ACTION
,
ACTION_ALL
);
}
if
(
!
options
.
highlight
)
{
$face
.
addClass
(
CLASS_INVISIBLE
);
}
if
(
options
.
background
)
{
$cropper
.
addClass
(
CLASS_BG
);
}
if
(
!
options
.
cropBoxResizable
)
{
$cropBox
.
find
(
'
.cropper-line, .cropper-point
'
).
addClass
(
CLASS_HIDDEN
);
}
this
.
setDragMode
(
options
.
dragMode
);
this
.
render
();
this
.
isBuilt
=
true
;
this
.
setData
(
options
.
data
);
$this
.
one
(
EVENT_BUILT
,
options
.
built
);
// Trigger the built event asynchronously to keep `data('cropper')` is defined
setTimeout
(
$
.
proxy
(
function
()
{
this
.
trigger
(
EVENT_BUILT
);
this
.
isCompleted
=
true
;
},
this
),
0
);
},
unbuild
:
function
()
{
if
(
!
this
.
isBuilt
)
{
return
;
}
this
.
isBuilt
=
false
;
this
.
isCompleted
=
false
;
this
.
initialImage
=
null
;
// Clear `initialCanvas` is necessary when replace
this
.
initialCanvas
=
null
;
this
.
initialCropBox
=
null
;
this
.
container
=
null
;
this
.
canvas
=
null
;
// Clear `cropBox` is necessary when replace
this
.
cropBox
=
null
;
this
.
unbind
();
this
.
resetPreview
();
this
.
$preview
=
null
;
this
.
$viewBox
=
null
;
this
.
$cropBox
=
null
;
this
.
$dragBox
=
null
;
this
.
$canvas
=
null
;
this
.
$container
=
null
;
this
.
$cropper
.
remove
();
this
.
$cropper
=
null
;
},
render
:
function
()
{
this
.
initContainer
();
this
.
initCanvas
();
this
.
initCropBox
();
this
.
renderCanvas
();
if
(
this
.
isCropped
)
{
this
.
renderCropBox
();
}
},
initContainer
:
function
()
{
var
options
=
this
.
options
;
var
$this
=
this
.
$element
;
var
$container
=
this
.
$container
;
var
$cropper
=
this
.
$cropper
;
$cropper
.
addClass
(
CLASS_HIDDEN
);
$this
.
removeClass
(
CLASS_HIDDEN
);
$cropper
.
css
((
this
.
container
=
{
width
:
max
(
$container
.
width
(),
num
(
options
.
minContainerWidth
)
||
200
),
height
:
max
(
$container
.
height
(),
num
(
options
.
minContainerHeight
)
||
100
)
}));
$this
.
addClass
(
CLASS_HIDDEN
);
$cropper
.
removeClass
(
CLASS_HIDDEN
);
},
// Canvas (image wrapper)
initCanvas
:
function
()
{
var
viewMode
=
this
.
options
.
viewMode
;
var
container
=
this
.
container
;
var
containerWidth
=
container
.
width
;
var
containerHeight
=
container
.
height
;
var
image
=
this
.
image
;
var
imageNaturalWidth
=
image
.
naturalWidth
;
var
imageNaturalHeight
=
image
.
naturalHeight
;
var
is90Degree
=
abs
(
image
.
rotate
)
===
90
;
var
naturalWidth
=
is90Degree
?
imageNaturalHeight
:
imageNaturalWidth
;
var
naturalHeight
=
is90Degree
?
imageNaturalWidth
:
imageNaturalHeight
;
var
aspectRatio
=
naturalWidth
/
naturalHeight
;
var
canvasWidth
=
containerWidth
;
var
canvasHeight
=
containerHeight
;
var
canvas
;
if
(
containerHeight
*
aspectRatio
>
containerWidth
)
{
if
(
viewMode
===
3
)
{
canvasWidth
=
containerHeight
*
aspectRatio
;
}
else
{
canvasHeight
=
containerWidth
/
aspectRatio
;
}
}
else
{
if
(
viewMode
===
3
)
{
canvasHeight
=
containerWidth
/
aspectRatio
;
}
else
{
canvasWidth
=
containerHeight
*
aspectRatio
;
}
}
canvas
=
{
naturalWidth
:
naturalWidth
,
naturalHeight
:
naturalHeight
,
aspectRatio
:
aspectRatio
,
width
:
canvasWidth
,
height
:
canvasHeight
};
canvas
.
oldLeft
=
canvas
.
left
=
(
containerWidth
-
canvasWidth
)
/
2
;
canvas
.
oldTop
=
canvas
.
top
=
(
containerHeight
-
canvasHeight
)
/
2
;
this
.
canvas
=
canvas
;
this
.
isLimited
=
(
viewMode
===
1
||
viewMode
===
2
);
this
.
limitCanvas
(
true
,
true
);
this
.
initialImage
=
$
.
extend
({},
image
);
this
.
initialCanvas
=
$
.
extend
({},
canvas
);
},
limitCanvas
:
function
(
isSizeLimited
,
isPositionLimited
)
{
var
options
=
this
.
options
;
var
viewMode
=
options
.
viewMode
;
var
container
=
this
.
container
;
var
containerWidth
=
container
.
width
;
var
containerHeight
=
container
.
height
;
var
canvas
=
this
.
canvas
;
var
aspectRatio
=
canvas
.
aspectRatio
;
var
cropBox
=
this
.
cropBox
;
var
isCropped
=
this
.
isCropped
&&
cropBox
;
var
minCanvasWidth
;
var
minCanvasHeight
;
var
newCanvasLeft
;
var
newCanvasTop
;
if
(
isSizeLimited
)
{
minCanvasWidth
=
num
(
options
.
minCanvasWidth
)
||
0
;
minCanvasHeight
=
num
(
options
.
minCanvasHeight
)
||
0
;
if
(
viewMode
)
{
if
(
viewMode
>
1
)
{
minCanvasWidth
=
max
(
minCanvasWidth
,
containerWidth
);
minCanvasHeight
=
max
(
minCanvasHeight
,
containerHeight
);
if
(
viewMode
===
3
)
{
if
(
minCanvasHeight
*
aspectRatio
>
minCanvasWidth
)
{
minCanvasWidth
=
minCanvasHeight
*
aspectRatio
;
}
else
{
minCanvasHeight
=
minCanvasWidth
/
aspectRatio
;
}
}
}
else
{
if
(
minCanvasWidth
)
{
minCanvasWidth
=
max
(
minCanvasWidth
,
isCropped
?
cropBox
.
width
:
0
);
}
else
if
(
minCanvasHeight
)
{
minCanvasHeight
=
max
(
minCanvasHeight
,
isCropped
?
cropBox
.
height
:
0
);
}
else
if
(
isCropped
)
{
minCanvasWidth
=
cropBox
.
width
;
minCanvasHeight
=
cropBox
.
height
;
if
(
minCanvasHeight
*
aspectRatio
>
minCanvasWidth
)
{
minCanvasWidth
=
minCanvasHeight
*
aspectRatio
;
}
else
{
minCanvasHeight
=
minCanvasWidth
/
aspectRatio
;
}
}
}
}
if
(
minCanvasWidth
&&
minCanvasHeight
)
{
if
(
minCanvasHeight
*
aspectRatio
>
minCanvasWidth
)
{
minCanvasHeight
=
minCanvasWidth
/
aspectRatio
;
}
else
{
minCanvasWidth
=
minCanvasHeight
*
aspectRatio
;
}
}
else
if
(
minCanvasWidth
)
{
minCanvasHeight
=
minCanvasWidth
/
aspectRatio
;
}
else
if
(
minCanvasHeight
)
{
minCanvasWidth
=
minCanvasHeight
*
aspectRatio
;
}
canvas
.
minWidth
=
minCanvasWidth
;
canvas
.
minHeight
=
minCanvasHeight
;
canvas
.
maxWidth
=
Infinity
;
canvas
.
maxHeight
=
Infinity
;
}
if
(
isPositionLimited
)
{
if
(
viewMode
)
{
newCanvasLeft
=
containerWidth
-
canvas
.
width
;
newCanvasTop
=
containerHeight
-
canvas
.
height
;
canvas
.
minLeft
=
min
(
0
,
newCanvasLeft
);
canvas
.
minTop
=
min
(
0
,
newCanvasTop
);
canvas
.
maxLeft
=
max
(
0
,
newCanvasLeft
);
canvas
.
maxTop
=
max
(
0
,
newCanvasTop
);
if
(
isCropped
&&
this
.
isLimited
)
{
canvas
.
minLeft
=
min
(
cropBox
.
left
,
cropBox
.
left
+
cropBox
.
width
-
canvas
.
width
);
canvas
.
minTop
=
min
(
cropBox
.
top
,
cropBox
.
top
+
cropBox
.
height
-
canvas
.
height
);
canvas
.
maxLeft
=
cropBox
.
left
;
canvas
.
maxTop
=
cropBox
.
top
;
if
(
viewMode
===
2
)
{
if
(
canvas
.
width
>=
containerWidth
)
{
canvas
.
minLeft
=
min
(
0
,
newCanvasLeft
);
canvas
.
maxLeft
=
max
(
0
,
newCanvasLeft
);
}
if
(
canvas
.
height
>=
containerHeight
)
{
canvas
.
minTop
=
min
(
0
,
newCanvasTop
);
canvas
.
maxTop
=
max
(
0
,
newCanvasTop
);
}
}
}
}
else
{
canvas
.
minLeft
=
-
canvas
.
width
;
canvas
.
minTop
=
-
canvas
.
height
;
canvas
.
maxLeft
=
containerWidth
;
canvas
.
maxTop
=
containerHeight
;
}
}
},
renderCanvas
:
function
(
isChanged
)
{
var
canvas
=
this
.
canvas
;
var
image
=
this
.
image
;
var
rotate
=
image
.
rotate
;
var
naturalWidth
=
image
.
naturalWidth
;
var
naturalHeight
=
image
.
naturalHeight
;
var
aspectRatio
;
var
rotated
;
if
(
this
.
isRotated
)
{
this
.
isRotated
=
false
;
// Computes rotated sizes with image sizes
rotated
=
getRotatedSizes
({
width
:
image
.
width
,
height
:
image
.
height
,
degree
:
rotate
});
aspectRatio
=
rotated
.
width
/
rotated
.
height
;
if
(
aspectRatio
!==
canvas
.
aspectRatio
)
{
canvas
.
left
-=
(
rotated
.
width
-
canvas
.
width
)
/
2
;
canvas
.
top
-=
(
rotated
.
height
-
canvas
.
height
)
/
2
;
canvas
.
width
=
rotated
.
width
;
canvas
.
height
=
rotated
.
height
;
canvas
.
aspectRatio
=
aspectRatio
;
canvas
.
naturalWidth
=
naturalWidth
;
canvas
.
naturalHeight
=
naturalHeight
;
// Computes rotated sizes with natural image sizes
if
(
rotate
%
180
)
{
rotated
=
getRotatedSizes
({
width
:
naturalWidth
,
height
:
naturalHeight
,
degree
:
rotate
});
canvas
.
naturalWidth
=
rotated
.
width
;
canvas
.
naturalHeight
=
rotated
.
height
;
}
this
.
limitCanvas
(
true
,
false
);
}
}
if
(
canvas
.
width
>
canvas
.
maxWidth
||
canvas
.
width
<
canvas
.
minWidth
)
{
canvas
.
left
=
canvas
.
oldLeft
;
}
if
(
canvas
.
height
>
canvas
.
maxHeight
||
canvas
.
height
<
canvas
.
minHeight
)
{
canvas
.
top
=
canvas
.
oldTop
;
}
canvas
.
width
=
min
(
max
(
canvas
.
width
,
canvas
.
minWidth
),
canvas
.
maxWidth
);
canvas
.
height
=
min
(
max
(
canvas
.
height
,
canvas
.
minHeight
),
canvas
.
maxHeight
);
this
.
limitCanvas
(
false
,
true
);
canvas
.
oldLeft
=
canvas
.
left
=
min
(
max
(
canvas
.
left
,
canvas
.
minLeft
),
canvas
.
maxLeft
);
canvas
.
oldTop
=
canvas
.
top
=
min
(
max
(
canvas
.
top
,
canvas
.
minTop
),
canvas
.
maxTop
);
this
.
$canvas
.
css
({
width
:
canvas
.
width
,
height
:
canvas
.
height
,
left
:
canvas
.
left
,
top
:
canvas
.
top
});
this
.
renderImage
();
if
(
this
.
isCropped
&&
this
.
isLimited
)
{
this
.
limitCropBox
(
true
,
true
);
}
if
(
isChanged
)
{
this
.
output
();
}
},
renderImage
:
function
(
isChanged
)
{
var
canvas
=
this
.
canvas
;
var
image
=
this
.
image
;
var
reversed
;
if
(
image
.
rotate
)
{
reversed
=
getRotatedSizes
({
width
:
canvas
.
width
,
height
:
canvas
.
height
,
degree
:
image
.
rotate
,
aspectRatio
:
image
.
aspectRatio
},
true
);
}
$
.
extend
(
image
,
reversed
?
{
width
:
reversed
.
width
,
height
:
reversed
.
height
,
left
:
(
canvas
.
width
-
reversed
.
width
)
/
2
,
top
:
(
canvas
.
height
-
reversed
.
height
)
/
2
}
:
{
width
:
canvas
.
width
,
height
:
canvas
.
height
,
left
:
0
,
top
:
0
});
this
.
$clone
.
css
({
width
:
image
.
width
,
height
:
image
.
height
,
marginLeft
:
image
.
left
,
marginTop
:
image
.
top
,
transform
:
getTransform
(
image
)
});
if
(
isChanged
)
{
this
.
output
();
}
},
initCropBox
:
function
()
{
var
options
=
this
.
options
;
var
canvas
=
this
.
canvas
;
var
aspectRatio
=
options
.
aspectRatio
;
var
autoCropArea
=
num
(
options
.
autoCropArea
)
||
0.8
;
var
cropBox
=
{
width
:
canvas
.
width
,
height
:
canvas
.
height
};
if
(
aspectRatio
)
{
if
(
canvas
.
height
*
aspectRatio
>
canvas
.
width
)
{
cropBox
.
height
=
cropBox
.
width
/
aspectRatio
;
}
else
{
cropBox
.
width
=
cropBox
.
height
*
aspectRatio
;
}
}
this
.
cropBox
=
cropBox
;
this
.
limitCropBox
(
true
,
true
);
// Initialize auto crop area
cropBox
.
width
=
min
(
max
(
cropBox
.
width
,
cropBox
.
minWidth
),
cropBox
.
maxWidth
);
cropBox
.
height
=
min
(
max
(
cropBox
.
height
,
cropBox
.
minHeight
),
cropBox
.
maxHeight
);
// The width of auto crop area must large than "minWidth", and the height too. (#164)
cropBox
.
width
=
max
(
cropBox
.
minWidth
,
cropBox
.
width
*
autoCropArea
);
cropBox
.
height
=
max
(
cropBox
.
minHeight
,
cropBox
.
height
*
autoCropArea
);
cropBox
.
oldLeft
=
cropBox
.
left
=
canvas
.
left
+
(
canvas
.
width
-
cropBox
.
width
)
/
2
;
cropBox
.
oldTop
=
cropBox
.
top
=
canvas
.
top
+
(
canvas
.
height
-
cropBox
.
height
)
/
2
;
this
.
initialCropBox
=
$
.
extend
({},
cropBox
);
},
limitCropBox
:
function
(
isSizeLimited
,
isPositionLimited
)
{
var
options
=
this
.
options
;
var
aspectRatio
=
options
.
aspectRatio
;
var
container
=
this
.
container
;
var
containerWidth
=
container
.
width
;
var
containerHeight
=
container
.
height
;
var
canvas
=
this
.
canvas
;
var
cropBox
=
this
.
cropBox
;
var
isLimited
=
this
.
isLimited
;
var
minCropBoxWidth
;
var
minCropBoxHeight
;
var
maxCropBoxWidth
;
var
maxCropBoxHeight
;
if
(
isSizeLimited
)
{
minCropBoxWidth
=
num
(
options
.
minCropBoxWidth
)
||
0
;
minCropBoxHeight
=
num
(
options
.
minCropBoxHeight
)
||
0
;
// The min/maxCropBoxWidth/Height must be less than containerWidth/Height
minCropBoxWidth
=
min
(
minCropBoxWidth
,
containerWidth
);
minCropBoxHeight
=
min
(
minCropBoxHeight
,
containerHeight
);
maxCropBoxWidth
=
min
(
containerWidth
,
isLimited
?
canvas
.
width
:
containerWidth
);
maxCropBoxHeight
=
min
(
containerHeight
,
isLimited
?
canvas
.
height
:
containerHeight
);
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
cropBox
.
minWidth
=
min
(
minCropBoxWidth
,
maxCropBoxWidth
);
cropBox
.
minHeight
=
min
(
minCropBoxHeight
,
maxCropBoxHeight
);
cropBox
.
maxWidth
=
maxCropBoxWidth
;
cropBox
.
maxHeight
=
maxCropBoxHeight
;
}
if
(
isPositionLimited
)
{
if
(
isLimited
)
{
cropBox
.
minLeft
=
max
(
0
,
canvas
.
left
);
cropBox
.
minTop
=
max
(
0
,
canvas
.
top
);
cropBox
.
maxLeft
=
min
(
containerWidth
,
canvas
.
left
+
canvas
.
width
)
-
cropBox
.
width
;
cropBox
.
maxTop
=
min
(
containerHeight
,
canvas
.
top
+
canvas
.
height
)
-
cropBox
.
height
;
}
else
{
cropBox
.
minLeft
=
0
;
cropBox
.
minTop
=
0
;
cropBox
.
maxLeft
=
containerWidth
-
cropBox
.
width
;
cropBox
.
maxTop
=
containerHeight
-
cropBox
.
height
;
}
}
},
renderCropBox
:
function
()
{
var
options
=
this
.
options
;
var
container
=
this
.
container
;
var
containerWidth
=
container
.
width
;
var
containerHeight
=
container
.
height
;
var
cropBox
=
this
.
cropBox
;
if
(
cropBox
.
width
>
cropBox
.
maxWidth
||
cropBox
.
width
<
cropBox
.
minWidth
)
{
cropBox
.
left
=
cropBox
.
oldLeft
;
}
if
(
cropBox
.
height
>
cropBox
.
maxHeight
||
cropBox
.
height
<
cropBox
.
minHeight
)
{
cropBox
.
top
=
cropBox
.
oldTop
;
}
cropBox
.
width
=
min
(
max
(
cropBox
.
width
,
cropBox
.
minWidth
),
cropBox
.
maxWidth
);
cropBox
.
height
=
min
(
max
(
cropBox
.
height
,
cropBox
.
minHeight
),
cropBox
.
maxHeight
);
this
.
limitCropBox
(
false
,
true
);
cropBox
.
oldLeft
=
cropBox
.
left
=
min
(
max
(
cropBox
.
left
,
cropBox
.
minLeft
),
cropBox
.
maxLeft
);
cropBox
.
oldTop
=
cropBox
.
top
=
min
(
max
(
cropBox
.
top
,
cropBox
.
minTop
),
cropBox
.
maxTop
);
if
(
options
.
movable
&&
options
.
cropBoxMovable
)
{
// Turn to move the canvas when the crop box is equal to the container
this
.
$face
.
data
(
DATA_ACTION
,
(
cropBox
.
width
===
containerWidth
&&
cropBox
.
height
===
containerHeight
)
?
ACTION_MOVE
:
ACTION_ALL
);
}
this
.
$cropBox
.
css
({
width
:
cropBox
.
width
,
height
:
cropBox
.
height
,
left
:
cropBox
.
left
,
top
:
cropBox
.
top
});
if
(
this
.
isCropped
&&
this
.
isLimited
)
{
this
.
limitCanvas
(
true
,
true
);
}
if
(
!
this
.
isDisabled
)
{
this
.
output
();
}
},
output
:
function
()
{
this
.
preview
();
if
(
this
.
isCompleted
)
{
this
.
trigger
(
EVENT_CROP
,
this
.
getData
());
}
else
if
(
!
this
.
isBuilt
)
{
// Only trigger one crop event before complete
this
.
$element
.
one
(
EVENT_BUILT
,
$
.
proxy
(
function
()
{
this
.
trigger
(
EVENT_CROP
,
this
.
getData
());
},
this
));
}
},
initPreview
:
function
()
{
var
crossOrigin
=
getCrossOrigin
(
this
.
crossOrigin
);
var
url
=
crossOrigin
?
this
.
crossOriginUrl
:
this
.
url
;
var
$clone2
;
this
.
$preview
=
$
(
this
.
options
.
preview
);
this
.
$clone2
=
$clone2
=
$
(
'
<img
'
+
crossOrigin
+
'
src="
'
+
url
+
'
">
'
);
this
.
$viewBox
.
html
(
$clone2
);
this
.
$preview
.
each
(
function
()
{
var
$this
=
$
(
this
);
// Save the original size for recover
$this
.
data
(
DATA_PREVIEW
,
{
width
:
$this
.
width
(),
height
:
$this
.
height
(),
html
:
$this
.
html
()
});
/**
* Override img element styles
* Add `display:block` to avoid margin top issue
* (Occur only when margin-top <= -height)
*/
$this
.
html
(
'
<img
'
+
crossOrigin
+
'
src="
'
+
url
+
'
" style="
'
+
'
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;">
'
);
});
},
resetPreview
:
function
()
{
this
.
$preview
.
each
(
function
()
{
var
$this
=
$
(
this
);
var
data
=
$this
.
data
(
DATA_PREVIEW
);
$this
.
css
({
width
:
data
.
width
,
height
:
data
.
height
}).
html
(
data
.
html
).
removeData
(
DATA_PREVIEW
);
});
},
preview
:
function
()
{
var
image
=
this
.
image
;
var
canvas
=
this
.
canvas
;
var
cropBox
=
this
.
cropBox
;
var
cropBoxWidth
=
cropBox
.
width
;
var
cropBoxHeight
=
cropBox
.
height
;
var
width
=
image
.
width
;
var
height
=
image
.
height
;
var
left
=
cropBox
.
left
-
canvas
.
left
-
image
.
left
;
var
top
=
cropBox
.
top
-
canvas
.
top
-
image
.
top
;
if
(
!
this
.
isCropped
||
this
.
isDisabled
)
{
return
;
}
this
.
$clone2
.
css
({
width
:
width
,
height
:
height
,
marginLeft
:
-
left
,
marginTop
:
-
top
,
transform
:
getTransform
(
image
)
});
this
.
$preview
.
each
(
function
()
{
var
$this
=
$
(
this
);
var
data
=
$this
.
data
(
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
;
}
$this
.
css
({
width
:
newWidth
,
height
:
newHeight
}).
find
(
'
img
'
).
css
({
width
:
width
*
ratio
,
height
:
height
*
ratio
,
marginLeft
:
-
left
*
ratio
,
marginTop
:
-
top
*
ratio
,
transform
:
getTransform
(
image
)
});
});
},
bind
:
function
()
{
var
options
=
this
.
options
;
var
$this
=
this
.
$element
;
var
$cropper
=
this
.
$cropper
;
if
(
$
.
isFunction
(
options
.
cropstart
))
{
$this
.
on
(
EVENT_CROP_START
,
options
.
cropstart
);
}
if
(
$
.
isFunction
(
options
.
cropmove
))
{
$this
.
on
(
EVENT_CROP_MOVE
,
options
.
cropmove
);
}
if
(
$
.
isFunction
(
options
.
cropend
))
{
$this
.
on
(
EVENT_CROP_END
,
options
.
cropend
);
}
if
(
$
.
isFunction
(
options
.
crop
))
{
$this
.
on
(
EVENT_CROP
,
options
.
crop
);
}
if
(
$
.
isFunction
(
options
.
zoom
))
{
$this
.
on
(
EVENT_ZOOM
,
options
.
zoom
);
}
$cropper
.
on
(
EVENT_MOUSE_DOWN
,
$
.
proxy
(
this
.
cropStart
,
this
));
if
(
options
.
zoomable
&&
options
.
zoomOnWheel
)
{
$cropper
.
on
(
EVENT_WHEEL
,
$
.
proxy
(
this
.
wheel
,
this
));
}
if
(
options
.
toggleDragModeOnDblclick
)
{
$cropper
.
on
(
EVENT_DBLCLICK
,
$
.
proxy
(
this
.
dblclick
,
this
));
}
$document
.
on
(
EVENT_MOUSE_MOVE
,
(
this
.
_cropMove
=
proxy
(
this
.
cropMove
,
this
))).
on
(
EVENT_MOUSE_UP
,
(
this
.
_cropEnd
=
proxy
(
this
.
cropEnd
,
this
)));
if
(
options
.
responsive
)
{
$window
.
on
(
EVENT_RESIZE
,
(
this
.
_resize
=
proxy
(
this
.
resize
,
this
)));
}
},
unbind
:
function
()
{
var
options
=
this
.
options
;
var
$this
=
this
.
$element
;
var
$cropper
=
this
.
$cropper
;
if
(
$
.
isFunction
(
options
.
cropstart
))
{
$this
.
off
(
EVENT_CROP_START
,
options
.
cropstart
);
}
if
(
$
.
isFunction
(
options
.
cropmove
))
{
$this
.
off
(
EVENT_CROP_MOVE
,
options
.
cropmove
);
}
if
(
$
.
isFunction
(
options
.
cropend
))
{
$this
.
off
(
EVENT_CROP_END
,
options
.
cropend
);
}
if
(
$
.
isFunction
(
options
.
crop
))
{
$this
.
off
(
EVENT_CROP
,
options
.
crop
);
}
if
(
$
.
isFunction
(
options
.
zoom
))
{
$this
.
off
(
EVENT_ZOOM
,
options
.
zoom
);
}
$cropper
.
off
(
EVENT_MOUSE_DOWN
,
this
.
cropStart
);
if
(
options
.
zoomable
&&
options
.
zoomOnWheel
)
{
$cropper
.
off
(
EVENT_WHEEL
,
this
.
wheel
);
}
if
(
options
.
toggleDragModeOnDblclick
)
{
$cropper
.
off
(
EVENT_DBLCLICK
,
this
.
dblclick
);
}
$document
.
off
(
EVENT_MOUSE_MOVE
,
this
.
_cropMove
).
off
(
EVENT_MOUSE_UP
,
this
.
_cropEnd
);
if
(
options
.
responsive
)
{
$window
.
off
(
EVENT_RESIZE
,
this
.
_resize
);
}
},
resize
:
function
()
{
var
restore
=
this
.
options
.
restore
;
var
$container
=
this
.
$container
;
var
container
=
this
.
container
;
var
canvasData
;
var
cropBoxData
;
var
ratio
;
// Check `container` is necessary for IE8
if
(
this
.
isDisabled
||
!
container
)
{
return
;
}
ratio
=
$container
.
width
()
/
container
.
width
;
// Resize when width changed or height changed
if
(
ratio
!==
1
||
$container
.
height
()
!==
container
.
height
)
{
if
(
restore
)
{
canvasData
=
this
.
getCanvasData
();
cropBoxData
=
this
.
getCropBoxData
();
}
this
.
render
();
if
(
restore
)
{
this
.
setCanvasData
(
$
.
each
(
canvasData
,
function
(
i
,
n
)
{
canvasData
[
i
]
=
n
*
ratio
;
}));
this
.
setCropBoxData
(
$
.
each
(
cropBoxData
,
function
(
i
,
n
)
{
cropBoxData
[
i
]
=
n
*
ratio
;
}));
}
}
},
dblclick
:
function
()
{
if
(
this
.
isDisabled
)
{
return
;
}
if
(
this
.
$dragBox
.
hasClass
(
CLASS_CROP
))
{
this
.
setDragMode
(
ACTION_MOVE
);
}
else
{
this
.
setDragMode
(
ACTION_CROP
);
}
},
wheel
:
function
(
event
)
{
var
e
=
event
.
originalEvent
||
event
;
var
ratio
=
num
(
this
.
options
.
wheelZoomRatio
)
||
0.1
;
var
delta
=
1
;
if
(
this
.
isDisabled
)
{
return
;
}
event
.
preventDefault
();
// Limit wheel speed to prevent zoom too fast
if
(
this
.
wheeling
)
{
return
;
}
this
.
wheeling
=
true
;
setTimeout
(
$
.
proxy
(
function
()
{
this
.
wheeling
=
false
;
},
this
),
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
,
event
);
},
cropStart
:
function
(
event
)
{
var
options
=
this
.
options
;
var
originalEvent
=
event
.
originalEvent
;
var
touches
=
originalEvent
&&
originalEvent
.
touches
;
var
e
=
event
;
var
touchesLength
;
var
action
;
if
(
this
.
isDisabled
)
{
return
;
}
if
(
touches
)
{
touchesLength
=
touches
.
length
;
if
(
touchesLength
>
1
)
{
if
(
options
.
zoomable
&&
options
.
zoomOnTouch
&&
touchesLength
===
2
)
{
e
=
touches
[
1
];
this
.
startX2
=
e
.
pageX
;
this
.
startY2
=
e
.
pageY
;
action
=
ACTION_ZOOM
;
}
else
{
return
;
}
}
e
=
touches
[
0
];
}
action
=
action
||
$
(
e
.
target
).
data
(
DATA_ACTION
);
if
(
REGEXP_ACTIONS
.
test
(
action
))
{
if
(
this
.
trigger
(
EVENT_CROP_START
,
{
originalEvent
:
originalEvent
,
action
:
action
}).
isDefaultPrevented
())
{
return
;
}
event
.
preventDefault
();
this
.
action
=
action
;
this
.
cropping
=
false
;
// IE8 has `event.pageX/Y`, but not `event.originalEvent.pageX/Y`
// IE10 has `event.originalEvent.pageX/Y`, but not `event.pageX/Y`
this
.
startX
=
e
.
pageX
||
originalEvent
&&
originalEvent
.
pageX
;
this
.
startY
=
e
.
pageY
||
originalEvent
&&
originalEvent
.
pageY
;
if
(
action
===
ACTION_CROP
)
{
this
.
cropping
=
true
;
this
.
$dragBox
.
addClass
(
CLASS_MODAL
);
}
}
},
cropMove
:
function
(
event
)
{
var
options
=
this
.
options
;
var
originalEvent
=
event
.
originalEvent
;
var
touches
=
originalEvent
&&
originalEvent
.
touches
;
var
e
=
event
;
var
action
=
this
.
action
;
var
touchesLength
;
if
(
this
.
isDisabled
)
{
return
;
}
if
(
touches
)
{
touchesLength
=
touches
.
length
;
if
(
touchesLength
>
1
)
{
if
(
options
.
zoomable
&&
options
.
zoomOnTouch
&&
touchesLength
===
2
)
{
e
=
touches
[
1
];
this
.
endX2
=
e
.
pageX
;
this
.
endY2
=
e
.
pageY
;
}
else
{
return
;
}
}
e
=
touches
[
0
];
}
if
(
action
)
{
if
(
this
.
trigger
(
EVENT_CROP_MOVE
,
{
originalEvent
:
originalEvent
,
action
:
action
}).
isDefaultPrevented
())
{
return
;
}
event
.
preventDefault
();
this
.
endX
=
e
.
pageX
||
originalEvent
&&
originalEvent
.
pageX
;
this
.
endY
=
e
.
pageY
||
originalEvent
&&
originalEvent
.
pageY
;
this
.
change
(
e
.
shiftKey
,
action
===
ACTION_ZOOM
?
event
:
null
);
}
},
cropEnd
:
function
(
event
)
{
var
originalEvent
=
event
.
originalEvent
;
var
action
=
this
.
action
;
if
(
this
.
isDisabled
)
{
return
;
}
if
(
action
)
{
event
.
preventDefault
();
if
(
this
.
cropping
)
{
this
.
cropping
=
false
;
this
.
$dragBox
.
toggleClass
(
CLASS_MODAL
,
this
.
isCropped
&&
this
.
options
.
modal
);
}
this
.
action
=
''
;
this
.
trigger
(
EVENT_CROP_END
,
{
originalEvent
:
originalEvent
,
action
:
action
});
}
},
change
:
function
(
shiftKey
,
event
)
{
var
options
=
this
.
options
;
var
aspectRatio
=
options
.
aspectRatio
;
var
action
=
this
.
action
;
var
container
=
this
.
container
;
var
canvas
=
this
.
canvas
;
var
cropBox
=
this
.
cropBox
;
var
width
=
cropBox
.
width
;
var
height
=
cropBox
.
height
;
var
left
=
cropBox
.
left
;
var
top
=
cropBox
.
top
;
var
right
=
left
+
width
;
var
bottom
=
top
+
height
;
var
minLeft
=
0
;
var
minTop
=
0
;
var
maxWidth
=
container
.
width
;
var
maxHeight
=
container
.
height
;
var
renderable
=
true
;
var
offset
;
var
range
;
// Locking aspect ratio in "free mode" by holding shift key (#259)
if
(
!
aspectRatio
&&
shiftKey
)
{
aspectRatio
=
width
&&
height
?
width
/
height
:
1
;
}
if
(
this
.
limited
)
{
minLeft
=
cropBox
.
minLeft
;
minTop
=
cropBox
.
minTop
;
maxWidth
=
minLeft
+
min
(
container
.
width
,
canvas
.
left
+
canvas
.
width
);
maxHeight
=
minTop
+
min
(
container
.
height
,
canvas
.
top
+
canvas
.
height
);
}
range
=
{
x
:
this
.
endX
-
this
.
startX
,
y
:
this
.
endY
-
this
.
startY
};
if
(
aspectRatio
)
{
range
.
X
=
range
.
y
*
aspectRatio
;
range
.
Y
=
range
.
x
/
aspectRatio
;
}
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
;
}
width
+=
range
.
x
;
if
(
aspectRatio
)
{
height
=
width
/
aspectRatio
;
top
-=
range
.
Y
/
2
;
}
if
(
width
<
0
)
{
action
=
ACTION_WEST
;
width
=
0
;
}
break
;
case
ACTION_NORTH
:
if
(
range
.
y
<=
0
&&
(
top
<=
minTop
||
aspectRatio
&&
(
left
<=
minLeft
||
right
>=
maxWidth
)))
{
renderable
=
false
;
break
;
}
height
-=
range
.
y
;
top
+=
range
.
y
;
if
(
aspectRatio
)
{
width
=
height
*
aspectRatio
;
left
+=
range
.
X
/
2
;
}
if
(
height
<
0
)
{
action
=
ACTION_SOUTH
;
height
=
0
;
}
break
;
case
ACTION_WEST
:
if
(
range
.
x
<=
0
&&
(
left
<=
minLeft
||
aspectRatio
&&
(
top
<=
minTop
||
bottom
>=
maxHeight
)))
{
renderable
=
false
;
break
;
}
width
-=
range
.
x
;
left
+=
range
.
x
;
if
(
aspectRatio
)
{
height
=
width
/
aspectRatio
;
top
+=
range
.
Y
/
2
;
}
if
(
width
<
0
)
{
action
=
ACTION_EAST
;
width
=
0
;
}
break
;
case
ACTION_SOUTH
:
if
(
range
.
y
>=
0
&&
(
bottom
>=
maxHeight
||
aspectRatio
&&
(
left
<=
minLeft
||
right
>=
maxWidth
)))
{
renderable
=
false
;
break
;
}
height
+=
range
.
y
;
if
(
aspectRatio
)
{
width
=
height
*
aspectRatio
;
left
-=
range
.
X
/
2
;
}
if
(
height
<
0
)
{
action
=
ACTION_NORTH
;
height
=
0
;
}
break
;
case
ACTION_NORTH_EAST
:
if
(
aspectRatio
)
{
if
(
range
.
y
<=
0
&&
(
top
<=
minTop
||
right
>=
maxWidth
))
{
renderable
=
false
;
break
;
}
height
-=
range
.
y
;
top
+=
range
.
y
;
width
=
height
*
aspectRatio
;
}
else
{
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
=
0
;
width
=
0
;
}
else
if
(
width
<
0
)
{
action
=
ACTION_NORTH_WEST
;
width
=
0
;
}
else
if
(
height
<
0
)
{
action
=
ACTION_SOUTH_EAST
;
height
=
0
;
}
break
;
case
ACTION_NORTH_WEST
:
if
(
aspectRatio
)
{
if
(
range
.
y
<=
0
&&
(
top
<=
minTop
||
left
<=
minLeft
))
{
renderable
=
false
;
break
;
}
height
-=
range
.
y
;
top
+=
range
.
y
;
width
=
height
*
aspectRatio
;
left
+=
range
.
X
;
}
else
{
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
=
0
;
width
=
0
;
}
else
if
(
width
<
0
)
{
action
=
ACTION_NORTH_EAST
;
width
=
0
;
}
else
if
(
height
<
0
)
{
action
=
ACTION_SOUTH_WEST
;
height
=
0
;
}
break
;
case
ACTION_SOUTH_WEST
:
if
(
aspectRatio
)
{
if
(
range
.
x
<=
0
&&
(
left
<=
minLeft
||
bottom
>=
maxHeight
))
{
renderable
=
false
;
break
;
}
width
-=
range
.
x
;
left
+=
range
.
x
;
height
=
width
/
aspectRatio
;
}
else
{
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
=
0
;
width
=
0
;
}
else
if
(
width
<
0
)
{
action
=
ACTION_SOUTH_EAST
;
width
=
0
;
}
else
if
(
height
<
0
)
{
action
=
ACTION_NORTH_WEST
;
height
=
0
;
}
break
;
case
ACTION_SOUTH_EAST
:
if
(
aspectRatio
)
{
if
(
range
.
x
>=
0
&&
(
right
>=
maxWidth
||
bottom
>=
maxHeight
))
{
renderable
=
false
;
break
;
}
width
+=
range
.
x
;
height
=
width
/
aspectRatio
;
}
else
{
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
=
0
;
width
=
0
;
}
else
if
(
width
<
0
)
{
action
=
ACTION_SOUTH_WEST
;
width
=
0
;
}
else
if
(
height
<
0
)
{
action
=
ACTION_NORTH_EAST
;
height
=
0
;
}
break
;
// Move canvas
case
ACTION_MOVE
:
this
.
move
(
range
.
x
,
range
.
y
);
renderable
=
false
;
break
;
// Zoom canvas
case
ACTION_ZOOM
:
this
.
zoom
((
function
(
x1
,
y1
,
x2
,
y2
)
{
var
z1
=
sqrt
(
x1
*
x1
+
y1
*
y1
);
var
z2
=
sqrt
(
x2
*
x2
+
y2
*
y2
);
return
(
z2
-
z1
)
/
z1
;
})(
abs
(
this
.
startX
-
this
.
startX2
),
abs
(
this
.
startY
-
this
.
startY2
),
abs
(
this
.
endX
-
this
.
endX2
),
abs
(
this
.
endY
-
this
.
endY2
)
),
event
);
this
.
startX2
=
this
.
endX2
;
this
.
startY2
=
this
.
endY2
;
renderable
=
false
;
break
;
// Create crop box
case
ACTION_CROP
:
if
(
!
range
.
x
||
!
range
.
y
)
{
renderable
=
false
;
break
;
}
offset
=
this
.
$cropper
.
offset
();
left
=
this
.
startX
-
offset
.
left
;
top
=
this
.
startY
-
offset
.
top
;
width
=
cropBox
.
minWidth
;
height
=
cropBox
.
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
.
isCropped
)
{
this
.
$cropBox
.
removeClass
(
CLASS_HIDDEN
);
this
.
isCropped
=
true
;
if
(
this
.
limited
)
{
this
.
limitCropBox
(
true
,
true
);
}
}
break
;
// No default
}
if
(
renderable
)
{
cropBox
.
width
=
width
;
cropBox
.
height
=
height
;
cropBox
.
left
=
left
;
cropBox
.
top
=
top
;
this
.
action
=
action
;
this
.
renderCropBox
();
}
// Override
this
.
startX
=
this
.
endX
;
this
.
startY
=
this
.
endY
;
},
// Show the crop box manually
crop
:
function
()
{
if
(
!
this
.
isBuilt
||
this
.
isDisabled
)
{
return
;
}
if
(
!
this
.
isCropped
)
{
this
.
isCropped
=
true
;
this
.
limitCropBox
(
true
,
true
);
if
(
this
.
options
.
modal
)
{
this
.
$dragBox
.
addClass
(
CLASS_MODAL
);
}
this
.
$cropBox
.
removeClass
(
CLASS_HIDDEN
);
}
this
.
setCropBoxData
(
this
.
initialCropBox
);
},
// Reset the image and crop box to their initial states
reset
:
function
()
{
if
(
!
this
.
isBuilt
||
this
.
isDisabled
)
{
return
;
}
this
.
image
=
$
.
extend
({},
this
.
initialImage
);
this
.
canvas
=
$
.
extend
({},
this
.
initialCanvas
);
this
.
cropBox
=
$
.
extend
({},
this
.
initialCropBox
);
this
.
renderCanvas
();
if
(
this
.
isCropped
)
{
this
.
renderCropBox
();
}
},
// Clear the crop box
clear
:
function
()
{
if
(
!
this
.
isCropped
||
this
.
isDisabled
)
{
return
;
}
$
.
extend
(
this
.
cropBox
,
{
left
:
0
,
top
:
0
,
width
:
0
,
height
:
0
});
this
.
isCropped
=
false
;
this
.
renderCropBox
();
this
.
limitCanvas
(
true
,
true
);
// Render canvas after crop box rendered
this
.
renderCanvas
();
this
.
$dragBox
.
removeClass
(
CLASS_MODAL
);
this
.
$cropBox
.
addClass
(
CLASS_HIDDEN
);
},
/**
* Replace the image's src and rebuild the cropper
*
* @param {String} url
* @param {Boolean} onlyColorChanged (optional)
*/
replace
:
function
(
url
,
onlyColorChanged
)
{
if
(
!
this
.
isDisabled
&&
url
)
{
if
(
this
.
isImg
)
{
this
.
$element
.
attr
(
'
src
'
,
url
);
}
if
(
onlyColorChanged
)
{
this
.
url
=
url
;
this
.
$clone
.
attr
(
'
src
'
,
url
);
if
(
this
.
isBuilt
)
{
this
.
$preview
.
find
(
'
img
'
).
add
(
this
.
$clone2
).
attr
(
'
src
'
,
url
);
}
}
else
{
if
(
this
.
isImg
)
{
this
.
isReplaced
=
true
;
}
// Clear previous data
this
.
options
.
data
=
null
;
this
.
load
(
url
);
}
}
},
// Enable (unfreeze) the cropper
enable
:
function
()
{
if
(
this
.
isBuilt
)
{
this
.
isDisabled
=
false
;
this
.
$cropper
.
removeClass
(
CLASS_DISABLED
);
}
},
// Disable (freeze) the cropper
disable
:
function
()
{
if
(
this
.
isBuilt
)
{
this
.
isDisabled
=
true
;
this
.
$cropper
.
addClass
(
CLASS_DISABLED
);
}
},
// Destroy the cropper and remove the instance from the image
destroy
:
function
()
{
var
$this
=
this
.
$element
;
if
(
this
.
isLoaded
)
{
if
(
this
.
isImg
&&
this
.
isReplaced
)
{
$this
.
attr
(
'
src
'
,
this
.
originalUrl
);
}
this
.
unbuild
();
$this
.
removeClass
(
CLASS_HIDDEN
);
}
else
{
if
(
this
.
isImg
)
{
$this
.
off
(
EVENT_LOAD
,
this
.
start
);
}
else
if
(
this
.
$clone
)
{
this
.
$clone
.
remove
();
}
}
$this
.
removeData
(
NAMESPACE
);
},
/**
* Move the canvas with relative offsets
*
* @param {Number} offsetX
* @param {Number} offsetY (optional)
*/
move
:
function
(
offsetX
,
offsetY
)
{
var
canvas
=
this
.
canvas
;
this
.
moveTo
(
isUndefined
(
offsetX
)
?
offsetX
:
canvas
.
left
+
num
(
offsetX
),
isUndefined
(
offsetY
)
?
offsetY
:
canvas
.
top
+
num
(
offsetY
)
);
},
/**
* Move the canvas to an absolute point
*
* @param {Number} x
* @param {Number} y (optional)
*/
moveTo
:
function
(
x
,
y
)
{
var
canvas
=
this
.
canvas
;
var
isChanged
=
false
;
// If "y" is not present, its default value is "x"
if
(
isUndefined
(
y
))
{
y
=
x
;
}
x
=
num
(
x
);
y
=
num
(
y
);
if
(
this
.
isBuilt
&&
!
this
.
isDisabled
&&
this
.
options
.
movable
)
{
if
(
isNumber
(
x
))
{
canvas
.
left
=
x
;
isChanged
=
true
;
}
if
(
isNumber
(
y
))
{
canvas
.
top
=
y
;
isChanged
=
true
;
}
if
(
isChanged
)
{
this
.
renderCanvas
(
true
);
}
}
},
/**
* Zoom the canvas with a relative ratio
*
* @param {Number} ratio
* @param {jQuery Event} _event (private)
*/
zoom
:
function
(
ratio
,
_event
)
{
var
canvas
=
this
.
canvas
;
ratio
=
num
(
ratio
);
if
(
ratio
<
0
)
{
ratio
=
1
/
(
1
-
ratio
);
}
else
{
ratio
=
1
+
ratio
;
}
this
.
zoomTo
(
canvas
.
width
*
ratio
/
canvas
.
naturalWidth
,
_event
);
},
/**
* Zoom the canvas to an absolute ratio
*
* @param {Number} ratio
* @param {jQuery Event} _event (private)
*/
zoomTo
:
function
(
ratio
,
_event
)
{
var
options
=
this
.
options
;
var
canvas
=
this
.
canvas
;
var
width
=
canvas
.
width
;
var
height
=
canvas
.
height
;
var
naturalWidth
=
canvas
.
naturalWidth
;
var
naturalHeight
=
canvas
.
naturalHeight
;
var
originalEvent
;
var
newWidth
;
var
newHeight
;
var
offset
;
var
center
;
ratio
=
num
(
ratio
);
if
(
ratio
>=
0
&&
this
.
isBuilt
&&
!
this
.
isDisabled
&&
options
.
zoomable
)
{
newWidth
=
naturalWidth
*
ratio
;
newHeight
=
naturalHeight
*
ratio
;
if
(
_event
)
{
originalEvent
=
_event
.
originalEvent
;
}
if
(
this
.
trigger
(
EVENT_ZOOM
,
{
originalEvent
:
originalEvent
,
oldRatio
:
width
/
naturalWidth
,
ratio
:
newWidth
/
naturalWidth
}).
isDefaultPrevented
())
{
return
;
}
if
(
originalEvent
)
{
offset
=
this
.
$cropper
.
offset
();
center
=
originalEvent
.
touches
?
getTouchesCenter
(
originalEvent
.
touches
)
:
{
pageX
:
_event
.
pageX
||
originalEvent
.
pageX
||
0
,
pageY
:
_event
.
pageY
||
originalEvent
.
pageY
||
0
};
// Zoom from the triggering point of the event
canvas
.
left
-=
(
newWidth
-
width
)
*
(
((
center
.
pageX
-
offset
.
left
)
-
canvas
.
left
)
/
width
);
canvas
.
top
-=
(
newHeight
-
height
)
*
(
((
center
.
pageY
-
offset
.
top
)
-
canvas
.
top
)
/
height
);
}
else
{
// Zoom from the center of the canvas
canvas
.
left
-=
(
newWidth
-
width
)
/
2
;
canvas
.
top
-=
(
newHeight
-
height
)
/
2
;
}
canvas
.
width
=
newWidth
;
canvas
.
height
=
newHeight
;
this
.
renderCanvas
(
true
);
}
},
/**
* Rotate the canvas with a relative degree
*
* @param {Number} degree
*/
rotate
:
function
(
degree
)
{
this
.
rotateTo
((
this
.
image
.
rotate
||
0
)
+
num
(
degree
));
},
/**
* Rotate the canvas to an absolute degree
* https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function#rotate()
*
* @param {Number} degree
*/
rotateTo
:
function
(
degree
)
{
degree
=
num
(
degree
);
if
(
isNumber
(
degree
)
&&
this
.
isBuilt
&&
!
this
.
isDisabled
&&
this
.
options
.
rotatable
)
{
this
.
image
.
rotate
=
degree
%
360
;
this
.
isRotated
=
true
;
this
.
renderCanvas
(
true
);
}
},
/**
* Scale the image
* https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function#scale()
*
* @param {Number} scaleX
* @param {Number} scaleY (optional)
*/
scale
:
function
(
scaleX
,
scaleY
)
{
var
image
=
this
.
image
;
var
isChanged
=
false
;
// If "scaleY" is not present, its default value is "scaleX"
if
(
isUndefined
(
scaleY
))
{
scaleY
=
scaleX
;
}
scaleX
=
num
(
scaleX
);
scaleY
=
num
(
scaleY
);
if
(
this
.
isBuilt
&&
!
this
.
isDisabled
&&
this
.
options
.
scalable
)
{
if
(
isNumber
(
scaleX
))
{
image
.
scaleX
=
scaleX
;
isChanged
=
true
;
}
if
(
isNumber
(
scaleY
))
{
image
.
scaleY
=
scaleY
;
isChanged
=
true
;
}
if
(
isChanged
)
{
this
.
renderImage
(
true
);
}
}
},
/**
* Scale the abscissa of the image
*
* @param {Number} scaleX
*/
scaleX
:
function
(
scaleX
)
{
var
scaleY
=
this
.
image
.
scaleY
;
this
.
scale
(
scaleX
,
isNumber
(
scaleY
)
?
scaleY
:
1
);
},
/**
* Scale the ordinate of the image
*
* @param {Number} scaleY
*/
scaleY
:
function
(
scaleY
)
{
var
scaleX
=
this
.
image
.
scaleX
;
this
.
scale
(
isNumber
(
scaleX
)
?
scaleX
:
1
,
scaleY
);
},
/**
* Get the cropped area position and size data (base on the original image)
*
* @param {Boolean} isRounded (optional)
* @return {Object} data
*/
getData
:
function
(
isRounded
)
{
var
options
=
this
.
options
;
var
image
=
this
.
image
;
var
canvas
=
this
.
canvas
;
var
cropBox
=
this
.
cropBox
;
var
ratio
;
var
data
;
if
(
this
.
isBuilt
&&
this
.
isCropped
)
{
data
=
{
x
:
cropBox
.
left
-
canvas
.
left
,
y
:
cropBox
.
top
-
canvas
.
top
,
width
:
cropBox
.
width
,
height
:
cropBox
.
height
};
ratio
=
image
.
width
/
image
.
naturalWidth
;
$
.
each
(
data
,
function
(
i
,
n
)
{
n
=
n
/
ratio
;
data
[
i
]
=
isRounded
?
round
(
n
)
:
n
;
});
}
else
{
data
=
{
x
:
0
,
y
:
0
,
width
:
0
,
height
:
0
};
}
if
(
options
.
rotatable
)
{
data
.
rotate
=
image
.
rotate
||
0
;
}
if
(
options
.
scalable
)
{
data
.
scaleX
=
image
.
scaleX
||
1
;
data
.
scaleY
=
image
.
scaleY
||
1
;
}
return
data
;
},
/**
* Set the cropped area position and size with new data
*
* @param {Object} data
*/
setData
:
function
(
data
)
{
var
options
=
this
.
options
;
var
image
=
this
.
image
;
var
canvas
=
this
.
canvas
;
var
cropBoxData
=
{};
var
isRotated
;
var
isScaled
;
var
ratio
;
if
(
$
.
isFunction
(
data
))
{
data
=
data
.
call
(
this
.
element
);
}
if
(
this
.
isBuilt
&&
!
this
.
isDisabled
&&
$
.
isPlainObject
(
data
))
{
if
(
options
.
rotatable
)
{
if
(
isNumber
(
data
.
rotate
)
&&
data
.
rotate
!==
image
.
rotate
)
{
image
.
rotate
=
data
.
rotate
;
this
.
isRotated
=
isRotated
=
true
;
}
}
if
(
options
.
scalable
)
{
if
(
isNumber
(
data
.
scaleX
)
&&
data
.
scaleX
!==
image
.
scaleX
)
{
image
.
scaleX
=
data
.
scaleX
;
isScaled
=
true
;
}
if
(
isNumber
(
data
.
scaleY
)
&&
data
.
scaleY
!==
image
.
scaleY
)
{
image
.
scaleY
=
data
.
scaleY
;
isScaled
=
true
;
}
}
if
(
isRotated
)
{
this
.
renderCanvas
();
}
else
if
(
isScaled
)
{
this
.
renderImage
();
}
ratio
=
image
.
width
/
image
.
naturalWidth
;
if
(
isNumber
(
data
.
x
))
{
cropBoxData
.
left
=
data
.
x
*
ratio
+
canvas
.
left
;
}
if
(
isNumber
(
data
.
y
))
{
cropBoxData
.
top
=
data
.
y
*
ratio
+
canvas
.
top
;
}
if
(
isNumber
(
data
.
width
))
{
cropBoxData
.
width
=
data
.
width
*
ratio
;
}
if
(
isNumber
(
data
.
height
))
{
cropBoxData
.
height
=
data
.
height
*
ratio
;
}
this
.
setCropBoxData
(
cropBoxData
);
}
},
/**
* Get the container size data
*
* @return {Object} data
*/
getContainerData
:
function
()
{
return
this
.
isBuilt
?
this
.
container
:
{};
},
/**
* Get the image position and size data
*
* @return {Object} data
*/
getImageData
:
function
()
{
return
this
.
isLoaded
?
this
.
image
:
{};
},
/**
* Get the canvas position and size data
*
* @return {Object} data
*/
getCanvasData
:
function
()
{
var
canvas
=
this
.
canvas
;
var
data
=
{};
if
(
this
.
isBuilt
)
{
$
.
each
([
'
left
'
,
'
top
'
,
'
width
'
,
'
height
'
,
'
naturalWidth
'
,
'
naturalHeight
'
],
function
(
i
,
n
)
{
data
[
n
]
=
canvas
[
n
];
});
}
return
data
;
},
/**
* Set the canvas position and size with new data
*
* @param {Object} data
*/
setCanvasData
:
function
(
data
)
{
var
canvas
=
this
.
canvas
;
var
aspectRatio
=
canvas
.
aspectRatio
;
if
(
$
.
isFunction
(
data
))
{
data
=
data
.
call
(
this
.
$element
);
}
if
(
this
.
isBuilt
&&
!
this
.
isDisabled
&&
$
.
isPlainObject
(
data
))
{
if
(
isNumber
(
data
.
left
))
{
canvas
.
left
=
data
.
left
;
}
if
(
isNumber
(
data
.
top
))
{
canvas
.
top
=
data
.
top
;
}
if
(
isNumber
(
data
.
width
))
{
canvas
.
width
=
data
.
width
;
canvas
.
height
=
data
.
width
/
aspectRatio
;
}
else
if
(
isNumber
(
data
.
height
))
{
canvas
.
height
=
data
.
height
;
canvas
.
width
=
data
.
height
*
aspectRatio
;
}
this
.
renderCanvas
(
true
);
}
},
/**
* Get the crop box position and size data
*
* @return {Object} data
*/
getCropBoxData
:
function
()
{
var
cropBox
=
this
.
cropBox
;
var
data
;
if
(
this
.
isBuilt
&&
this
.
isCropped
)
{
data
=
{
left
:
cropBox
.
left
,
top
:
cropBox
.
top
,
width
:
cropBox
.
width
,
height
:
cropBox
.
height
};
}
return
data
||
{};
},
/**
* Set the crop box position and size with new data
*
* @param {Object} data
*/
setCropBoxData
:
function
(
data
)
{
var
cropBox
=
this
.
cropBox
;
var
aspectRatio
=
this
.
options
.
aspectRatio
;
var
isWidthChanged
;
var
isHeightChanged
;
if
(
$
.
isFunction
(
data
))
{
data
=
data
.
call
(
this
.
$element
);
}
if
(
this
.
isBuilt
&&
this
.
isCropped
&&
!
this
.
isDisabled
&&
$
.
isPlainObject
(
data
))
{
if
(
isNumber
(
data
.
left
))
{
cropBox
.
left
=
data
.
left
;
}
if
(
isNumber
(
data
.
top
))
{
cropBox
.
top
=
data
.
top
;
}
if
(
isNumber
(
data
.
width
))
{
isWidthChanged
=
true
;
cropBox
.
width
=
data
.
width
;
}
if
(
isNumber
(
data
.
height
))
{
isHeightChanged
=
true
;
cropBox
.
height
=
data
.
height
;
}
if
(
aspectRatio
)
{
if
(
isWidthChanged
)
{
cropBox
.
height
=
cropBox
.
width
/
aspectRatio
;
}
else
if
(
isHeightChanged
)
{
cropBox
.
width
=
cropBox
.
height
*
aspectRatio
;
}
}
this
.
renderCropBox
();
}
},
/**
* Get a canvas drawn the cropped image
*
* @param {Object} options (optional)
* @return {HTMLCanvasElement} canvas
*/
getCroppedCanvas
:
function
(
options
)
{
var
originalWidth
;
var
originalHeight
;
var
canvasWidth
;
var
canvasHeight
;
var
scaledWidth
;
var
scaledHeight
;
var
scaledRatio
;
var
aspectRatio
;
var
canvas
;
var
context
;
var
data
;
if
(
!
this
.
isBuilt
||
!
this
.
isCropped
||
!
SUPPORT_CANVAS
)
{
return
;
}
if
(
!
$
.
isPlainObject
(
options
))
{
options
=
{};
}
data
=
this
.
getData
();
originalWidth
=
data
.
width
;
originalHeight
=
data
.
height
;
aspectRatio
=
originalWidth
/
originalHeight
;
if
(
$
.
isPlainObject
(
options
))
{
scaledWidth
=
options
.
width
;
scaledHeight
=
options
.
height
;
if
(
scaledWidth
)
{
scaledHeight
=
scaledWidth
/
aspectRatio
;
scaledRatio
=
scaledWidth
/
originalWidth
;
}
else
if
(
scaledHeight
)
{
scaledWidth
=
scaledHeight
*
aspectRatio
;
scaledRatio
=
scaledHeight
/
originalHeight
;
}
}
// The canvas element will use `Math.floor` on a float number, so floor first
canvasWidth
=
floor
(
scaledWidth
||
originalWidth
);
canvasHeight
=
floor
(
scaledHeight
||
originalHeight
);
canvas
=
$
(
'
<canvas>
'
)[
0
];
canvas
.
width
=
canvasWidth
;
canvas
.
height
=
canvasHeight
;
context
=
canvas
.
getContext
(
'
2d
'
);
if
(
options
.
fillColor
)
{
context
.
fillStyle
=
options
.
fillColor
;
context
.
fillRect
(
0
,
0
,
canvasWidth
,
canvasHeight
);
}
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D.drawImage
context
.
drawImage
.
apply
(
context
,
(
function
()
{
var
source
=
getSourceCanvas
(
this
.
$clone
[
0
],
this
.
image
);
var
sourceWidth
=
source
.
width
;
var
sourceHeight
=
source
.
height
;
var
canvas
=
this
.
canvas
;
var
params
=
[
source
];
// Source canvas
var
srcX
=
data
.
x
+
canvas
.
naturalWidth
*
(
abs
(
data
.
scaleX
||
1
)
-
1
)
/
2
;
var
srcY
=
data
.
y
+
canvas
.
naturalHeight
*
(
abs
(
data
.
scaleY
||
1
)
-
1
)
/
2
;
var
srcWidth
;
var
srcHeight
;
// Destination canvas
var
dstX
;
var
dstY
;
var
dstWidth
;
var
dstHeight
;
if
(
srcX
<=
-
originalWidth
||
srcX
>
sourceWidth
)
{
srcX
=
srcWidth
=
dstX
=
dstWidth
=
0
;
}
else
if
(
srcX
<=
0
)
{
dstX
=
-
srcX
;
srcX
=
0
;
srcWidth
=
dstWidth
=
min
(
sourceWidth
,
originalWidth
+
srcX
);
}
else
if
(
srcX
<=
sourceWidth
)
{
dstX
=
0
;
srcWidth
=
dstWidth
=
min
(
originalWidth
,
sourceWidth
-
srcX
);
}
if
(
srcWidth
<=
0
||
srcY
<=
-
originalHeight
||
srcY
>
sourceHeight
)
{
srcY
=
srcHeight
=
dstY
=
dstHeight
=
0
;
}
else
if
(
srcY
<=
0
)
{
dstY
=
-
srcY
;
srcY
=
0
;
srcHeight
=
dstHeight
=
min
(
sourceHeight
,
originalHeight
+
srcY
);
}
else
if
(
srcY
<=
sourceHeight
)
{
dstY
=
0
;
srcHeight
=
dstHeight
=
min
(
originalHeight
,
sourceHeight
-
srcY
);
}
// All the numerical parameters should be integer for `drawImage` (#476)
params
.
push
(
floor
(
srcX
),
floor
(
srcY
),
floor
(
srcWidth
),
floor
(
srcHeight
));
// Scale destination sizes
if
(
scaledRatio
)
{
dstX
*=
scaledRatio
;
dstY
*=
scaledRatio
;
dstWidth
*=
scaledRatio
;
dstHeight
*=
scaledRatio
;
}
// Avoid "IndexSizeError" in IE and Firefox
if
(
dstWidth
>
0
&&
dstHeight
>
0
)
{
params
.
push
(
floor
(
dstX
),
floor
(
dstY
),
floor
(
dstWidth
),
floor
(
dstHeight
));
}
return
params
;
}).
call
(
this
));
return
canvas
;
},
/**
* Change the aspect ratio of the crop box
*
* @param {Number} aspectRatio
*/
setAspectRatio
:
function
(
aspectRatio
)
{
var
options
=
this
.
options
;
if
(
!
this
.
isDisabled
&&
!
isUndefined
(
aspectRatio
))
{
// 0 -> NaN
options
.
aspectRatio
=
max
(
0
,
aspectRatio
)
||
NaN
;
if
(
this
.
isBuilt
)
{
this
.
initCropBox
();
if
(
this
.
isCropped
)
{
this
.
renderCropBox
();
}
}
}
},
/**
* Change the drag mode
*
* @param {String} mode (optional)
*/
setDragMode
:
function
(
mode
)
{
var
options
=
this
.
options
;
var
croppable
;
var
movable
;
if
(
this
.
isLoaded
&&
!
this
.
isDisabled
)
{
croppable
=
mode
===
ACTION_CROP
;
movable
=
options
.
movable
&&
mode
===
ACTION_MOVE
;
mode
=
(
croppable
||
movable
)
?
mode
:
ACTION_NONE
;
this
.
$dragBox
.
data
(
DATA_ACTION
,
mode
).
toggleClass
(
CLASS_CROP
,
croppable
).
toggleClass
(
CLASS_MOVE
,
movable
);
if
(
!
options
.
cropBoxMovable
)
{
// Sync drag mode to crop box when it is not movable(#300)
this
.
$face
.
data
(
DATA_ACTION
,
mode
).
toggleClass
(
CLASS_CROP
,
croppable
).
toggleClass
(
CLASS_MOVE
,
movable
);
}
}
}
};
Cropper
.
DEFAULTS
=
{
// Define the view mode of the cropper
viewMode
:
0
,
// 0, 1, 2, 3
// Define the dragging mode of the cropper
dragMode
:
'
crop
'
,
// 'crop', 'move' or 'none'
// Define the aspect ratio of the crop box
aspectRatio
:
NaN
,
// An object with the previous cropping result data
data
:
null
,
// A jQuery 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
build
:
null
,
built
:
null
,
cropstart
:
null
,
cropmove
:
null
,
cropend
:
null
,
crop
:
null
,
zoom
:
null
};
Cropper
.
setDefaults
=
function
(
options
)
{
$
.
extend
(
Cropper
.
DEFAULTS
,
options
);
};
Cropper
.
TEMPLATE
=
(
'
<div class="cropper-container">
'
+
'
<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-action="e"></span>
'
+
'
<span class="cropper-line line-n" data-action="n"></span>
'
+
'
<span class="cropper-line line-w" data-action="w"></span>
'
+
'
<span class="cropper-line line-s" data-action="s"></span>
'
+
'
<span class="cropper-point point-e" data-action="e"></span>
'
+
'
<span class="cropper-point point-n" data-action="n"></span>
'
+
'
<span class="cropper-point point-w" data-action="w"></span>
'
+
'
<span class="cropper-point point-s" data-action="s"></span>
'
+
'
<span class="cropper-point point-ne" data-action="ne"></span>
'
+
'
<span class="cropper-point point-nw" data-action="nw"></span>
'
+
'
<span class="cropper-point point-sw" data-action="sw"></span>
'
+
'
<span class="cropper-point point-se" data-action="se"></span>
'
+
'
</div>
'
+
'
</div>
'
);
// Save the other cropper
Cropper
.
other
=
$
.
fn
.
cropper
;
// Register as jQuery plugin
$
.
fn
.
cropper
=
function
(
option
)
{
var
args
=
toArray
(
arguments
,
1
);
var
result
;
this
.
each
(
function
()
{
var
$this
=
$
(
this
);
var
data
=
$this
.
data
(
NAMESPACE
);
var
options
;
var
fn
;
if
(
!
data
)
{
if
(
/destroy/
.
test
(
option
))
{
return
;
}
options
=
$
.
extend
({},
$this
.
data
(),
$
.
isPlainObject
(
option
)
&&
option
);
$this
.
data
(
NAMESPACE
,
(
data
=
new
Cropper
(
this
,
options
)));
}
if
(
typeof
option
===
'
string
'
&&
$
.
isFunction
(
fn
=
data
[
option
]))
{
result
=
fn
.
apply
(
data
,
args
);
}
});
return
isUndefined
(
result
)
?
this
:
result
;
};
$
.
fn
.
cropper
.
Constructor
=
Cropper
;
$
.
fn
.
cropper
.
setDefaults
=
Cropper
.
setDefaults
;
// No conflict
$
.
fn
.
cropper
.
noConflict
=
function
()
{
$
.
fn
.
cropper
=
Cropper
.
other
;
return
this
;
};
});
app/assets/stylesheets/application.scss
View file @
78530162
...
@@ -9,6 +9,7 @@
...
@@ -9,6 +9,7 @@
*= require_self
*= require_self
*= require dropzone/basic
*= require dropzone/basic
*= require cal-heatmap
*= require cal-heatmap
*= require cropper.css
*/
*/
/*
/*
...
...
app/assets/stylesheets/cropper.css
0 → 100644
View file @
78530162
/*!
* Cropper v2.3.0
* https://github.com/fengyuanchen/cropper
*
* Copyright (c) 2014-2016 Fengyuan Chen and contributors
* Released under the MIT license
*
* Date: 2016-02-22T02:13:13.332Z
*/
.cropper-container
{
font-size
:
0
;
line-height
:
0
;
position
:
relative
;
-webkit-user-select
:
none
;
-moz-user-select
:
none
;
-ms-user-select
:
none
;
user-select
:
none
;
direction
:
ltr
!important
;
-ms-touch-action
:
none
;
touch-action
:
none
;
-webkit-tap-highlight-color
:
transparent
;
-webkit-touch-callout
:
none
;
}
.cropper-container
img
{
display
:
block
;
width
:
100%
;
min-width
:
0
!important
;
max-width
:
none
!important
;
height
:
100%
;
min-height
:
0
!important
;
max-height
:
none
!important
;
image-orientation
:
0deg
!important
;
}
.cropper-wrap-box
,
.cropper-canvas
,
.cropper-drag-box
,
.cropper-crop-box
,
.cropper-modal
{
position
:
absolute
;
top
:
0
;
right
:
0
;
bottom
:
0
;
left
:
0
;
}
.cropper-wrap-box
{
overflow
:
hidden
;
}
.cropper-drag-box
{
opacity
:
0
;
background-color
:
#fff
;
filter
:
alpha
(
opacity
=
0
);
}
.cropper-modal
{
opacity
:
.5
;
background-color
:
#000
;
filter
:
alpha
(
opacity
=
50
);
}
.cropper-view-box
{
display
:
block
;
overflow
:
hidden
;
width
:
100%
;
height
:
100%
;
outline
:
1px
solid
#39f
;
outline-color
:
rgba
(
51
,
153
,
255
,
.75
);
}
.cropper-dashed
{
position
:
absolute
;
display
:
block
;
opacity
:
.5
;
border
:
0
dashed
#eee
;
filter
:
alpha
(
opacity
=
50
);
}
.cropper-dashed.dashed-h
{
top
:
33.33333%
;
left
:
0
;
width
:
100%
;
height
:
33.33333%
;
border-top-width
:
1px
;
border-bottom-width
:
1px
;
}
.cropper-dashed.dashed-v
{
top
:
0
;
left
:
33.33333%
;
width
:
33.33333%
;
height
:
100%
;
border-right-width
:
1px
;
border-left-width
:
1px
;
}
.cropper-center
{
position
:
absolute
;
top
:
50%
;
left
:
50%
;
display
:
block
;
width
:
0
;
height
:
0
;
opacity
:
.75
;
filter
:
alpha
(
opacity
=
75
);
}
.cropper-center
:before
,
.cropper-center
:after
{
position
:
absolute
;
display
:
block
;
content
:
' '
;
background-color
:
#eee
;
}
.cropper-center
:before
{
top
:
0
;
left
:
-3px
;
width
:
7px
;
height
:
1px
;
}
.cropper-center
:after
{
top
:
-3px
;
left
:
0
;
width
:
1px
;
height
:
7px
;
}
.cropper-face
,
.cropper-line
,
.cropper-point
{
position
:
absolute
;
display
:
block
;
width
:
100%
;
height
:
100%
;
opacity
:
.1
;
filter
:
alpha
(
opacity
=
10
);
}
.cropper-face
{
top
:
0
;
left
:
0
;
background-color
:
#fff
;
}
.cropper-line
{
background-color
:
#39f
;
}
.cropper-line.line-e
{
top
:
0
;
right
:
-3px
;
width
:
5px
;
cursor
:
e-resize
;
}
.cropper-line.line-n
{
top
:
-3px
;
left
:
0
;
height
:
5px
;
cursor
:
n-resize
;
}
.cropper-line.line-w
{
top
:
0
;
left
:
-3px
;
width
:
5px
;
cursor
:
w-resize
;
}
.cropper-line.line-s
{
bottom
:
-3px
;
left
:
0
;
height
:
5px
;
cursor
:
s-resize
;
}
.cropper-point
{
width
:
5px
;
height
:
5px
;
opacity
:
.75
;
background-color
:
#39f
;
filter
:
alpha
(
opacity
=
75
);
}
.cropper-point.point-e
{
top
:
50%
;
right
:
-3px
;
margin-top
:
-3px
;
cursor
:
e-resize
;
}
.cropper-point.point-n
{
top
:
-3px
;
left
:
50%
;
margin-left
:
-3px
;
cursor
:
n-resize
;
}
.cropper-point.point-w
{
top
:
50%
;
left
:
-3px
;
margin-top
:
-3px
;
cursor
:
w-resize
;
}
.cropper-point.point-s
{
bottom
:
-3px
;
left
:
50%
;
margin-left
:
-3px
;
cursor
:
s-resize
;
}
.cropper-point.point-ne
{
top
:
-3px
;
right
:
-3px
;
cursor
:
ne-resize
;
}
.cropper-point.point-nw
{
top
:
-3px
;
left
:
-3px
;
cursor
:
nw-resize
;
}
.cropper-point.point-sw
{
bottom
:
-3px
;
left
:
-3px
;
cursor
:
sw-resize
;
}
.cropper-point.point-se
{
right
:
-3px
;
bottom
:
-3px
;
width
:
20px
;
height
:
20px
;
cursor
:
se-resize
;
opacity
:
1
;
filter
:
alpha
(
opacity
=
100
);
}
.cropper-point.point-se
:before
{
position
:
absolute
;
right
:
-50%
;
bottom
:
-50%
;
display
:
block
;
width
:
200%
;
height
:
200%
;
content
:
' '
;
opacity
:
0
;
background-color
:
#39f
;
filter
:
alpha
(
opacity
=
0
);
}
@media
(
min-width
:
768px
)
{
.cropper-point.point-se
{
width
:
15px
;
height
:
15px
;
}
}
@media
(
min-width
:
992px
)
{
.cropper-point.point-se
{
width
:
10px
;
height
:
10px
;
}
}
@media
(
min-width
:
1200px
)
{
.cropper-point.point-se
{
width
:
5px
;
height
:
5px
;
opacity
:
.75
;
filter
:
alpha
(
opacity
=
75
);
}
}
.cropper-invisible
{
opacity
:
0
;
filter
:
alpha
(
opacity
=
0
);
}
.cropper-bg
{
background-image
:
url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC')
;
}
.cropper-hide
{
position
:
absolute
;
display
:
block
;
width
:
0
;
height
:
0
;
}
.cropper-hidden
{
display
:
none
!important
;
}
.cropper-move
{
cursor
:
move
;
}
.cropper-crop
{
cursor
:
crosshair
;
}
.cropper-disabled
.cropper-drag-box
,
.cropper-disabled
.cropper-face
,
.cropper-disabled
.cropper-line
,
.cropper-disabled
.cropper-point
{
cursor
:
not-allowed
;
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment