paper_cropper 0.0.2

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