croppie_rails 1.2.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,1148 +1,1273 @@
1
- /*************************
2
- * Croppie
3
- * Copyright 2016
4
- * Foliotek
5
- * Version: 2.1.1
6
- *************************/
7
- (function (root, factory) {
8
- if (typeof define === 'function' && define.amd) {
9
- // AMD. Register as an anonymous module.
10
- define(['exports'], factory);
11
- } else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') {
12
- // CommonJS
13
- factory(exports);
14
- } else {
15
- // Browser globals
16
- factory((root.commonJsStrict = {}));
17
- }
18
- }(this, function (exports) {
19
-
20
- if (typeof Promise !== 'function') {
21
- /*! promise-polyfill 3.1.0 */
22
- !function(a){function b(a,b){return function(){a.apply(b,arguments)}}function c(a){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof a)throw new TypeError("not a function");this._state=null,this._value=null,this._deferreds=[],i(a,b(e,this),b(f,this))}function d(a){var b=this;return null===this._state?void this._deferreds.push(a):void k(function(){var c=b._state?a.onFulfilled:a.onRejected;if(null===c)return void(b._state?a.resolve:a.reject)(b._value);var d;try{d=c(b._value)}catch(e){return void a.reject(e)}a.resolve(d)})}function e(a){try{if(a===this)throw new TypeError("A promise cannot be resolved with itself.");if(a&&("object"==typeof a||"function"==typeof a)){var c=a.then;if("function"==typeof c)return void i(b(c,a),b(e,this),b(f,this))}this._state=!0,this._value=a,g.call(this)}catch(d){f.call(this,d)}}function f(a){this._state=!1,this._value=a,g.call(this)}function g(){for(var a=0,b=this._deferreds.length;b>a;a++)d.call(this,this._deferreds[a]);this._deferreds=null}function h(a,b,c,d){this.onFulfilled="function"==typeof a?a:null,this.onRejected="function"==typeof b?b:null,this.resolve=c,this.reject=d}function i(a,b,c){var d=!1;try{a(function(a){d||(d=!0,b(a))},function(a){d||(d=!0,c(a))})}catch(e){if(d)return;d=!0,c(e)}}var j=setTimeout,k="function"==typeof setImmediate&&setImmediate||function(a){j(a,1)},l=Array.isArray||function(a){return"[object Array]"===Object.prototype.toString.call(a)};c.prototype["catch"]=function(a){return this.then(null,a)},c.prototype.then=function(a,b){var e=this;return new c(function(c,f){d.call(e,new h(a,b,c,f))})},c.all=function(){var a=Array.prototype.slice.call(1===arguments.length&&l(arguments[0])?arguments[0]:arguments);return new c(function(b,c){function d(f,g){try{if(g&&("object"==typeof g||"function"==typeof g)){var h=g.then;if("function"==typeof h)return void h.call(g,function(a){d(f,a)},c)}a[f]=g,0===--e&&b(a)}catch(i){c(i)}}if(0===a.length)return b([]);for(var e=a.length,f=0;f<a.length;f++)d(f,a[f])})},c.resolve=function(a){return a&&"object"==typeof a&&a.constructor===c?a:new c(function(b){b(a)})},c.reject=function(a){return new c(function(b,c){c(a)})},c.race=function(a){return new c(function(b,c){for(var d=0,e=a.length;e>d;d++)a[d].then(b,c)})},c._setImmediateFn=function(a){k=a},"undefined"!=typeof module&&module.exports?module.exports=c:a.Promise||(a.Promise=c)}(this);
23
- }
24
-
25
- var cssPrefixes = ['Webkit', 'Moz', 'ms'],
26
- emptyStyles = document.createElement('div').style,
27
- CSS_TRANS_ORG,
28
- CSS_TRANSFORM,
29
- CSS_USERSELECT;
30
-
31
- function vendorPrefix(prop) {
32
- if (prop in emptyStyles) {
33
- return prop;
34
- }
35
-
36
- var capProp = prop[0].toUpperCase() + prop.slice(1),
37
- i = cssPrefixes.length;
38
-
39
- while (i--) {
40
- prop = cssPrefixes[i] + capProp;
41
- if (prop in emptyStyles) {
42
- return prop;
43
- }
44
- }
45
- }
46
-
47
- CSS_TRANSFORM = vendorPrefix('transform');
48
- CSS_TRANS_ORG = vendorPrefix('transformOrigin');
49
- CSS_USERSELECT = vendorPrefix('userSelect');
50
-
51
- // Credits to : Andrew Dupont - http://andrewdupont.net/2009/08/28/deep-extending-objects-in-javascript/
52
- function deepExtend(destination, source) {
53
- destination = destination || {};
54
- for (var property in source) {
55
- if (source[property] && source[property].constructor && source[property].constructor === Object) {
56
- destination[property] = destination[property] || {};
57
- arguments.callee(destination[property], source[property]);
58
- } else {
59
- destination[property] = source[property];
60
- }
61
- }
62
- return destination;
63
- }
64
-
65
- function debounce(func, wait, immediate) {
66
- var timeout;
67
- return function () {
68
- var context = this, args = arguments;
69
- var later = function () {
70
- timeout = null;
71
- if (!immediate) func.apply(context, args);
72
- };
73
- var callNow = immediate && !timeout;
74
- clearTimeout(timeout);
75
- timeout = setTimeout(later, wait);
76
- if (callNow) func.apply(context, args);
77
- };
78
- }
79
-
80
- function dispatchChange(element) {
81
- if ("createEvent" in document) {
82
- var evt = document.createEvent("HTMLEvents");
83
- evt.initEvent("change", false, true);
84
- element.dispatchEvent(evt);
85
- }
86
- else {
87
- element.fireEvent("onchange");
88
- }
89
- }
90
-
91
- //http://jsperf.com/vanilla-css
92
- function css(el, styles, val) {
93
- if (typeof (styles) === 'string') {
94
- var tmp = styles;
95
- styles = {};
96
- styles[tmp] = val;
97
- }
98
-
99
- for (var prop in styles) {
100
- el.style[prop] = styles[prop];
101
- }
102
- }
103
-
104
- function addClass(el, c) {
105
- if (el.classList) {
106
- el.classList.add(c);
107
- }
108
- else {
109
- el.className += ' ' + c;
110
- }
111
- }
112
-
113
- function removeClass(el, c) {
114
- if (el.classList) {
115
- el.classList.remove(c);
116
- }
117
- else {
118
- el.className = el.className.replace(c, '');
119
- }
120
- }
121
-
122
- /* Utilities */
123
- function loadImage(src, imageEl) {
124
- var img = imageEl || new Image(),
125
- prom;
126
-
127
- if (img.src === src) {
128
- // If image source hasn't changed, return a promise that resolves immediately
129
- prom = new Promise(function (resolve, reject) {
130
- resolve(img);
131
- });
132
- } else {
133
- prom = new Promise(function (resolve, reject) {
134
- if (src.substring(0,4).toLowerCase() === 'http') {
135
- img.setAttribute('crossOrigin', 'anonymous');
136
- }
137
- img.onload = function () {
138
- setTimeout(function () {
139
- resolve(img);
140
- }, 1);
141
- };
142
- });
143
-
144
- img.src = src;
145
- }
146
-
147
- img.style.opacity = 0;
148
-
149
- return prom;
150
- }
151
-
152
- /* CSS Transform Prototype */
153
- var _TRANSLATE = 'translate3d',
154
- _TRANSLATE_SUFFIX = ', 0px';
155
- var Transform = function (x, y, scale) {
156
- this.x = parseFloat(x);
157
- this.y = parseFloat(y);
158
- this.scale = parseFloat(scale);
159
- };
160
-
161
- Transform.parse = function (v) {
162
- if (v.style) {
163
- return Transform.parse(v.style[CSS_TRANSFORM]);
164
- }
165
- else if (v.indexOf('matrix') > -1 || v.indexOf('none') > -1) {
166
- return Transform.fromMatrix(v);
167
- }
168
- else {
169
- return Transform.fromString(v);
170
- }
171
- };
172
-
173
- Transform.fromMatrix = function (v) {
174
- var vals = v.substring(7).split(',');
175
- if (!vals.length || v === 'none') {
176
- vals = [1, 0, 0, 1, 0, 0];
177
- }
178
-
179
- return new Transform(parseInt(vals[4], 10), parseInt(vals[5], 10), parseFloat(vals[0]));
180
- };
181
-
182
- Transform.fromString = function (v) {
183
- var values = v.split(') '),
184
- translate = values[0].substring(_TRANSLATE.length + 1).split(','),
185
- scale = values.length > 1 ? values[1].substring(6) : 1,
186
- x = translate.length > 1 ? translate[0] : 0,
187
- y = translate.length > 1 ? translate[1] : 0;
188
-
189
- return new Transform(x, y, scale);
190
- };
191
-
192
- Transform.prototype.toString = function () {
193
- return _TRANSLATE + '(' + this.x + 'px, ' + this.y + 'px' + _TRANSLATE_SUFFIX + ') scale(' + this.scale + ')';
194
- };
195
-
196
- var TransformOrigin = function (el) {
197
- if (!el || !el.style[CSS_TRANS_ORG]) {
198
- this.x = 0;
199
- this.y = 0;
200
- return;
201
- }
202
- var css = el.style[CSS_TRANS_ORG].split(' ');
203
- this.x = parseFloat(css[0]);
204
- this.y = parseFloat(css[1]);
205
- };
206
-
207
- TransformOrigin.prototype.toString = function () {
208
- return this.x + 'px ' + this.y + 'px';
209
- };
210
-
211
- function getExifOrientation (img, cb) {
212
- if (!window.EXIF) {
213
- cb(0);
214
- }
215
-
216
- EXIF.getData(img, function () {
217
- var orientation = EXIF.getTag(this, 'Orientation');
218
- cb(orientation);
219
- });
220
- }
221
-
222
- function drawCanvas(canvas, img, orientation) {
223
- var width = img.width,
224
- height = img.height,
225
- ctx = canvas.getContext('2d');
226
-
227
- canvas.width = img.width;
228
- canvas.height = img.height;
229
-
230
- ctx.save();
231
- switch (orientation) {
232
- case 2:
233
- ctx.translate(width, 0);
234
- ctx.scale(-1, 1);
235
- break;
236
-
237
- case 3:
238
- ctx.translate(width, height);
239
- ctx.rotate(180*Math.PI/180);
240
- break;
241
-
242
- case 4:
243
- ctx.translate(0, height);
244
- ctx.scale(1, -1);
245
- break;
246
-
247
- case 5:
248
- canvas.width = height;
249
- canvas.height = width;
250
- ctx.rotate(90*Math.PI/180);
251
- ctx.scale(1, -1);
252
- break;
253
-
254
- case 6:
255
- canvas.width = height;
256
- canvas.height = width;
257
- ctx.rotate(90*Math.PI/180);
258
- ctx.translate(0, -height);
259
- break;
260
-
261
- case 7:
262
- canvas.width = height;
263
- canvas.height = width;
264
- ctx.rotate(-90*Math.PI/180);
265
- ctx.translate(-width, height);
266
- ctx.scale(1, -1);
267
- break;
268
-
269
- case 8:
270
- canvas.width = height;
271
- canvas.height = width;
272
- ctx.translate(0, width);
273
- ctx.rotate(-90*Math.PI/180);
274
- break;
275
- }
276
- ctx.drawImage(img, 0,0, width, height);
277
- ctx.restore();
278
- }
279
-
280
- /* Private Methods */
281
- function _create() {
282
- var self = this,
283
- contClass = 'croppie-container',
284
- customViewportClass = self.options.viewport.type ? 'cr-vp-' + self.options.viewport.type : null,
285
- boundary, img, viewport, overlay, canvas;
286
-
287
- self.options.useCanvas = self.options.enableOrientation || _hasExif.call(self);
288
- // Properties on class
289
- self.data = {};
290
- self.elements = {};
291
-
292
- // Generating Markup
293
- boundary = self.elements.boundary = document.createElement('div');
294
- viewport = self.elements.viewport = document.createElement('div');
295
- img = self.elements.img = document.createElement('img');
296
- overlay = self.elements.overlay = document.createElement('div');
297
-
298
- if (self.options.useCanvas) {
299
- self.elements.canvas = document.createElement('canvas');
300
- self.elements.preview = self.elements.canvas;
301
- }
302
- else {
303
- self.elements.preview = self.elements.img;
304
- }
305
-
306
- addClass(boundary, 'cr-boundary');
307
- css(boundary, {
308
- width: self.options.boundary.width + 'px',
309
- height: self.options.boundary.height + 'px'
310
- });
311
-
312
- addClass(viewport, 'cr-viewport');
313
- if (customViewportClass) {
314
- addClass(viewport, customViewportClass);
315
- }
316
- css(viewport, {
317
- width: self.options.viewport.width + 'px',
318
- height: self.options.viewport.height + 'px'
319
- });
320
-
321
- addClass(self.elements.preview, 'cr-image');
322
- addClass(overlay, 'cr-overlay');
323
-
324
- self.element.appendChild(boundary);
325
- boundary.appendChild(self.elements.preview);
326
- boundary.appendChild(viewport);
327
- boundary.appendChild(overlay);
328
-
329
- addClass(self.element, contClass);
330
- if (self.options.customClass) {
331
- addClass(self.element, self.options.customClass);
332
- }
333
-
334
- // Initialize drag & zoom
335
- _initDraggable.call(this);
336
-
337
- if (self.options.enableZoom) {
338
- _initializeZoom.call(self);
339
- }
340
-
341
- // if (self.options.enableOrientation) {
342
- // _initRotationControls.call(self);
343
- // }
344
- }
345
-
346
- function _initRotationControls () {
347
- // TODO - Not a fan of these controls
348
- return;
349
- var self = this,
350
- wrap, btnLeft, btnRight, iLeft, iRight;
351
-
352
- wrap = document.createElement('div');
353
- self.elements.orientationBtnLeft = btnLeft = document.createElement('button');
354
- self.elements.orientationBtnRight = btnRight = document.createElement('button');
355
-
356
- wrap.appendChild(btnLeft);
357
- wrap.appendChild(btnRight);
358
-
359
- iLeft = document.createElement('i');
360
- iRight = document.createElement('i');
361
- btnLeft.appendChild(iLeft);
362
- btnRight.appendChild(iRight);
363
-
364
- addClass(wrap, 'cr-rotate-controls');
365
- addClass(btnLeft, 'cr-rotate-l');
366
- addClass(btnRight, 'cr-rotate-r');
367
-
368
- self.elements.boundary.appendChild(wrap);
369
-
370
- btnLeft.addEventListener('click', function () {
371
- self.rotate(-90);
372
- });
373
- btnRight.addEventListener('click', function () {
374
- self.rotate(90);
375
- });
376
- }
377
-
378
- function _hasExif() {
379
- // todo - remove options.exif after deprecation
380
- return (this.options.enableExif || this.options.exif) && window.EXIF;
381
- }
382
-
383
- function _setZoomerVal(v) {
384
- if (this.options.enableZoom) {
385
- this.elements.zoomer.value = fix(v, 4);
386
- }
387
- }
388
-
389
- function _initializeZoom() {
390
- var self = this,
391
- wrap = self.elements.zoomerWrap = document.createElement('div'),
392
- zoomer = self.elements.zoomer = document.createElement('input');
393
-
394
- addClass(wrap, 'cr-slider-wrap');
395
- addClass(zoomer, 'cr-slider');
396
- zoomer.type = 'range';
397
- zoomer.step = '0.01';
398
- zoomer.value = 1;
399
- zoomer.style.display = self.options.showZoomer ? '' : 'none';
400
-
401
- self.element.appendChild(wrap);
402
- wrap.appendChild(zoomer);
403
-
404
- self._currentZoom = 1;
405
-
406
- function change() {
407
- _onZoom.call(self, {
408
- value: parseFloat(zoomer.value),
409
- origin: new TransformOrigin(self.elements.preview),
410
- viewportRect: self.elements.viewport.getBoundingClientRect(),
411
- transform: Transform.parse(self.elements.preview)
412
- });
413
- }
414
-
415
- function scroll(ev) {
416
- var delta, targetZoom;
417
-
418
- if (ev.wheelDelta) {
419
- delta = ev.wheelDelta / 1200; //wheelDelta min: -120 max: 120 // max x 10 x 2
420
- } else if (ev.deltaY) {
421
- delta = ev.deltaY / 1060; //deltaY min: -53 max: 53 // max x 10 x 2
422
- } else if (ev.detail) {
423
- delta = ev.detail / -60; //delta min: -3 max: 3 // max x 10 x 2
424
- } else {
425
- delta = 0;
426
- }
427
-
428
- targetZoom = self._currentZoom + delta;
429
-
430
- ev.preventDefault();
431
- _setZoomerVal.call(self, targetZoom);
432
- change();
433
- }
434
-
435
- self.elements.zoomer.addEventListener('input', change);// this is being fired twice on keypress
436
- self.elements.zoomer.addEventListener('change', change);
437
-
438
- if (self.options.mouseWheelZoom) {
439
- self.elements.boundary.addEventListener('mousewheel', scroll);
440
- self.elements.boundary.addEventListener('DOMMouseScroll', scroll);
441
- }
442
- }
443
-
444
- function _onZoom(ui) {
445
- var self = this,
446
- transform = ui ? ui.transform : Transform.parse(self.elements.preview),
447
- vpRect = ui ? ui.viewportRect : self.elements.viewport.getBoundingClientRect(),
448
- origin = ui ? ui.origin : new TransformOrigin(self.elements.preview);
449
-
450
- self._currentZoom = ui ? ui.value : self._currentZoom;
451
- transform.scale = self._currentZoom;
452
-
453
- if (self.options.enforceBoundary) {
454
- var boundaries = _getVirtualBoundaries.call(self, vpRect),
455
- transBoundaries = boundaries.translate,
456
- oBoundaries = boundaries.origin;
457
-
458
- if (transform.x >= transBoundaries.maxX) {
459
- origin.x = oBoundaries.minX;
460
- transform.x = transBoundaries.maxX;
461
- }
462
-
463
- if (transform.x <= transBoundaries.minX) {
464
- origin.x = oBoundaries.maxX;
465
- transform.x = transBoundaries.minX;
466
- }
467
-
468
- if (transform.y >= transBoundaries.maxY) {
469
- origin.y = oBoundaries.minY;
470
- transform.y = transBoundaries.maxY;
471
- }
472
-
473
- if (transform.y <= transBoundaries.minY) {
474
- origin.y = oBoundaries.maxY;
475
- transform.y = transBoundaries.minY;
476
- }
477
- }
478
-
479
- var transCss = {};
480
- transCss[CSS_TRANSFORM] = transform.toString();
481
- transCss[CSS_TRANS_ORG] = origin.toString();
482
- css(self.elements.preview, transCss);
483
-
484
- _debouncedOverlay.call(self);
485
- _triggerUpdate.call(self);
486
- }
487
-
488
- function _getVirtualBoundaries(viewport) {
489
- var self = this,
490
- scale = self._currentZoom,
491
- vpWidth = viewport.width,
492
- vpHeight = viewport.height,
493
- centerFromBoundaryX = self.options.boundary.width / 2,
494
- centerFromBoundaryY = self.options.boundary.height / 2,
495
- imgRect = self.elements.preview.getBoundingClientRect(),
496
- curImgWidth = imgRect.width,
497
- curImgHeight = imgRect.height,
498
- halfWidth = vpWidth / 2,
499
- halfHeight = vpHeight / 2;
500
-
501
-
502
- var maxX = ((halfWidth / scale) - centerFromBoundaryX) * -1;
503
- var minX = maxX - ((curImgWidth * (1 / scale)) - (vpWidth * (1 / scale)));
504
-
505
- var maxY = ((halfHeight / scale) - centerFromBoundaryY) * -1;
506
- var minY = maxY - ((curImgHeight * (1 / scale)) - (vpHeight * (1 / scale)));
507
-
508
- var originMinX = (1 / scale) * halfWidth;
509
- var originMaxX = (curImgWidth * (1 / scale)) - originMinX;
510
-
511
- var originMinY = (1 / scale) * halfHeight;
512
- var originMaxY = (curImgHeight * (1 / scale)) - originMinY;
513
-
514
- return {
515
- translate: {
516
- maxX: maxX,
517
- minX: minX,
518
- maxY: maxY,
519
- minY: minY
520
- },
521
- origin: {
522
- maxX: originMaxX,
523
- minX: originMinX,
524
- maxY: originMaxY,
525
- minY: originMinY
526
- }
527
- };
528
- }
529
-
530
- function _updateCenterPoint() {
531
- var self = this,
532
- scale = self._currentZoom,
533
- data = self.elements.preview.getBoundingClientRect(),
534
- vpData = self.elements.viewport.getBoundingClientRect(),
535
- transform = Transform.parse(self.elements.preview.style[CSS_TRANSFORM]),
536
- pc = new TransformOrigin(self.elements.preview),
537
- top = (vpData.top - data.top) + (vpData.height / 2),
538
- left = (vpData.left - data.left) + (vpData.width / 2),
539
- center = {},
540
- adj = {};
541
-
542
- center.y = top / scale;
543
- center.x = left / scale;
544
-
545
- adj.y = (center.y - pc.y) * (1 - scale);
546
- adj.x = (center.x - pc.x) * (1 - scale);
547
-
548
- transform.x -= adj.x;
549
- transform.y -= adj.y;
550
-
551
- var newCss = {};
552
- newCss[CSS_TRANS_ORG] = center.x + 'px ' + center.y + 'px';
553
- newCss[CSS_TRANSFORM] = transform.toString();
554
- css(self.elements.preview, newCss);
555
- }
556
-
557
- function _initDraggable() {
558
- var self = this,
559
- isDragging = false,
560
- originalX,
561
- originalY,
562
- originalDistance,
563
- vpRect;
564
-
565
- function mouseDown(ev) {
566
- ev.preventDefault();
567
- if (isDragging) return;
568
- isDragging = true;
569
- originalX = ev.pageX;
570
- originalY = ev.pageY;
571
-
572
- if (ev.touches) {
573
- var touches = ev.touches[0];
574
- originalX = touches.pageX;
575
- originalY = touches.pageY;
576
- }
577
-
578
- transform = Transform.parse(self.elements.preview);
579
- window.addEventListener('mousemove', mouseMove);
580
- window.addEventListener('touchmove', mouseMove);
581
- window.addEventListener('mouseup', mouseUp);
582
- window.addEventListener('touchend', mouseUp);
583
- document.body.style[CSS_USERSELECT] = 'none';
584
- vpRect = self.elements.viewport.getBoundingClientRect();
585
- }
586
-
587
- function mouseMove(ev) {
588
- ev.preventDefault();
589
- var pageX = ev.pageX,
590
- pageY = ev.pageY;
591
-
592
- if (ev.touches) {
593
- var touches = ev.touches[0];
594
- pageX = touches.pageX;
595
- pageY = touches.pageY;
596
- }
597
-
598
- var deltaX = pageX - originalX,
599
- deltaY = pageY - originalY,
600
- imgRect = self.elements.preview.getBoundingClientRect(),
601
- top = transform.y + deltaY,
602
- left = transform.x + deltaX,
603
- newCss = {};
604
-
605
- if (ev.type == 'touchmove') {
606
- if (ev.touches.length > 1) {
607
- var touch1 = ev.touches[0];
608
- var touch2 = ev.touches[1];
609
- var dist = Math.sqrt((touch1.pageX - touch2.pageX) * (touch1.pageX - touch2.pageX) + (touch1.pageY - touch2.pageY) * (touch1.pageY - touch2.pageY));
610
-
611
- if (!originalDistance) {
612
- originalDistance = dist / self._currentZoom;
613
- }
614
-
615
- var scale = dist / originalDistance;
616
-
617
- _setZoomerVal.call(self, scale);
618
- dispatchChange(self.elements.zoomer);
619
- return;
620
- }
621
- }
622
-
623
- if (self.options.enforceBoundary) {
624
- if (vpRect.top > imgRect.top + deltaY && vpRect.bottom < imgRect.bottom + deltaY) {
625
- transform.y = top;
626
- }
627
-
628
- if (vpRect.left > imgRect.left + deltaX && vpRect.right < imgRect.right + deltaX) {
629
- transform.x = left;
630
- }
631
- }
632
- else {
633
- transform.y = top;
634
- transform.x = left;
635
- }
636
-
637
- newCss[CSS_TRANSFORM] = transform.toString();
638
- css(self.elements.preview, newCss);
639
- _updateOverlay.call(self);
640
- originalY = pageY;
641
- originalX = pageX;
642
- }
643
-
644
- function mouseUp() {
645
- isDragging = false;
646
- window.removeEventListener('mousemove', mouseMove);
647
- window.removeEventListener('touchmove', mouseMove);
648
- window.removeEventListener('mouseup', mouseUp);
649
- window.removeEventListener('touchend', mouseUp);
650
- document.body.style[CSS_USERSELECT] = '';
651
- _updateCenterPoint.call(self);
652
- _triggerUpdate.call(self);
653
- originalDistance = 0;
654
- }
655
-
656
- self.elements.overlay.addEventListener('mousedown', mouseDown);
657
- self.elements.overlay.addEventListener('touchstart', mouseDown);
658
- }
659
-
660
- function _updateOverlay() {
661
- var self = this,
662
- boundRect = self.elements.boundary.getBoundingClientRect(),
663
- imgData = self.elements.preview.getBoundingClientRect();
664
-
665
- css(self.elements.overlay, {
666
- width: imgData.width + 'px',
667
- height: imgData.height + 'px',
668
- top: (imgData.top - boundRect.top) + 'px',
669
- left: (imgData.left - boundRect.left) + 'px'
670
- });
671
- }
672
- var _debouncedOverlay = debounce(_updateOverlay, 500);
673
-
674
- function _triggerUpdate() {
675
- var self = this;
676
- if (_isVisible.call(self)) {
677
- self.options.update.call(self, self.get());
678
- }
679
- }
680
-
681
- function _isVisible() {
682
- return this.elements.preview.offsetHeight > 0 && this.elements.preview.offsetWidth > 0;
683
- }
684
-
685
- function _updatePropertiesFromImage() {
686
- var self = this,
687
- minZoom = 0,
688
- maxZoom = 1.5,
689
- initialZoom = 1,
690
- cssReset = {},
691
- img = self.elements.preview,
692
- zoomer = self.elements.zoomer,
693
- transformReset = new Transform(0, 0, initialZoom),
694
- originReset = new TransformOrigin(),
695
- isVisible = _isVisible.call(self),
696
- imgData,
697
- vpData,
698
- boundaryData,
699
- minW,
700
- minH;
701
-
702
- if (!isVisible || self.data.bound) {
703
- // if the croppie isn't visible or it doesn't need binding
704
- return;
705
- }
706
-
707
- self.data.bound = true;
708
- cssReset[CSS_TRANSFORM] = transformReset.toString();
709
- cssReset[CSS_TRANS_ORG] = originReset.toString();
710
- cssReset['opacity'] = 1;
711
- css(img, cssReset);
712
-
713
- imgData = img.getBoundingClientRect();
714
- vpData = self.elements.viewport.getBoundingClientRect();
715
- boundaryData = self.elements.boundary.getBoundingClientRect();
716
- self._originalImageWidth = imgData.width;
717
- self._originalImageHeight = imgData.height;
718
-
719
- if (self.options.enableZoom) {
720
- if (self.options.enforceBoundary) {
721
- minW = vpData.width / imgData.width;
722
- minH = vpData.height / imgData.height;
723
- minZoom = Math.max(minW, minH);
724
- }
725
-
726
- if (minZoom >= maxZoom) {
727
- maxZoom = minZoom + 1;
728
- }
729
-
730
- zoomer.min = fix(minZoom, 4);
731
- zoomer.max = fix(maxZoom, 4);
732
- initialZoom = Math.max((boundaryData.width / imgData.width), (boundaryData.height / imgData.height));
733
- _setZoomerVal.call(self, initialZoom);
734
- dispatchChange(zoomer);
735
- }
736
-
737
- self._currentZoom = transformReset.scale = initialZoom;
738
- cssReset[CSS_TRANSFORM] = transformReset.toString();
739
- css(img, cssReset);
740
-
741
- if (self.data.points.length) {
742
- _bindPoints.call(self, self.data.points);
743
- }
744
- else {
745
- _centerImage.call(self);
746
- }
747
-
748
- _updateCenterPoint.call(self);
749
- _updateOverlay.call(self);
750
- }
751
-
752
- function _bindPoints(points) {
753
- if (points.length != 4) {
754
- throw "Croppie - Invalid number of points supplied: " + points;
755
- }
756
- var self = this,
757
- pointsWidth = points[2] - points[0],
758
- // pointsHeight = points[3] - points[1],
759
- vpData = self.elements.viewport.getBoundingClientRect(),
760
- boundRect = self.elements.boundary.getBoundingClientRect(),
761
- vpOffset = {
762
- left: vpData.left - boundRect.left,
763
- top: vpData.top - boundRect.top
764
- },
765
- scale = vpData.width / pointsWidth,
766
- originTop = points[1],
767
- originLeft = points[0],
768
- transformTop = (-1 * points[1]) + vpOffset.top,
769
- transformLeft = (-1 * points[0]) + vpOffset.left,
770
- newCss = {};
771
-
772
- newCss[CSS_TRANS_ORG] = originLeft + 'px ' + originTop + 'px';
773
- newCss[CSS_TRANSFORM] = new Transform(transformLeft, transformTop, scale).toString();
774
- css(self.elements.preview, newCss);
775
-
776
- _setZoomerVal.call(self, scale);
777
- self._currentZoom = scale;
778
- }
779
-
780
- function _centerImage() {
781
- var self = this,
782
- imgDim = self.elements.preview.getBoundingClientRect(),
783
- vpDim = self.elements.viewport.getBoundingClientRect(),
784
- boundDim = self.elements.boundary.getBoundingClientRect(),
785
- vpLeft = vpDim.left - boundDim.left,
786
- vpTop = vpDim.top - boundDim.top,
787
- w = vpLeft - ((imgDim.width - vpDim.width) / 2),
788
- h = vpTop - ((imgDim.height - vpDim.height) / 2),
789
- transform = new Transform(w, h, self._currentZoom);
790
-
791
- css(self.elements.preview, CSS_TRANSFORM, transform.toString());
792
- }
793
-
794
- function _transferImageToCanvas(customOrientation) {
795
- var self = this,
796
- canvas = self.elements.canvas,
797
- img = self.elements.img,
798
- ctx = canvas.getContext('2d'),
799
- exif = _hasExif.call(self),
800
- customOrientation = self.options.enableOrientation && customOrientation;
801
-
802
- ctx.clearRect(0, 0, canvas.width, canvas.height);
803
- canvas.width = img.width;
804
- canvas.height = img.height;
805
-
806
- if (exif) {
807
- getExifOrientation(img, function (orientation) {
808
- drawCanvas(canvas, img, parseInt(orientation));
809
- if (customOrientation) {
810
- drawCanvas(canvas, img, customOrientation);
811
- }
812
- });
813
- } else if (customOrientation) {
814
- drawCanvas(canvas, img, customOrientation);
815
- }
816
- }
817
-
818
- function _getHtmlResult(data) {
819
- var points = data.points,
820
- div = document.createElement('div'),
821
- img = document.createElement('img'),
822
- width = points[2] - points[0],
823
- height = points[3] - points[1];
824
-
825
- addClass(div, 'croppie-result');
826
- div.appendChild(img);
827
- css(img, {
828
- left: (-1 * points[0]) + 'px',
829
- top: (-1 * points[1]) + 'px'
830
- });
831
- img.src = data.url;
832
- css(div, {
833
- width: width + 'px',
834
- height: height + 'px'
835
- });
836
-
837
- return div;
838
- }
839
-
840
- function _getCanvasResult(img, data) {
841
- var points = data.points,
842
- left = points[0],
843
- top = points[1],
844
- width = (points[2] - points[0]),
845
- height = (points[3] - points[1]),
846
- circle = data.circle,
847
- canvas = document.createElement('canvas'),
848
- ctx = canvas.getContext('2d'),
849
- outWidth = width,
850
- outHeight = height;
851
-
852
- if (data.outputWidth && data.outputHeight) {
853
- outWidth = data.outputWidth;
854
- outHeight = data.outputHeight;
855
- }
856
-
857
- canvas.width = outWidth;
858
- canvas.height = outHeight;
859
-
860
- ctx.drawImage(img, left, top, width, height, 0, 0, outWidth, outHeight);
861
- if (circle) {
862
- ctx.fillStyle = '#fff';
863
- ctx.globalCompositeOperation = 'destination-in';
864
- ctx.beginPath();
865
- ctx.arc(outWidth / 2, outHeight / 2, outWidth / 2, 0, Math.PI * 2, true);
866
- ctx.closePath();
867
- ctx.fill();
868
- }
869
- return canvas.toDataURL(data.format, data.quality);
870
- }
871
-
872
- function _bind(options, cb) {
873
- var self = this,
874
- url,
875
- points = [];
876
-
877
- if (typeof (options) === 'string') {
878
- url = options;
879
- options = {};
880
- }
881
- else if (Array.isArray(options)) {
882
- points = options.slice();
883
- }
884
- else if (typeof (options) == 'undefined' && self.data.url) { //refreshing
885
- _updatePropertiesFromImage.call(self);
886
- _triggerUpdate.call(self);
887
- return null;
888
- }
889
- else {
890
- url = options.url;
891
- points = options.points || [];
892
- }
893
-
894
- self.data.bound = false;
895
- self.data.url = url || self.data.url;
896
- self.data.points = (points || self.data.points).map(function (p) {
897
- return parseFloat(p);
898
- });
899
- var prom = loadImage(url, self.elements.img);
900
- prom.then(function () {
901
- if (self.options.useCanvas) {
902
- self.elements.img.exifdata = null;
903
- _transferImageToCanvas.call(self, options.orientation || 1);
904
- }
905
- _updatePropertiesFromImage.call(self);
906
- _triggerUpdate.call(self);
907
- if (cb) {
908
- cb();
909
- }
910
- });
911
- return prom;
912
- }
913
-
914
- function fix(v, decimalPoints) {
915
- return parseFloat(v).toFixed(decimalPoints || 0);
916
- }
917
-
918
- function _get() {
919
- var self = this,
920
- imgData = self.elements.preview.getBoundingClientRect(),
921
- vpData = self.elements.viewport.getBoundingClientRect(),
922
- x1 = vpData.left - imgData.left,
923
- y1 = vpData.top - imgData.top,
924
- x2 = x1 + vpData.width,
925
- y2 = y1 + vpData.height,
926
- scale = self._currentZoom;
927
-
928
- if (scale === Infinity || isNaN(scale)) {
929
- scale = 1;
930
- }
931
-
932
- var max = self.options.enforceBoundary ? 0 : Number.NEGATIVE_INFINITY;
933
- x1 = Math.max(max, x1 / scale);
934
- y1 = Math.max(max, y1 / scale);
935
- x2 = Math.max(max, x2 / scale);
936
- y2 = Math.max(max, y2 / scale);
937
-
938
- return {
939
- points: [fix(x1), fix(y1), fix(x2), fix(y2)],
940
- zoom: scale
941
- };
942
- }
943
-
944
- var RESULT_DEFAULTS = {
945
- type: 'canvas',
946
- format: 'png',
947
- quality: 1
948
- },
949
- RESULT_FORMATS = ['jpeg', 'webp', 'png'];
950
-
951
- function _result(options) {
952
- var self = this,
953
- data = _get.call(self),
954
- opts = deepExtend(RESULT_DEFAULTS, deepExtend({}, options)),
955
- type = (typeof (options) === 'string' ? options : (opts.type || 'viewport')),
956
- size = opts.size,
957
- format = opts.format,
958
- quality = opts.quality,
959
- vpRect = self.elements.viewport.getBoundingClientRect(),
960
- ratio = vpRect.width / vpRect.height,
961
- prom;
962
-
963
- if (size === 'viewport') {
964
- data.outputWidth = vpRect.width;
965
- data.outputHeight = vpRect.height;
966
- } else if (typeof size === 'object') {
967
- if (size.width && size.height) {
968
- data.outputWidth = size.width;
969
- data.outputHeight = size.height;
970
- } else if (size.width) {
971
- data.outputWidth = size.width;
972
- data.outputHeight = size.width / ratio;
973
- } else if (size.height) {
974
- data.outputWidth = size.height * ratio;
975
- data.outputHeight = size.height;
976
- }
977
- }
978
-
979
- if (RESULT_FORMATS.indexOf(format) > -1) {
980
- data.format = 'image/' + format;
981
- data.quality = quality;
982
- }
983
-
984
- data.circle = self.options.viewport.type === 'circle';
985
- data.url = self.data.url;
986
-
987
- prom = new Promise(function (resolve, reject) {
988
- if (type === 'canvas') {
989
- resolve(_getCanvasResult.call(self, self.elements.preview, data));
990
- }
991
- else {
992
- resolve(_getHtmlResult.call(self, data));
993
- }
994
- });
995
- return prom;
996
- }
997
-
998
- function _refresh() {
999
- _updatePropertiesFromImage.call(this);
1000
- }
1001
-
1002
- function _rotate(deg) {
1003
- if (!this.options.useCanvas) {
1004
- throw 'Croppie: Cannot rotate without enableOrientation';
1005
- }
1006
-
1007
- var self = this,
1008
- canvas = self.elements.canvas,
1009
- img = self.elements.img,
1010
- copy = document.createElement('canvas'),
1011
- ornt = 1;
1012
-
1013
- copy.width = canvas.width;
1014
- copy.height = canvas.height;
1015
- var ctx = copy.getContext('2d');
1016
- ctx.drawImage(canvas, 0, 0);
1017
-
1018
- if (deg === 90 || deg === -270) ornt = 6;
1019
- if (deg === -90 || deg === 270) ornt = 8;
1020
- if (deg === 180 || deg === -180) ornt = 3;
1021
-
1022
- drawCanvas(canvas, copy, ornt);
1023
- _onZoom.call(self);
1024
- }
1025
-
1026
- function _destroy() {
1027
- var self = this;
1028
- self.element.removeChild(self.elements.boundary);
1029
- removeClass(self.element, 'croppie-container');
1030
- if (self.options.enableZoom) {
1031
- self.element.removeChild(self.elements.zoomerWrap);
1032
- }
1033
- delete self.elements;
1034
- }
1035
-
1036
- if (window.jQuery) {
1037
- var $ = window.jQuery;
1038
- $.fn.croppie = function (opts) {
1039
- var ot = typeof opts;
1040
-
1041
- if (ot === 'string') {
1042
- var args = Array.prototype.slice.call(arguments, 1);
1043
- var singleInst = $(this).data('croppie');
1044
-
1045
- if (opts === 'get') {
1046
- return singleInst.get();
1047
- }
1048
- else if (opts === 'result') {
1049
- return singleInst.result.apply(singleInst, args);
1050
- }
1051
-
1052
- return this.each(function () {
1053
- var i = $(this).data('croppie');
1054
- if (!i) return;
1055
-
1056
- var method = i[opts];
1057
- if ($.isFunction(method)) {
1058
- method.apply(i, args);
1059
- if (opts === 'destroy') {
1060
- $(this).removeData('croppie');
1061
- }
1062
- }
1063
- else {
1064
- throw 'Croppie ' + opts + ' method not found';
1065
- }
1066
- });
1067
- }
1068
- else {
1069
- return this.each(function () {
1070
- var i = new Croppie(this, opts);
1071
- $(this).data('croppie', i);
1072
- });
1073
- }
1074
- };
1075
- }
1076
-
1077
- function Croppie(element, opts) {
1078
- this.element = element;
1079
- this.options = deepExtend(deepExtend({}, Croppie.defaults), opts);
1080
-
1081
- _create.call(this);
1082
- if (this.options.url) {
1083
- var bindOpts = {
1084
- url: this.options.url,
1085
- points: this.options.points
1086
- };
1087
- delete this.options['url'];
1088
- delete this.options['points'];
1089
- _bind.call(this, bindOpts);
1090
- }
1091
- }
1092
-
1093
- Croppie.defaults = {
1094
- viewport: {
1095
- width: 100,
1096
- height: 100,
1097
- type: 'square'
1098
- },
1099
- boundary: {
1100
- width: 300,
1101
- height: 300
1102
- },
1103
- orientationControls: {
1104
- enabled: true,
1105
- leftClass: '',
1106
- rightClass: ''
1107
- },
1108
- customClass: '',
1109
- showZoomer: true,
1110
- enableZoom: true,
1111
- mouseWheelZoom: true,
1112
- enableExif: false,
1113
- enforceBoundary: true,
1114
- enableOrientation: false,
1115
- update: function () { }
1116
- };
1117
-
1118
- deepExtend(Croppie.prototype, {
1119
- bind: function (options, cb) {
1120
- return _bind.call(this, options, cb);
1121
- },
1122
- get: function () {
1123
- return _get.call(this);
1124
- },
1125
- result: function (type) {
1126
- return _result.call(this, type);
1127
- },
1128
- refresh: function () {
1129
- return _refresh.call(this);
1130
- },
1131
- setZoom: function (v) {
1132
- _setZoomerVal.call(this, v);
1133
- dispatchChange(this.elements.zoomer);
1134
- },
1135
- rotate: function (deg) {
1136
- _rotate.call(this, deg);
1137
- },
1138
- destroy: function () {
1139
- return _destroy.call(this);
1140
- }
1141
- });
1142
-
1143
- exports.Croppie = window.Croppie = Croppie;
1144
-
1145
- if (typeof module === 'object' && !!module.exports) {
1146
- module.exports = Croppie;
1147
- }
1148
- }));
1
+ /*************************
2
+ * Croppie
3
+ * Copyright 2016
4
+ * Foliotek
5
+ * Version: 2.3.0
6
+ *************************/
7
+ (function (root, factory) {
8
+ if (typeof define === 'function' && define.amd) {
9
+ // AMD. Register as an anonymous module.
10
+ define(['exports'], factory);
11
+ } else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') {
12
+ // CommonJS
13
+ factory(exports);
14
+ } else {
15
+ // Browser globals
16
+ factory((root.commonJsStrict = {}));
17
+ }
18
+ }(this, function (exports) {
19
+
20
+ /* Polyfills */
21
+ if (typeof Promise !== 'function') {
22
+ /*! promise-polyfill 3.1.0 */
23
+ !function(a){function b(a,b){return function(){a.apply(b,arguments)}}function c(a){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof a)throw new TypeError("not a function");this._state=null,this._value=null,this._deferreds=[],i(a,b(e,this),b(f,this))}function d(a){var b=this;return null===this._state?void this._deferreds.push(a):void k(function(){var c=b._state?a.onFulfilled:a.onRejected;if(null===c)return void(b._state?a.resolve:a.reject)(b._value);var d;try{d=c(b._value)}catch(e){return void a.reject(e)}a.resolve(d)})}function e(a){try{if(a===this)throw new TypeError("A promise cannot be resolved with itself.");if(a&&("object"==typeof a||"function"==typeof a)){var c=a.then;if("function"==typeof c)return void i(b(c,a),b(e,this),b(f,this))}this._state=!0,this._value=a,g.call(this)}catch(d){f.call(this,d)}}function f(a){this._state=!1,this._value=a,g.call(this)}function g(){for(var a=0,b=this._deferreds.length;b>a;a++)d.call(this,this._deferreds[a]);this._deferreds=null}function h(a,b,c,d){this.onFulfilled="function"==typeof a?a:null,this.onRejected="function"==typeof b?b:null,this.resolve=c,this.reject=d}function i(a,b,c){var d=!1;try{a(function(a){d||(d=!0,b(a))},function(a){d||(d=!0,c(a))})}catch(e){if(d)return;d=!0,c(e)}}var j=setTimeout,k="function"==typeof setImmediate&&setImmediate||function(a){j(a,1)},l=Array.isArray||function(a){return"[object Array]"===Object.prototype.toString.call(a)};c.prototype["catch"]=function(a){return this.then(null,a)},c.prototype.then=function(a,b){var e=this;return new c(function(c,f){d.call(e,new h(a,b,c,f))})},c.all=function(){var a=Array.prototype.slice.call(1===arguments.length&&l(arguments[0])?arguments[0]:arguments);return new c(function(b,c){function d(f,g){try{if(g&&("object"==typeof g||"function"==typeof g)){var h=g.then;if("function"==typeof h)return void h.call(g,function(a){d(f,a)},c)}a[f]=g,0===--e&&b(a)}catch(i){c(i)}}if(0===a.length)return b([]);for(var e=a.length,f=0;f<a.length;f++)d(f,a[f])})},c.resolve=function(a){return a&&"object"==typeof a&&a.constructor===c?a:new c(function(b){b(a)})},c.reject=function(a){return new c(function(b,c){c(a)})},c.race=function(a){return new c(function(b,c){for(var d=0,e=a.length;e>d;d++)a[d].then(b,c)})},c._setImmediateFn=function(a){k=a},"undefined"!=typeof module&&module.exports?module.exports=c:a.Promise||(a.Promise=c)}(this);
24
+ }
25
+
26
+ if ( typeof window.CustomEvent !== "function" ) {
27
+ (function(){
28
+ function CustomEvent ( event, params ) {
29
+ params = params || { bubbles: false, cancelable: false, detail: undefined };
30
+ var evt = document.createEvent( 'CustomEvent' );
31
+ evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail );
32
+ return evt;
33
+ }
34
+ CustomEvent.prototype = window.Event.prototype;
35
+ window.CustomEvent = CustomEvent;
36
+ }());
37
+ }
38
+ /* End Polyfills */
39
+
40
+ var cssPrefixes = ['Webkit', 'Moz', 'ms'],
41
+ emptyStyles = document.createElement('div').style,
42
+ CSS_TRANS_ORG,
43
+ CSS_TRANSFORM,
44
+ CSS_USERSELECT;
45
+
46
+ function vendorPrefix(prop) {
47
+ if (prop in emptyStyles) {
48
+ return prop;
49
+ }
50
+
51
+ var capProp = prop[0].toUpperCase() + prop.slice(1),
52
+ i = cssPrefixes.length;
53
+
54
+ while (i--) {
55
+ prop = cssPrefixes[i] + capProp;
56
+ if (prop in emptyStyles) {
57
+ return prop;
58
+ }
59
+ }
60
+ }
61
+
62
+ CSS_TRANSFORM = vendorPrefix('transform');
63
+ CSS_TRANS_ORG = vendorPrefix('transformOrigin');
64
+ CSS_USERSELECT = vendorPrefix('userSelect');
65
+
66
+ // Credits to : Andrew Dupont - http://andrewdupont.net/2009/08/28/deep-extending-objects-in-javascript/
67
+ function deepExtend(destination, source) {
68
+ destination = destination || {};
69
+ for (var property in source) {
70
+ if (source[property] && source[property].constructor && source[property].constructor === Object) {
71
+ destination[property] = destination[property] || {};
72
+ deepExtend(destination[property], source[property]);
73
+ } else {
74
+ destination[property] = source[property];
75
+ }
76
+ }
77
+ return destination;
78
+ }
79
+
80
+ function debounce(func, wait, immediate) {
81
+ var timeout;
82
+ return function () {
83
+ var context = this, args = arguments;
84
+ var later = function () {
85
+ timeout = null;
86
+ if (!immediate) func.apply(context, args);
87
+ };
88
+ var callNow = immediate && !timeout;
89
+ clearTimeout(timeout);
90
+ timeout = setTimeout(later, wait);
91
+ if (callNow) func.apply(context, args);
92
+ };
93
+ }
94
+
95
+ function dispatchChange(element) {
96
+ if ("createEvent" in document) {
97
+ var evt = document.createEvent("HTMLEvents");
98
+ evt.initEvent("change", false, true);
99
+ element.dispatchEvent(evt);
100
+ }
101
+ else {
102
+ element.fireEvent("onchange");
103
+ }
104
+ }
105
+
106
+ //http://jsperf.com/vanilla-css
107
+ function css(el, styles, val) {
108
+ if (typeof (styles) === 'string') {
109
+ var tmp = styles;
110
+ styles = {};
111
+ styles[tmp] = val;
112
+ }
113
+
114
+ for (var prop in styles) {
115
+ el.style[prop] = styles[prop];
116
+ }
117
+ }
118
+
119
+ function addClass(el, c) {
120
+ if (el.classList) {
121
+ el.classList.add(c);
122
+ }
123
+ else {
124
+ el.className += ' ' + c;
125
+ }
126
+ }
127
+
128
+ function removeClass(el, c) {
129
+ if (el.classList) {
130
+ el.classList.remove(c);
131
+ }
132
+ else {
133
+ el.className = el.className.replace(c, '');
134
+ }
135
+ }
136
+
137
+ /* Utilities */
138
+ function loadImage(src, imageEl) {
139
+ var img = imageEl || new Image(),
140
+ prom;
141
+
142
+ if (img.src === src) {
143
+ // If image source hasn't changed, return a promise that resolves immediately
144
+ prom = new Promise(function (resolve, reject) {
145
+ resolve(img);
146
+ });
147
+ } else {
148
+ prom = new Promise(function (resolve, reject) {
149
+ if (src.substring(0,4).toLowerCase() === 'http') {
150
+ img.setAttribute('crossOrigin', 'anonymous');
151
+ }
152
+ img.onload = function () {
153
+ setTimeout(function () {
154
+ resolve(img);
155
+ }, 1);
156
+ };
157
+ });
158
+
159
+ img.src = src;
160
+ }
161
+
162
+ img.style.opacity = 0;
163
+
164
+ return prom;
165
+ }
166
+
167
+ /* CSS Transform Prototype */
168
+ var _TRANSLATE = 'translate3d',
169
+ _TRANSLATE_SUFFIX = ', 0px';
170
+ var Transform = function (x, y, scale) {
171
+ this.x = parseFloat(x);
172
+ this.y = parseFloat(y);
173
+ this.scale = parseFloat(scale);
174
+ };
175
+
176
+ Transform.parse = function (v) {
177
+ if (v.style) {
178
+ return Transform.parse(v.style[CSS_TRANSFORM]);
179
+ }
180
+ else if (v.indexOf('matrix') > -1 || v.indexOf('none') > -1) {
181
+ return Transform.fromMatrix(v);
182
+ }
183
+ else {
184
+ return Transform.fromString(v);
185
+ }
186
+ };
187
+
188
+ Transform.fromMatrix = function (v) {
189
+ var vals = v.substring(7).split(',');
190
+ if (!vals.length || v === 'none') {
191
+ vals = [1, 0, 0, 1, 0, 0];
192
+ }
193
+
194
+ return new Transform(parseInt(vals[4], 10), parseInt(vals[5], 10), parseFloat(vals[0]));
195
+ };
196
+
197
+ Transform.fromString = function (v) {
198
+ var values = v.split(') '),
199
+ translate = values[0].substring(_TRANSLATE.length + 1).split(','),
200
+ scale = values.length > 1 ? values[1].substring(6) : 1,
201
+ x = translate.length > 1 ? translate[0] : 0,
202
+ y = translate.length > 1 ? translate[1] : 0;
203
+
204
+ return new Transform(x, y, scale);
205
+ };
206
+
207
+ Transform.prototype.toString = function () {
208
+ return _TRANSLATE + '(' + this.x + 'px, ' + this.y + 'px' + _TRANSLATE_SUFFIX + ') scale(' + this.scale + ')';
209
+ };
210
+
211
+ var TransformOrigin = function (el) {
212
+ if (!el || !el.style[CSS_TRANS_ORG]) {
213
+ this.x = 0;
214
+ this.y = 0;
215
+ return;
216
+ }
217
+ var css = el.style[CSS_TRANS_ORG].split(' ');
218
+ this.x = parseFloat(css[0]);
219
+ this.y = parseFloat(css[1]);
220
+ };
221
+
222
+ TransformOrigin.prototype.toString = function () {
223
+ return this.x + 'px ' + this.y + 'px';
224
+ };
225
+
226
+ function getExifOrientation (img, cb) {
227
+ if (!window.EXIF) {
228
+ cb(0);
229
+ }
230
+
231
+ EXIF.getData(img, function () {
232
+ var orientation = EXIF.getTag(this, 'Orientation');
233
+ cb(orientation);
234
+ });
235
+ }
236
+
237
+ function drawCanvas(canvas, img, orientation) {
238
+ var width = img.width,
239
+ height = img.height,
240
+ ctx = canvas.getContext('2d');
241
+
242
+ canvas.width = img.width;
243
+ canvas.height = img.height;
244
+
245
+ ctx.save();
246
+ switch (orientation) {
247
+ case 2:
248
+ ctx.translate(width, 0);
249
+ ctx.scale(-1, 1);
250
+ break;
251
+
252
+ case 3:
253
+ ctx.translate(width, height);
254
+ ctx.rotate(180*Math.PI/180);
255
+ break;
256
+
257
+ case 4:
258
+ ctx.translate(0, height);
259
+ ctx.scale(1, -1);
260
+ break;
261
+
262
+ case 5:
263
+ canvas.width = height;
264
+ canvas.height = width;
265
+ ctx.rotate(90*Math.PI/180);
266
+ ctx.scale(1, -1);
267
+ break;
268
+
269
+ case 6:
270
+ canvas.width = height;
271
+ canvas.height = width;
272
+ ctx.rotate(90*Math.PI/180);
273
+ ctx.translate(0, -height);
274
+ break;
275
+
276
+ case 7:
277
+ canvas.width = height;
278
+ canvas.height = width;
279
+ ctx.rotate(-90*Math.PI/180);
280
+ ctx.translate(-width, height);
281
+ ctx.scale(1, -1);
282
+ break;
283
+
284
+ case 8:
285
+ canvas.width = height;
286
+ canvas.height = width;
287
+ ctx.translate(0, width);
288
+ ctx.rotate(-90*Math.PI/180);
289
+ break;
290
+ }
291
+ ctx.drawImage(img, 0,0, width, height);
292
+ ctx.restore();
293
+ }
294
+
295
+ /* Private Methods */
296
+ function _create() {
297
+ var self = this,
298
+ contClass = 'croppie-container',
299
+ customViewportClass = self.options.viewport.type ? 'cr-vp-' + self.options.viewport.type : null,
300
+ boundary, img, viewport, overlay, canvas;
301
+
302
+ self.options.useCanvas = self.options.enableOrientation || _hasExif.call(self);
303
+ // Properties on class
304
+ self.data = {};
305
+ self.elements = {};
306
+
307
+ // Generating Markup
308
+ boundary = self.elements.boundary = document.createElement('div');
309
+ viewport = self.elements.viewport = document.createElement('div');
310
+ img = self.elements.img = document.createElement('img');
311
+ overlay = self.elements.overlay = document.createElement('div');
312
+
313
+ if (self.options.useCanvas) {
314
+ self.elements.canvas = document.createElement('canvas');
315
+ self.elements.preview = self.elements.canvas;
316
+ }
317
+ else {
318
+ self.elements.preview = self.elements.img;
319
+ }
320
+
321
+ addClass(boundary, 'cr-boundary');
322
+ css(boundary, {
323
+ width: self.options.boundary.width + 'px',
324
+ height: self.options.boundary.height + 'px'
325
+ });
326
+
327
+ addClass(viewport, 'cr-viewport');
328
+ if (customViewportClass) {
329
+ addClass(viewport, customViewportClass);
330
+ }
331
+ css(viewport, {
332
+ width: self.options.viewport.width + 'px',
333
+ height: self.options.viewport.height + 'px'
334
+ });
335
+ viewport.setAttribute('tabindex', 0);
336
+
337
+ addClass(self.elements.preview, 'cr-image');
338
+ addClass(overlay, 'cr-overlay');
339
+
340
+ self.element.appendChild(boundary);
341
+ boundary.appendChild(self.elements.preview);
342
+ boundary.appendChild(viewport);
343
+ boundary.appendChild(overlay);
344
+
345
+ addClass(self.element, contClass);
346
+ if (self.options.customClass) {
347
+ addClass(self.element, self.options.customClass);
348
+ }
349
+
350
+ // Initialize drag & zoom
351
+ _initDraggable.call(this);
352
+
353
+ if (self.options.enableZoom) {
354
+ _initializeZoom.call(self);
355
+ }
356
+
357
+ // if (self.options.enableOrientation) {
358
+ // _initRotationControls.call(self);
359
+ // }
360
+ }
361
+
362
+ function _initRotationControls () {
363
+ // TODO - Not a fan of these controls
364
+ return;
365
+ var self = this,
366
+ wrap, btnLeft, btnRight, iLeft, iRight;
367
+
368
+ wrap = document.createElement('div');
369
+ self.elements.orientationBtnLeft = btnLeft = document.createElement('button');
370
+ self.elements.orientationBtnRight = btnRight = document.createElement('button');
371
+
372
+ wrap.appendChild(btnLeft);
373
+ wrap.appendChild(btnRight);
374
+
375
+ iLeft = document.createElement('i');
376
+ iRight = document.createElement('i');
377
+ btnLeft.appendChild(iLeft);
378
+ btnRight.appendChild(iRight);
379
+
380
+ addClass(wrap, 'cr-rotate-controls');
381
+ addClass(btnLeft, 'cr-rotate-l');
382
+ addClass(btnRight, 'cr-rotate-r');
383
+
384
+ self.elements.boundary.appendChild(wrap);
385
+
386
+ btnLeft.addEventListener('click', function () {
387
+ self.rotate(-90);
388
+ });
389
+ btnRight.addEventListener('click', function () {
390
+ self.rotate(90);
391
+ });
392
+ }
393
+
394
+ function _hasExif() {
395
+ // todo - remove options.exif after deprecation
396
+ return this.options.enableExif && window.EXIF;
397
+ }
398
+
399
+ function _setZoomerVal(v) {
400
+ if (this.options.enableZoom) {
401
+ var z = this.elements.zoomer,
402
+ val = fix(v, 4);
403
+
404
+ z.value = Math.max(z.min, Math.min(z.max, val));
405
+ }
406
+ }
407
+
408
+ function _initializeZoom() {
409
+ var self = this,
410
+ wrap = self.elements.zoomerWrap = document.createElement('div'),
411
+ zoomer = self.elements.zoomer = document.createElement('input');
412
+
413
+ addClass(wrap, 'cr-slider-wrap');
414
+ addClass(zoomer, 'cr-slider');
415
+ zoomer.type = 'range';
416
+ zoomer.step = '0.0001';
417
+ zoomer.value = 1;
418
+ zoomer.style.display = self.options.showZoomer ? '' : 'none';
419
+
420
+ self.element.appendChild(wrap);
421
+ wrap.appendChild(zoomer);
422
+
423
+ self._currentZoom = 1;
424
+
425
+ function change() {
426
+ _onZoom.call(self, {
427
+ value: parseFloat(zoomer.value),
428
+ origin: new TransformOrigin(self.elements.preview),
429
+ viewportRect: self.elements.viewport.getBoundingClientRect(),
430
+ transform: Transform.parse(self.elements.preview)
431
+ });
432
+ }
433
+
434
+ function scroll(ev) {
435
+ var delta, targetZoom;
436
+
437
+ if (ev.wheelDelta) {
438
+ delta = ev.wheelDelta / 1200; //wheelDelta min: -120 max: 120 // max x 10 x 2
439
+ } else if (ev.deltaY) {
440
+ delta = ev.deltaY / 1060; //deltaY min: -53 max: 53 // max x 10 x 2
441
+ } else if (ev.detail) {
442
+ delta = ev.detail / -60; //delta min: -3 max: 3 // max x 10 x 2
443
+ } else {
444
+ delta = 0;
445
+ }
446
+
447
+ targetZoom = self._currentZoom + delta;
448
+
449
+ ev.preventDefault();
450
+ _setZoomerVal.call(self, targetZoom);
451
+ change.call(self);
452
+ }
453
+
454
+ self.elements.zoomer.addEventListener('input', change);// this is being fired twice on keypress
455
+ self.elements.zoomer.addEventListener('change', change);
456
+
457
+ if (self.options.mouseWheelZoom) {
458
+ self.elements.boundary.addEventListener('mousewheel', scroll);
459
+ self.elements.boundary.addEventListener('DOMMouseScroll', scroll);
460
+ }
461
+ }
462
+
463
+ function _onZoom(ui) {
464
+ var self = this,
465
+ transform = ui ? ui.transform : Transform.parse(self.elements.preview),
466
+ vpRect = ui ? ui.viewportRect : self.elements.viewport.getBoundingClientRect(),
467
+ origin = ui ? ui.origin : new TransformOrigin(self.elements.preview),
468
+ transCss = {};
469
+
470
+ function applyCss() {
471
+ var transCss = {};
472
+ transCss[CSS_TRANSFORM] = transform.toString();
473
+ transCss[CSS_TRANS_ORG] = origin.toString();
474
+ css(self.elements.preview, transCss);
475
+ }
476
+
477
+ self._currentZoom = ui ? ui.value : self._currentZoom;
478
+ transform.scale = self._currentZoom;
479
+ applyCss();
480
+
481
+
482
+ if (self.options.enforceBoundary) {
483
+ var boundaries = _getVirtualBoundaries.call(self, vpRect),
484
+ transBoundaries = boundaries.translate,
485
+ oBoundaries = boundaries.origin;
486
+
487
+ if (transform.x >= transBoundaries.maxX) {
488
+ origin.x = oBoundaries.minX;
489
+ transform.x = transBoundaries.maxX;
490
+ }
491
+
492
+ if (transform.x <= transBoundaries.minX) {
493
+ origin.x = oBoundaries.maxX;
494
+ transform.x = transBoundaries.minX;
495
+ }
496
+
497
+ if (transform.y >= transBoundaries.maxY) {
498
+ origin.y = oBoundaries.minY;
499
+ transform.y = transBoundaries.maxY;
500
+ }
501
+
502
+ if (transform.y <= transBoundaries.minY) {
503
+ origin.y = oBoundaries.maxY;
504
+ transform.y = transBoundaries.minY;
505
+ }
506
+ }
507
+ applyCss();
508
+ _debouncedOverlay.call(self);
509
+ _triggerUpdate.call(self);
510
+ }
511
+
512
+ function _getVirtualBoundaries(viewport) {
513
+ var self = this,
514
+ scale = self._currentZoom,
515
+ vpWidth = viewport.width,
516
+ vpHeight = viewport.height,
517
+ centerFromBoundaryX = self.options.boundary.width / 2,
518
+ centerFromBoundaryY = self.options.boundary.height / 2,
519
+ imgRect = self.elements.preview.getBoundingClientRect(),
520
+ curImgWidth = imgRect.width,
521
+ curImgHeight = imgRect.height,
522
+ halfWidth = vpWidth / 2,
523
+ halfHeight = vpHeight / 2;
524
+
525
+ var maxX = ((halfWidth / scale) - centerFromBoundaryX) * -1;
526
+ var minX = maxX - ((curImgWidth * (1 / scale)) - (vpWidth * (1 / scale)));
527
+
528
+ var maxY = ((halfHeight / scale) - centerFromBoundaryY) * -1;
529
+ var minY = maxY - ((curImgHeight * (1 / scale)) - (vpHeight * (1 / scale)));
530
+
531
+ var originMinX = (1 / scale) * halfWidth;
532
+ var originMaxX = (curImgWidth * (1 / scale)) - originMinX;
533
+
534
+ var originMinY = (1 / scale) * halfHeight;
535
+ var originMaxY = (curImgHeight * (1 / scale)) - originMinY;
536
+
537
+ return {
538
+ translate: {
539
+ maxX: maxX,
540
+ minX: minX,
541
+ maxY: maxY,
542
+ minY: minY
543
+ },
544
+ origin: {
545
+ maxX: originMaxX,
546
+ minX: originMinX,
547
+ maxY: originMaxY,
548
+ minY: originMinY
549
+ }
550
+ };
551
+ }
552
+
553
+ function _updateCenterPoint() {
554
+ var self = this,
555
+ scale = self._currentZoom,
556
+ data = self.elements.preview.getBoundingClientRect(),
557
+ vpData = self.elements.viewport.getBoundingClientRect(),
558
+ transform = Transform.parse(self.elements.preview.style[CSS_TRANSFORM]),
559
+ pc = new TransformOrigin(self.elements.preview),
560
+ top = (vpData.top - data.top) + (vpData.height / 2),
561
+ left = (vpData.left - data.left) + (vpData.width / 2),
562
+ center = {},
563
+ adj = {};
564
+
565
+ center.y = top / scale;
566
+ center.x = left / scale;
567
+
568
+ adj.y = (center.y - pc.y) * (1 - scale);
569
+ adj.x = (center.x - pc.x) * (1 - scale);
570
+
571
+ transform.x -= adj.x;
572
+ transform.y -= adj.y;
573
+
574
+ var newCss = {};
575
+ newCss[CSS_TRANS_ORG] = center.x + 'px ' + center.y + 'px';
576
+ newCss[CSS_TRANSFORM] = transform.toString();
577
+ css(self.elements.preview, newCss);
578
+ }
579
+
580
+ function _initDraggable() {
581
+ var self = this,
582
+ isDragging = false,
583
+ originalX,
584
+ originalY,
585
+ originalDistance,
586
+ vpRect,
587
+ transform;
588
+
589
+ function assignTransformCoordinates(deltaX, deltaY) {
590
+ var imgRect = self.elements.preview.getBoundingClientRect(),
591
+ top = transform.y + deltaY,
592
+ left = transform.x + deltaX;
593
+
594
+ if (self.options.enforceBoundary) {
595
+ if (vpRect.top > imgRect.top + deltaY && vpRect.bottom < imgRect.bottom + deltaY) {
596
+ transform.y = top;
597
+ }
598
+
599
+ if (vpRect.left > imgRect.left + deltaX && vpRect.right < imgRect.right + deltaX) {
600
+ transform.x = left;
601
+ }
602
+ }
603
+ else {
604
+ transform.y = top;
605
+ transform.x = left;
606
+ }
607
+ }
608
+
609
+ function keyDown(ev) {
610
+ var LEFT_ARROW = 37,
611
+ UP_ARROW = 38,
612
+ RIGHT_ARROW = 39,
613
+ DOWN_ARROW = 40;
614
+
615
+ if (ev.shiftKey && (ev.keyCode == UP_ARROW || ev.keyCode == DOWN_ARROW)) {
616
+ var zoom = 0.0;
617
+ if (ev.keyCode == UP_ARROW) {
618
+ zoom = parseFloat(self.elements.zoomer.value, 10) + parseFloat(self.elements.zoomer.step, 10)
619
+ }
620
+ else {
621
+ zoom = parseFloat(self.elements.zoomer.value, 10) - parseFloat(self.elements.zoomer.step, 10)
622
+ }
623
+ self.setZoom(zoom);
624
+ }
625
+ else if (ev.keyCode >= 37 && ev.keyCode <= 40) {
626
+ ev.preventDefault();
627
+ var movement = parseKeyDown(ev.keyCode);
628
+
629
+ transform = Transform.parse(self.elements.preview);
630
+ document.body.style[CSS_USERSELECT] = 'none';
631
+ vpRect = self.elements.viewport.getBoundingClientRect();
632
+ keyMove(movement);
633
+ };
634
+
635
+ function parseKeyDown(key) {
636
+ switch (key) {
637
+ case LEFT_ARROW:
638
+ return [1, 0];
639
+ case UP_ARROW:
640
+ return [0, 1];
641
+ case RIGHT_ARROW:
642
+ return [-1, 0];
643
+ case DOWN_ARROW:
644
+ return [0, -1];
645
+ };
646
+ };
647
+ }
648
+
649
+ function keyMove(movement) {
650
+ var deltaX = movement[0],
651
+ deltaY = movement[1],
652
+ newCss = {};
653
+
654
+ assignTransformCoordinates(deltaX, deltaY);
655
+
656
+ newCss[CSS_TRANSFORM] = transform.toString();
657
+ css(self.elements.preview, newCss);
658
+ _updateOverlay.call(self);
659
+ document.body.style[CSS_USERSELECT] = '';
660
+ _updateCenterPoint.call(self);
661
+ _triggerUpdate.call(self);
662
+ originalDistance = 0;
663
+ }
664
+
665
+ function mouseDown(ev) {
666
+ ev.preventDefault();
667
+ if (isDragging) return;
668
+ isDragging = true;
669
+ originalX = ev.pageX;
670
+ originalY = ev.pageY;
671
+
672
+ if (ev.touches) {
673
+ var touches = ev.touches[0];
674
+ originalX = touches.pageX;
675
+ originalY = touches.pageY;
676
+ }
677
+
678
+ transform = Transform.parse(self.elements.preview);
679
+ window.addEventListener('mousemove', mouseMove);
680
+ window.addEventListener('touchmove', mouseMove);
681
+ window.addEventListener('mouseup', mouseUp);
682
+ window.addEventListener('touchend', mouseUp);
683
+ document.body.style[CSS_USERSELECT] = 'none';
684
+ vpRect = self.elements.viewport.getBoundingClientRect();
685
+ }
686
+
687
+ function mouseMove(ev) {
688
+ ev.preventDefault();
689
+ var pageX = ev.pageX,
690
+ pageY = ev.pageY;
691
+
692
+ if (ev.touches) {
693
+ var touches = ev.touches[0];
694
+ pageX = touches.pageX;
695
+ pageY = touches.pageY;
696
+ }
697
+
698
+ var deltaX = pageX - originalX,
699
+ deltaY = pageY - originalY,
700
+ newCss = {};
701
+
702
+ if (ev.type == 'touchmove') {
703
+ if (ev.touches.length > 1) {
704
+ var touch1 = ev.touches[0];
705
+ var touch2 = ev.touches[1];
706
+ var dist = Math.sqrt((touch1.pageX - touch2.pageX) * (touch1.pageX - touch2.pageX) + (touch1.pageY - touch2.pageY) * (touch1.pageY - touch2.pageY));
707
+
708
+ if (!originalDistance) {
709
+ originalDistance = dist / self._currentZoom;
710
+ }
711
+
712
+ var scale = dist / originalDistance;
713
+
714
+ _setZoomerVal.call(self, scale);
715
+ dispatchChange(self.elements.zoomer);
716
+ return;
717
+ }
718
+ }
719
+
720
+ assignTransformCoordinates(deltaX, deltaY);
721
+
722
+ newCss[CSS_TRANSFORM] = transform.toString();
723
+ css(self.elements.preview, newCss);
724
+ _updateOverlay.call(self);
725
+ originalY = pageY;
726
+ originalX = pageX;
727
+ }
728
+
729
+ function mouseUp() {
730
+ isDragging = false;
731
+ window.removeEventListener('mousemove', mouseMove);
732
+ window.removeEventListener('touchmove', mouseMove);
733
+ window.removeEventListener('mouseup', mouseUp);
734
+ window.removeEventListener('touchend', mouseUp);
735
+ document.body.style[CSS_USERSELECT] = '';
736
+ _updateCenterPoint.call(self);
737
+ _triggerUpdate.call(self);
738
+ originalDistance = 0;
739
+ }
740
+
741
+ self.elements.overlay.addEventListener('mousedown', mouseDown);
742
+ self.elements.viewport.addEventListener('keydown', keyDown);
743
+ self.elements.overlay.addEventListener('touchstart', mouseDown);
744
+ }
745
+
746
+ function _updateOverlay() {
747
+ var self = this,
748
+ boundRect = self.elements.boundary.getBoundingClientRect(),
749
+ imgData = self.elements.preview.getBoundingClientRect();
750
+
751
+ css(self.elements.overlay, {
752
+ width: imgData.width + 'px',
753
+ height: imgData.height + 'px',
754
+ top: (imgData.top - boundRect.top) + 'px',
755
+ left: (imgData.left - boundRect.left) + 'px'
756
+ });
757
+ }
758
+ var _debouncedOverlay = debounce(_updateOverlay, 500);
759
+
760
+ function _triggerUpdate() {
761
+ var self = this,
762
+ data = self.get(),
763
+ ev;
764
+
765
+ if (!_isVisible.call(self)) {
766
+ return;
767
+ }
768
+
769
+ self.options.update.call(self, data);
770
+ if (self.$) {
771
+ self.$(self.element).trigger('update', data)
772
+ }
773
+ else {
774
+ var ev;
775
+ if (window.CustomEvent) {
776
+ ev = new CustomEvent('update', { detail: data });
777
+ } else {
778
+ ev = document.createEvent('CustomEvent');
779
+ ev.initCustomEvent('update', true, true, data);
780
+ }
781
+
782
+ self.element.dispatchEvent(ev);
783
+ }
784
+ }
785
+
786
+ function _isVisible() {
787
+ return this.elements.preview.offsetHeight > 0 && this.elements.preview.offsetWidth > 0;
788
+ }
789
+
790
+ function _updatePropertiesFromImage() {
791
+ var self = this,
792
+ minZoom = 0,
793
+ maxZoom = 1.5,
794
+ initialZoom = 1,
795
+ cssReset = {},
796
+ img = self.elements.preview,
797
+ zoomer = self.elements.zoomer,
798
+ transformReset = new Transform(0, 0, initialZoom),
799
+ originReset = new TransformOrigin(),
800
+ isVisible = _isVisible.call(self),
801
+ imgData,
802
+ vpData,
803
+ boundaryData,
804
+ minW,
805
+ minH;
806
+
807
+ if (!isVisible || self.data.bound) {
808
+ // if the croppie isn't visible or it doesn't need binding
809
+ return;
810
+ }
811
+
812
+ self.data.bound = true;
813
+ cssReset[CSS_TRANSFORM] = transformReset.toString();
814
+ cssReset[CSS_TRANS_ORG] = originReset.toString();
815
+ cssReset['opacity'] = 1;
816
+ css(img, cssReset);
817
+
818
+ imgData = img.getBoundingClientRect();
819
+ vpData = self.elements.viewport.getBoundingClientRect();
820
+ boundaryData = self.elements.boundary.getBoundingClientRect();
821
+ self._originalImageWidth = imgData.width;
822
+ self._originalImageHeight = imgData.height;
823
+
824
+ if (self.options.enableZoom) {
825
+ if (self.options.enforceBoundary) {
826
+ minW = vpData.width / imgData.width;
827
+ minH = vpData.height / imgData.height;
828
+ minZoom = Math.max(minW, minH);
829
+ }
830
+
831
+ if (minZoom >= maxZoom) {
832
+ maxZoom = minZoom + 1;
833
+ }
834
+
835
+ zoomer.min = fix(minZoom, 4);
836
+ zoomer.max = fix(maxZoom, 4);
837
+ var defaultInitialZoom = Math.max((boundaryData.width / imgData.width), (boundaryData.height / imgData.height));
838
+ initialZoom = self.data.boundZoom !== null ? self.data.boundZoom : defaultInitialZoom;
839
+ _setZoomerVal.call(self, initialZoom);
840
+ dispatchChange(zoomer);
841
+ }
842
+ else {
843
+ self._currentZoom = initialZoom;
844
+ }
845
+
846
+ transformReset.scale = self._currentZoom;
847
+ cssReset[CSS_TRANSFORM] = transformReset.toString();
848
+ css(img, cssReset);
849
+
850
+ if (self.data.points.length) {
851
+ _bindPoints.call(self, self.data.points);
852
+ }
853
+ else {
854
+ _centerImage.call(self);
855
+ }
856
+
857
+ _updateCenterPoint.call(self);
858
+ _updateOverlay.call(self);
859
+ }
860
+
861
+ function _bindPoints(points) {
862
+ if (points.length != 4) {
863
+ throw "Croppie - Invalid number of points supplied: " + points;
864
+ }
865
+ var self = this,
866
+ pointsWidth = points[2] - points[0],
867
+ // pointsHeight = points[3] - points[1],
868
+ vpData = self.elements.viewport.getBoundingClientRect(),
869
+ boundRect = self.elements.boundary.getBoundingClientRect(),
870
+ vpOffset = {
871
+ left: vpData.left - boundRect.left,
872
+ top: vpData.top - boundRect.top
873
+ },
874
+ scale = vpData.width / pointsWidth,
875
+ originTop = points[1],
876
+ originLeft = points[0],
877
+ transformTop = (-1 * points[1]) + vpOffset.top,
878
+ transformLeft = (-1 * points[0]) + vpOffset.left,
879
+ newCss = {};
880
+
881
+ newCss[CSS_TRANS_ORG] = originLeft + 'px ' + originTop + 'px';
882
+ newCss[CSS_TRANSFORM] = new Transform(transformLeft, transformTop, scale).toString();
883
+ css(self.elements.preview, newCss);
884
+
885
+ _setZoomerVal.call(self, scale);
886
+ self._currentZoom = scale;
887
+ }
888
+
889
+ function _centerImage() {
890
+ var self = this,
891
+ imgDim = self.elements.preview.getBoundingClientRect(),
892
+ vpDim = self.elements.viewport.getBoundingClientRect(),
893
+ boundDim = self.elements.boundary.getBoundingClientRect(),
894
+ vpLeft = vpDim.left - boundDim.left,
895
+ vpTop = vpDim.top - boundDim.top,
896
+ w = vpLeft - ((imgDim.width - vpDim.width) / 2),
897
+ h = vpTop - ((imgDim.height - vpDim.height) / 2),
898
+ transform = new Transform(w, h, self._currentZoom);
899
+
900
+ css(self.elements.preview, CSS_TRANSFORM, transform.toString());
901
+ }
902
+
903
+ function _transferImageToCanvas(customOrientation) {
904
+ var self = this,
905
+ canvas = self.elements.canvas,
906
+ img = self.elements.img,
907
+ ctx = canvas.getContext('2d'),
908
+ exif = _hasExif.call(self),
909
+ customOrientation = self.options.enableOrientation && customOrientation;
910
+
911
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
912
+ canvas.width = img.width;
913
+ canvas.height = img.height;
914
+
915
+ if (exif) {
916
+ getExifOrientation(img, function (orientation) {
917
+ drawCanvas(canvas, img, parseInt(orientation));
918
+ if (customOrientation) {
919
+ drawCanvas(canvas, img, customOrientation);
920
+ }
921
+ });
922
+ } else if (customOrientation) {
923
+ drawCanvas(canvas, img, customOrientation);
924
+ }
925
+ }
926
+
927
+ function _getHtmlResult(data) {
928
+ var points = data.points,
929
+ div = document.createElement('div'),
930
+ img = document.createElement('img'),
931
+ width = points[2] - points[0],
932
+ height = points[3] - points[1];
933
+
934
+ addClass(div, 'croppie-result');
935
+ div.appendChild(img);
936
+ css(img, {
937
+ left: (-1 * points[0]) + 'px',
938
+ top: (-1 * points[1]) + 'px'
939
+ });
940
+ img.src = data.url;
941
+ css(div, {
942
+ width: width + 'px',
943
+ height: height + 'px'
944
+ });
945
+
946
+ return div;
947
+ }
948
+
949
+ function _getCanvasResult(img, data) {
950
+ var points = data.points,
951
+ left = points[0],
952
+ top = points[1],
953
+ width = (points[2] - points[0]),
954
+ height = (points[3] - points[1]),
955
+ circle = data.circle,
956
+ canvas = document.createElement('canvas'),
957
+ ctx = canvas.getContext('2d'),
958
+ outWidth = width,
959
+ outHeight = height;
960
+
961
+ if (data.outputWidth && data.outputHeight) {
962
+ outWidth = data.outputWidth;
963
+ outHeight = data.outputHeight;
964
+ }
965
+
966
+ canvas.width = outWidth;
967
+ canvas.height = outHeight;
968
+
969
+ if (data.backgroundColor) {
970
+ ctx.fillStyle = data.backgroundColor;
971
+ ctx.fillRect(0, 0, outWidth, outHeight);
972
+ }
973
+ ctx.drawImage(img, left, top, width, height, 0, 0, outWidth, outHeight);
974
+ if (circle) {
975
+ ctx.fillStyle = '#fff';
976
+ ctx.globalCompositeOperation = 'destination-in';
977
+ ctx.beginPath();
978
+ ctx.arc(outWidth / 2, outHeight / 2, outWidth / 2, 0, Math.PI * 2, true);
979
+ ctx.closePath();
980
+ ctx.fill();
981
+ }
982
+ return canvas.toDataURL(data.format, data.quality);
983
+ }
984
+
985
+ function _bind(options, cb) {
986
+ var self = this,
987
+ url,
988
+ points = [],
989
+ zoom = null;
990
+
991
+ if (typeof (options) === 'string') {
992
+ url = options;
993
+ options = {};
994
+ }
995
+ else if (Array.isArray(options)) {
996
+ points = options.slice();
997
+ }
998
+ else if (typeof (options) == 'undefined' && self.data.url) { //refreshing
999
+ _updatePropertiesFromImage.call(self);
1000
+ _triggerUpdate.call(self);
1001
+ return null;
1002
+ }
1003
+ else {
1004
+ url = options.url;
1005
+ points = options.points || [];
1006
+ zoom = typeof(options.zoom) === 'undefined' ? null : options.zoom;
1007
+ }
1008
+
1009
+ self.data.bound = false;
1010
+ self.data.url = url || self.data.url;
1011
+ self.data.points = (points || self.data.points).map(function (p) {
1012
+ return parseFloat(p);
1013
+ });
1014
+ self.data.boundZoom = zoom;
1015
+ var prom = loadImage(url, self.elements.img);
1016
+ prom.then(function () {
1017
+ if (self.options.useCanvas) {
1018
+ self.elements.img.exifdata = null;
1019
+ _transferImageToCanvas.call(self, options.orientation || 1);
1020
+ }
1021
+ _updatePropertiesFromImage.call(self);
1022
+ _triggerUpdate.call(self);
1023
+ if (cb) {
1024
+ cb();
1025
+ }
1026
+ });
1027
+ return prom;
1028
+ }
1029
+
1030
+ function fix(v, decimalPoints) {
1031
+ return parseFloat(v).toFixed(decimalPoints || 0);
1032
+ }
1033
+
1034
+ function _get() {
1035
+ var self = this,
1036
+ imgData = self.elements.preview.getBoundingClientRect(),
1037
+ vpData = self.elements.viewport.getBoundingClientRect(),
1038
+ x1 = vpData.left - imgData.left,
1039
+ y1 = vpData.top - imgData.top,
1040
+ widthDiff = (vpData.width - self.elements.viewport.offsetWidth) / 2,
1041
+ heightDiff = (vpData.height - self.elements.viewport.offsetHeight) / 2,
1042
+ x2 = x1 + self.elements.viewport.offsetWidth + widthDiff,
1043
+ y2 = y1 + self.elements.viewport.offsetHeight + heightDiff,
1044
+ scale = self._currentZoom;
1045
+
1046
+ if (scale === Infinity || isNaN(scale)) {
1047
+ scale = 1;
1048
+ }
1049
+
1050
+ var max = self.options.enforceBoundary ? 0 : Number.NEGATIVE_INFINITY;
1051
+ x1 = Math.max(max, x1 / scale);
1052
+ y1 = Math.max(max, y1 / scale);
1053
+ x2 = Math.max(max, x2 / scale);
1054
+ y2 = Math.max(max, y2 / scale);
1055
+
1056
+ return {
1057
+ points: [fix(x1), fix(y1), fix(x2), fix(y2)],
1058
+ zoom: scale
1059
+ };
1060
+ }
1061
+
1062
+ var RESULT_DEFAULTS = {
1063
+ type: 'canvas',
1064
+ format: 'png',
1065
+ quality: 1
1066
+ },
1067
+ RESULT_FORMATS = ['jpeg', 'webp', 'png'];
1068
+
1069
+ function _result(options) {
1070
+ var self = this,
1071
+ data = _get.call(self),
1072
+ opts = deepExtend(RESULT_DEFAULTS, deepExtend({}, options)),
1073
+ type = (typeof (options) === 'string' ? options : (opts.type || 'viewport')),
1074
+ size = opts.size,
1075
+ format = opts.format,
1076
+ quality = opts.quality,
1077
+ backgroundColor = opts.backgroundColor,
1078
+ circle = typeof opts.circle === 'boolean' ? opts.circle : (self.options.viewport.type === 'circle'),
1079
+ vpRect = self.elements.viewport.getBoundingClientRect(),
1080
+ ratio = vpRect.width / vpRect.height,
1081
+ prom;
1082
+
1083
+ if (size === 'viewport') {
1084
+ data.outputWidth = vpRect.width;
1085
+ data.outputHeight = vpRect.height;
1086
+ } else if (typeof size === 'object') {
1087
+ if (size.width && size.height) {
1088
+ data.outputWidth = size.width;
1089
+ data.outputHeight = size.height;
1090
+ } else if (size.width) {
1091
+ data.outputWidth = size.width;
1092
+ data.outputHeight = size.width / ratio;
1093
+ } else if (size.height) {
1094
+ data.outputWidth = size.height * ratio;
1095
+ data.outputHeight = size.height;
1096
+ }
1097
+ }
1098
+
1099
+ if (RESULT_FORMATS.indexOf(format) > -1) {
1100
+ data.format = 'image/' + format;
1101
+ data.quality = quality;
1102
+ }
1103
+
1104
+ data.circle = circle;
1105
+ data.url = self.data.url;
1106
+ data.backgroundColor = backgroundColor;
1107
+
1108
+ prom = new Promise(function (resolve, reject) {
1109
+ if (type === 'canvas') {
1110
+ resolve(_getCanvasResult.call(self, self.elements.preview, data));
1111
+ }
1112
+ else {
1113
+ resolve(_getHtmlResult.call(self, data));
1114
+ }
1115
+ });
1116
+ return prom;
1117
+ }
1118
+
1119
+ function _refresh() {
1120
+ _updatePropertiesFromImage.call(this);
1121
+ }
1122
+
1123
+ function _rotate(deg) {
1124
+ if (!this.options.useCanvas) {
1125
+ throw 'Croppie: Cannot rotate without enableOrientation';
1126
+ }
1127
+
1128
+ var self = this,
1129
+ canvas = self.elements.canvas,
1130
+ img = self.elements.img,
1131
+ copy = document.createElement('canvas'),
1132
+ ornt = 1;
1133
+
1134
+ copy.width = canvas.width;
1135
+ copy.height = canvas.height;
1136
+ var ctx = copy.getContext('2d');
1137
+ ctx.drawImage(canvas, 0, 0);
1138
+
1139
+ if (deg === 90 || deg === -270) ornt = 6;
1140
+ if (deg === -90 || deg === 270) ornt = 8;
1141
+ if (deg === 180 || deg === -180) ornt = 3;
1142
+
1143
+ drawCanvas(canvas, copy, ornt);
1144
+ _onZoom.call(self);
1145
+ }
1146
+
1147
+ function _destroy() {
1148
+ var self = this;
1149
+ self.element.removeChild(self.elements.boundary);
1150
+ removeClass(self.element, 'croppie-container');
1151
+ if (self.options.enableZoom) {
1152
+ self.element.removeChild(self.elements.zoomerWrap);
1153
+ }
1154
+ delete self.elements;
1155
+ }
1156
+
1157
+ if (window.jQuery) {
1158
+ var $ = window.jQuery;
1159
+ $.fn.croppie = function (opts) {
1160
+ var ot = typeof opts;
1161
+
1162
+ if (ot === 'string') {
1163
+ var args = Array.prototype.slice.call(arguments, 1);
1164
+ var singleInst = $(this).data('croppie');
1165
+
1166
+ if (opts === 'get') {
1167
+ return singleInst.get();
1168
+ }
1169
+ else if (opts === 'result') {
1170
+ return singleInst.result.apply(singleInst, args);
1171
+ }
1172
+ else if (opts === 'bind') {
1173
+ return singleInst.bind.apply(singleInst, args);
1174
+ }
1175
+
1176
+ return this.each(function () {
1177
+ var i = $(this).data('croppie');
1178
+ if (!i) return;
1179
+
1180
+ var method = i[opts];
1181
+ if ($.isFunction(method)) {
1182
+ method.apply(i, args);
1183
+ if (opts === 'destroy') {
1184
+ $(this).removeData('croppie');
1185
+ }
1186
+ }
1187
+ else {
1188
+ throw 'Croppie ' + opts + ' method not found';
1189
+ }
1190
+ });
1191
+ }
1192
+ else {
1193
+ return this.each(function () {
1194
+ var i = new Croppie(this, opts);
1195
+ i.$ = $;
1196
+ $(this).data('croppie', i);
1197
+ });
1198
+ }
1199
+ };
1200
+ }
1201
+
1202
+ function Croppie(element, opts) {
1203
+ this.element = element;
1204
+ this.options = deepExtend(deepExtend({}, Croppie.defaults), opts);
1205
+
1206
+ _create.call(this);
1207
+ if (this.options.url) {
1208
+ var bindOpts = {
1209
+ url: this.options.url,
1210
+ points: this.options.points
1211
+ };
1212
+ delete this.options['url'];
1213
+ delete this.options['points'];
1214
+ _bind.call(this, bindOpts);
1215
+ }
1216
+ }
1217
+
1218
+ Croppie.defaults = {
1219
+ viewport: {
1220
+ width: 100,
1221
+ height: 100,
1222
+ type: 'square'
1223
+ },
1224
+ boundary: {
1225
+ width: 300,
1226
+ height: 300
1227
+ },
1228
+ orientationControls: {
1229
+ enabled: true,
1230
+ leftClass: '',
1231
+ rightClass: ''
1232
+ },
1233
+ customClass: '',
1234
+ showZoomer: true,
1235
+ enableZoom: true,
1236
+ mouseWheelZoom: true,
1237
+ enableExif: false,
1238
+ enforceBoundary: true,
1239
+ enableOrientation: false,
1240
+ update: function () { }
1241
+ };
1242
+
1243
+ deepExtend(Croppie.prototype, {
1244
+ bind: function (options, cb) {
1245
+ return _bind.call(this, options, cb);
1246
+ },
1247
+ get: function () {
1248
+ return _get.call(this);
1249
+ },
1250
+ result: function (type) {
1251
+ return _result.call(this, type);
1252
+ },
1253
+ refresh: function () {
1254
+ return _refresh.call(this);
1255
+ },
1256
+ setZoom: function (v) {
1257
+ _setZoomerVal.call(this, v);
1258
+ dispatchChange(this.elements.zoomer);
1259
+ },
1260
+ rotate: function (deg) {
1261
+ _rotate.call(this, deg);
1262
+ },
1263
+ destroy: function () {
1264
+ return _destroy.call(this);
1265
+ }
1266
+ });
1267
+
1268
+ exports.Croppie = window.Croppie = Croppie;
1269
+
1270
+ if (typeof module === 'object' && !!module.exports) {
1271
+ module.exports = Croppie;
1272
+ }
1273
+ }));