cropper-rails 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/README.md +44 -0
- data/Rakefile +1 -0
- data/cropper-rails.gemspec +25 -0
- data/lib/cropper/cropper-rails.rb +1 -0
- data/lib/cropper/rails.rb +7 -0
- data/lib/cropper/rails/engine.rb +6 -0
- data/lib/cropper/rails/version.rb +5 -0
- data/vendor/assets/javascripts/.keep +0 -0
- data/vendor/assets/javascripts/cropper.js +2928 -0
- data/vendor/assets/stylesheets/.keep +0 -0
- data/vendor/assets/stylesheets/cropper.css +313 -0
- metadata +99 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 6156cbafc1db4541ab625d623e17aad4965f2c45
|
4
|
+
data.tar.gz: 50fcddd3765c61ba68dc11e23c168ce046c66d39
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6f3a42d78e704f18d52f80ad0d1ad2fd1fe191fff087bed4088f3bede08f82dde9858b7fdcdf2a7965193ec863939d9d091ed9d12617555b537de5454273221e
|
7
|
+
data.tar.gz: a58c367dbd312b516cdbf9b9aeea3f96764aca756b1a1f8447b88ea01f852fb50657e4bf474998ec353a5cd5321e8346d2a95f8de52f4b14af4dec38fc623123
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# Cropper::Rails
|
2
|
+
|
3
|
+
[Jquery Cropper](https://github.com/fengyuanchen/cropper) for Rails assets pipeline.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
This gem packs [cropper](https://github.com/fengyuanchen/cropper) (jquery version) and makes it available for rails assets pipeline. Add this gem and jquery-rails to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'jquery-rails'
|
11
|
+
gem 'cropper-rails'
|
12
|
+
```
|
13
|
+
|
14
|
+
And then run `bundle install`.
|
15
|
+
|
16
|
+
Next add jquery and cropper to your assets:
|
17
|
+
|
18
|
+
`app/assets/javascripts/application.js`
|
19
|
+
|
20
|
+
```javascript
|
21
|
+
//= require jquery
|
22
|
+
//= require cropper
|
23
|
+
```
|
24
|
+
|
25
|
+
`app/assets/javascripts/application.css`
|
26
|
+
|
27
|
+
```css
|
28
|
+
/*
|
29
|
+
*= require jquery
|
30
|
+
*= require cropper
|
31
|
+
*/
|
32
|
+
```
|
33
|
+
|
34
|
+
## Usage
|
35
|
+
|
36
|
+
Read more about usage [here](https://github.com/fengyuanchen/cropper#getting-started)
|
37
|
+
|
38
|
+
## Contributing
|
39
|
+
|
40
|
+
1. Fork it
|
41
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
42
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
43
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
44
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'cropper/rails/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "cropper-rails"
|
8
|
+
spec.version = Cropper::Rails::VERSION
|
9
|
+
spec.authors = ["Cristian Bica"]
|
10
|
+
spec.email = ["cristian.bica@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Fengyuanchen's Cropper with Rails assets pipeline.}
|
13
|
+
spec.description = %q{This gem wraps fengyuanchen's cropper to be used inside Rails applications.}
|
14
|
+
spec.homepage = "https://github.com/cristianbica/cropper-rails"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
|
+
spec.bindir = "exe"
|
18
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "railties", ">= 3.1.0"
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
24
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
25
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require "cropper/rails"
|
File without changes
|
@@ -0,0 +1,2928 @@
|
|
1
|
+
/*!
|
2
|
+
* Cropper v2.2.1
|
3
|
+
* https://github.com/fengyuanchen/cropper
|
4
|
+
*
|
5
|
+
* Copyright (c) 2014-2015 Fengyuan Chen and contributors
|
6
|
+
* Released under the MIT license
|
7
|
+
*
|
8
|
+
* Date: 2015-12-12T07:24:25.791Z
|
9
|
+
*/
|
10
|
+
|
11
|
+
(function (factory) {
|
12
|
+
if (typeof define === 'function' && define.amd) {
|
13
|
+
// AMD. Register as anonymous module.
|
14
|
+
define(['jquery'], factory);
|
15
|
+
} else if (typeof exports === 'object') {
|
16
|
+
// Node / CommonJS
|
17
|
+
factory(require('jquery'));
|
18
|
+
} else {
|
19
|
+
// Browser globals.
|
20
|
+
factory(jQuery);
|
21
|
+
}
|
22
|
+
})(function ($) {
|
23
|
+
|
24
|
+
'use strict';
|
25
|
+
|
26
|
+
// Globals
|
27
|
+
var $window = $(window);
|
28
|
+
var $document = $(document);
|
29
|
+
var location = window.location;
|
30
|
+
var ArrayBuffer = window.ArrayBuffer;
|
31
|
+
var Uint8Array = window.Uint8Array;
|
32
|
+
var DataView = window.DataView;
|
33
|
+
var btoa = window.btoa;
|
34
|
+
|
35
|
+
// Constants
|
36
|
+
var NAMESPACE = 'cropper';
|
37
|
+
|
38
|
+
// Classes
|
39
|
+
var CLASS_MODAL = 'cropper-modal';
|
40
|
+
var CLASS_HIDE = 'cropper-hide';
|
41
|
+
var CLASS_HIDDEN = 'cropper-hidden';
|
42
|
+
var CLASS_INVISIBLE = 'cropper-invisible';
|
43
|
+
var CLASS_MOVE = 'cropper-move';
|
44
|
+
var CLASS_CROP = 'cropper-crop';
|
45
|
+
var CLASS_DISABLED = 'cropper-disabled';
|
46
|
+
var CLASS_BG = 'cropper-bg';
|
47
|
+
|
48
|
+
// Events
|
49
|
+
var EVENT_MOUSE_DOWN = 'mousedown touchstart pointerdown MSPointerDown';
|
50
|
+
var EVENT_MOUSE_MOVE = 'mousemove touchmove pointermove MSPointerMove';
|
51
|
+
var EVENT_MOUSE_UP = 'mouseup touchend touchcancel pointerup pointercancel MSPointerUp MSPointerCancel';
|
52
|
+
var EVENT_WHEEL = 'wheel mousewheel DOMMouseScroll';
|
53
|
+
var EVENT_DBLCLICK = 'dblclick';
|
54
|
+
var EVENT_LOAD = 'load.' + NAMESPACE;
|
55
|
+
var EVENT_ERROR = 'error.' + NAMESPACE;
|
56
|
+
var EVENT_RESIZE = 'resize.' + NAMESPACE; // Bind to window with namespace
|
57
|
+
var EVENT_BUILD = 'build.' + NAMESPACE;
|
58
|
+
var EVENT_BUILT = 'built.' + NAMESPACE;
|
59
|
+
var EVENT_CROP_START = 'cropstart.' + NAMESPACE;
|
60
|
+
var EVENT_CROP_MOVE = 'cropmove.' + NAMESPACE;
|
61
|
+
var EVENT_CROP_END = 'cropend.' + NAMESPACE;
|
62
|
+
var EVENT_CROP = 'crop.' + NAMESPACE;
|
63
|
+
var EVENT_ZOOM = 'zoom.' + NAMESPACE;
|
64
|
+
|
65
|
+
// RegExps
|
66
|
+
var REGEXP_ACTIONS = /e|w|s|n|se|sw|ne|nw|all|crop|move|zoom/;
|
67
|
+
var REGEXP_DATA_URL = /^data\:/;
|
68
|
+
var REGEXP_DATA_URL_HEAD = /^data\:([^\;]+)\;base64,/;
|
69
|
+
var REGEXP_DATA_URL_JPEG = /^data\:image\/jpeg.*;base64,/;
|
70
|
+
|
71
|
+
// Data keys
|
72
|
+
var DATA_PREVIEW = 'preview';
|
73
|
+
var DATA_ACTION = 'action';
|
74
|
+
|
75
|
+
// Actions
|
76
|
+
var ACTION_EAST = 'e';
|
77
|
+
var ACTION_WEST = 'w';
|
78
|
+
var ACTION_SOUTH = 's';
|
79
|
+
var ACTION_NORTH = 'n';
|
80
|
+
var ACTION_SOUTH_EAST = 'se';
|
81
|
+
var ACTION_SOUTH_WEST = 'sw';
|
82
|
+
var ACTION_NORTH_EAST = 'ne';
|
83
|
+
var ACTION_NORTH_WEST = 'nw';
|
84
|
+
var ACTION_ALL = 'all';
|
85
|
+
var ACTION_CROP = 'crop';
|
86
|
+
var ACTION_MOVE = 'move';
|
87
|
+
var ACTION_ZOOM = 'zoom';
|
88
|
+
var ACTION_NONE = 'none';
|
89
|
+
|
90
|
+
// Supports
|
91
|
+
var SUPPORT_CANVAS = $.isFunction($('<canvas>')[0].getContext);
|
92
|
+
|
93
|
+
// Maths
|
94
|
+
var num = Number;
|
95
|
+
var min = Math.min;
|
96
|
+
var max = Math.max;
|
97
|
+
var abs = Math.abs;
|
98
|
+
var sin = Math.sin;
|
99
|
+
var cos = Math.cos;
|
100
|
+
var sqrt = Math.sqrt;
|
101
|
+
var round = Math.round;
|
102
|
+
var floor = Math.floor;
|
103
|
+
|
104
|
+
// Prototype
|
105
|
+
var prototype = {
|
106
|
+
version: '2.2.1'
|
107
|
+
};
|
108
|
+
|
109
|
+
var fromCharCode = String.fromCharCode;
|
110
|
+
|
111
|
+
function isNumber(n) {
|
112
|
+
return typeof n === 'number' && !isNaN(n);
|
113
|
+
}
|
114
|
+
|
115
|
+
function isUndefined(n) {
|
116
|
+
return typeof n === 'undefined';
|
117
|
+
}
|
118
|
+
|
119
|
+
function toArray(obj, offset) {
|
120
|
+
var args = [];
|
121
|
+
|
122
|
+
// This is necessary for IE8
|
123
|
+
if (isNumber(offset)) {
|
124
|
+
args.push(offset);
|
125
|
+
}
|
126
|
+
|
127
|
+
return args.slice.apply(obj, args);
|
128
|
+
}
|
129
|
+
|
130
|
+
// Custom proxy to avoid jQuery's guid
|
131
|
+
function proxy(fn, context) {
|
132
|
+
var args = toArray(arguments, 2);
|
133
|
+
|
134
|
+
return function () {
|
135
|
+
return fn.apply(context, args.concat(toArray(arguments)));
|
136
|
+
};
|
137
|
+
}
|
138
|
+
|
139
|
+
function isCrossOriginURL(url) {
|
140
|
+
var parts = url.match(/^(https?:)\/\/([^\:\/\?#]+):?(\d*)/i);
|
141
|
+
|
142
|
+
return parts && (
|
143
|
+
parts[1] !== location.protocol ||
|
144
|
+
parts[2] !== location.hostname ||
|
145
|
+
parts[3] !== location.port
|
146
|
+
);
|
147
|
+
}
|
148
|
+
|
149
|
+
function addTimestamp(url) {
|
150
|
+
var timestamp = 'timestamp=' + (new Date()).getTime();
|
151
|
+
|
152
|
+
return (url + (url.indexOf('?') === -1 ? '?' : '&') + timestamp);
|
153
|
+
}
|
154
|
+
|
155
|
+
function getCrossOrigin(crossOrigin) {
|
156
|
+
return crossOrigin ? ' crossOrigin="' + crossOrigin + '"' : '';
|
157
|
+
}
|
158
|
+
|
159
|
+
function getImageSize(image, callback) {
|
160
|
+
var newImage;
|
161
|
+
|
162
|
+
// Modern browsers
|
163
|
+
if (image.naturalWidth) {
|
164
|
+
return callback(image.naturalWidth, image.naturalHeight);
|
165
|
+
}
|
166
|
+
|
167
|
+
// IE8: Don't use `new Image()` here (#319)
|
168
|
+
newImage = document.createElement('img');
|
169
|
+
|
170
|
+
newImage.onload = function () {
|
171
|
+
callback(this.width, this.height);
|
172
|
+
};
|
173
|
+
|
174
|
+
newImage.src = image.src;
|
175
|
+
}
|
176
|
+
|
177
|
+
function getTransform(options) {
|
178
|
+
var transforms = [];
|
179
|
+
var rotate = options.rotate;
|
180
|
+
var scaleX = options.scaleX;
|
181
|
+
var scaleY = options.scaleY;
|
182
|
+
|
183
|
+
if (isNumber(rotate)) {
|
184
|
+
transforms.push('rotate(' + rotate + 'deg)');
|
185
|
+
}
|
186
|
+
|
187
|
+
if (isNumber(scaleX) && isNumber(scaleY)) {
|
188
|
+
transforms.push('scale(' + scaleX + ',' + scaleY + ')');
|
189
|
+
}
|
190
|
+
|
191
|
+
return transforms.length ? transforms.join(' ') : 'none';
|
192
|
+
}
|
193
|
+
|
194
|
+
function getRotatedSizes(data, isReversed) {
|
195
|
+
var deg = abs(data.degree) % 180;
|
196
|
+
var arc = (deg > 90 ? (180 - deg) : deg) * Math.PI / 180;
|
197
|
+
var sinArc = sin(arc);
|
198
|
+
var cosArc = cos(arc);
|
199
|
+
var width = data.width;
|
200
|
+
var height = data.height;
|
201
|
+
var aspectRatio = data.aspectRatio;
|
202
|
+
var newWidth;
|
203
|
+
var newHeight;
|
204
|
+
|
205
|
+
if (!isReversed) {
|
206
|
+
newWidth = width * cosArc + height * sinArc;
|
207
|
+
newHeight = width * sinArc + height * cosArc;
|
208
|
+
} else {
|
209
|
+
newWidth = width / (cosArc + sinArc / aspectRatio);
|
210
|
+
newHeight = newWidth / aspectRatio;
|
211
|
+
}
|
212
|
+
|
213
|
+
return {
|
214
|
+
width: newWidth,
|
215
|
+
height: newHeight
|
216
|
+
};
|
217
|
+
}
|
218
|
+
|
219
|
+
function getSourceCanvas(image, data) {
|
220
|
+
var canvas = $('<canvas>')[0];
|
221
|
+
var context = canvas.getContext('2d');
|
222
|
+
var x = 0;
|
223
|
+
var y = 0;
|
224
|
+
var width = data.naturalWidth;
|
225
|
+
var height = data.naturalHeight;
|
226
|
+
var rotate = data.rotate;
|
227
|
+
var scaleX = data.scaleX;
|
228
|
+
var scaleY = data.scaleY;
|
229
|
+
var scalable = isNumber(scaleX) && isNumber(scaleY) && (scaleX !== 1 || scaleY !== 1);
|
230
|
+
var rotatable = isNumber(rotate) && rotate !== 0;
|
231
|
+
var advanced = rotatable || scalable;
|
232
|
+
var canvasWidth = width;
|
233
|
+
var canvasHeight = height;
|
234
|
+
var translateX;
|
235
|
+
var translateY;
|
236
|
+
var rotated;
|
237
|
+
|
238
|
+
if (scalable) {
|
239
|
+
translateX = width / 2;
|
240
|
+
translateY = height / 2;
|
241
|
+
}
|
242
|
+
|
243
|
+
if (rotatable) {
|
244
|
+
rotated = getRotatedSizes({
|
245
|
+
width: width,
|
246
|
+
height: height,
|
247
|
+
degree: rotate
|
248
|
+
});
|
249
|
+
|
250
|
+
canvasWidth = rotated.width;
|
251
|
+
canvasHeight = rotated.height;
|
252
|
+
translateX = rotated.width / 2;
|
253
|
+
translateY = rotated.height / 2;
|
254
|
+
}
|
255
|
+
|
256
|
+
canvas.width = canvasWidth;
|
257
|
+
canvas.height = canvasHeight;
|
258
|
+
|
259
|
+
if (advanced) {
|
260
|
+
x = -width / 2;
|
261
|
+
y = -height / 2;
|
262
|
+
|
263
|
+
context.save();
|
264
|
+
context.translate(translateX, translateY);
|
265
|
+
}
|
266
|
+
|
267
|
+
if (rotatable) {
|
268
|
+
context.rotate(rotate * Math.PI / 180);
|
269
|
+
}
|
270
|
+
|
271
|
+
// Should call `scale` after rotated
|
272
|
+
if (scalable) {
|
273
|
+
context.scale(scaleX, scaleY);
|
274
|
+
}
|
275
|
+
|
276
|
+
context.drawImage(image, floor(x), floor(y), floor(width), floor(height));
|
277
|
+
|
278
|
+
if (advanced) {
|
279
|
+
context.restore();
|
280
|
+
}
|
281
|
+
|
282
|
+
return canvas;
|
283
|
+
}
|
284
|
+
|
285
|
+
function getStringFromCharCode(dataView, start, length) {
|
286
|
+
var str = '';
|
287
|
+
var i;
|
288
|
+
|
289
|
+
for (i = start, length += start; i < length; i++) {
|
290
|
+
str += fromCharCode(dataView.getUint8(i));
|
291
|
+
}
|
292
|
+
|
293
|
+
return str;
|
294
|
+
}
|
295
|
+
|
296
|
+
function getOrientation(arrayBuffer) {
|
297
|
+
var dataView = new DataView(arrayBuffer);
|
298
|
+
var length = dataView.byteLength;
|
299
|
+
var orientation;
|
300
|
+
var exifIDCode;
|
301
|
+
var tiffOffset;
|
302
|
+
var firstIFDOffset;
|
303
|
+
var littleEndian;
|
304
|
+
var endianness;
|
305
|
+
var app1Start;
|
306
|
+
var ifdStart;
|
307
|
+
var offset;
|
308
|
+
var i;
|
309
|
+
|
310
|
+
// Only handle JPEG image (start by 0xFFD8)
|
311
|
+
if (dataView.getUint8(0) === 0xFF && dataView.getUint8(1) === 0xD8) {
|
312
|
+
offset = 2;
|
313
|
+
|
314
|
+
while (offset < length) {
|
315
|
+
if (dataView.getUint8(offset) === 0xFF && dataView.getUint8(offset + 1) === 0xE1) {
|
316
|
+
app1Start = offset;
|
317
|
+
break;
|
318
|
+
}
|
319
|
+
|
320
|
+
offset++;
|
321
|
+
}
|
322
|
+
}
|
323
|
+
|
324
|
+
if (app1Start) {
|
325
|
+
exifIDCode = app1Start + 4;
|
326
|
+
tiffOffset = app1Start + 10;
|
327
|
+
|
328
|
+
if (getStringFromCharCode(dataView, exifIDCode, 4) === 'Exif') {
|
329
|
+
endianness = dataView.getUint16(tiffOffset);
|
330
|
+
littleEndian = endianness === 0x4949;
|
331
|
+
|
332
|
+
if (littleEndian || endianness === 0x4D4D /* bigEndian */) {
|
333
|
+
if (dataView.getUint16(tiffOffset + 2, littleEndian) === 0x002A) {
|
334
|
+
firstIFDOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
|
335
|
+
|
336
|
+
if (firstIFDOffset >= 0x00000008) {
|
337
|
+
ifdStart = tiffOffset + firstIFDOffset;
|
338
|
+
}
|
339
|
+
}
|
340
|
+
}
|
341
|
+
}
|
342
|
+
}
|
343
|
+
|
344
|
+
if (ifdStart) {
|
345
|
+
length = dataView.getUint16(ifdStart, littleEndian);
|
346
|
+
|
347
|
+
for (i = 0; i < length; i++) {
|
348
|
+
offset = ifdStart + i * 12 + 2;
|
349
|
+
|
350
|
+
if (dataView.getUint16(offset, littleEndian) === 0x0112 /* Orientation */) {
|
351
|
+
|
352
|
+
// 8 is the offset of the current tag's value
|
353
|
+
offset += 8;
|
354
|
+
|
355
|
+
// Get the original orientation value
|
356
|
+
orientation = dataView.getUint16(offset, littleEndian);
|
357
|
+
|
358
|
+
// Override the orientation with the default value: 1
|
359
|
+
dataView.setUint16(offset, 1, littleEndian);
|
360
|
+
break;
|
361
|
+
}
|
362
|
+
}
|
363
|
+
}
|
364
|
+
|
365
|
+
return orientation;
|
366
|
+
}
|
367
|
+
|
368
|
+
function dataURLToArrayBuffer(dataURL) {
|
369
|
+
var base64 = dataURL.replace(REGEXP_DATA_URL_HEAD, '');
|
370
|
+
var binary = atob(base64);
|
371
|
+
var length = binary.length;
|
372
|
+
var arrayBuffer = new ArrayBuffer(length);
|
373
|
+
var dataView = new Uint8Array(arrayBuffer);
|
374
|
+
var i;
|
375
|
+
|
376
|
+
for (i = 0; i < length; i++) {
|
377
|
+
dataView[i] = binary.charCodeAt(i);
|
378
|
+
}
|
379
|
+
|
380
|
+
return arrayBuffer;
|
381
|
+
}
|
382
|
+
|
383
|
+
// Only available for JPEG image
|
384
|
+
function arrayBufferToDataURL(arrayBuffer) {
|
385
|
+
var dataView = new Uint8Array(arrayBuffer);
|
386
|
+
var length = dataView.length;
|
387
|
+
var base64 = '';
|
388
|
+
var i;
|
389
|
+
|
390
|
+
for (i = 0; i < length; i++) {
|
391
|
+
base64 += fromCharCode(dataView[i]);
|
392
|
+
}
|
393
|
+
|
394
|
+
return 'data:image/jpeg;base64,' + btoa(base64);
|
395
|
+
}
|
396
|
+
|
397
|
+
function Cropper(element, options) {
|
398
|
+
this.$element = $(element);
|
399
|
+
this.options = $.extend({}, Cropper.DEFAULTS, $.isPlainObject(options) && options);
|
400
|
+
this.isLoaded = false;
|
401
|
+
this.isBuilt = false;
|
402
|
+
this.isCompleted = false;
|
403
|
+
this.isRotated = false;
|
404
|
+
this.isCropped = false;
|
405
|
+
this.isDisabled = false;
|
406
|
+
this.isReplaced = false;
|
407
|
+
this.isLimited = false;
|
408
|
+
this.isImg = false;
|
409
|
+
this.originalUrl = '';
|
410
|
+
this.canvas = null;
|
411
|
+
this.cropBox = null;
|
412
|
+
this.init();
|
413
|
+
}
|
414
|
+
|
415
|
+
$.extend(prototype, {
|
416
|
+
init: function () {
|
417
|
+
var $this = this.$element;
|
418
|
+
var url;
|
419
|
+
|
420
|
+
if ($this.is('img')) {
|
421
|
+
this.isImg = true;
|
422
|
+
|
423
|
+
// Should use `$.fn.attr` here. e.g.: "img/picture.jpg"
|
424
|
+
this.originalUrl = url = $this.attr('src');
|
425
|
+
|
426
|
+
// Stop when it's a blank image
|
427
|
+
if (!url) {
|
428
|
+
return;
|
429
|
+
}
|
430
|
+
|
431
|
+
// Should use `$.fn.prop` here. e.g.: "http://example.com/img/picture.jpg"
|
432
|
+
url = $this.prop('src');
|
433
|
+
} else if ($this.is('canvas') && SUPPORT_CANVAS) {
|
434
|
+
url = $this[0].toDataURL();
|
435
|
+
}
|
436
|
+
|
437
|
+
this.load(url);
|
438
|
+
},
|
439
|
+
|
440
|
+
// A shortcut for triggering custom events
|
441
|
+
trigger: function (type, data) {
|
442
|
+
var e = $.Event(type, data);
|
443
|
+
|
444
|
+
this.$element.trigger(e);
|
445
|
+
|
446
|
+
return e;
|
447
|
+
},
|
448
|
+
|
449
|
+
load: function (url) {
|
450
|
+
var options = this.options;
|
451
|
+
var $this = this.$element;
|
452
|
+
var read;
|
453
|
+
var xhr;
|
454
|
+
|
455
|
+
if (!url) {
|
456
|
+
return;
|
457
|
+
}
|
458
|
+
|
459
|
+
// Trigger build event first
|
460
|
+
$this.one(EVENT_BUILD, options.build);
|
461
|
+
|
462
|
+
if (this.trigger(EVENT_BUILD).isDefaultPrevented()) {
|
463
|
+
return;
|
464
|
+
}
|
465
|
+
|
466
|
+
this.url = url;
|
467
|
+
this.image = {};
|
468
|
+
|
469
|
+
if (!options.checkOrientation || !ArrayBuffer) {
|
470
|
+
return this.clone();
|
471
|
+
}
|
472
|
+
|
473
|
+
read = $.proxy(this.read, this);
|
474
|
+
|
475
|
+
// XMLHttpRequest disallows to open a Data URL in some browsers like IE11 and Safari
|
476
|
+
if (REGEXP_DATA_URL.test(url)) {
|
477
|
+
return REGEXP_DATA_URL_JPEG.test(url) ?
|
478
|
+
read(dataURLToArrayBuffer(url)) :
|
479
|
+
this.clone();
|
480
|
+
}
|
481
|
+
|
482
|
+
xhr = new XMLHttpRequest();
|
483
|
+
|
484
|
+
xhr.onerror = xhr.onabort = $.proxy(function () {
|
485
|
+
this.clone();
|
486
|
+
}, this);
|
487
|
+
|
488
|
+
xhr.onload = function () {
|
489
|
+
read(this.response);
|
490
|
+
};
|
491
|
+
|
492
|
+
xhr.open('get', url);
|
493
|
+
xhr.responseType = 'arraybuffer';
|
494
|
+
xhr.send();
|
495
|
+
},
|
496
|
+
|
497
|
+
read: function (arrayBuffer) {
|
498
|
+
var options = this.options;
|
499
|
+
var orientation = getOrientation(arrayBuffer);
|
500
|
+
var image = this.image;
|
501
|
+
var rotate;
|
502
|
+
var scaleX;
|
503
|
+
var scaleY;
|
504
|
+
|
505
|
+
if (orientation > 1) {
|
506
|
+
this.url = arrayBufferToDataURL(arrayBuffer);
|
507
|
+
|
508
|
+
switch (orientation) {
|
509
|
+
|
510
|
+
// flip horizontal
|
511
|
+
case 2:
|
512
|
+
scaleX = -1;
|
513
|
+
break;
|
514
|
+
|
515
|
+
// rotate left 180°
|
516
|
+
case 3:
|
517
|
+
rotate = -180;
|
518
|
+
break;
|
519
|
+
|
520
|
+
// flip vertical
|
521
|
+
case 4:
|
522
|
+
scaleY = -1;
|
523
|
+
break;
|
524
|
+
|
525
|
+
// flip vertical + rotate right 90°
|
526
|
+
case 5:
|
527
|
+
rotate = 90;
|
528
|
+
scaleY = -1;
|
529
|
+
break;
|
530
|
+
|
531
|
+
// rotate right 90°
|
532
|
+
case 6:
|
533
|
+
rotate = 90;
|
534
|
+
break;
|
535
|
+
|
536
|
+
// flip horizontal + rotate right 90°
|
537
|
+
case 7:
|
538
|
+
rotate = 90;
|
539
|
+
scaleX = -1;
|
540
|
+
break;
|
541
|
+
|
542
|
+
// rotate left 90°
|
543
|
+
case 8:
|
544
|
+
rotate = -90;
|
545
|
+
break;
|
546
|
+
}
|
547
|
+
}
|
548
|
+
|
549
|
+
if (options.rotatable) {
|
550
|
+
image.rotate = rotate;
|
551
|
+
}
|
552
|
+
|
553
|
+
if (options.scalable) {
|
554
|
+
image.scaleX = scaleX;
|
555
|
+
image.scaleY = scaleY;
|
556
|
+
}
|
557
|
+
|
558
|
+
this.clone();
|
559
|
+
},
|
560
|
+
|
561
|
+
clone: function () {
|
562
|
+
var options = this.options;
|
563
|
+
var $this = this.$element;
|
564
|
+
var url = this.url;
|
565
|
+
var crossOrigin = '';
|
566
|
+
var crossOriginUrl;
|
567
|
+
var $clone;
|
568
|
+
|
569
|
+
if (options.checkCrossOrigin && isCrossOriginURL(url)) {
|
570
|
+
crossOrigin = $this.prop('crossOrigin');
|
571
|
+
|
572
|
+
// Bust cache (#148), only when there was not a "crossOrigin" property
|
573
|
+
if (!crossOrigin) {
|
574
|
+
crossOrigin = 'anonymous';
|
575
|
+
crossOriginUrl = addTimestamp(url);
|
576
|
+
}
|
577
|
+
}
|
578
|
+
|
579
|
+
this.crossOrigin = crossOrigin;
|
580
|
+
this.crossOriginUrl = crossOriginUrl;
|
581
|
+
this.$clone = $clone = $('<img' + getCrossOrigin(crossOrigin) + ' src="' + (crossOriginUrl || url) + '">');
|
582
|
+
|
583
|
+
if (this.isImg) {
|
584
|
+
if ($this[0].complete) {
|
585
|
+
this.start();
|
586
|
+
} else {
|
587
|
+
$this.one(EVENT_LOAD, $.proxy(this.start, this));
|
588
|
+
}
|
589
|
+
} else {
|
590
|
+
$clone.
|
591
|
+
one(EVENT_LOAD, $.proxy(this.start, this)).
|
592
|
+
one(EVENT_ERROR, $.proxy(this.stop, this)).
|
593
|
+
addClass(CLASS_HIDE).
|
594
|
+
insertAfter($this);
|
595
|
+
}
|
596
|
+
},
|
597
|
+
|
598
|
+
start: function () {
|
599
|
+
var $image = this.$element;
|
600
|
+
var $clone = this.$clone;
|
601
|
+
|
602
|
+
if (!this.isImg) {
|
603
|
+
$clone.off(EVENT_ERROR, this.stop);
|
604
|
+
$image = $clone;
|
605
|
+
}
|
606
|
+
|
607
|
+
getImageSize($image[0], $.proxy(function (naturalWidth, naturalHeight) {
|
608
|
+
$.extend(this.image, {
|
609
|
+
naturalWidth: naturalWidth,
|
610
|
+
naturalHeight: naturalHeight,
|
611
|
+
aspectRatio: naturalWidth / naturalHeight
|
612
|
+
});
|
613
|
+
|
614
|
+
this.isLoaded = true;
|
615
|
+
this.build();
|
616
|
+
}, this));
|
617
|
+
},
|
618
|
+
|
619
|
+
stop: function () {
|
620
|
+
this.$clone.remove();
|
621
|
+
this.$clone = null;
|
622
|
+
}
|
623
|
+
});
|
624
|
+
|
625
|
+
$.extend(prototype, {
|
626
|
+
build: function () {
|
627
|
+
var options = this.options;
|
628
|
+
var $this = this.$element;
|
629
|
+
var $clone = this.$clone;
|
630
|
+
var $cropper;
|
631
|
+
var $cropBox;
|
632
|
+
var $face;
|
633
|
+
|
634
|
+
if (!this.isLoaded) {
|
635
|
+
return;
|
636
|
+
}
|
637
|
+
|
638
|
+
// Unbuild first when replace
|
639
|
+
if (this.isBuilt) {
|
640
|
+
this.unbuild();
|
641
|
+
}
|
642
|
+
|
643
|
+
// Create cropper elements
|
644
|
+
this.$container = $this.parent();
|
645
|
+
this.$cropper = $cropper = $(Cropper.TEMPLATE);
|
646
|
+
this.$canvas = $cropper.find('.cropper-canvas').append($clone);
|
647
|
+
this.$dragBox = $cropper.find('.cropper-drag-box');
|
648
|
+
this.$cropBox = $cropBox = $cropper.find('.cropper-crop-box');
|
649
|
+
this.$viewBox = $cropper.find('.cropper-view-box');
|
650
|
+
this.$face = $face = $cropBox.find('.cropper-face');
|
651
|
+
|
652
|
+
// Hide the original image
|
653
|
+
$this.addClass(CLASS_HIDDEN).after($cropper);
|
654
|
+
|
655
|
+
// Show the clone image if is hidden
|
656
|
+
if (!this.isImg) {
|
657
|
+
$clone.removeClass(CLASS_HIDE);
|
658
|
+
}
|
659
|
+
|
660
|
+
this.initPreview();
|
661
|
+
this.bind();
|
662
|
+
|
663
|
+
options.aspectRatio = max(0, options.aspectRatio) || NaN;
|
664
|
+
options.viewMode = max(0, min(3, round(options.viewMode))) || 0;
|
665
|
+
|
666
|
+
if (options.autoCrop) {
|
667
|
+
this.isCropped = true;
|
668
|
+
|
669
|
+
if (options.modal) {
|
670
|
+
this.$dragBox.addClass(CLASS_MODAL);
|
671
|
+
}
|
672
|
+
} else {
|
673
|
+
$cropBox.addClass(CLASS_HIDDEN);
|
674
|
+
}
|
675
|
+
|
676
|
+
if (!options.guides) {
|
677
|
+
$cropBox.find('.cropper-dashed').addClass(CLASS_HIDDEN);
|
678
|
+
}
|
679
|
+
|
680
|
+
if (!options.center) {
|
681
|
+
$cropBox.find('.cropper-center').addClass(CLASS_HIDDEN);
|
682
|
+
}
|
683
|
+
|
684
|
+
if (options.cropBoxMovable) {
|
685
|
+
$face.addClass(CLASS_MOVE).data(DATA_ACTION, ACTION_ALL);
|
686
|
+
}
|
687
|
+
|
688
|
+
if (!options.highlight) {
|
689
|
+
$face.addClass(CLASS_INVISIBLE);
|
690
|
+
}
|
691
|
+
|
692
|
+
if (options.background) {
|
693
|
+
$cropper.addClass(CLASS_BG);
|
694
|
+
}
|
695
|
+
|
696
|
+
if (!options.cropBoxResizable) {
|
697
|
+
$cropBox.find('.cropper-line, .cropper-point').addClass(CLASS_HIDDEN);
|
698
|
+
}
|
699
|
+
|
700
|
+
this.setDragMode(options.dragMode);
|
701
|
+
this.render();
|
702
|
+
this.isBuilt = true;
|
703
|
+
this.setData(options.data);
|
704
|
+
$this.one(EVENT_BUILT, options.built);
|
705
|
+
|
706
|
+
// Trigger the built event asynchronously to keep `data('cropper')` is defined
|
707
|
+
setTimeout($.proxy(function () {
|
708
|
+
this.trigger(EVENT_BUILT);
|
709
|
+
this.isCompleted = true;
|
710
|
+
}, this), 0);
|
711
|
+
},
|
712
|
+
|
713
|
+
unbuild: function () {
|
714
|
+
if (!this.isBuilt) {
|
715
|
+
return;
|
716
|
+
}
|
717
|
+
|
718
|
+
this.isBuilt = false;
|
719
|
+
this.isCompleted = false;
|
720
|
+
this.initialImage = null;
|
721
|
+
|
722
|
+
// Clear `initialCanvas` is necessary when replace
|
723
|
+
this.initialCanvas = null;
|
724
|
+
this.initialCropBox = null;
|
725
|
+
this.container = null;
|
726
|
+
this.canvas = null;
|
727
|
+
|
728
|
+
// Clear `cropBox` is necessary when replace
|
729
|
+
this.cropBox = null;
|
730
|
+
this.unbind();
|
731
|
+
|
732
|
+
this.resetPreview();
|
733
|
+
this.$preview = null;
|
734
|
+
|
735
|
+
this.$viewBox = null;
|
736
|
+
this.$cropBox = null;
|
737
|
+
this.$dragBox = null;
|
738
|
+
this.$canvas = null;
|
739
|
+
this.$container = null;
|
740
|
+
|
741
|
+
this.$cropper.remove();
|
742
|
+
this.$cropper = null;
|
743
|
+
}
|
744
|
+
});
|
745
|
+
|
746
|
+
$.extend(prototype, {
|
747
|
+
render: function () {
|
748
|
+
this.initContainer();
|
749
|
+
this.initCanvas();
|
750
|
+
this.initCropBox();
|
751
|
+
|
752
|
+
this.renderCanvas();
|
753
|
+
|
754
|
+
if (this.isCropped) {
|
755
|
+
this.renderCropBox();
|
756
|
+
}
|
757
|
+
},
|
758
|
+
|
759
|
+
initContainer: function () {
|
760
|
+
var options = this.options;
|
761
|
+
var $this = this.$element;
|
762
|
+
var $container = this.$container;
|
763
|
+
var $cropper = this.$cropper;
|
764
|
+
|
765
|
+
$cropper.addClass(CLASS_HIDDEN);
|
766
|
+
$this.removeClass(CLASS_HIDDEN);
|
767
|
+
|
768
|
+
$cropper.css((this.container = {
|
769
|
+
width: max($container.width(), num(options.minContainerWidth) || 200),
|
770
|
+
height: max($container.height(), num(options.minContainerHeight) || 100)
|
771
|
+
}));
|
772
|
+
|
773
|
+
$this.addClass(CLASS_HIDDEN);
|
774
|
+
$cropper.removeClass(CLASS_HIDDEN);
|
775
|
+
},
|
776
|
+
|
777
|
+
// Canvas (image wrapper)
|
778
|
+
initCanvas: function () {
|
779
|
+
var viewMode = this.options.viewMode;
|
780
|
+
var container = this.container;
|
781
|
+
var containerWidth = container.width;
|
782
|
+
var containerHeight = container.height;
|
783
|
+
var image = this.image;
|
784
|
+
var imageNaturalWidth = image.naturalWidth;
|
785
|
+
var imageNaturalHeight = image.naturalHeight;
|
786
|
+
var is90Degree = abs(image.rotate) === 90;
|
787
|
+
var naturalWidth = is90Degree ? imageNaturalHeight : imageNaturalWidth;
|
788
|
+
var naturalHeight = is90Degree ? imageNaturalWidth : imageNaturalHeight;
|
789
|
+
var aspectRatio = naturalWidth / naturalHeight;
|
790
|
+
var canvasWidth = containerWidth;
|
791
|
+
var canvasHeight = containerHeight;
|
792
|
+
var canvas;
|
793
|
+
|
794
|
+
if (containerHeight * aspectRatio > containerWidth) {
|
795
|
+
if (viewMode === 3) {
|
796
|
+
canvasWidth = containerHeight * aspectRatio;
|
797
|
+
} else {
|
798
|
+
canvasHeight = containerWidth / aspectRatio;
|
799
|
+
}
|
800
|
+
} else {
|
801
|
+
if (viewMode === 3) {
|
802
|
+
canvasHeight = containerWidth / aspectRatio;
|
803
|
+
} else {
|
804
|
+
canvasWidth = containerHeight * aspectRatio;
|
805
|
+
}
|
806
|
+
}
|
807
|
+
|
808
|
+
canvas = {
|
809
|
+
naturalWidth: naturalWidth,
|
810
|
+
naturalHeight: naturalHeight,
|
811
|
+
aspectRatio: aspectRatio,
|
812
|
+
width: canvasWidth,
|
813
|
+
height: canvasHeight
|
814
|
+
};
|
815
|
+
|
816
|
+
canvas.oldLeft = canvas.left = (containerWidth - canvasWidth) / 2;
|
817
|
+
canvas.oldTop = canvas.top = (containerHeight - canvasHeight) / 2;
|
818
|
+
|
819
|
+
this.canvas = canvas;
|
820
|
+
this.isLimited = (viewMode === 1 || viewMode === 2);
|
821
|
+
this.limitCanvas(true, true);
|
822
|
+
this.initialImage = $.extend({}, image);
|
823
|
+
this.initialCanvas = $.extend({}, canvas);
|
824
|
+
},
|
825
|
+
|
826
|
+
limitCanvas: function (isSizeLimited, isPositionLimited) {
|
827
|
+
var options = this.options;
|
828
|
+
var viewMode = options.viewMode;
|
829
|
+
var container = this.container;
|
830
|
+
var containerWidth = container.width;
|
831
|
+
var containerHeight = container.height;
|
832
|
+
var canvas = this.canvas;
|
833
|
+
var aspectRatio = canvas.aspectRatio;
|
834
|
+
var cropBox = this.cropBox;
|
835
|
+
var isCropped = this.isCropped && cropBox;
|
836
|
+
var minCanvasWidth;
|
837
|
+
var minCanvasHeight;
|
838
|
+
var newCanvasLeft;
|
839
|
+
var newCanvasTop;
|
840
|
+
|
841
|
+
if (isSizeLimited) {
|
842
|
+
minCanvasWidth = num(options.minCanvasWidth) || 0;
|
843
|
+
minCanvasHeight = num(options.minCanvasHeight) || 0;
|
844
|
+
|
845
|
+
if (viewMode) {
|
846
|
+
if (viewMode > 1) {
|
847
|
+
minCanvasWidth = max(minCanvasWidth, containerWidth);
|
848
|
+
minCanvasHeight = max(minCanvasHeight, containerHeight);
|
849
|
+
|
850
|
+
if (viewMode === 3) {
|
851
|
+
if (minCanvasHeight * aspectRatio > minCanvasWidth) {
|
852
|
+
minCanvasWidth = minCanvasHeight * aspectRatio;
|
853
|
+
} else {
|
854
|
+
minCanvasHeight = minCanvasWidth / aspectRatio;
|
855
|
+
}
|
856
|
+
}
|
857
|
+
} else {
|
858
|
+
if (minCanvasWidth) {
|
859
|
+
minCanvasWidth = max(minCanvasWidth, isCropped ? cropBox.width : 0);
|
860
|
+
} else if (minCanvasHeight) {
|
861
|
+
minCanvasHeight = max(minCanvasHeight, isCropped ? cropBox.height : 0);
|
862
|
+
} else if (isCropped) {
|
863
|
+
minCanvasWidth = cropBox.width;
|
864
|
+
minCanvasHeight = cropBox.height;
|
865
|
+
|
866
|
+
if (minCanvasHeight * aspectRatio > minCanvasWidth) {
|
867
|
+
minCanvasWidth = minCanvasHeight * aspectRatio;
|
868
|
+
} else {
|
869
|
+
minCanvasHeight = minCanvasWidth / aspectRatio;
|
870
|
+
}
|
871
|
+
}
|
872
|
+
}
|
873
|
+
}
|
874
|
+
|
875
|
+
if (minCanvasWidth && minCanvasHeight) {
|
876
|
+
if (minCanvasHeight * aspectRatio > minCanvasWidth) {
|
877
|
+
minCanvasHeight = minCanvasWidth / aspectRatio;
|
878
|
+
} else {
|
879
|
+
minCanvasWidth = minCanvasHeight * aspectRatio;
|
880
|
+
}
|
881
|
+
} else if (minCanvasWidth) {
|
882
|
+
minCanvasHeight = minCanvasWidth / aspectRatio;
|
883
|
+
} else if (minCanvasHeight) {
|
884
|
+
minCanvasWidth = minCanvasHeight * aspectRatio;
|
885
|
+
}
|
886
|
+
|
887
|
+
canvas.minWidth = minCanvasWidth;
|
888
|
+
canvas.minHeight = minCanvasHeight;
|
889
|
+
canvas.maxWidth = Infinity;
|
890
|
+
canvas.maxHeight = Infinity;
|
891
|
+
}
|
892
|
+
|
893
|
+
if (isPositionLimited) {
|
894
|
+
if (viewMode) {
|
895
|
+
newCanvasLeft = containerWidth - canvas.width;
|
896
|
+
newCanvasTop = containerHeight - canvas.height;
|
897
|
+
|
898
|
+
canvas.minLeft = min(0, newCanvasLeft);
|
899
|
+
canvas.minTop = min(0, newCanvasTop);
|
900
|
+
canvas.maxLeft = max(0, newCanvasLeft);
|
901
|
+
canvas.maxTop = max(0, newCanvasTop);
|
902
|
+
|
903
|
+
if (isCropped && this.isLimited) {
|
904
|
+
canvas.minLeft = min(
|
905
|
+
cropBox.left,
|
906
|
+
cropBox.left + cropBox.width - canvas.width
|
907
|
+
);
|
908
|
+
canvas.minTop = min(
|
909
|
+
cropBox.top,
|
910
|
+
cropBox.top + cropBox.height - canvas.height
|
911
|
+
);
|
912
|
+
canvas.maxLeft = cropBox.left;
|
913
|
+
canvas.maxTop = cropBox.top;
|
914
|
+
|
915
|
+
if (viewMode === 2) {
|
916
|
+
if (canvas.width >= containerWidth) {
|
917
|
+
canvas.minLeft = min(0, newCanvasLeft);
|
918
|
+
canvas.maxLeft = max(0, newCanvasLeft);
|
919
|
+
}
|
920
|
+
|
921
|
+
if (canvas.height >= containerHeight) {
|
922
|
+
canvas.minTop = min(0, newCanvasTop);
|
923
|
+
canvas.maxTop = max(0, newCanvasTop);
|
924
|
+
}
|
925
|
+
}
|
926
|
+
}
|
927
|
+
} else {
|
928
|
+
canvas.minLeft = -canvas.width;
|
929
|
+
canvas.minTop = -canvas.height;
|
930
|
+
canvas.maxLeft = containerWidth;
|
931
|
+
canvas.maxTop = containerHeight;
|
932
|
+
}
|
933
|
+
}
|
934
|
+
},
|
935
|
+
|
936
|
+
renderCanvas: function (isChanged) {
|
937
|
+
var canvas = this.canvas;
|
938
|
+
var image = this.image;
|
939
|
+
var rotate = image.rotate;
|
940
|
+
var naturalWidth = image.naturalWidth;
|
941
|
+
var naturalHeight = image.naturalHeight;
|
942
|
+
var aspectRatio;
|
943
|
+
var rotated;
|
944
|
+
|
945
|
+
if (this.isRotated) {
|
946
|
+
this.isRotated = false;
|
947
|
+
|
948
|
+
// Computes rotated sizes with image sizes
|
949
|
+
rotated = getRotatedSizes({
|
950
|
+
width: image.width,
|
951
|
+
height: image.height,
|
952
|
+
degree: rotate
|
953
|
+
});
|
954
|
+
|
955
|
+
aspectRatio = rotated.width / rotated.height;
|
956
|
+
|
957
|
+
if (aspectRatio !== canvas.aspectRatio) {
|
958
|
+
canvas.left -= (rotated.width - canvas.width) / 2;
|
959
|
+
canvas.top -= (rotated.height - canvas.height) / 2;
|
960
|
+
canvas.width = rotated.width;
|
961
|
+
canvas.height = rotated.height;
|
962
|
+
canvas.aspectRatio = aspectRatio;
|
963
|
+
canvas.naturalWidth = naturalWidth;
|
964
|
+
canvas.naturalHeight = naturalHeight;
|
965
|
+
|
966
|
+
// Computes rotated sizes with natural image sizes
|
967
|
+
if (rotate % 180) {
|
968
|
+
rotated = getRotatedSizes({
|
969
|
+
width: naturalWidth,
|
970
|
+
height: naturalHeight,
|
971
|
+
degree: rotate
|
972
|
+
});
|
973
|
+
|
974
|
+
canvas.naturalWidth = rotated.width;
|
975
|
+
canvas.naturalHeight = rotated.height;
|
976
|
+
}
|
977
|
+
|
978
|
+
this.limitCanvas(true, false);
|
979
|
+
}
|
980
|
+
}
|
981
|
+
|
982
|
+
if (canvas.width > canvas.maxWidth || canvas.width < canvas.minWidth) {
|
983
|
+
canvas.left = canvas.oldLeft;
|
984
|
+
}
|
985
|
+
|
986
|
+
if (canvas.height > canvas.maxHeight || canvas.height < canvas.minHeight) {
|
987
|
+
canvas.top = canvas.oldTop;
|
988
|
+
}
|
989
|
+
|
990
|
+
canvas.width = min(max(canvas.width, canvas.minWidth), canvas.maxWidth);
|
991
|
+
canvas.height = min(max(canvas.height, canvas.minHeight), canvas.maxHeight);
|
992
|
+
|
993
|
+
this.limitCanvas(false, true);
|
994
|
+
|
995
|
+
canvas.oldLeft = canvas.left = min(max(canvas.left, canvas.minLeft), canvas.maxLeft);
|
996
|
+
canvas.oldTop = canvas.top = min(max(canvas.top, canvas.minTop), canvas.maxTop);
|
997
|
+
|
998
|
+
this.$canvas.css({
|
999
|
+
width: canvas.width,
|
1000
|
+
height: canvas.height,
|
1001
|
+
left: canvas.left,
|
1002
|
+
top: canvas.top
|
1003
|
+
});
|
1004
|
+
|
1005
|
+
this.renderImage();
|
1006
|
+
|
1007
|
+
if (this.isCropped && this.isLimited) {
|
1008
|
+
this.limitCropBox(true, true);
|
1009
|
+
}
|
1010
|
+
|
1011
|
+
if (isChanged) {
|
1012
|
+
this.output();
|
1013
|
+
}
|
1014
|
+
},
|
1015
|
+
|
1016
|
+
renderImage: function (isChanged) {
|
1017
|
+
var canvas = this.canvas;
|
1018
|
+
var image = this.image;
|
1019
|
+
var reversed;
|
1020
|
+
|
1021
|
+
if (image.rotate) {
|
1022
|
+
reversed = getRotatedSizes({
|
1023
|
+
width: canvas.width,
|
1024
|
+
height: canvas.height,
|
1025
|
+
degree: image.rotate,
|
1026
|
+
aspectRatio: image.aspectRatio
|
1027
|
+
}, true);
|
1028
|
+
}
|
1029
|
+
|
1030
|
+
$.extend(image, reversed ? {
|
1031
|
+
width: reversed.width,
|
1032
|
+
height: reversed.height,
|
1033
|
+
left: (canvas.width - reversed.width) / 2,
|
1034
|
+
top: (canvas.height - reversed.height) / 2
|
1035
|
+
} : {
|
1036
|
+
width: canvas.width,
|
1037
|
+
height: canvas.height,
|
1038
|
+
left: 0,
|
1039
|
+
top: 0
|
1040
|
+
});
|
1041
|
+
|
1042
|
+
this.$clone.css({
|
1043
|
+
width: image.width,
|
1044
|
+
height: image.height,
|
1045
|
+
marginLeft: image.left,
|
1046
|
+
marginTop: image.top,
|
1047
|
+
transform: getTransform(image)
|
1048
|
+
});
|
1049
|
+
|
1050
|
+
if (isChanged) {
|
1051
|
+
this.output();
|
1052
|
+
}
|
1053
|
+
},
|
1054
|
+
|
1055
|
+
initCropBox: function () {
|
1056
|
+
var options = this.options;
|
1057
|
+
var canvas = this.canvas;
|
1058
|
+
var aspectRatio = options.aspectRatio;
|
1059
|
+
var autoCropArea = num(options.autoCropArea) || 0.8;
|
1060
|
+
var cropBox = {
|
1061
|
+
width: canvas.width,
|
1062
|
+
height: canvas.height
|
1063
|
+
};
|
1064
|
+
|
1065
|
+
if (aspectRatio) {
|
1066
|
+
if (canvas.height * aspectRatio > canvas.width) {
|
1067
|
+
cropBox.height = cropBox.width / aspectRatio;
|
1068
|
+
} else {
|
1069
|
+
cropBox.width = cropBox.height * aspectRatio;
|
1070
|
+
}
|
1071
|
+
}
|
1072
|
+
|
1073
|
+
this.cropBox = cropBox;
|
1074
|
+
this.limitCropBox(true, true);
|
1075
|
+
|
1076
|
+
// Initialize auto crop area
|
1077
|
+
cropBox.width = min(max(cropBox.width, cropBox.minWidth), cropBox.maxWidth);
|
1078
|
+
cropBox.height = min(max(cropBox.height, cropBox.minHeight), cropBox.maxHeight);
|
1079
|
+
|
1080
|
+
// The width of auto crop area must large than "minWidth", and the height too. (#164)
|
1081
|
+
cropBox.width = max(cropBox.minWidth, cropBox.width * autoCropArea);
|
1082
|
+
cropBox.height = max(cropBox.minHeight, cropBox.height * autoCropArea);
|
1083
|
+
cropBox.oldLeft = cropBox.left = canvas.left + (canvas.width - cropBox.width) / 2;
|
1084
|
+
cropBox.oldTop = cropBox.top = canvas.top + (canvas.height - cropBox.height) / 2;
|
1085
|
+
|
1086
|
+
this.initialCropBox = $.extend({}, cropBox);
|
1087
|
+
},
|
1088
|
+
|
1089
|
+
limitCropBox: function (isSizeLimited, isPositionLimited) {
|
1090
|
+
var options = this.options;
|
1091
|
+
var aspectRatio = options.aspectRatio;
|
1092
|
+
var container = this.container;
|
1093
|
+
var containerWidth = container.width;
|
1094
|
+
var containerHeight = container.height;
|
1095
|
+
var canvas = this.canvas;
|
1096
|
+
var cropBox = this.cropBox;
|
1097
|
+
var isLimited = this.isLimited;
|
1098
|
+
var minCropBoxWidth;
|
1099
|
+
var minCropBoxHeight;
|
1100
|
+
var maxCropBoxWidth;
|
1101
|
+
var maxCropBoxHeight;
|
1102
|
+
|
1103
|
+
if (isSizeLimited) {
|
1104
|
+
minCropBoxWidth = num(options.minCropBoxWidth) || 0;
|
1105
|
+
minCropBoxHeight = num(options.minCropBoxHeight) || 0;
|
1106
|
+
|
1107
|
+
// The min/maxCropBoxWidth/Height must be less than containerWidth/Height
|
1108
|
+
minCropBoxWidth = min(minCropBoxWidth, containerWidth);
|
1109
|
+
minCropBoxHeight = min(minCropBoxHeight, containerHeight);
|
1110
|
+
maxCropBoxWidth = min(containerWidth, isLimited ? canvas.width : containerWidth);
|
1111
|
+
maxCropBoxHeight = min(containerHeight, isLimited ? canvas.height : containerHeight);
|
1112
|
+
|
1113
|
+
if (aspectRatio) {
|
1114
|
+
if (minCropBoxWidth && minCropBoxHeight) {
|
1115
|
+
if (minCropBoxHeight * aspectRatio > minCropBoxWidth) {
|
1116
|
+
minCropBoxHeight = minCropBoxWidth / aspectRatio;
|
1117
|
+
} else {
|
1118
|
+
minCropBoxWidth = minCropBoxHeight * aspectRatio;
|
1119
|
+
}
|
1120
|
+
} else if (minCropBoxWidth) {
|
1121
|
+
minCropBoxHeight = minCropBoxWidth / aspectRatio;
|
1122
|
+
} else if (minCropBoxHeight) {
|
1123
|
+
minCropBoxWidth = minCropBoxHeight * aspectRatio;
|
1124
|
+
}
|
1125
|
+
|
1126
|
+
if (maxCropBoxHeight * aspectRatio > maxCropBoxWidth) {
|
1127
|
+
maxCropBoxHeight = maxCropBoxWidth / aspectRatio;
|
1128
|
+
} else {
|
1129
|
+
maxCropBoxWidth = maxCropBoxHeight * aspectRatio;
|
1130
|
+
}
|
1131
|
+
}
|
1132
|
+
|
1133
|
+
// The minWidth/Height must be less than maxWidth/Height
|
1134
|
+
cropBox.minWidth = min(minCropBoxWidth, maxCropBoxWidth);
|
1135
|
+
cropBox.minHeight = min(minCropBoxHeight, maxCropBoxHeight);
|
1136
|
+
cropBox.maxWidth = maxCropBoxWidth;
|
1137
|
+
cropBox.maxHeight = maxCropBoxHeight;
|
1138
|
+
}
|
1139
|
+
|
1140
|
+
if (isPositionLimited) {
|
1141
|
+
if (isLimited) {
|
1142
|
+
cropBox.minLeft = max(0, canvas.left);
|
1143
|
+
cropBox.minTop = max(0, canvas.top);
|
1144
|
+
cropBox.maxLeft = min(containerWidth, canvas.left + canvas.width) - cropBox.width;
|
1145
|
+
cropBox.maxTop = min(containerHeight, canvas.top + canvas.height) - cropBox.height;
|
1146
|
+
} else {
|
1147
|
+
cropBox.minLeft = 0;
|
1148
|
+
cropBox.minTop = 0;
|
1149
|
+
cropBox.maxLeft = containerWidth - cropBox.width;
|
1150
|
+
cropBox.maxTop = containerHeight - cropBox.height;
|
1151
|
+
}
|
1152
|
+
}
|
1153
|
+
},
|
1154
|
+
|
1155
|
+
renderCropBox: function () {
|
1156
|
+
var options = this.options;
|
1157
|
+
var container = this.container;
|
1158
|
+
var containerWidth = container.width;
|
1159
|
+
var containerHeight = container.height;
|
1160
|
+
var cropBox = this.cropBox;
|
1161
|
+
|
1162
|
+
if (cropBox.width > cropBox.maxWidth || cropBox.width < cropBox.minWidth) {
|
1163
|
+
cropBox.left = cropBox.oldLeft;
|
1164
|
+
}
|
1165
|
+
|
1166
|
+
if (cropBox.height > cropBox.maxHeight || cropBox.height < cropBox.minHeight) {
|
1167
|
+
cropBox.top = cropBox.oldTop;
|
1168
|
+
}
|
1169
|
+
|
1170
|
+
cropBox.width = min(max(cropBox.width, cropBox.minWidth), cropBox.maxWidth);
|
1171
|
+
cropBox.height = min(max(cropBox.height, cropBox.minHeight), cropBox.maxHeight);
|
1172
|
+
|
1173
|
+
this.limitCropBox(false, true);
|
1174
|
+
|
1175
|
+
cropBox.oldLeft = cropBox.left = min(max(cropBox.left, cropBox.minLeft), cropBox.maxLeft);
|
1176
|
+
cropBox.oldTop = cropBox.top = min(max(cropBox.top, cropBox.minTop), cropBox.maxTop);
|
1177
|
+
|
1178
|
+
if (options.movable && options.cropBoxMovable) {
|
1179
|
+
|
1180
|
+
// Turn to move the canvas when the crop box is equal to the container
|
1181
|
+
this.$face.data(DATA_ACTION, (cropBox.width === containerWidth && cropBox.height === containerHeight) ? ACTION_MOVE : ACTION_ALL);
|
1182
|
+
}
|
1183
|
+
|
1184
|
+
this.$cropBox.css({
|
1185
|
+
width: cropBox.width,
|
1186
|
+
height: cropBox.height,
|
1187
|
+
left: cropBox.left,
|
1188
|
+
top: cropBox.top
|
1189
|
+
});
|
1190
|
+
|
1191
|
+
if (this.isCropped && this.isLimited) {
|
1192
|
+
this.limitCanvas(true, true);
|
1193
|
+
}
|
1194
|
+
|
1195
|
+
if (!this.isDisabled) {
|
1196
|
+
this.output();
|
1197
|
+
}
|
1198
|
+
},
|
1199
|
+
|
1200
|
+
output: function () {
|
1201
|
+
this.preview();
|
1202
|
+
|
1203
|
+
if (this.isCompleted) {
|
1204
|
+
this.trigger(EVENT_CROP, this.getData());
|
1205
|
+
} else if (!this.isBuilt) {
|
1206
|
+
|
1207
|
+
// Only trigger one crop event before complete
|
1208
|
+
this.$element.one(EVENT_BUILT, $.proxy(function () {
|
1209
|
+
this.trigger(EVENT_CROP, this.getData());
|
1210
|
+
}, this));
|
1211
|
+
}
|
1212
|
+
}
|
1213
|
+
});
|
1214
|
+
|
1215
|
+
$.extend(prototype, {
|
1216
|
+
initPreview: function () {
|
1217
|
+
var crossOrigin = getCrossOrigin(this.crossOrigin);
|
1218
|
+
var url = crossOrigin ? this.crossOriginUrl : this.url;
|
1219
|
+
|
1220
|
+
this.$preview = $(this.options.preview);
|
1221
|
+
this.$viewBox.html('<img' + crossOrigin + ' src="' + url + '">');
|
1222
|
+
this.$preview.each(function () {
|
1223
|
+
var $this = $(this);
|
1224
|
+
|
1225
|
+
// Save the original size for recover
|
1226
|
+
$this.data(DATA_PREVIEW, {
|
1227
|
+
width: $this.width(),
|
1228
|
+
height: $this.height(),
|
1229
|
+
html: $this.html()
|
1230
|
+
});
|
1231
|
+
|
1232
|
+
/**
|
1233
|
+
* Override img element styles
|
1234
|
+
* Add `display:block` to avoid margin top issue
|
1235
|
+
* (Occur only when margin-top <= -height)
|
1236
|
+
*/
|
1237
|
+
$this.html(
|
1238
|
+
'<img' + crossOrigin + ' src="' + url + '" style="' +
|
1239
|
+
'display:block;width:100%;height:auto;' +
|
1240
|
+
'min-width:0!important;min-height:0!important;' +
|
1241
|
+
'max-width:none!important;max-height:none!important;' +
|
1242
|
+
'image-orientation:0deg!important;">'
|
1243
|
+
);
|
1244
|
+
});
|
1245
|
+
},
|
1246
|
+
|
1247
|
+
resetPreview: function () {
|
1248
|
+
this.$preview.each(function () {
|
1249
|
+
var $this = $(this);
|
1250
|
+
var data = $this.data(DATA_PREVIEW);
|
1251
|
+
|
1252
|
+
$this.css({
|
1253
|
+
width: data.width,
|
1254
|
+
height: data.height
|
1255
|
+
}).html(data.html).removeData(DATA_PREVIEW);
|
1256
|
+
});
|
1257
|
+
},
|
1258
|
+
|
1259
|
+
preview: function () {
|
1260
|
+
var image = this.image;
|
1261
|
+
var canvas = this.canvas;
|
1262
|
+
var cropBox = this.cropBox;
|
1263
|
+
var cropBoxWidth = cropBox.width;
|
1264
|
+
var cropBoxHeight = cropBox.height;
|
1265
|
+
var width = image.width;
|
1266
|
+
var height = image.height;
|
1267
|
+
var left = cropBox.left - canvas.left - image.left;
|
1268
|
+
var top = cropBox.top - canvas.top - image.top;
|
1269
|
+
|
1270
|
+
if (!this.isCropped || this.isDisabled) {
|
1271
|
+
return;
|
1272
|
+
}
|
1273
|
+
|
1274
|
+
this.$viewBox.find('img').css({
|
1275
|
+
width: width,
|
1276
|
+
height: height,
|
1277
|
+
marginLeft: -left,
|
1278
|
+
marginTop: -top,
|
1279
|
+
transform: getTransform(image)
|
1280
|
+
});
|
1281
|
+
|
1282
|
+
this.$preview.each(function () {
|
1283
|
+
var $this = $(this);
|
1284
|
+
var data = $this.data(DATA_PREVIEW);
|
1285
|
+
var originalWidth = data.width;
|
1286
|
+
var originalHeight = data.height;
|
1287
|
+
var newWidth = originalWidth;
|
1288
|
+
var newHeight = originalHeight;
|
1289
|
+
var ratio = 1;
|
1290
|
+
|
1291
|
+
if (cropBoxWidth) {
|
1292
|
+
ratio = originalWidth / cropBoxWidth;
|
1293
|
+
newHeight = cropBoxHeight * ratio;
|
1294
|
+
}
|
1295
|
+
|
1296
|
+
if (cropBoxHeight && newHeight > originalHeight) {
|
1297
|
+
ratio = originalHeight / cropBoxHeight;
|
1298
|
+
newWidth = cropBoxWidth * ratio;
|
1299
|
+
newHeight = originalHeight;
|
1300
|
+
}
|
1301
|
+
|
1302
|
+
$this.css({
|
1303
|
+
width: newWidth,
|
1304
|
+
height: newHeight
|
1305
|
+
}).find('img').css({
|
1306
|
+
width: width * ratio,
|
1307
|
+
height: height * ratio,
|
1308
|
+
marginLeft: -left * ratio,
|
1309
|
+
marginTop: -top * ratio,
|
1310
|
+
transform: getTransform(image)
|
1311
|
+
});
|
1312
|
+
});
|
1313
|
+
}
|
1314
|
+
});
|
1315
|
+
|
1316
|
+
$.extend(prototype, {
|
1317
|
+
bind: function () {
|
1318
|
+
var options = this.options;
|
1319
|
+
var $this = this.$element;
|
1320
|
+
var $cropper = this.$cropper;
|
1321
|
+
|
1322
|
+
if ($.isFunction(options.cropstart)) {
|
1323
|
+
$this.on(EVENT_CROP_START, options.cropstart);
|
1324
|
+
}
|
1325
|
+
|
1326
|
+
if ($.isFunction(options.cropmove)) {
|
1327
|
+
$this.on(EVENT_CROP_MOVE, options.cropmove);
|
1328
|
+
}
|
1329
|
+
|
1330
|
+
if ($.isFunction(options.cropend)) {
|
1331
|
+
$this.on(EVENT_CROP_END, options.cropend);
|
1332
|
+
}
|
1333
|
+
|
1334
|
+
if ($.isFunction(options.crop)) {
|
1335
|
+
$this.on(EVENT_CROP, options.crop);
|
1336
|
+
}
|
1337
|
+
|
1338
|
+
if ($.isFunction(options.zoom)) {
|
1339
|
+
$this.on(EVENT_ZOOM, options.zoom);
|
1340
|
+
}
|
1341
|
+
|
1342
|
+
$cropper.on(EVENT_MOUSE_DOWN, $.proxy(this.cropStart, this));
|
1343
|
+
|
1344
|
+
if (options.zoomable && options.zoomOnWheel) {
|
1345
|
+
$cropper.on(EVENT_WHEEL, $.proxy(this.wheel, this));
|
1346
|
+
}
|
1347
|
+
|
1348
|
+
if (options.toggleDragModeOnDblclick) {
|
1349
|
+
$cropper.on(EVENT_DBLCLICK, $.proxy(this.dblclick, this));
|
1350
|
+
}
|
1351
|
+
|
1352
|
+
$document.
|
1353
|
+
on(EVENT_MOUSE_MOVE, (this._cropMove = proxy(this.cropMove, this))).
|
1354
|
+
on(EVENT_MOUSE_UP, (this._cropEnd = proxy(this.cropEnd, this)));
|
1355
|
+
|
1356
|
+
if (options.responsive) {
|
1357
|
+
$window.on(EVENT_RESIZE, (this._resize = proxy(this.resize, this)));
|
1358
|
+
}
|
1359
|
+
},
|
1360
|
+
|
1361
|
+
unbind: function () {
|
1362
|
+
var options = this.options;
|
1363
|
+
var $this = this.$element;
|
1364
|
+
var $cropper = this.$cropper;
|
1365
|
+
|
1366
|
+
if ($.isFunction(options.cropstart)) {
|
1367
|
+
$this.off(EVENT_CROP_START, options.cropstart);
|
1368
|
+
}
|
1369
|
+
|
1370
|
+
if ($.isFunction(options.cropmove)) {
|
1371
|
+
$this.off(EVENT_CROP_MOVE, options.cropmove);
|
1372
|
+
}
|
1373
|
+
|
1374
|
+
if ($.isFunction(options.cropend)) {
|
1375
|
+
$this.off(EVENT_CROP_END, options.cropend);
|
1376
|
+
}
|
1377
|
+
|
1378
|
+
if ($.isFunction(options.crop)) {
|
1379
|
+
$this.off(EVENT_CROP, options.crop);
|
1380
|
+
}
|
1381
|
+
|
1382
|
+
if ($.isFunction(options.zoom)) {
|
1383
|
+
$this.off(EVENT_ZOOM, options.zoom);
|
1384
|
+
}
|
1385
|
+
|
1386
|
+
$cropper.off(EVENT_MOUSE_DOWN, this.cropStart);
|
1387
|
+
|
1388
|
+
if (options.zoomable && options.zoomOnWheel) {
|
1389
|
+
$cropper.off(EVENT_WHEEL, this.wheel);
|
1390
|
+
}
|
1391
|
+
|
1392
|
+
if (options.toggleDragModeOnDblclick) {
|
1393
|
+
$cropper.off(EVENT_DBLCLICK, this.dblclick);
|
1394
|
+
}
|
1395
|
+
|
1396
|
+
$document.
|
1397
|
+
off(EVENT_MOUSE_MOVE, this._cropMove).
|
1398
|
+
off(EVENT_MOUSE_UP, this._cropEnd);
|
1399
|
+
|
1400
|
+
if (options.responsive) {
|
1401
|
+
$window.off(EVENT_RESIZE, this._resize);
|
1402
|
+
}
|
1403
|
+
}
|
1404
|
+
});
|
1405
|
+
|
1406
|
+
$.extend(prototype, {
|
1407
|
+
resize: function () {
|
1408
|
+
var restore = this.options.restore;
|
1409
|
+
var $container = this.$container;
|
1410
|
+
var container = this.container;
|
1411
|
+
var canvasData;
|
1412
|
+
var cropBoxData;
|
1413
|
+
var ratio;
|
1414
|
+
|
1415
|
+
// Check `container` is necessary for IE8
|
1416
|
+
if (this.isDisabled || !container) {
|
1417
|
+
return;
|
1418
|
+
}
|
1419
|
+
|
1420
|
+
ratio = $container.width() / container.width;
|
1421
|
+
|
1422
|
+
// Resize when width changed or height changed
|
1423
|
+
if (ratio !== 1 || $container.height() !== container.height) {
|
1424
|
+
if (restore) {
|
1425
|
+
canvasData = this.getCanvasData();
|
1426
|
+
cropBoxData = this.getCropBoxData();
|
1427
|
+
}
|
1428
|
+
|
1429
|
+
this.render();
|
1430
|
+
|
1431
|
+
if (restore) {
|
1432
|
+
this.setCanvasData($.each(canvasData, function (i, n) {
|
1433
|
+
canvasData[i] = n * ratio;
|
1434
|
+
}));
|
1435
|
+
this.setCropBoxData($.each(cropBoxData, function (i, n) {
|
1436
|
+
cropBoxData[i] = n * ratio;
|
1437
|
+
}));
|
1438
|
+
}
|
1439
|
+
}
|
1440
|
+
},
|
1441
|
+
|
1442
|
+
dblclick: function () {
|
1443
|
+
if (this.isDisabled) {
|
1444
|
+
return;
|
1445
|
+
}
|
1446
|
+
|
1447
|
+
if (this.$dragBox.hasClass(CLASS_CROP)) {
|
1448
|
+
this.setDragMode(ACTION_MOVE);
|
1449
|
+
} else {
|
1450
|
+
this.setDragMode(ACTION_CROP);
|
1451
|
+
}
|
1452
|
+
},
|
1453
|
+
|
1454
|
+
wheel: function (event) {
|
1455
|
+
var originalEvent = event.originalEvent;
|
1456
|
+
var e = originalEvent || event;
|
1457
|
+
var ratio = num(this.options.wheelZoomRatio) || 0.1;
|
1458
|
+
var delta = 1;
|
1459
|
+
|
1460
|
+
if (this.isDisabled) {
|
1461
|
+
return;
|
1462
|
+
}
|
1463
|
+
|
1464
|
+
event.preventDefault();
|
1465
|
+
|
1466
|
+
if (e.deltaY) {
|
1467
|
+
delta = e.deltaY > 0 ? 1 : -1;
|
1468
|
+
} else if (e.wheelDelta) {
|
1469
|
+
delta = -e.wheelDelta / 120;
|
1470
|
+
} else if (e.detail) {
|
1471
|
+
delta = e.detail > 0 ? 1 : -1;
|
1472
|
+
}
|
1473
|
+
|
1474
|
+
this.zoom(-delta * ratio, originalEvent);
|
1475
|
+
},
|
1476
|
+
|
1477
|
+
cropStart: function (event) {
|
1478
|
+
var options = this.options;
|
1479
|
+
var originalEvent = event.originalEvent;
|
1480
|
+
var touches = originalEvent && originalEvent.touches;
|
1481
|
+
var e = event;
|
1482
|
+
var touchesLength;
|
1483
|
+
var action;
|
1484
|
+
|
1485
|
+
if (this.isDisabled) {
|
1486
|
+
return;
|
1487
|
+
}
|
1488
|
+
|
1489
|
+
if (touches) {
|
1490
|
+
touchesLength = touches.length;
|
1491
|
+
|
1492
|
+
if (touchesLength > 1) {
|
1493
|
+
if (options.zoomable && options.zoomOnTouch && touchesLength === 2) {
|
1494
|
+
e = touches[1];
|
1495
|
+
this.startX2 = e.pageX;
|
1496
|
+
this.startY2 = e.pageY;
|
1497
|
+
action = ACTION_ZOOM;
|
1498
|
+
} else {
|
1499
|
+
return;
|
1500
|
+
}
|
1501
|
+
}
|
1502
|
+
|
1503
|
+
e = touches[0];
|
1504
|
+
}
|
1505
|
+
|
1506
|
+
action = action || $(e.target).data(DATA_ACTION);
|
1507
|
+
|
1508
|
+
if (REGEXP_ACTIONS.test(action)) {
|
1509
|
+
if (this.trigger(EVENT_CROP_START, {
|
1510
|
+
originalEvent: originalEvent,
|
1511
|
+
action: action
|
1512
|
+
}).isDefaultPrevented()) {
|
1513
|
+
return;
|
1514
|
+
}
|
1515
|
+
|
1516
|
+
event.preventDefault();
|
1517
|
+
|
1518
|
+
this.action = action;
|
1519
|
+
this.cropping = false;
|
1520
|
+
|
1521
|
+
// IE8 has `event.pageX/Y`, but not `event.originalEvent.pageX/Y`
|
1522
|
+
// IE10 has `event.originalEvent.pageX/Y`, but not `event.pageX/Y`
|
1523
|
+
this.startX = e.pageX || originalEvent && originalEvent.pageX;
|
1524
|
+
this.startY = e.pageY || originalEvent && originalEvent.pageY;
|
1525
|
+
|
1526
|
+
if (action === ACTION_CROP) {
|
1527
|
+
this.cropping = true;
|
1528
|
+
this.$dragBox.addClass(CLASS_MODAL);
|
1529
|
+
}
|
1530
|
+
}
|
1531
|
+
},
|
1532
|
+
|
1533
|
+
cropMove: function (event) {
|
1534
|
+
var options = this.options;
|
1535
|
+
var originalEvent = event.originalEvent;
|
1536
|
+
var touches = originalEvent && originalEvent.touches;
|
1537
|
+
var e = event;
|
1538
|
+
var action = this.action;
|
1539
|
+
var touchesLength;
|
1540
|
+
|
1541
|
+
if (this.isDisabled) {
|
1542
|
+
return;
|
1543
|
+
}
|
1544
|
+
|
1545
|
+
if (touches) {
|
1546
|
+
touchesLength = touches.length;
|
1547
|
+
|
1548
|
+
if (touchesLength > 1) {
|
1549
|
+
if (options.zoomable && options.zoomOnTouch && touchesLength === 2) {
|
1550
|
+
e = touches[1];
|
1551
|
+
this.endX2 = e.pageX;
|
1552
|
+
this.endY2 = e.pageY;
|
1553
|
+
} else {
|
1554
|
+
return;
|
1555
|
+
}
|
1556
|
+
}
|
1557
|
+
|
1558
|
+
e = touches[0];
|
1559
|
+
}
|
1560
|
+
|
1561
|
+
if (action) {
|
1562
|
+
if (this.trigger(EVENT_CROP_MOVE, {
|
1563
|
+
originalEvent: originalEvent,
|
1564
|
+
action: action
|
1565
|
+
}).isDefaultPrevented()) {
|
1566
|
+
return;
|
1567
|
+
}
|
1568
|
+
|
1569
|
+
event.preventDefault();
|
1570
|
+
|
1571
|
+
this.endX = e.pageX || originalEvent && originalEvent.pageX;
|
1572
|
+
this.endY = e.pageY || originalEvent && originalEvent.pageY;
|
1573
|
+
|
1574
|
+
this.change(e.shiftKey, action === ACTION_ZOOM ? originalEvent : null);
|
1575
|
+
}
|
1576
|
+
},
|
1577
|
+
|
1578
|
+
cropEnd: function (event) {
|
1579
|
+
var originalEvent = event.originalEvent;
|
1580
|
+
var action = this.action;
|
1581
|
+
|
1582
|
+
if (this.isDisabled) {
|
1583
|
+
return;
|
1584
|
+
}
|
1585
|
+
|
1586
|
+
if (action) {
|
1587
|
+
event.preventDefault();
|
1588
|
+
|
1589
|
+
if (this.cropping) {
|
1590
|
+
this.cropping = false;
|
1591
|
+
this.$dragBox.toggleClass(CLASS_MODAL, this.isCropped && this.options.modal);
|
1592
|
+
}
|
1593
|
+
|
1594
|
+
this.action = '';
|
1595
|
+
|
1596
|
+
this.trigger(EVENT_CROP_END, {
|
1597
|
+
originalEvent: originalEvent,
|
1598
|
+
action: action
|
1599
|
+
});
|
1600
|
+
}
|
1601
|
+
}
|
1602
|
+
});
|
1603
|
+
|
1604
|
+
$.extend(prototype, {
|
1605
|
+
change: function (shiftKey, originalEvent) {
|
1606
|
+
var options = this.options;
|
1607
|
+
var aspectRatio = options.aspectRatio;
|
1608
|
+
var action = this.action;
|
1609
|
+
var container = this.container;
|
1610
|
+
var canvas = this.canvas;
|
1611
|
+
var cropBox = this.cropBox;
|
1612
|
+
var width = cropBox.width;
|
1613
|
+
var height = cropBox.height;
|
1614
|
+
var left = cropBox.left;
|
1615
|
+
var top = cropBox.top;
|
1616
|
+
var right = left + width;
|
1617
|
+
var bottom = top + height;
|
1618
|
+
var minLeft = 0;
|
1619
|
+
var minTop = 0;
|
1620
|
+
var maxWidth = container.width;
|
1621
|
+
var maxHeight = container.height;
|
1622
|
+
var renderable = true;
|
1623
|
+
var offset;
|
1624
|
+
var range;
|
1625
|
+
|
1626
|
+
// Locking aspect ratio in "free mode" by holding shift key (#259)
|
1627
|
+
if (!aspectRatio && shiftKey) {
|
1628
|
+
aspectRatio = width && height ? width / height : 1;
|
1629
|
+
}
|
1630
|
+
|
1631
|
+
if (this.limited) {
|
1632
|
+
minLeft = cropBox.minLeft;
|
1633
|
+
minTop = cropBox.minTop;
|
1634
|
+
maxWidth = minLeft + min(container.width, canvas.width);
|
1635
|
+
maxHeight = minTop + min(container.height, canvas.height);
|
1636
|
+
}
|
1637
|
+
|
1638
|
+
range = {
|
1639
|
+
x: this.endX - this.startX,
|
1640
|
+
y: this.endY - this.startY
|
1641
|
+
};
|
1642
|
+
|
1643
|
+
if (aspectRatio) {
|
1644
|
+
range.X = range.y * aspectRatio;
|
1645
|
+
range.Y = range.x / aspectRatio;
|
1646
|
+
}
|
1647
|
+
|
1648
|
+
switch (action) {
|
1649
|
+
// Move crop box
|
1650
|
+
case ACTION_ALL:
|
1651
|
+
left += range.x;
|
1652
|
+
top += range.y;
|
1653
|
+
break;
|
1654
|
+
|
1655
|
+
// Resize crop box
|
1656
|
+
case ACTION_EAST:
|
1657
|
+
if (range.x >= 0 && (right >= maxWidth || aspectRatio &&
|
1658
|
+
(top <= minTop || bottom >= maxHeight))) {
|
1659
|
+
|
1660
|
+
renderable = false;
|
1661
|
+
break;
|
1662
|
+
}
|
1663
|
+
|
1664
|
+
width += range.x;
|
1665
|
+
|
1666
|
+
if (aspectRatio) {
|
1667
|
+
height = width / aspectRatio;
|
1668
|
+
top -= range.Y / 2;
|
1669
|
+
}
|
1670
|
+
|
1671
|
+
if (width < 0) {
|
1672
|
+
action = ACTION_WEST;
|
1673
|
+
width = 0;
|
1674
|
+
}
|
1675
|
+
|
1676
|
+
break;
|
1677
|
+
|
1678
|
+
case ACTION_NORTH:
|
1679
|
+
if (range.y <= 0 && (top <= minTop || aspectRatio &&
|
1680
|
+
(left <= minLeft || right >= maxWidth))) {
|
1681
|
+
|
1682
|
+
renderable = false;
|
1683
|
+
break;
|
1684
|
+
}
|
1685
|
+
|
1686
|
+
height -= range.y;
|
1687
|
+
top += range.y;
|
1688
|
+
|
1689
|
+
if (aspectRatio) {
|
1690
|
+
width = height * aspectRatio;
|
1691
|
+
left += range.X / 2;
|
1692
|
+
}
|
1693
|
+
|
1694
|
+
if (height < 0) {
|
1695
|
+
action = ACTION_SOUTH;
|
1696
|
+
height = 0;
|
1697
|
+
}
|
1698
|
+
|
1699
|
+
break;
|
1700
|
+
|
1701
|
+
case ACTION_WEST:
|
1702
|
+
if (range.x <= 0 && (left <= minLeft || aspectRatio &&
|
1703
|
+
(top <= minTop || bottom >= maxHeight))) {
|
1704
|
+
|
1705
|
+
renderable = false;
|
1706
|
+
break;
|
1707
|
+
}
|
1708
|
+
|
1709
|
+
width -= range.x;
|
1710
|
+
left += range.x;
|
1711
|
+
|
1712
|
+
if (aspectRatio) {
|
1713
|
+
height = width / aspectRatio;
|
1714
|
+
top += range.Y / 2;
|
1715
|
+
}
|
1716
|
+
|
1717
|
+
if (width < 0) {
|
1718
|
+
action = ACTION_EAST;
|
1719
|
+
width = 0;
|
1720
|
+
}
|
1721
|
+
|
1722
|
+
break;
|
1723
|
+
|
1724
|
+
case ACTION_SOUTH:
|
1725
|
+
if (range.y >= 0 && (bottom >= maxHeight || aspectRatio &&
|
1726
|
+
(left <= minLeft || right >= maxWidth))) {
|
1727
|
+
|
1728
|
+
renderable = false;
|
1729
|
+
break;
|
1730
|
+
}
|
1731
|
+
|
1732
|
+
height += range.y;
|
1733
|
+
|
1734
|
+
if (aspectRatio) {
|
1735
|
+
width = height * aspectRatio;
|
1736
|
+
left -= range.X / 2;
|
1737
|
+
}
|
1738
|
+
|
1739
|
+
if (height < 0) {
|
1740
|
+
action = ACTION_NORTH;
|
1741
|
+
height = 0;
|
1742
|
+
}
|
1743
|
+
|
1744
|
+
break;
|
1745
|
+
|
1746
|
+
case ACTION_NORTH_EAST:
|
1747
|
+
if (aspectRatio) {
|
1748
|
+
if (range.y <= 0 && (top <= minTop || right >= maxWidth)) {
|
1749
|
+
renderable = false;
|
1750
|
+
break;
|
1751
|
+
}
|
1752
|
+
|
1753
|
+
height -= range.y;
|
1754
|
+
top += range.y;
|
1755
|
+
width = height * aspectRatio;
|
1756
|
+
} else {
|
1757
|
+
if (range.x >= 0) {
|
1758
|
+
if (right < maxWidth) {
|
1759
|
+
width += range.x;
|
1760
|
+
} else if (range.y <= 0 && top <= minTop) {
|
1761
|
+
renderable = false;
|
1762
|
+
}
|
1763
|
+
} else {
|
1764
|
+
width += range.x;
|
1765
|
+
}
|
1766
|
+
|
1767
|
+
if (range.y <= 0) {
|
1768
|
+
if (top > minTop) {
|
1769
|
+
height -= range.y;
|
1770
|
+
top += range.y;
|
1771
|
+
}
|
1772
|
+
} else {
|
1773
|
+
height -= range.y;
|
1774
|
+
top += range.y;
|
1775
|
+
}
|
1776
|
+
}
|
1777
|
+
|
1778
|
+
if (width < 0 && height < 0) {
|
1779
|
+
action = ACTION_SOUTH_WEST;
|
1780
|
+
height = 0;
|
1781
|
+
width = 0;
|
1782
|
+
} else if (width < 0) {
|
1783
|
+
action = ACTION_NORTH_WEST;
|
1784
|
+
width = 0;
|
1785
|
+
} else if (height < 0) {
|
1786
|
+
action = ACTION_SOUTH_EAST;
|
1787
|
+
height = 0;
|
1788
|
+
}
|
1789
|
+
|
1790
|
+
break;
|
1791
|
+
|
1792
|
+
case ACTION_NORTH_WEST:
|
1793
|
+
if (aspectRatio) {
|
1794
|
+
if (range.y <= 0 && (top <= minTop || left <= minLeft)) {
|
1795
|
+
renderable = false;
|
1796
|
+
break;
|
1797
|
+
}
|
1798
|
+
|
1799
|
+
height -= range.y;
|
1800
|
+
top += range.y;
|
1801
|
+
width = height * aspectRatio;
|
1802
|
+
left += range.X;
|
1803
|
+
} else {
|
1804
|
+
if (range.x <= 0) {
|
1805
|
+
if (left > minLeft) {
|
1806
|
+
width -= range.x;
|
1807
|
+
left += range.x;
|
1808
|
+
} else if (range.y <= 0 && top <= minTop) {
|
1809
|
+
renderable = false;
|
1810
|
+
}
|
1811
|
+
} else {
|
1812
|
+
width -= range.x;
|
1813
|
+
left += range.x;
|
1814
|
+
}
|
1815
|
+
|
1816
|
+
if (range.y <= 0) {
|
1817
|
+
if (top > minTop) {
|
1818
|
+
height -= range.y;
|
1819
|
+
top += range.y;
|
1820
|
+
}
|
1821
|
+
} else {
|
1822
|
+
height -= range.y;
|
1823
|
+
top += range.y;
|
1824
|
+
}
|
1825
|
+
}
|
1826
|
+
|
1827
|
+
if (width < 0 && height < 0) {
|
1828
|
+
action = ACTION_SOUTH_EAST;
|
1829
|
+
height = 0;
|
1830
|
+
width = 0;
|
1831
|
+
} else if (width < 0) {
|
1832
|
+
action = ACTION_NORTH_EAST;
|
1833
|
+
width = 0;
|
1834
|
+
} else if (height < 0) {
|
1835
|
+
action = ACTION_SOUTH_WEST;
|
1836
|
+
height = 0;
|
1837
|
+
}
|
1838
|
+
|
1839
|
+
break;
|
1840
|
+
|
1841
|
+
case ACTION_SOUTH_WEST:
|
1842
|
+
if (aspectRatio) {
|
1843
|
+
if (range.x <= 0 && (left <= minLeft || bottom >= maxHeight)) {
|
1844
|
+
renderable = false;
|
1845
|
+
break;
|
1846
|
+
}
|
1847
|
+
|
1848
|
+
width -= range.x;
|
1849
|
+
left += range.x;
|
1850
|
+
height = width / aspectRatio;
|
1851
|
+
} else {
|
1852
|
+
if (range.x <= 0) {
|
1853
|
+
if (left > minLeft) {
|
1854
|
+
width -= range.x;
|
1855
|
+
left += range.x;
|
1856
|
+
} else if (range.y >= 0 && bottom >= maxHeight) {
|
1857
|
+
renderable = false;
|
1858
|
+
}
|
1859
|
+
} else {
|
1860
|
+
width -= range.x;
|
1861
|
+
left += range.x;
|
1862
|
+
}
|
1863
|
+
|
1864
|
+
if (range.y >= 0) {
|
1865
|
+
if (bottom < maxHeight) {
|
1866
|
+
height += range.y;
|
1867
|
+
}
|
1868
|
+
} else {
|
1869
|
+
height += range.y;
|
1870
|
+
}
|
1871
|
+
}
|
1872
|
+
|
1873
|
+
if (width < 0 && height < 0) {
|
1874
|
+
action = ACTION_NORTH_EAST;
|
1875
|
+
height = 0;
|
1876
|
+
width = 0;
|
1877
|
+
} else if (width < 0) {
|
1878
|
+
action = ACTION_SOUTH_EAST;
|
1879
|
+
width = 0;
|
1880
|
+
} else if (height < 0) {
|
1881
|
+
action = ACTION_NORTH_WEST;
|
1882
|
+
height = 0;
|
1883
|
+
}
|
1884
|
+
|
1885
|
+
break;
|
1886
|
+
|
1887
|
+
case ACTION_SOUTH_EAST:
|
1888
|
+
if (aspectRatio) {
|
1889
|
+
if (range.x >= 0 && (right >= maxWidth || bottom >= maxHeight)) {
|
1890
|
+
renderable = false;
|
1891
|
+
break;
|
1892
|
+
}
|
1893
|
+
|
1894
|
+
width += range.x;
|
1895
|
+
height = width / aspectRatio;
|
1896
|
+
} else {
|
1897
|
+
if (range.x >= 0) {
|
1898
|
+
if (right < maxWidth) {
|
1899
|
+
width += range.x;
|
1900
|
+
} else if (range.y >= 0 && bottom >= maxHeight) {
|
1901
|
+
renderable = false;
|
1902
|
+
}
|
1903
|
+
} else {
|
1904
|
+
width += range.x;
|
1905
|
+
}
|
1906
|
+
|
1907
|
+
if (range.y >= 0) {
|
1908
|
+
if (bottom < maxHeight) {
|
1909
|
+
height += range.y;
|
1910
|
+
}
|
1911
|
+
} else {
|
1912
|
+
height += range.y;
|
1913
|
+
}
|
1914
|
+
}
|
1915
|
+
|
1916
|
+
if (width < 0 && height < 0) {
|
1917
|
+
action = ACTION_NORTH_WEST;
|
1918
|
+
height = 0;
|
1919
|
+
width = 0;
|
1920
|
+
} else if (width < 0) {
|
1921
|
+
action = ACTION_SOUTH_WEST;
|
1922
|
+
width = 0;
|
1923
|
+
} else if (height < 0) {
|
1924
|
+
action = ACTION_NORTH_EAST;
|
1925
|
+
height = 0;
|
1926
|
+
}
|
1927
|
+
|
1928
|
+
break;
|
1929
|
+
|
1930
|
+
// Move canvas
|
1931
|
+
case ACTION_MOVE:
|
1932
|
+
this.move(range.x, range.y);
|
1933
|
+
renderable = false;
|
1934
|
+
break;
|
1935
|
+
|
1936
|
+
// Zoom canvas
|
1937
|
+
case ACTION_ZOOM:
|
1938
|
+
this.zoom((function (x1, y1, x2, y2) {
|
1939
|
+
var z1 = sqrt(x1 * x1 + y1 * y1);
|
1940
|
+
var z2 = sqrt(x2 * x2 + y2 * y2);
|
1941
|
+
|
1942
|
+
return (z2 - z1) / z1;
|
1943
|
+
})(
|
1944
|
+
abs(this.startX - this.startX2),
|
1945
|
+
abs(this.startY - this.startY2),
|
1946
|
+
abs(this.endX - this.endX2),
|
1947
|
+
abs(this.endY - this.endY2)
|
1948
|
+
), originalEvent);
|
1949
|
+
this.startX2 = this.endX2;
|
1950
|
+
this.startY2 = this.endY2;
|
1951
|
+
renderable = false;
|
1952
|
+
break;
|
1953
|
+
|
1954
|
+
// Create crop box
|
1955
|
+
case ACTION_CROP:
|
1956
|
+
if (!range.x || !range.y) {
|
1957
|
+
renderable = false;
|
1958
|
+
break;
|
1959
|
+
}
|
1960
|
+
|
1961
|
+
offset = this.$cropper.offset();
|
1962
|
+
left = this.startX - offset.left;
|
1963
|
+
top = this.startY - offset.top;
|
1964
|
+
width = cropBox.minWidth;
|
1965
|
+
height = cropBox.minHeight;
|
1966
|
+
|
1967
|
+
if (range.x > 0) {
|
1968
|
+
action = range.y > 0 ? ACTION_SOUTH_EAST : ACTION_NORTH_EAST;
|
1969
|
+
} else if (range.x < 0) {
|
1970
|
+
left -= width;
|
1971
|
+
action = range.y > 0 ? ACTION_SOUTH_WEST : ACTION_NORTH_WEST;
|
1972
|
+
}
|
1973
|
+
|
1974
|
+
if (range.y < 0) {
|
1975
|
+
top -= height;
|
1976
|
+
}
|
1977
|
+
|
1978
|
+
// Show the crop box if is hidden
|
1979
|
+
if (!this.isCropped) {
|
1980
|
+
this.$cropBox.removeClass(CLASS_HIDDEN);
|
1981
|
+
this.isCropped = true;
|
1982
|
+
|
1983
|
+
if (this.limited) {
|
1984
|
+
this.limitCropBox(true, true);
|
1985
|
+
}
|
1986
|
+
}
|
1987
|
+
|
1988
|
+
break;
|
1989
|
+
|
1990
|
+
// No default
|
1991
|
+
}
|
1992
|
+
|
1993
|
+
if (renderable) {
|
1994
|
+
cropBox.width = width;
|
1995
|
+
cropBox.height = height;
|
1996
|
+
cropBox.left = left;
|
1997
|
+
cropBox.top = top;
|
1998
|
+
this.action = action;
|
1999
|
+
|
2000
|
+
this.renderCropBox();
|
2001
|
+
}
|
2002
|
+
|
2003
|
+
// Override
|
2004
|
+
this.startX = this.endX;
|
2005
|
+
this.startY = this.endY;
|
2006
|
+
}
|
2007
|
+
});
|
2008
|
+
|
2009
|
+
$.extend(prototype, {
|
2010
|
+
|
2011
|
+
// Show the crop box manually
|
2012
|
+
crop: function () {
|
2013
|
+
if (!this.isBuilt || this.isDisabled) {
|
2014
|
+
return;
|
2015
|
+
}
|
2016
|
+
|
2017
|
+
if (!this.isCropped) {
|
2018
|
+
this.isCropped = true;
|
2019
|
+
this.limitCropBox(true, true);
|
2020
|
+
|
2021
|
+
if (this.options.modal) {
|
2022
|
+
this.$dragBox.addClass(CLASS_MODAL);
|
2023
|
+
}
|
2024
|
+
|
2025
|
+
this.$cropBox.removeClass(CLASS_HIDDEN);
|
2026
|
+
}
|
2027
|
+
|
2028
|
+
this.setCropBoxData(this.initialCropBox);
|
2029
|
+
},
|
2030
|
+
|
2031
|
+
// Reset the image and crop box to their initial states
|
2032
|
+
reset: function () {
|
2033
|
+
if (!this.isBuilt || this.isDisabled) {
|
2034
|
+
return;
|
2035
|
+
}
|
2036
|
+
|
2037
|
+
this.image = $.extend({}, this.initialImage);
|
2038
|
+
this.canvas = $.extend({}, this.initialCanvas);
|
2039
|
+
this.cropBox = $.extend({}, this.initialCropBox);
|
2040
|
+
|
2041
|
+
this.renderCanvas();
|
2042
|
+
|
2043
|
+
if (this.isCropped) {
|
2044
|
+
this.renderCropBox();
|
2045
|
+
}
|
2046
|
+
},
|
2047
|
+
|
2048
|
+
// Clear the crop box
|
2049
|
+
clear: function () {
|
2050
|
+
if (!this.isCropped || this.isDisabled) {
|
2051
|
+
return;
|
2052
|
+
}
|
2053
|
+
|
2054
|
+
$.extend(this.cropBox, {
|
2055
|
+
left: 0,
|
2056
|
+
top: 0,
|
2057
|
+
width: 0,
|
2058
|
+
height: 0
|
2059
|
+
});
|
2060
|
+
|
2061
|
+
this.isCropped = false;
|
2062
|
+
this.renderCropBox();
|
2063
|
+
|
2064
|
+
this.limitCanvas(true, true);
|
2065
|
+
|
2066
|
+
// Render canvas after crop box rendered
|
2067
|
+
this.renderCanvas();
|
2068
|
+
|
2069
|
+
this.$dragBox.removeClass(CLASS_MODAL);
|
2070
|
+
this.$cropBox.addClass(CLASS_HIDDEN);
|
2071
|
+
},
|
2072
|
+
|
2073
|
+
/**
|
2074
|
+
* Replace the image's src and rebuild the cropper
|
2075
|
+
*
|
2076
|
+
* @param {String} url
|
2077
|
+
*/
|
2078
|
+
replace: function (url) {
|
2079
|
+
if (!this.isDisabled && url) {
|
2080
|
+
if (this.isImg) {
|
2081
|
+
this.isReplaced = true;
|
2082
|
+
this.$element.attr('src', url);
|
2083
|
+
}
|
2084
|
+
|
2085
|
+
// Clear previous data
|
2086
|
+
this.options.data = null;
|
2087
|
+
this.load(url);
|
2088
|
+
}
|
2089
|
+
},
|
2090
|
+
|
2091
|
+
// Enable (unfreeze) the cropper
|
2092
|
+
enable: function () {
|
2093
|
+
if (this.isBuilt) {
|
2094
|
+
this.isDisabled = false;
|
2095
|
+
this.$cropper.removeClass(CLASS_DISABLED);
|
2096
|
+
}
|
2097
|
+
},
|
2098
|
+
|
2099
|
+
// Disable (freeze) the cropper
|
2100
|
+
disable: function () {
|
2101
|
+
if (this.isBuilt) {
|
2102
|
+
this.isDisabled = true;
|
2103
|
+
this.$cropper.addClass(CLASS_DISABLED);
|
2104
|
+
}
|
2105
|
+
},
|
2106
|
+
|
2107
|
+
// Destroy the cropper and remove the instance from the image
|
2108
|
+
destroy: function () {
|
2109
|
+
var $this = this.$element;
|
2110
|
+
|
2111
|
+
if (this.isLoaded) {
|
2112
|
+
if (this.isImg && this.isReplaced) {
|
2113
|
+
$this.attr('src', this.originalUrl);
|
2114
|
+
}
|
2115
|
+
|
2116
|
+
this.unbuild();
|
2117
|
+
$this.removeClass(CLASS_HIDDEN);
|
2118
|
+
} else {
|
2119
|
+
if (this.isImg) {
|
2120
|
+
$this.off(EVENT_LOAD, this.start);
|
2121
|
+
} else if (this.$clone) {
|
2122
|
+
this.$clone.remove();
|
2123
|
+
}
|
2124
|
+
}
|
2125
|
+
|
2126
|
+
$this.removeData(NAMESPACE);
|
2127
|
+
},
|
2128
|
+
|
2129
|
+
/**
|
2130
|
+
* Move the canvas with relative offsets
|
2131
|
+
*
|
2132
|
+
* @param {Number} offsetX
|
2133
|
+
* @param {Number} offsetY (optional)
|
2134
|
+
*/
|
2135
|
+
move: function (offsetX, offsetY) {
|
2136
|
+
var canvas = this.canvas;
|
2137
|
+
|
2138
|
+
this.moveTo(
|
2139
|
+
isUndefined(offsetX) ? offsetX : canvas.left + num(offsetX),
|
2140
|
+
isUndefined(offsetY) ? offsetY : canvas.top + num(offsetY)
|
2141
|
+
);
|
2142
|
+
},
|
2143
|
+
|
2144
|
+
/**
|
2145
|
+
* Move the canvas to an absolute point
|
2146
|
+
*
|
2147
|
+
* @param {Number} x
|
2148
|
+
* @param {Number} y (optional)
|
2149
|
+
*/
|
2150
|
+
moveTo: function (x, y) {
|
2151
|
+
var canvas = this.canvas;
|
2152
|
+
var isChanged = false;
|
2153
|
+
|
2154
|
+
// If "y" is not present, its default value is "x"
|
2155
|
+
if (isUndefined(y)) {
|
2156
|
+
y = x;
|
2157
|
+
}
|
2158
|
+
|
2159
|
+
x = num(x);
|
2160
|
+
y = num(y);
|
2161
|
+
|
2162
|
+
if (this.isBuilt && !this.isDisabled && this.options.movable) {
|
2163
|
+
if (isNumber(x)) {
|
2164
|
+
canvas.left = x;
|
2165
|
+
isChanged = true;
|
2166
|
+
}
|
2167
|
+
|
2168
|
+
if (isNumber(y)) {
|
2169
|
+
canvas.top = y;
|
2170
|
+
isChanged = true;
|
2171
|
+
}
|
2172
|
+
|
2173
|
+
if (isChanged) {
|
2174
|
+
this.renderCanvas(true);
|
2175
|
+
}
|
2176
|
+
}
|
2177
|
+
},
|
2178
|
+
|
2179
|
+
/**
|
2180
|
+
* Zoom the canvas with a relative ratio
|
2181
|
+
*
|
2182
|
+
* @param {Number} ratio
|
2183
|
+
* @param {Event} _originalEvent (private)
|
2184
|
+
*/
|
2185
|
+
zoom: function (ratio, _originalEvent) {
|
2186
|
+
var canvas = this.canvas;
|
2187
|
+
|
2188
|
+
ratio = num(ratio);
|
2189
|
+
|
2190
|
+
if (ratio < 0) {
|
2191
|
+
ratio = 1 / (1 - ratio);
|
2192
|
+
} else {
|
2193
|
+
ratio = 1 + ratio;
|
2194
|
+
}
|
2195
|
+
|
2196
|
+
this.zoomTo(canvas.width * ratio / canvas.naturalWidth, _originalEvent);
|
2197
|
+
},
|
2198
|
+
|
2199
|
+
/**
|
2200
|
+
* Zoom the canvas to an absolute ratio
|
2201
|
+
*
|
2202
|
+
* @param {Number} ratio
|
2203
|
+
* @param {Event} _originalEvent (private)
|
2204
|
+
*/
|
2205
|
+
zoomTo: function (ratio, _originalEvent) {
|
2206
|
+
var options = this.options;
|
2207
|
+
var canvas = this.canvas;
|
2208
|
+
var width = canvas.width;
|
2209
|
+
var height = canvas.height;
|
2210
|
+
var naturalWidth = canvas.naturalWidth;
|
2211
|
+
var naturalHeight = canvas.naturalHeight;
|
2212
|
+
var newWidth;
|
2213
|
+
var newHeight;
|
2214
|
+
|
2215
|
+
ratio = num(ratio);
|
2216
|
+
|
2217
|
+
if (ratio >= 0 && this.isBuilt && !this.isDisabled && options.zoomable) {
|
2218
|
+
newWidth = naturalWidth * ratio;
|
2219
|
+
newHeight = naturalHeight * ratio;
|
2220
|
+
|
2221
|
+
if (this.trigger(EVENT_ZOOM, {
|
2222
|
+
originalEvent: _originalEvent,
|
2223
|
+
oldRatio: width / naturalWidth,
|
2224
|
+
ratio: newWidth / naturalWidth
|
2225
|
+
}).isDefaultPrevented()) {
|
2226
|
+
return;
|
2227
|
+
}
|
2228
|
+
|
2229
|
+
canvas.left -= (newWidth - width) / 2;
|
2230
|
+
canvas.top -= (newHeight - height) / 2;
|
2231
|
+
canvas.width = newWidth;
|
2232
|
+
canvas.height = newHeight;
|
2233
|
+
this.renderCanvas(true);
|
2234
|
+
}
|
2235
|
+
},
|
2236
|
+
|
2237
|
+
/**
|
2238
|
+
* Rotate the canvas with a relative degree
|
2239
|
+
*
|
2240
|
+
* @param {Number} degree
|
2241
|
+
*/
|
2242
|
+
rotate: function (degree) {
|
2243
|
+
this.rotateTo((this.image.rotate || 0) + num(degree));
|
2244
|
+
},
|
2245
|
+
|
2246
|
+
/**
|
2247
|
+
* Rotate the canvas to an absolute degree
|
2248
|
+
* https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function#rotate()
|
2249
|
+
*
|
2250
|
+
* @param {Number} degree
|
2251
|
+
*/
|
2252
|
+
rotateTo: function (degree) {
|
2253
|
+
degree = num(degree);
|
2254
|
+
|
2255
|
+
if (isNumber(degree) && this.isBuilt && !this.isDisabled && this.options.rotatable) {
|
2256
|
+
this.image.rotate = degree % 360;
|
2257
|
+
this.isRotated = true;
|
2258
|
+
this.renderCanvas(true);
|
2259
|
+
}
|
2260
|
+
},
|
2261
|
+
|
2262
|
+
/**
|
2263
|
+
* Scale the image
|
2264
|
+
* https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function#scale()
|
2265
|
+
*
|
2266
|
+
* @param {Number} scaleX
|
2267
|
+
* @param {Number} scaleY (optional)
|
2268
|
+
*/
|
2269
|
+
scale: function (scaleX, scaleY) {
|
2270
|
+
var image = this.image;
|
2271
|
+
var isChanged = false;
|
2272
|
+
|
2273
|
+
// If "scaleY" is not present, its default value is "scaleX"
|
2274
|
+
if (isUndefined(scaleY)) {
|
2275
|
+
scaleY = scaleX;
|
2276
|
+
}
|
2277
|
+
|
2278
|
+
scaleX = num(scaleX);
|
2279
|
+
scaleY = num(scaleY);
|
2280
|
+
|
2281
|
+
if (this.isBuilt && !this.isDisabled && this.options.scalable) {
|
2282
|
+
if (isNumber(scaleX)) {
|
2283
|
+
image.scaleX = scaleX;
|
2284
|
+
isChanged = true;
|
2285
|
+
}
|
2286
|
+
|
2287
|
+
if (isNumber(scaleY)) {
|
2288
|
+
image.scaleY = scaleY;
|
2289
|
+
isChanged = true;
|
2290
|
+
}
|
2291
|
+
|
2292
|
+
if (isChanged) {
|
2293
|
+
this.renderImage(true);
|
2294
|
+
}
|
2295
|
+
}
|
2296
|
+
},
|
2297
|
+
|
2298
|
+
/**
|
2299
|
+
* Scale the abscissa of the image
|
2300
|
+
*
|
2301
|
+
* @param {Number} scaleX
|
2302
|
+
*/
|
2303
|
+
scaleX: function (scaleX) {
|
2304
|
+
var scaleY = this.image.scaleY;
|
2305
|
+
|
2306
|
+
this.scale(scaleX, isNumber(scaleY) ? scaleY : 1);
|
2307
|
+
},
|
2308
|
+
|
2309
|
+
/**
|
2310
|
+
* Scale the ordinate of the image
|
2311
|
+
*
|
2312
|
+
* @param {Number} scaleY
|
2313
|
+
*/
|
2314
|
+
scaleY: function (scaleY) {
|
2315
|
+
var scaleX = this.image.scaleX;
|
2316
|
+
|
2317
|
+
this.scale(isNumber(scaleX) ? scaleX : 1, scaleY);
|
2318
|
+
},
|
2319
|
+
|
2320
|
+
/**
|
2321
|
+
* Get the cropped area position and size data (base on the original image)
|
2322
|
+
*
|
2323
|
+
* @param {Boolean} isRounded (optional)
|
2324
|
+
* @return {Object} data
|
2325
|
+
*/
|
2326
|
+
getData: function (isRounded) {
|
2327
|
+
var options = this.options;
|
2328
|
+
var image = this.image;
|
2329
|
+
var canvas = this.canvas;
|
2330
|
+
var cropBox = this.cropBox;
|
2331
|
+
var ratio;
|
2332
|
+
var data;
|
2333
|
+
|
2334
|
+
if (this.isBuilt && this.isCropped) {
|
2335
|
+
data = {
|
2336
|
+
x: cropBox.left - canvas.left,
|
2337
|
+
y: cropBox.top - canvas.top,
|
2338
|
+
width: cropBox.width,
|
2339
|
+
height: cropBox.height
|
2340
|
+
};
|
2341
|
+
|
2342
|
+
ratio = image.width / image.naturalWidth;
|
2343
|
+
|
2344
|
+
$.each(data, function (i, n) {
|
2345
|
+
n = n / ratio;
|
2346
|
+
data[i] = isRounded ? round(n) : n;
|
2347
|
+
});
|
2348
|
+
|
2349
|
+
} else {
|
2350
|
+
data = {
|
2351
|
+
x: 0,
|
2352
|
+
y: 0,
|
2353
|
+
width: 0,
|
2354
|
+
height: 0
|
2355
|
+
};
|
2356
|
+
}
|
2357
|
+
|
2358
|
+
if (options.rotatable) {
|
2359
|
+
data.rotate = image.rotate || 0;
|
2360
|
+
}
|
2361
|
+
|
2362
|
+
if (options.scalable) {
|
2363
|
+
data.scaleX = image.scaleX || 1;
|
2364
|
+
data.scaleY = image.scaleY || 1;
|
2365
|
+
}
|
2366
|
+
|
2367
|
+
return data;
|
2368
|
+
},
|
2369
|
+
|
2370
|
+
/**
|
2371
|
+
* Set the cropped area position and size with new data
|
2372
|
+
*
|
2373
|
+
* @param {Object} data
|
2374
|
+
*/
|
2375
|
+
setData: function (data) {
|
2376
|
+
var options = this.options;
|
2377
|
+
var image = this.image;
|
2378
|
+
var canvas = this.canvas;
|
2379
|
+
var cropBoxData = {};
|
2380
|
+
var isRotated;
|
2381
|
+
var isScaled;
|
2382
|
+
var ratio;
|
2383
|
+
|
2384
|
+
if ($.isFunction(data)) {
|
2385
|
+
data = data.call(this.element);
|
2386
|
+
}
|
2387
|
+
|
2388
|
+
if (this.isBuilt && !this.isDisabled && $.isPlainObject(data)) {
|
2389
|
+
if (options.rotatable) {
|
2390
|
+
if (isNumber(data.rotate) && data.rotate !== image.rotate) {
|
2391
|
+
image.rotate = data.rotate;
|
2392
|
+
this.isRotated = isRotated = true;
|
2393
|
+
}
|
2394
|
+
}
|
2395
|
+
|
2396
|
+
if (options.scalable) {
|
2397
|
+
if (isNumber(data.scaleX) && data.scaleX !== image.scaleX) {
|
2398
|
+
image.scaleX = data.scaleX;
|
2399
|
+
isScaled = true;
|
2400
|
+
}
|
2401
|
+
|
2402
|
+
if (isNumber(data.scaleY) && data.scaleY !== image.scaleY) {
|
2403
|
+
image.scaleY = data.scaleY;
|
2404
|
+
isScaled = true;
|
2405
|
+
}
|
2406
|
+
}
|
2407
|
+
|
2408
|
+
if (isRotated) {
|
2409
|
+
this.renderCanvas();
|
2410
|
+
} else if (isScaled) {
|
2411
|
+
this.renderImage();
|
2412
|
+
}
|
2413
|
+
|
2414
|
+
ratio = image.width / image.naturalWidth;
|
2415
|
+
|
2416
|
+
if (isNumber(data.x)) {
|
2417
|
+
cropBoxData.left = data.x * ratio + canvas.left;
|
2418
|
+
}
|
2419
|
+
|
2420
|
+
if (isNumber(data.y)) {
|
2421
|
+
cropBoxData.top = data.y * ratio + canvas.top;
|
2422
|
+
}
|
2423
|
+
|
2424
|
+
if (isNumber(data.width)) {
|
2425
|
+
cropBoxData.width = data.width * ratio;
|
2426
|
+
}
|
2427
|
+
|
2428
|
+
if (isNumber(data.height)) {
|
2429
|
+
cropBoxData.height = data.height * ratio;
|
2430
|
+
}
|
2431
|
+
|
2432
|
+
this.setCropBoxData(cropBoxData);
|
2433
|
+
}
|
2434
|
+
},
|
2435
|
+
|
2436
|
+
/**
|
2437
|
+
* Get the container size data
|
2438
|
+
*
|
2439
|
+
* @return {Object} data
|
2440
|
+
*/
|
2441
|
+
getContainerData: function () {
|
2442
|
+
return this.isBuilt ? this.container : {};
|
2443
|
+
},
|
2444
|
+
|
2445
|
+
/**
|
2446
|
+
* Get the image position and size data
|
2447
|
+
*
|
2448
|
+
* @return {Object} data
|
2449
|
+
*/
|
2450
|
+
getImageData: function () {
|
2451
|
+
return this.isLoaded ? this.image : {};
|
2452
|
+
},
|
2453
|
+
|
2454
|
+
/**
|
2455
|
+
* Get the canvas position and size data
|
2456
|
+
*
|
2457
|
+
* @return {Object} data
|
2458
|
+
*/
|
2459
|
+
getCanvasData: function () {
|
2460
|
+
var canvas = this.canvas;
|
2461
|
+
var data = {};
|
2462
|
+
|
2463
|
+
if (this.isBuilt) {
|
2464
|
+
$.each([
|
2465
|
+
'left',
|
2466
|
+
'top',
|
2467
|
+
'width',
|
2468
|
+
'height',
|
2469
|
+
'naturalWidth',
|
2470
|
+
'naturalHeight'
|
2471
|
+
], function (i, n) {
|
2472
|
+
data[n] = canvas[n];
|
2473
|
+
});
|
2474
|
+
}
|
2475
|
+
|
2476
|
+
return data;
|
2477
|
+
},
|
2478
|
+
|
2479
|
+
/**
|
2480
|
+
* Set the canvas position and size with new data
|
2481
|
+
*
|
2482
|
+
* @param {Object} data
|
2483
|
+
*/
|
2484
|
+
setCanvasData: function (data) {
|
2485
|
+
var canvas = this.canvas;
|
2486
|
+
var aspectRatio = canvas.aspectRatio;
|
2487
|
+
|
2488
|
+
if ($.isFunction(data)) {
|
2489
|
+
data = data.call(this.$element);
|
2490
|
+
}
|
2491
|
+
|
2492
|
+
if (this.isBuilt && !this.isDisabled && $.isPlainObject(data)) {
|
2493
|
+
if (isNumber(data.left)) {
|
2494
|
+
canvas.left = data.left;
|
2495
|
+
}
|
2496
|
+
|
2497
|
+
if (isNumber(data.top)) {
|
2498
|
+
canvas.top = data.top;
|
2499
|
+
}
|
2500
|
+
|
2501
|
+
if (isNumber(data.width)) {
|
2502
|
+
canvas.width = data.width;
|
2503
|
+
canvas.height = data.width / aspectRatio;
|
2504
|
+
} else if (isNumber(data.height)) {
|
2505
|
+
canvas.height = data.height;
|
2506
|
+
canvas.width = data.height * aspectRatio;
|
2507
|
+
}
|
2508
|
+
|
2509
|
+
this.renderCanvas(true);
|
2510
|
+
}
|
2511
|
+
},
|
2512
|
+
|
2513
|
+
/**
|
2514
|
+
* Get the crop box position and size data
|
2515
|
+
*
|
2516
|
+
* @return {Object} data
|
2517
|
+
*/
|
2518
|
+
getCropBoxData: function () {
|
2519
|
+
var cropBox = this.cropBox;
|
2520
|
+
var data;
|
2521
|
+
|
2522
|
+
if (this.isBuilt && this.isCropped) {
|
2523
|
+
data = {
|
2524
|
+
left: cropBox.left,
|
2525
|
+
top: cropBox.top,
|
2526
|
+
width: cropBox.width,
|
2527
|
+
height: cropBox.height
|
2528
|
+
};
|
2529
|
+
}
|
2530
|
+
|
2531
|
+
return data || {};
|
2532
|
+
},
|
2533
|
+
|
2534
|
+
/**
|
2535
|
+
* Set the crop box position and size with new data
|
2536
|
+
*
|
2537
|
+
* @param {Object} data
|
2538
|
+
*/
|
2539
|
+
setCropBoxData: function (data) {
|
2540
|
+
var cropBox = this.cropBox;
|
2541
|
+
var aspectRatio = this.options.aspectRatio;
|
2542
|
+
var isWidthChanged;
|
2543
|
+
var isHeightChanged;
|
2544
|
+
|
2545
|
+
if ($.isFunction(data)) {
|
2546
|
+
data = data.call(this.$element);
|
2547
|
+
}
|
2548
|
+
|
2549
|
+
if (this.isBuilt && this.isCropped && !this.isDisabled && $.isPlainObject(data)) {
|
2550
|
+
|
2551
|
+
if (isNumber(data.left)) {
|
2552
|
+
cropBox.left = data.left;
|
2553
|
+
}
|
2554
|
+
|
2555
|
+
if (isNumber(data.top)) {
|
2556
|
+
cropBox.top = data.top;
|
2557
|
+
}
|
2558
|
+
|
2559
|
+
if (isNumber(data.width) && data.width !== cropBox.width) {
|
2560
|
+
isWidthChanged = true;
|
2561
|
+
cropBox.width = data.width;
|
2562
|
+
}
|
2563
|
+
|
2564
|
+
if (isNumber(data.height) && data.height !== cropBox.height) {
|
2565
|
+
isHeightChanged = true;
|
2566
|
+
cropBox.height = data.height;
|
2567
|
+
}
|
2568
|
+
|
2569
|
+
if (aspectRatio) {
|
2570
|
+
if (isWidthChanged) {
|
2571
|
+
cropBox.height = cropBox.width / aspectRatio;
|
2572
|
+
} else if (isHeightChanged) {
|
2573
|
+
cropBox.width = cropBox.height * aspectRatio;
|
2574
|
+
}
|
2575
|
+
}
|
2576
|
+
|
2577
|
+
this.renderCropBox();
|
2578
|
+
}
|
2579
|
+
},
|
2580
|
+
|
2581
|
+
/**
|
2582
|
+
* Get a canvas drawn the cropped image
|
2583
|
+
*
|
2584
|
+
* @param {Object} options (optional)
|
2585
|
+
* @return {HTMLCanvasElement} canvas
|
2586
|
+
*/
|
2587
|
+
getCroppedCanvas: function (options) {
|
2588
|
+
var originalWidth;
|
2589
|
+
var originalHeight;
|
2590
|
+
var canvasWidth;
|
2591
|
+
var canvasHeight;
|
2592
|
+
var scaledWidth;
|
2593
|
+
var scaledHeight;
|
2594
|
+
var scaledRatio;
|
2595
|
+
var aspectRatio;
|
2596
|
+
var canvas;
|
2597
|
+
var context;
|
2598
|
+
var data;
|
2599
|
+
|
2600
|
+
if (!this.isBuilt || !this.isCropped || !SUPPORT_CANVAS) {
|
2601
|
+
return;
|
2602
|
+
}
|
2603
|
+
|
2604
|
+
if (!$.isPlainObject(options)) {
|
2605
|
+
options = {};
|
2606
|
+
}
|
2607
|
+
|
2608
|
+
data = this.getData();
|
2609
|
+
originalWidth = data.width;
|
2610
|
+
originalHeight = data.height;
|
2611
|
+
aspectRatio = originalWidth / originalHeight;
|
2612
|
+
|
2613
|
+
if ($.isPlainObject(options)) {
|
2614
|
+
scaledWidth = options.width;
|
2615
|
+
scaledHeight = options.height;
|
2616
|
+
|
2617
|
+
if (scaledWidth) {
|
2618
|
+
scaledHeight = scaledWidth / aspectRatio;
|
2619
|
+
scaledRatio = scaledWidth / originalWidth;
|
2620
|
+
} else if (scaledHeight) {
|
2621
|
+
scaledWidth = scaledHeight * aspectRatio;
|
2622
|
+
scaledRatio = scaledHeight / originalHeight;
|
2623
|
+
}
|
2624
|
+
}
|
2625
|
+
|
2626
|
+
|
2627
|
+
canvasWidth = round(scaledWidth || originalWidth);
|
2628
|
+
canvasHeight = round(scaledHeight || originalHeight);
|
2629
|
+
|
2630
|
+
canvas = $('<canvas>')[0];
|
2631
|
+
canvas.width = canvasWidth;
|
2632
|
+
canvas.height = canvasHeight;
|
2633
|
+
context = canvas.getContext('2d');
|
2634
|
+
|
2635
|
+
if (options.fillColor) {
|
2636
|
+
context.fillStyle = options.fillColor;
|
2637
|
+
context.fillRect(0, 0, canvasWidth, canvasHeight);
|
2638
|
+
}
|
2639
|
+
|
2640
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D.drawImage
|
2641
|
+
context.drawImage.apply(context, (function () {
|
2642
|
+
var source = getSourceCanvas(this.$clone[0], this.image);
|
2643
|
+
var sourceWidth = source.width;
|
2644
|
+
var sourceHeight = source.height;
|
2645
|
+
var args = [source];
|
2646
|
+
|
2647
|
+
// Source canvas
|
2648
|
+
var srcX = data.x;
|
2649
|
+
var srcY = data.y;
|
2650
|
+
var srcWidth;
|
2651
|
+
var srcHeight;
|
2652
|
+
|
2653
|
+
// Destination canvas
|
2654
|
+
var dstX;
|
2655
|
+
var dstY;
|
2656
|
+
var dstWidth;
|
2657
|
+
var dstHeight;
|
2658
|
+
|
2659
|
+
if (srcX <= -originalWidth || srcX > sourceWidth) {
|
2660
|
+
srcX = srcWidth = dstX = dstWidth = 0;
|
2661
|
+
} else if (srcX <= 0) {
|
2662
|
+
dstX = -srcX;
|
2663
|
+
srcX = 0;
|
2664
|
+
srcWidth = dstWidth = min(sourceWidth, originalWidth + srcX);
|
2665
|
+
} else if (srcX <= sourceWidth) {
|
2666
|
+
dstX = 0;
|
2667
|
+
srcWidth = dstWidth = min(originalWidth, sourceWidth - srcX);
|
2668
|
+
}
|
2669
|
+
|
2670
|
+
if (srcWidth <= 0 || srcY <= -originalHeight || srcY > sourceHeight) {
|
2671
|
+
srcY = srcHeight = dstY = dstHeight = 0;
|
2672
|
+
} else if (srcY <= 0) {
|
2673
|
+
dstY = -srcY;
|
2674
|
+
srcY = 0;
|
2675
|
+
srcHeight = dstHeight = min(sourceHeight, originalHeight + srcY);
|
2676
|
+
} else if (srcY <= sourceHeight) {
|
2677
|
+
dstY = 0;
|
2678
|
+
srcHeight = dstHeight = min(originalHeight, sourceHeight - srcY);
|
2679
|
+
}
|
2680
|
+
|
2681
|
+
// All the numerical parameters should be integer for `drawImage` (#476)
|
2682
|
+
args.push(floor(srcX), floor(srcY), floor(srcWidth), floor(srcHeight));
|
2683
|
+
|
2684
|
+
// Scale destination sizes
|
2685
|
+
if (scaledRatio) {
|
2686
|
+
dstX *= scaledRatio;
|
2687
|
+
dstY *= scaledRatio;
|
2688
|
+
dstWidth *= scaledRatio;
|
2689
|
+
dstHeight *= scaledRatio;
|
2690
|
+
}
|
2691
|
+
|
2692
|
+
// Avoid "IndexSizeError" in IE and Firefox
|
2693
|
+
if (dstWidth > 0 && dstHeight > 0) {
|
2694
|
+
args.push(floor(dstX), floor(dstY), floor(dstWidth), floor(dstHeight));
|
2695
|
+
}
|
2696
|
+
|
2697
|
+
return args;
|
2698
|
+
}).call(this));
|
2699
|
+
|
2700
|
+
return canvas;
|
2701
|
+
},
|
2702
|
+
|
2703
|
+
/**
|
2704
|
+
* Change the aspect ratio of the crop box
|
2705
|
+
*
|
2706
|
+
* @param {Number} aspectRatio
|
2707
|
+
*/
|
2708
|
+
setAspectRatio: function (aspectRatio) {
|
2709
|
+
var options = this.options;
|
2710
|
+
|
2711
|
+
if (!this.isDisabled && !isUndefined(aspectRatio)) {
|
2712
|
+
|
2713
|
+
// 0 -> NaN
|
2714
|
+
options.aspectRatio = max(0, aspectRatio) || NaN;
|
2715
|
+
|
2716
|
+
if (this.isBuilt) {
|
2717
|
+
this.initCropBox();
|
2718
|
+
|
2719
|
+
if (this.isCropped) {
|
2720
|
+
this.renderCropBox();
|
2721
|
+
}
|
2722
|
+
}
|
2723
|
+
}
|
2724
|
+
},
|
2725
|
+
|
2726
|
+
/**
|
2727
|
+
* Change the drag mode
|
2728
|
+
*
|
2729
|
+
* @param {String} mode (optional)
|
2730
|
+
*/
|
2731
|
+
setDragMode: function (mode) {
|
2732
|
+
var options = this.options;
|
2733
|
+
var croppable;
|
2734
|
+
var movable;
|
2735
|
+
|
2736
|
+
if (this.isLoaded && !this.isDisabled) {
|
2737
|
+
croppable = mode === ACTION_CROP;
|
2738
|
+
movable = options.movable && mode === ACTION_MOVE;
|
2739
|
+
mode = (croppable || movable) ? mode : ACTION_NONE;
|
2740
|
+
|
2741
|
+
this.$dragBox.
|
2742
|
+
data(DATA_ACTION, mode).
|
2743
|
+
toggleClass(CLASS_CROP, croppable).
|
2744
|
+
toggleClass(CLASS_MOVE, movable);
|
2745
|
+
|
2746
|
+
if (!options.cropBoxMovable) {
|
2747
|
+
|
2748
|
+
// Sync drag mode to crop box when it is not movable(#300)
|
2749
|
+
this.$face.
|
2750
|
+
data(DATA_ACTION, mode).
|
2751
|
+
toggleClass(CLASS_CROP, croppable).
|
2752
|
+
toggleClass(CLASS_MOVE, movable);
|
2753
|
+
}
|
2754
|
+
}
|
2755
|
+
}
|
2756
|
+
});
|
2757
|
+
|
2758
|
+
$.extend(Cropper.prototype, prototype);
|
2759
|
+
|
2760
|
+
Cropper.DEFAULTS = {
|
2761
|
+
|
2762
|
+
// Define the view mode of the cropper
|
2763
|
+
viewMode: 0, // 0, 1, 2, 3
|
2764
|
+
|
2765
|
+
// Define the dragging mode of the cropper
|
2766
|
+
dragMode: 'crop', // 'crop', 'move' or 'none'
|
2767
|
+
|
2768
|
+
// Define the aspect ratio of the crop box
|
2769
|
+
aspectRatio: NaN,
|
2770
|
+
|
2771
|
+
// An object with the previous cropping result data
|
2772
|
+
data: null,
|
2773
|
+
|
2774
|
+
// A jQuery selector for adding extra containers to preview
|
2775
|
+
preview: '',
|
2776
|
+
|
2777
|
+
// Re-render the cropper when resize the window
|
2778
|
+
responsive: true,
|
2779
|
+
|
2780
|
+
// Restore the cropped area after resize the window
|
2781
|
+
restore: true,
|
2782
|
+
|
2783
|
+
// Check if the current image is a cross-origin image
|
2784
|
+
checkCrossOrigin: true,
|
2785
|
+
|
2786
|
+
// Check the current image's Exif Orientation information
|
2787
|
+
checkOrientation: true,
|
2788
|
+
|
2789
|
+
// Show the black modal
|
2790
|
+
modal: true,
|
2791
|
+
|
2792
|
+
// Show the dashed lines for guiding
|
2793
|
+
guides: true,
|
2794
|
+
|
2795
|
+
// Show the center indicator for guiding
|
2796
|
+
center: true,
|
2797
|
+
|
2798
|
+
// Show the white modal to highlight the crop box
|
2799
|
+
highlight: true,
|
2800
|
+
|
2801
|
+
// Show the grid background
|
2802
|
+
background: true,
|
2803
|
+
|
2804
|
+
// Enable to crop the image automatically when initialize
|
2805
|
+
autoCrop: true,
|
2806
|
+
|
2807
|
+
// Define the percentage of automatic cropping area when initializes
|
2808
|
+
autoCropArea: 0.8,
|
2809
|
+
|
2810
|
+
// Enable to move the image
|
2811
|
+
movable: true,
|
2812
|
+
|
2813
|
+
// Enable to rotate the image
|
2814
|
+
rotatable: true,
|
2815
|
+
|
2816
|
+
// Enable to scale the image
|
2817
|
+
scalable: true,
|
2818
|
+
|
2819
|
+
// Enable to zoom the image
|
2820
|
+
zoomable: true,
|
2821
|
+
|
2822
|
+
// Enable to zoom the image by dragging touch
|
2823
|
+
zoomOnTouch: true,
|
2824
|
+
|
2825
|
+
// Enable to zoom the image by wheeling mouse
|
2826
|
+
zoomOnWheel: true,
|
2827
|
+
|
2828
|
+
// Define zoom ratio when zoom the image by wheeling mouse
|
2829
|
+
wheelZoomRatio: 0.1,
|
2830
|
+
|
2831
|
+
// Enable to move the crop box
|
2832
|
+
cropBoxMovable: true,
|
2833
|
+
|
2834
|
+
// Enable to resize the crop box
|
2835
|
+
cropBoxResizable: true,
|
2836
|
+
|
2837
|
+
// Toggle drag mode between "crop" and "move" when click twice on the cropper
|
2838
|
+
toggleDragModeOnDblclick: true,
|
2839
|
+
|
2840
|
+
// Size limitation
|
2841
|
+
minCanvasWidth: 0,
|
2842
|
+
minCanvasHeight: 0,
|
2843
|
+
minCropBoxWidth: 0,
|
2844
|
+
minCropBoxHeight: 0,
|
2845
|
+
minContainerWidth: 200,
|
2846
|
+
minContainerHeight: 100,
|
2847
|
+
|
2848
|
+
// Shortcuts of events
|
2849
|
+
build: null,
|
2850
|
+
built: null,
|
2851
|
+
cropstart: null,
|
2852
|
+
cropmove: null,
|
2853
|
+
cropend: null,
|
2854
|
+
crop: null,
|
2855
|
+
zoom: null
|
2856
|
+
};
|
2857
|
+
|
2858
|
+
Cropper.setDefaults = function (options) {
|
2859
|
+
$.extend(Cropper.DEFAULTS, options);
|
2860
|
+
};
|
2861
|
+
|
2862
|
+
Cropper.TEMPLATE = (
|
2863
|
+
'<div class="cropper-container">' +
|
2864
|
+
'<div class="cropper-wrap-box">' +
|
2865
|
+
'<div class="cropper-canvas"></div>' +
|
2866
|
+
'</div>' +
|
2867
|
+
'<div class="cropper-drag-box"></div>' +
|
2868
|
+
'<div class="cropper-crop-box">' +
|
2869
|
+
'<span class="cropper-view-box"></span>' +
|
2870
|
+
'<span class="cropper-dashed dashed-h"></span>' +
|
2871
|
+
'<span class="cropper-dashed dashed-v"></span>' +
|
2872
|
+
'<span class="cropper-center"></span>' +
|
2873
|
+
'<span class="cropper-face"></span>' +
|
2874
|
+
'<span class="cropper-line line-e" data-action="e"></span>' +
|
2875
|
+
'<span class="cropper-line line-n" data-action="n"></span>' +
|
2876
|
+
'<span class="cropper-line line-w" data-action="w"></span>' +
|
2877
|
+
'<span class="cropper-line line-s" data-action="s"></span>' +
|
2878
|
+
'<span class="cropper-point point-e" data-action="e"></span>' +
|
2879
|
+
'<span class="cropper-point point-n" data-action="n"></span>' +
|
2880
|
+
'<span class="cropper-point point-w" data-action="w"></span>' +
|
2881
|
+
'<span class="cropper-point point-s" data-action="s"></span>' +
|
2882
|
+
'<span class="cropper-point point-ne" data-action="ne"></span>' +
|
2883
|
+
'<span class="cropper-point point-nw" data-action="nw"></span>' +
|
2884
|
+
'<span class="cropper-point point-sw" data-action="sw"></span>' +
|
2885
|
+
'<span class="cropper-point point-se" data-action="se"></span>' +
|
2886
|
+
'</div>' +
|
2887
|
+
'</div>'
|
2888
|
+
);
|
2889
|
+
|
2890
|
+
// Save the other cropper
|
2891
|
+
Cropper.other = $.fn.cropper;
|
2892
|
+
|
2893
|
+
// Register as jQuery plugin
|
2894
|
+
$.fn.cropper = function (options) {
|
2895
|
+
var args = toArray(arguments, 1);
|
2896
|
+
var result;
|
2897
|
+
|
2898
|
+
this.each(function () {
|
2899
|
+
var $this = $(this);
|
2900
|
+
var data = $this.data(NAMESPACE);
|
2901
|
+
var fn;
|
2902
|
+
|
2903
|
+
if (!data) {
|
2904
|
+
if (/destroy/.test(options)) {
|
2905
|
+
return;
|
2906
|
+
}
|
2907
|
+
|
2908
|
+
$this.data(NAMESPACE, (data = new Cropper(this, options)));
|
2909
|
+
}
|
2910
|
+
|
2911
|
+
if (typeof options === 'string' && $.isFunction(fn = data[options])) {
|
2912
|
+
result = fn.apply(data, args);
|
2913
|
+
}
|
2914
|
+
});
|
2915
|
+
|
2916
|
+
return isUndefined(result) ? this : result;
|
2917
|
+
};
|
2918
|
+
|
2919
|
+
$.fn.cropper.Constructor = Cropper;
|
2920
|
+
$.fn.cropper.setDefaults = Cropper.setDefaults;
|
2921
|
+
|
2922
|
+
// No conflict
|
2923
|
+
$.fn.cropper.noConflict = function () {
|
2924
|
+
$.fn.cropper = Cropper.other;
|
2925
|
+
return this;
|
2926
|
+
};
|
2927
|
+
|
2928
|
+
});
|