croppie_rails 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1038 @@
1
+ /*************************
2
+ * Croppie
3
+ * Copyright 2016
4
+ * Foliotek
5
+ * Version: 2.0.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
+ prom = new Promise(function (resolve, reject) {
128
+ if (src.substring(0,4).toLowerCase() === 'http') {
129
+ img.setAttribute('crossOrigin', 'anonymous');
130
+ }
131
+ img.onload = function () {
132
+ setTimeout(function () {
133
+ resolve(img);
134
+ }, 1);
135
+ };
136
+ });
137
+
138
+ img.style.opacity = 0;
139
+ img.src = src;
140
+ return prom;
141
+ }
142
+
143
+ /* CSS Transform Prototype */
144
+ var _TRANSLATE = 'translate3d',
145
+ _TRANSLATE_SUFFIX = ', 0px';
146
+ var Transform = function (x, y, scale) {
147
+ this.x = parseFloat(x);
148
+ this.y = parseFloat(y);
149
+ this.scale = parseFloat(scale);
150
+ };
151
+
152
+ Transform.parse = function (v) {
153
+ if (v.style) {
154
+ return Transform.parse(v.style[CSS_TRANSFORM]);
155
+ }
156
+ else if (v.indexOf('matrix') > -1 || v.indexOf('none') > -1) {
157
+ return Transform.fromMatrix(v);
158
+ }
159
+ else {
160
+ return Transform.fromString(v);
161
+ }
162
+ };
163
+
164
+ Transform.fromMatrix = function (v) {
165
+ var vals = v.substring(7).split(',');
166
+ if (!vals.length || v === 'none') {
167
+ vals = [1, 0, 0, 1, 0, 0];
168
+ }
169
+
170
+ return new Transform(parseInt(vals[4], 10), parseInt(vals[5], 10), parseFloat(vals[0]));
171
+ };
172
+
173
+ Transform.fromString = function (v) {
174
+ var values = v.split(') '),
175
+ translate = values[0].substring(_TRANSLATE.length + 1).split(','),
176
+ scale = values.length > 1 ? values[1].substring(6) : 1,
177
+ x = translate.length > 1 ? translate[0] : 0,
178
+ y = translate.length > 1 ? translate[1] : 0;
179
+
180
+ return new Transform(x, y, scale);
181
+ };
182
+
183
+ Transform.prototype.toString = function () {
184
+ return _TRANSLATE + '(' + this.x + 'px, ' + this.y + 'px' + _TRANSLATE_SUFFIX + ') scale(' + this.scale + ')';
185
+ };
186
+
187
+ var TransformOrigin = function (el) {
188
+ if (!el || !el.style[CSS_TRANS_ORG]) {
189
+ this.x = 0;
190
+ this.y = 0;
191
+ return;
192
+ }
193
+ var css = el.style[CSS_TRANS_ORG].split(' ');
194
+ this.x = parseFloat(css[0]);
195
+ this.y = parseFloat(css[1]);
196
+ };
197
+
198
+ TransformOrigin.prototype.toString = function () {
199
+ return this.x + 'px ' + this.y + 'px';
200
+ };
201
+
202
+ function getExifOrientation (img, cb) {
203
+ if (!window.EXIF) {
204
+ cb(0);
205
+ }
206
+
207
+ EXIF.getData(img, function () {
208
+ var orientation = EXIF.getTag(this, 'Orientation');
209
+ cb(orientation);
210
+ });
211
+ }
212
+
213
+ function rotateCanvas(canvas, ctx, img, orientation) {
214
+ var width = img.width,
215
+ height = img.height;
216
+
217
+ canvas.width = img.width;
218
+ canvas.height = img.height;
219
+
220
+ ctx.save();
221
+ switch (orientation) {
222
+ case 2:
223
+ ctx.translate(width, 0);
224
+ ctx.scale(-1, 1);
225
+ break;
226
+
227
+ case 3:
228
+ ctx.translate(width, height);
229
+ ctx.rotate(180*Math.PI/180);
230
+ break;
231
+
232
+ case 4:
233
+ ctx.translate(0, height);
234
+ ctx.scale(1, -1);
235
+ break;
236
+
237
+ case 5:
238
+ canvas.width = height;
239
+ canvas.height = width;
240
+ ctx.rotate(90*Math.PI/180);
241
+ ctx.scale(1, -1);
242
+ break;
243
+
244
+ case 6:
245
+ canvas.width = height;
246
+ canvas.height = width;
247
+ ctx.rotate(90*Math.PI/180);
248
+ ctx.translate(0, -height);
249
+ break;
250
+
251
+ case 7:
252
+ canvas.width = height;
253
+ canvas.height = width;
254
+ ctx.rotate(-90*Math.PI/180);
255
+ ctx.translate(-width, height);
256
+ ctx.scale(1, -1);
257
+ break;
258
+
259
+ case 8:
260
+ canvas.width = height;
261
+ canvas.height = width;
262
+ ctx.translate(0, width);
263
+ ctx.rotate(-90*Math.PI/180);
264
+ break;
265
+ }
266
+ ctx.drawImage(img, 0,0, width, height);
267
+ ctx.restore();
268
+ }
269
+
270
+ /* Private Methods */
271
+ function _create() {
272
+ var self = this,
273
+ contClass = 'croppie-container',
274
+ customViewportClass = self.options.viewport.type ? 'cr-vp-' + self.options.viewport.type : null,
275
+ boundary, img, viewport, overlay, canvas;
276
+
277
+ self.options.useCanvas = self.options.exif && window.EXIF;
278
+ // Properties on class
279
+ self.data = {};
280
+ self.elements = {};
281
+
282
+ // Generating Markup
283
+ boundary = self.elements.boundary = document.createElement('div');
284
+ viewport = self.elements.viewport = document.createElement('div');
285
+ img = self.elements.img = document.createElement('img');
286
+ overlay = self.elements.overlay = document.createElement('div');
287
+
288
+ if (self.options.useCanvas) {
289
+ self.elements.canvas = document.createElement('canvas');
290
+ self.elements.preview = self.elements.canvas;
291
+ }
292
+ else {
293
+ self.elements.preview = self.elements.img;
294
+ }
295
+
296
+ addClass(boundary, 'cr-boundary');
297
+ css(boundary, {
298
+ width: self.options.boundary.width + 'px',
299
+ height: self.options.boundary.height + 'px'
300
+ });
301
+
302
+ addClass(viewport, 'cr-viewport');
303
+ if (customViewportClass) {
304
+ addClass(viewport, customViewportClass);
305
+ }
306
+ css(viewport, {
307
+ width: self.options.viewport.width + 'px',
308
+ height: self.options.viewport.height + 'px'
309
+ });
310
+
311
+ addClass(self.elements.preview, 'cr-image');
312
+ addClass(overlay, 'cr-overlay');
313
+
314
+ self.element.appendChild(boundary);
315
+ boundary.appendChild(self.elements.preview);
316
+ boundary.appendChild(viewport);
317
+ boundary.appendChild(overlay);
318
+
319
+ addClass(self.element, contClass);
320
+ if (self.options.customClass) {
321
+ addClass(self.element, self.options.customClass);
322
+ }
323
+
324
+ // Initialize drag & zoom
325
+ _initDraggable.call(this);
326
+
327
+ if (self.options.enableZoom) {
328
+ _initializeZoom.call(self);
329
+ }
330
+ }
331
+
332
+ function _setZoomerVal(v) {
333
+ if (this.options.enableZoom) {
334
+ this.elements.zoomer.value = fix(v, 4);
335
+ }
336
+ }
337
+
338
+ function _initializeZoom() {
339
+ var self = this,
340
+ wrap = self.elements.zoomerWrap = document.createElement('div'),
341
+ zoomer = self.elements.zoomer = document.createElement('input'),
342
+ origin,
343
+ viewportRect,
344
+ transform;
345
+
346
+ addClass(wrap, 'cr-slider-wrap');
347
+ addClass(zoomer, 'cr-slider');
348
+ zoomer.type = 'range';
349
+ zoomer.step = '0.01';
350
+ zoomer.value = 1;
351
+ zoomer.style.display = self.options.showZoomer ? '' : 'none';
352
+
353
+ self.element.appendChild(wrap);
354
+ wrap.appendChild(zoomer);
355
+
356
+ self._currentZoom = 1;
357
+ function start() {
358
+ _updateCenterPoint.call(self);
359
+ origin = new TransformOrigin(self.elements.preview);
360
+ viewportRect = self.elements.viewport.getBoundingClientRect();
361
+ transform = Transform.parse(self.elements.preview);
362
+ }
363
+
364
+ function change() {
365
+ _onZoom.call(self, {
366
+ value: parseFloat(zoomer.value),
367
+ origin: origin || new TransformOrigin(self.elements.preview),
368
+ viewportRect: viewportRect || self.elements.viewport.getBoundingClientRect(),
369
+ transform: transform || Transform.parse(self.elements.preview)
370
+ });
371
+ }
372
+
373
+ function scroll(ev) {
374
+ var delta, targetZoom;
375
+
376
+ if (ev.wheelDelta) {
377
+ delta = ev.wheelDelta / 1200; //wheelDelta min: -120 max: 120 // max x 10 x 2
378
+ } else if (ev.deltaY) {
379
+ delta = ev.deltaY / 1060; //deltaY min: -53 max: 53 // max x 10 x 2
380
+ } else if (ev.detail) {
381
+ delta = ev.detail / 60; //delta min: -3 max: 3 // max x 10 x 2
382
+ } else {
383
+ delta = 0;
384
+ }
385
+
386
+ targetZoom = self._currentZoom + delta;
387
+
388
+ ev.preventDefault();
389
+ start();
390
+ _setZoomerVal.call(self, targetZoom);
391
+ change();
392
+ }
393
+
394
+ self.elements.zoomer.addEventListener('mousedown', start);
395
+ self.elements.zoomer.addEventListener('touchstart', start);
396
+
397
+ self.elements.zoomer.addEventListener('input', change);// this is being fired twice on keypress
398
+ self.elements.zoomer.addEventListener('change', change);
399
+
400
+ if (self.options.mouseWheelZoom) {
401
+ self.elements.boundary.addEventListener('mousewheel', scroll);
402
+ self.elements.boundary.addEventListener('DOMMouseScroll', scroll);
403
+ }
404
+ }
405
+
406
+ function _onZoom(ui) {
407
+ var self = this,
408
+ transform = ui.transform,
409
+ vpRect = ui.viewportRect,
410
+ origin = ui.origin;
411
+
412
+ self._currentZoom = ui.value;
413
+ transform.scale = self._currentZoom;
414
+
415
+ var boundaries = _getVirtualBoundaries.call(self, vpRect),
416
+ transBoundaries = boundaries.translate,
417
+ oBoundaries = boundaries.origin;
418
+
419
+ if (transform.x >= transBoundaries.maxX) {
420
+ origin.x = oBoundaries.minX;
421
+ transform.x = transBoundaries.maxX;
422
+ }
423
+
424
+ if (transform.x <= transBoundaries.minX) {
425
+ origin.x = oBoundaries.maxX;
426
+ transform.x = transBoundaries.minX;
427
+ }
428
+
429
+ if (transform.y >= transBoundaries.maxY) {
430
+ origin.y = oBoundaries.minY;
431
+ transform.y = transBoundaries.maxY;
432
+ }
433
+
434
+ if (transform.y <= transBoundaries.minY) {
435
+ origin.y = oBoundaries.maxY;
436
+ transform.y = transBoundaries.minY;
437
+ }
438
+
439
+ var transCss = {};
440
+ transCss[CSS_TRANSFORM] = transform.toString();
441
+ transCss[CSS_TRANS_ORG] = origin.toString();
442
+ css(self.elements.preview, transCss);
443
+
444
+ _debouncedOverlay.call(self);
445
+ _triggerUpdate.call(self);
446
+ }
447
+
448
+ function _getVirtualBoundaries(viewport) {
449
+ var self = this,
450
+ scale = self._currentZoom,
451
+ vpWidth = viewport.width,
452
+ vpHeight = viewport.height,
453
+ centerFromBoundaryX = self.options.boundary.width / 2,
454
+ centerFromBoundaryY = self.options.boundary.height / 2,
455
+ originalImgWidth = self._originalImageWidth,
456
+ originalImgHeight = self._originalImageHeight,
457
+ curImgWidth = originalImgWidth * scale,
458
+ curImgHeight = originalImgHeight * scale,
459
+ halfWidth = vpWidth / 2,
460
+ halfHeight = vpHeight / 2;
461
+
462
+
463
+ var maxX = ((halfWidth / scale) - centerFromBoundaryX) * -1;
464
+ var minX = maxX - ((curImgWidth * (1 / scale)) - (vpWidth * (1 / scale)));
465
+
466
+ var maxY = ((halfHeight / scale) - centerFromBoundaryY) * -1;
467
+ var minY = maxY - ((curImgHeight * (1 / scale)) - (vpHeight * (1 / scale)));
468
+
469
+ var originMinX = (1 / scale) * halfWidth;
470
+ var originMaxX = (curImgWidth * (1 / scale)) - originMinX;
471
+
472
+ var originMinY = (1 / scale) * halfHeight;
473
+ var originMaxY = (curImgHeight * (1 / scale)) - originMinY;
474
+
475
+ return {
476
+ translate: {
477
+ maxX: maxX,
478
+ minX: minX,
479
+ maxY: maxY,
480
+ minY: minY
481
+ },
482
+ origin: {
483
+ maxX: originMaxX,
484
+ minX: originMinX,
485
+ maxY: originMaxY,
486
+ minY: originMinY
487
+ }
488
+ };
489
+ }
490
+
491
+ function _updateCenterPoint() {
492
+ var self = this,
493
+ scale = self._currentZoom,
494
+ data = self.elements.preview.getBoundingClientRect(),
495
+ vpData = self.elements.viewport.getBoundingClientRect(),
496
+ transform = Transform.parse(self.elements.preview.style[CSS_TRANSFORM]),
497
+ pc = new TransformOrigin(self.elements.preview),
498
+ top = (vpData.top - data.top) + (vpData.height / 2),
499
+ left = (vpData.left - data.left) + (vpData.width / 2),
500
+ center = {},
501
+ adj = {};
502
+
503
+ center.y = top / scale;
504
+ center.x = left / scale;
505
+
506
+ adj.y = (center.y - pc.y) * (1 - scale);
507
+ adj.x = (center.x - pc.x) * (1 - scale);
508
+
509
+ transform.x -= adj.x;
510
+ transform.y -= adj.y;
511
+
512
+ var newCss = {};
513
+ newCss[CSS_TRANS_ORG] = center.x + 'px ' + center.y + 'px';
514
+ newCss[CSS_TRANSFORM] = transform.toString();
515
+ css(self.elements.preview, newCss);
516
+ }
517
+
518
+ function _initDraggable() {
519
+ var self = this,
520
+ isDragging = false,
521
+ originalX,
522
+ originalY,
523
+ originalDistance,
524
+ vpRect;
525
+
526
+ function mouseDown(ev) {
527
+ ev.preventDefault();
528
+ if (isDragging) return;
529
+ isDragging = true;
530
+ originalX = ev.pageX;
531
+ originalY = ev.pageY;
532
+
533
+ if (ev.touches) {
534
+ var touches = ev.touches[0];
535
+ originalX = touches.pageX;
536
+ originalY = touches.pageY;
537
+ }
538
+
539
+ transform = Transform.parse(self.elements.preview);
540
+ window.addEventListener('mousemove', mouseMove);
541
+ window.addEventListener('touchmove', mouseMove);
542
+ window.addEventListener('mouseup', mouseUp);
543
+ window.addEventListener('touchend', mouseUp);
544
+ document.body.style[CSS_USERSELECT] = 'none';
545
+ vpRect = self.elements.viewport.getBoundingClientRect();
546
+ }
547
+
548
+ function mouseMove(ev) {
549
+ ev.preventDefault();
550
+ var pageX = ev.pageX,
551
+ pageY = ev.pageY;
552
+
553
+ if (ev.touches) {
554
+ var touches = ev.touches[0];
555
+ pageX = touches.pageX;
556
+ pageY = touches.pageY;
557
+ }
558
+
559
+ var deltaX = pageX - originalX,
560
+ deltaY = pageY - originalY,
561
+ imgRect = self.elements.preview.getBoundingClientRect(),
562
+ top = transform.y + deltaY,
563
+ left = transform.x + deltaX,
564
+ newCss = {};
565
+
566
+ if (ev.type == 'touchmove') {
567
+ if (ev.touches.length > 1) {
568
+ var touch1 = ev.touches[0];
569
+ var touch2 = ev.touches[1];
570
+ var dist = Math.sqrt((touch1.pageX - touch2.pageX) * (touch1.pageX - touch2.pageX) + (touch1.pageY - touch2.pageY) * (touch1.pageY - touch2.pageY));
571
+
572
+ if (!originalDistance) {
573
+ originalDistance = dist / self._currentZoom;
574
+ }
575
+
576
+ var scale = dist / originalDistance;
577
+
578
+ _setZoomerVal.call(self, scale);
579
+ dispatchChange(self.elements.zoomer);
580
+ return;
581
+ }
582
+ }
583
+
584
+ if (vpRect.top > imgRect.top + deltaY && vpRect.bottom < imgRect.bottom + deltaY) {
585
+ transform.y = top;
586
+ }
587
+
588
+ if (vpRect.left > imgRect.left + deltaX && vpRect.right < imgRect.right + deltaX) {
589
+ transform.x = left;
590
+ }
591
+
592
+ newCss[CSS_TRANSFORM] = transform.toString();
593
+ css(self.elements.preview, newCss);
594
+ _updateOverlay.call(self);
595
+ originalY = pageY;
596
+ originalX = pageX;
597
+ }
598
+
599
+ function mouseUp() {
600
+ isDragging = false;
601
+ window.removeEventListener('mousemove', mouseMove);
602
+ window.removeEventListener('touchmove', mouseMove);
603
+ window.removeEventListener('mouseup', mouseUp);
604
+ window.removeEventListener('touchend', mouseUp);
605
+ document.body.style[CSS_USERSELECT] = '';
606
+ _updateCenterPoint.call(self);
607
+ _triggerUpdate.call(self);
608
+ originalDistance = 0;
609
+ }
610
+
611
+ self.elements.overlay.addEventListener('mousedown', mouseDown);
612
+ self.elements.overlay.addEventListener('touchstart', mouseDown);
613
+ }
614
+
615
+ function _updateOverlay() {
616
+ var self = this,
617
+ boundRect = self.elements.boundary.getBoundingClientRect(),
618
+ imgData = self.elements.preview.getBoundingClientRect();
619
+
620
+ css(self.elements.overlay, {
621
+ width: imgData.width + 'px',
622
+ height: imgData.height + 'px',
623
+ top: (imgData.top - boundRect.top) + 'px',
624
+ left: (imgData.left - boundRect.left) + 'px'
625
+ });
626
+ }
627
+ var _debouncedOverlay = debounce(_updateOverlay, 500);
628
+
629
+ function _triggerUpdate() {
630
+ var self = this;
631
+ if (_isVisible.call(self)) {
632
+ self.options.update.call(self, self.get());
633
+ }
634
+ }
635
+
636
+ function _isVisible() {
637
+ return this.elements.preview.offsetHeight > 0 && this.elements.preview.offsetWidth > 0;
638
+ }
639
+
640
+ function _updatePropertiesFromImage() {
641
+ var self = this,
642
+ minZoom = 0,
643
+ maxZoom = 1.5,
644
+ initialZoom = 1,
645
+ cssReset = {},
646
+ img = self.elements.preview,
647
+ zoomer = self.elements.zoomer,
648
+ transformReset = new Transform(0, 0, initialZoom),
649
+ originReset = new TransformOrigin(),
650
+ isVisible = _isVisible.call(self),
651
+ imgData,
652
+ vpData,
653
+ boundaryData,
654
+ minW,
655
+ minH;
656
+
657
+ if (!isVisible || self.data.bound) {
658
+ // if the croppie isn't visible or it doesn't need binding
659
+ return;
660
+ }
661
+
662
+ self.data.bound = true;
663
+ cssReset[CSS_TRANSFORM] = transformReset.toString();
664
+ cssReset[CSS_TRANS_ORG] = originReset.toString();
665
+ cssReset['opacity'] = 1;
666
+ css(img, cssReset);
667
+
668
+ imgData = img.getBoundingClientRect();
669
+ vpData = self.elements.viewport.getBoundingClientRect();
670
+ boundaryData = self.elements.boundary.getBoundingClientRect();
671
+ self._originalImageWidth = imgData.width;
672
+ self._originalImageHeight = imgData.height;
673
+
674
+ if (self.options.enableZoom) {
675
+ minW = vpData.width / imgData.width;
676
+ minH = vpData.height / imgData.height;
677
+ minZoom = Math.max(minW, minH);
678
+
679
+ if (minZoom >= maxZoom) {
680
+ maxZoom = minZoom + 1;
681
+ }
682
+
683
+ zoomer.min = fix(minZoom, 4);
684
+ zoomer.max = fix(maxZoom, 4);
685
+ initialZoom = Math.max((boundaryData.width / imgData.width), (boundaryData.height / imgData.height));
686
+ _setZoomerVal.call(self, initialZoom);
687
+ dispatchChange(zoomer);
688
+ }
689
+
690
+ self._currentZoom = transformReset.scale = initialZoom;
691
+ cssReset[CSS_TRANSFORM] = transformReset.toString();
692
+ css(img, cssReset);
693
+
694
+ if (self.data.points.length) {
695
+ _bindPoints.call(self, self.data.points);
696
+ }
697
+ else {
698
+ _centerImage.call(self);
699
+ }
700
+
701
+
702
+ _updateOverlay.call(self);
703
+ }
704
+
705
+ function _bindPoints(points) {
706
+ if (points.length != 4) {
707
+ throw "Croppie - Invalid number of points supplied: " + points;
708
+ }
709
+ var self = this,
710
+ pointsWidth = points[2] - points[0],
711
+ // pointsHeight = points[3] - points[1],
712
+ vpData = self.elements.viewport.getBoundingClientRect(),
713
+ boundRect = self.elements.boundary.getBoundingClientRect(),
714
+ vpOffset = {
715
+ left: vpData.left - boundRect.left,
716
+ top: vpData.top - boundRect.top
717
+ },
718
+ scale = vpData.width / pointsWidth,
719
+ originTop = points[1],
720
+ originLeft = points[0],
721
+ transformTop = (-1 * points[1]) + vpOffset.top,
722
+ transformLeft = (-1 * points[0]) + vpOffset.left,
723
+ newCss = {};
724
+
725
+ newCss[CSS_TRANS_ORG] = originLeft + 'px ' + originTop + 'px';
726
+ newCss[CSS_TRANSFORM] = new Transform(transformLeft, transformTop, scale).toString();
727
+ css(self.elements.preview, newCss);
728
+
729
+ _setZoomerVal.call(self, scale);
730
+ self._currentZoom = scale;
731
+ }
732
+
733
+ function _centerImage() {
734
+ var self = this,
735
+ imgDim = self.elements.preview.getBoundingClientRect(),
736
+ vpDim = self.elements.viewport.getBoundingClientRect(),
737
+ boundDim = self.elements.boundary.getBoundingClientRect(),
738
+ vpLeft = vpDim.left - boundDim.left,
739
+ vpTop = vpDim.top - boundDim.top,
740
+ w = vpLeft - ((imgDim.width - vpDim.width) / 2),
741
+ h = vpTop - ((imgDim.height - vpDim.height) / 2),
742
+ transform = new Transform(w, h, self._currentZoom);
743
+
744
+ css(self.elements.preview, CSS_TRANSFORM, transform.toString());
745
+ }
746
+
747
+ function _transferImageToCanvas() {
748
+ var self = this,
749
+ canvas = self.elements.canvas,
750
+ img = self.elements.img,
751
+ ctx = canvas.getContext('2d');
752
+
753
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
754
+ canvas.width = img.width;
755
+ canvas.height = img.height;
756
+
757
+ getExifOrientation(img, function (orientation) {
758
+ rotateCanvas(canvas, ctx, img, parseInt(orientation));
759
+ });
760
+ }
761
+
762
+ function _getHtmlResult(data) {
763
+ var points = data.points,
764
+ div = document.createElement('div'),
765
+ img = document.createElement('img'),
766
+ width = points[2] - points[0],
767
+ height = points[3] - points[1];
768
+
769
+ addClass(div, 'croppie-result');
770
+ div.appendChild(img);
771
+ css(img, {
772
+ left: (-1 * points[0]) + 'px',
773
+ top: (-1 * points[1]) + 'px'
774
+ });
775
+ img.src = data.url;
776
+ css(div, {
777
+ width: width + 'px',
778
+ height: height + 'px'
779
+ });
780
+
781
+ return div;
782
+ }
783
+
784
+ function _getCanvasResult(img, data) {
785
+ var points = data.points,
786
+ left = points[0],
787
+ top = points[1],
788
+ width = (points[2] - points[0]),
789
+ height = (points[3] - points[1]),
790
+ circle = data.circle,
791
+ canvas = document.createElement('canvas'),
792
+ ctx = canvas.getContext('2d'),
793
+ outWidth = width,
794
+ outHeight = height;
795
+
796
+ if (data.outputWidth && data.outputHeight) {
797
+ outWidth = data.outputWidth;
798
+ outHeight = data.outputHeight;
799
+ }
800
+
801
+ canvas.width = outWidth;
802
+ canvas.height = outHeight;
803
+
804
+ ctx.drawImage(img, left, top, width, height, 0, 0, outWidth, outHeight);
805
+ if (circle) {
806
+ ctx.fillStyle = '#fff';
807
+ ctx.globalCompositeOperation = 'destination-in';
808
+ ctx.beginPath();
809
+ ctx.arc(outWidth / 2, outHeight / 2, outWidth / 2, 0, Math.PI * 2, true);
810
+ ctx.closePath();
811
+ ctx.fill();
812
+ }
813
+ return canvas.toDataURL(data.format, data.quality);
814
+ }
815
+
816
+ function _bind(options, cb) {
817
+ var self = this,
818
+ url,
819
+ points = [];
820
+
821
+ if (typeof (options) === 'string') {
822
+ url = options;
823
+ options = {};
824
+ }
825
+ else if (Array.isArray(options)) {
826
+ points = options.slice();
827
+ }
828
+ else if (typeof (options) == 'undefined' && self.data.url) { //refreshing
829
+ _updatePropertiesFromImage.call(self);
830
+ _triggerUpdate.call(self);
831
+ return null;
832
+ }
833
+ else {
834
+ url = options.url;
835
+ points = options.points || [];
836
+ }
837
+
838
+ self.data.bound = false;
839
+ self.data.url = url || self.data.url;
840
+ self.data.points = (points || self.data.points).map(function (p) {
841
+ return parseFloat(p);
842
+ });
843
+ var prom = loadImage(url, self.elements.img);
844
+ prom.then(function () {
845
+ if (self.options.useCanvas) {
846
+ self.elements.img.exifdata = null;
847
+ _transferImageToCanvas.call(self);
848
+ }
849
+ _updatePropertiesFromImage.call(self);
850
+ _triggerUpdate.call(self);
851
+ if (cb) {
852
+ cb();
853
+ }
854
+ });
855
+ return prom;
856
+ }
857
+
858
+ function fix(v, decimalPoints) {
859
+ return parseFloat(v).toFixed(decimalPoints || 0);
860
+ }
861
+
862
+ function _get() {
863
+ var self = this,
864
+ imgData = self.elements.preview.getBoundingClientRect(),
865
+ vpData = self.elements.viewport.getBoundingClientRect(),
866
+ x1 = vpData.left - imgData.left,
867
+ y1 = vpData.top - imgData.top,
868
+ x2 = x1 + vpData.width,
869
+ y2 = y1 + vpData.height,
870
+ scale = self._currentZoom;
871
+
872
+ if (scale === Infinity || isNaN(scale)) {
873
+ scale = 1;
874
+ }
875
+
876
+ x1 = Math.max(0, x1 / scale);
877
+ y1 = Math.max(0, y1 / scale);
878
+ x2 = Math.max(0, x2 / scale);
879
+ y2 = Math.max(0, y2 / scale);
880
+
881
+ return {
882
+ points: [fix(x1), fix(y1), fix(x2), fix(y2)],
883
+ zoom: scale
884
+ };
885
+ }
886
+
887
+ var RESULT_DEFAULTS = {
888
+ type: 'canvas',
889
+ size: 'viewport',
890
+ format: 'png',
891
+ quality: 1
892
+ },
893
+ RESULT_FORMATS = ['jpeg', 'webp', 'png'];
894
+
895
+ function _result(options) {
896
+ var self = this,
897
+ data = _get.call(self),
898
+ opts = deepExtend(RESULT_DEFAULTS, deepExtend({}, options)),
899
+ type = (typeof (options) === 'string' ? options : opts.type),
900
+ size = opts.size,
901
+ format = opts.format,
902
+ quality = opts.quality,
903
+ vpRect,
904
+ prom;
905
+
906
+ if (size === 'viewport') {
907
+ vpRect = self.elements.viewport.getBoundingClientRect();
908
+ data.outputWidth = vpRect.width;
909
+ data.outputHeight = vpRect.height;
910
+ }
911
+
912
+ if (RESULT_FORMATS.indexOf(format) > -1) {
913
+ data.format = 'image/' + format;
914
+ data.quality = quality;
915
+ }
916
+
917
+ data.circle = self.options.viewport.type === 'circle';
918
+ data.url = self.data.url;
919
+
920
+ prom = new Promise(function (resolve, reject) {
921
+ if (type === 'canvas') {
922
+ resolve(_getCanvasResult.call(self, self.elements.preview, data));
923
+ }
924
+ else {
925
+ resolve(_getHtmlResult.call(self, data));
926
+ }
927
+ });
928
+ return prom;
929
+ }
930
+
931
+ function _refresh() {
932
+ _updatePropertiesFromImage.call(this);
933
+ }
934
+
935
+ function _destroy() {
936
+ var self = this;
937
+ self.element.removeChild(self.elements.boundary);
938
+ removeClass(self.element, 'croppie-container');
939
+ if (self.options.enableZoom) {
940
+ self.element.removeChild(self.elements.zoomerWrap);
941
+ }
942
+ delete self.elements;
943
+ }
944
+
945
+ if (this.jQuery) {
946
+ var $ = this.jQuery;
947
+ $.fn.croppie = function (opts) {
948
+ var ot = typeof opts;
949
+
950
+ if (ot === 'string') {
951
+ var args = Array.prototype.slice.call(arguments, 1);
952
+ var singleInst = $(this).data('croppie');
953
+
954
+ if (opts === 'get') {
955
+ return singleInst.get();
956
+ }
957
+ else if (opts === 'result') {
958
+ return singleInst.result.apply(singleInst, args);
959
+ }
960
+
961
+ return this.each(function () {
962
+ var i = $(this).data('croppie');
963
+ if (!i) return;
964
+
965
+ var method = i[opts];
966
+ if ($.isFunction(method)) {
967
+ method.apply(i, args);
968
+ if (opts === 'destroy') {
969
+ $(this).removeData('croppie');
970
+ }
971
+ }
972
+ else {
973
+ throw 'Croppie ' + opts + ' method not found';
974
+ }
975
+ });
976
+ }
977
+ else {
978
+ return this.each(function () {
979
+ var i = new Croppie(this, opts);
980
+ $(this).data('croppie', i);
981
+ });
982
+ }
983
+ };
984
+ }
985
+
986
+ function Croppie(element, opts) {
987
+ this.element = element;
988
+ this.options = deepExtend(deepExtend({}, Croppie.defaults), opts);
989
+
990
+ _create.call(this);
991
+ }
992
+
993
+ Croppie.defaults = {
994
+ viewport: {
995
+ width: 100,
996
+ height: 100,
997
+ type: 'square'
998
+ },
999
+ boundary: {
1000
+ width: 300,
1001
+ height: 300
1002
+ },
1003
+ customClass: '',
1004
+ showZoomer: true,
1005
+ enableZoom: true,
1006
+ mouseWheelZoom: true,
1007
+ exif: false,
1008
+ update: function () { }
1009
+ };
1010
+
1011
+ deepExtend(Croppie.prototype, {
1012
+ bind: function (options, cb) {
1013
+ return _bind.call(this, options, cb);
1014
+ },
1015
+ get: function () {
1016
+ return _get.call(this);
1017
+ },
1018
+ result: function (type) {
1019
+ return _result.call(this, type);
1020
+ },
1021
+ refresh: function () {
1022
+ return _refresh.call(this);
1023
+ },
1024
+ setZoom: function (v) {
1025
+ _setZoomerVal.call(this, v);
1026
+ dispatchChange(this.elements.zoomer);
1027
+ },
1028
+ destroy: function () {
1029
+ return _destroy.call(this);
1030
+ }
1031
+ });
1032
+
1033
+ exports.Croppie = window.Croppie = Croppie;
1034
+
1035
+ if (typeof module === 'object' && !!module.exports) {
1036
+ module.exports = Croppie;
1037
+ }
1038
+ }));