eed73efdeae89b287aabbf28ca7b3f785f2040c2
[roojs1] / Roo / bootstrap / UploadCropbox.js
1
2 /*
3 * Licence: LGPL
4 */
5
6 /**
7  * @class Roo.bootstrap.UploadCropbox
8  * @extends Roo.bootstrap.Component
9  * Bootstrap UploadCropbox class
10  * @cfg {String} emptyText show when image has been loaded
11  * @cfg {String} rotateNotify show when image too small to rotate
12  * @cfg {Number} errorTimeout default 3000
13  * @cfg {Number} minWidth default 300
14  * @cfg {Number} minHeight default 300
15  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
16  * 
17  * @constructor
18  * Create a new UploadCropbox
19  * @param {Object} config The config object
20  */
21
22 Roo.bootstrap.UploadCropbox = function(config){
23     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24     
25     this.addEvents({
26         /**
27          * @event beforeselectfile
28          * Fire before select file
29          * @param {Roo.bootstrap.UploadCropbox} this
30          */
31         "beforeselectfile" : true,
32         /**
33          * @event initial
34          * Fire after initEvent
35          * @param {Roo.bootstrap.UploadCropbox} this
36          */
37         "initial" : true,
38         /**
39          * @event crop
40          * Fire after initEvent
41          * @param {Roo.bootstrap.UploadCropbox} this
42          * @param {String} data
43          */
44         "crop" : true,
45         /**
46          * @event prepare
47          * Fire when preparing the file data
48          * @param {Roo.bootstrap.UploadCropbox} this
49          * @param {Object} file
50          */
51         "prepare" : true,
52         /**
53          * @event exception
54          * Fire when get exception
55          * @param {Roo.bootstrap.UploadCropbox} this
56          * @param {Object} options
57          */
58         "exception" : true,
59         /**
60          * @event beforeloadcanvas
61          * Fire before load the canvas
62          * @param {Roo.bootstrap.UploadCropbox} this
63          * @param {String} src
64          */
65         "beforeloadcanvas" : true,
66         /**
67          * @event trash
68          * Fire when trash image
69          * @param {Roo.bootstrap.UploadCropbox} this
70          */
71         "trash" : true,
72         /**
73          * @event download
74          * Fire when download the image
75          * @param {Roo.bootstrap.UploadCropbox} this
76          */
77         "download" : true,
78         /**
79          * @event footerbuttonclick
80          * Fire when footerbuttonclick
81          * @param {Roo.bootstrap.UploadCropbox} this
82          * @param {String} type
83          */
84         "footerbuttonclick" : true,
85         /**
86          * @event resize
87          * Fire when resize
88          * @param {Roo.bootstrap.UploadCropbox} this
89          */
90         "resize" : true
91         
92     });
93     
94     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
95 };
96
97 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
98     
99     emptyText : 'Click to upload image',
100     rotateNotify : 'Image is too small to rotate',
101     errorTimeout : 3000,
102     scale : 0,
103     baseScale : 1,
104     rotate : 0,
105     dragable : false,
106     pinching : false,
107     mouseX : 0,
108     mouseY : 0,
109     cropData : false,
110     minWidth : 300,
111     minHeight : 300,
112     file : false,
113     exif : {},
114     baseRotate : 1,
115     cropType : 'image/jpeg',
116     buttons : false,
117     canvasLoaded : false,
118     
119     getAutoCreate : function()
120     {
121         var cfg = {
122             tag : 'div',
123             cls : 'roo-upload-cropbox',
124             cn : [
125                 {
126                     tag : 'div',
127                     cls : 'roo-upload-cropbox-body',
128                     style : 'cursor:pointer',
129                     cn : [
130                         {
131                             tag : 'div',
132                             cls : 'roo-upload-cropbox-preview'
133                         },
134                         {
135                             tag : 'div',
136                             cls : 'roo-upload-cropbox-thumb'
137                         },
138                         {
139                             tag : 'div',
140                             cls : 'roo-upload-cropbox-empty-notify',
141                             html : this.emptyText
142                         },
143                         {
144                             tag : 'div',
145                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
146                             html : this.rotateNotify
147                         }
148                     ]
149                 },
150                 {
151                     tag : 'div',
152                     cls : 'roo-upload-cropbox-footer',
153                     cn : {
154                         tag : 'div',
155                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
156                         cn : []
157                     }
158                 }
159             ]
160         };
161         
162         return cfg;
163     },
164     
165     onRender : function(ct, position)
166     {
167         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
168         
169         if (this.buttons.length) {
170             
171             Roo.each(this.buttons, function(bb) {
172                 
173                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
174                 
175                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
176                 
177             }, this);
178         }
179     },
180     
181     initEvents : function()
182     {
183         this.urlAPI = (window.createObjectURL && window) || 
184                                 (window.URL && URL.revokeObjectURL && URL) || 
185                                 (window.webkitURL && webkitURL);
186                         
187         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
188         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
189         
190         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
191         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
192         
193         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
194         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
195         this.thumbEl.hide();
196         
197         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
198         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
199         
200         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
201         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
202         this.errorEl.hide();
203         
204         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
205         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
206         this.footerEl.hide();
207         
208         this.setThumbBoxSize();
209         
210         this.bind();
211         
212         this.resize();
213         
214         this.fireEvent('initial', this);
215     },
216
217     bind : function()
218     {
219         var _this = this;
220         
221         window.addEventListener("resize", function() { _this.resize(); } );
222         
223         this.bodyEl.on('click', this.beforeSelectFile, this);
224         
225         if(Roo.isTouch){
226             this.bodyEl.on('touchstart', this.onTouchStart, this);
227             this.bodyEl.on('touchmove', this.onTouchMove, this);
228             this.bodyEl.on('touchend', this.onTouchEnd, this);
229         }
230         
231         if(!Roo.isTouch){
232             this.bodyEl.on('mousedown', this.onMouseDown, this);
233             this.bodyEl.on('mousemove', this.onMouseMove, this);
234             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
235             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
236             Roo.get(document).on('mouseup', this.onMouseUp, this);
237         }
238     },
239     
240     reset : function()
241     {    
242         this.scale = 0;
243         this.baseScale = 1;
244         this.rotate = 0;
245         this.baseRotate = 1;
246         this.dragable = false;
247         this.pinching = false;
248         this.mouseX = 0;
249         this.mouseY = 0;
250         this.cropData = false;
251         this.notifyEl.dom.innerHTML = this.emptyText;
252         
253     },
254     
255     resize : function()
256     {
257         if(this.fireEvent('resize', this) != false){
258             this.setThumbBoxPosition();
259             this.setCanvasPosition();
260         }
261     },
262     
263     onFooterButtonClick : function(e, el, o, type)
264     {
265         switch (type) {
266             case 'rotate-left' :
267                 this.onRotateLeft(e);
268                 break;
269             case 'rotate-right' :
270                 this.onRotateRight(e);
271                 break;
272             case 'picture' :
273                 this.beforeSelectFile(e);
274                 break;
275             case 'trash' :
276                 this.trash(e);
277                 break;
278             case 'crop' :
279                 this.crop(e);
280                 break;
281             case 'download' :
282                 this.download(e);
283                 break;
284             default :
285                 break;
286         }
287         
288         this.fireEvent('footerbuttonclick', this, type);
289     },
290     
291     beforeSelectFile : function(e)
292     {
293         this.fireEvent('beforeselectfile', this);
294     },
295     
296     trash : function(e)
297     {
298         this.fireEvent('trash', this);
299     },
300     
301     download : function(e)
302     {
303         this.fireEvent('download', this);
304     },
305     
306     loadCanvas : function(src)
307     {   
308         if(this.fireEvent('beforeloadcanvas', this, src) != false){
309             
310             this.reset();
311             
312             this.imageEl = document.createElement('img');
313             
314             var _this = this;
315             
316             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
317             
318             this.imageEl.src = src;
319         }
320     },
321     
322     onLoadCanvas : function()
323     {   
324         this.bodyEl.un('click', this.beforeSelectFile, this);
325         
326         this.notifyEl.hide();
327         this.thumbEl.show();
328         this.footerEl.show();
329         
330         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
331         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
332         
333         this.setThumbBoxPosition();
334         this.baseRotateLevel();
335         this.baseScaleLevel();
336         
337         this.draw();
338         
339         this.resize();
340         
341         this.canvasLoaded = true;
342         
343     },
344     
345     setCanvasPosition : function()
346     {   
347         if(!this.canvasEl){
348             return;
349         }
350         
351         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
352         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
353         
354         this.previewEl.setLeft(pw);
355         this.previewEl.setTop(ph);
356     },
357     
358     onMouseDown : function(e)
359     {   
360         e.stopEvent();
361         
362         this.dragable = true;
363         this.pinching = false;
364         
365         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
366         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
367         
368     },
369     
370     onMouseMove : function(e)
371     {   
372         e.stopEvent();
373         
374         if(!this.canvasLoaded){
375             return;
376         }
377         
378         if (!this.dragable){
379             return;
380         }
381         
382         var minX = Math.ceil(this.thumbEl.getLeft(true));
383         var minY = Math.ceil(this.thumbEl.getTop(true));
384         
385         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
386         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
387         
388         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
389         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
390         
391         x = x - this.mouseX;
392         y = y - this.mouseY;
393         
394         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
395         var bgY = Math.ceil(y + this.previewEl.getTop(true));
396         
397         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
398         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
399         
400         this.previewEl.setLeft(bgX);
401         this.previewEl.setTop(bgY);
402         
403         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
404         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
405     },
406     
407     onMouseUp : function(e)
408     {   
409         e.stopEvent();
410         
411         this.dragable = false;
412     },
413     
414     onMouseWheel : function(e)
415     {   
416         e.stopEvent();
417         
418         this.startScale = this.scale;
419         
420         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
421         
422         if(!this.zoomable()){
423             this.scale = this.startScale;
424             return;
425         }
426         
427         this.draw();
428         
429         return;
430     },
431     
432     zoomable : function()
433     {
434         var minScale = this.thumbEl.getWidth() / this.minWidth;
435         
436         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel());
437         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel());
438         
439         if(
440                 (this.rotate == 0 || this.rotate == 180) && 
441                 (
442                     width / minScale < this.minWidth || 
443                     width / minScale > this.imageEl.OriginWidth || 
444                     height / minScale < this.minHeight || 
445                     height / minScale > this.imageEl.OriginHeight
446                 )
447         ){
448             return false;
449         }
450         
451         if(
452                 (this.rotate == 90 || this.rotate == 270) && 
453                 (
454                     width / minScale < this.minHeight || 
455                     width / minScale > this.imageEl.OriginWidth || 
456                     height / minScale < this.minWidth || 
457                     height / minScale > this.imageEl.OriginHeight
458                 )
459         ){
460             return false;
461         }
462         
463         return true;
464         
465     },
466     
467     onRotateLeft : function(e)
468     {   
469         var minScale = this.thumbEl.getWidth() / this.minWidth;
470         
471         if(this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight()){
472             
473             var bw = this.canvasEl.width / this.getScaleLevel();
474             var bh = this.canvasEl.height / this.getScaleLevel();
475             
476             this.startScale = this.scale;
477             
478             while (this.getScaleLevel() < minScale){
479             
480                 this.scale = this.scale + 1;
481                 
482                 if(!this.zoomable()){
483                     break;
484                 }
485                 
486                 if(
487                         bw * this.getScaleLevel() < this.thumbEl.getHeight() ||
488                         bh * this.getScaleLevel() < this.thumbEl.getWidth()
489                 ){
490                     continue;
491                 }
492                 
493                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
494
495                 this.draw();
496                 
497                 return;
498             }
499             
500             this.scale = this.startScale;
501             
502             this.onRotateFail();
503             
504             return false;
505         }
506         
507         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
508
509         this.draw();
510         
511     },
512     
513     onRotateRight : function(e)
514     {
515         var minScale = this.thumbEl.getWidth() / this.minWidth;
516         
517         if(this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight()){
518             
519             var bw = this.canvasEl.width / this.getScaleLevel();
520             var bh = this.canvasEl.height / this.getScaleLevel();
521             
522             this.startScale = this.scale;
523             
524             while (this.getScaleLevel() < minScale){
525             
526                 this.scale = this.scale + 1;
527                 
528                 if(!this.zoomable()){
529                     break;
530                 }
531                 
532                 if(
533                         bw * this.getScaleLevel() < this.thumbEl.getHeight() ||
534                         bh * this.getScaleLevel() < this.thumbEl.getWidth()
535                 ){
536                     continue;
537                 }
538                 
539                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
540
541                 this.draw();
542                 
543                 return;
544             }
545             
546             this.scale = this.startScale;
547             
548             this.onRotateFail();
549             
550             return false;
551         }
552         
553         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
554
555         this.draw();
556     },
557     
558     onRotateFail : function()
559     {
560         this.errorEl.show(true);
561         
562         var _this = this;
563         
564         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
565     },
566     
567     draw : function()
568     {
569         this.previewEl.dom.innerHTML = '';
570         
571         var canvasEl = document.createElement("canvas");
572         
573         var contextEl = canvasEl.getContext("2d");
574         
575         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
576         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
577         var center = this.imageEl.OriginWidth / 2;
578         
579         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
580             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
581             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
582             center = this.imageEl.OriginHeight / 2;
583         }
584         
585         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
586         
587         contextEl.translate(center, center);
588         contextEl.rotate(this.rotate * Math.PI / 180);
589
590         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
591         
592         this.canvasEl = document.createElement("canvas");
593         
594         this.contextEl = this.canvasEl.getContext("2d");
595         
596         switch (this.rotate) {
597             case 0 :
598                 
599                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
600                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
601                 
602                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
603                 
604                 break;
605             case 90 : 
606                 
607                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
608                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
609                 
610                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
611                     this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
612                     break;
613                 }
614                 
615                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
616                 
617                 break;
618             case 180 :
619                 
620                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
621                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
622                 
623                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
624                     this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
625                     break;
626                 }
627                 
628                 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
629                 
630                 break;
631             case 270 :
632                 
633                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
634                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
635         
636                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
637                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
638                     break;
639                 }
640                 
641                 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
642                 
643                 break;
644             default : 
645                 break;
646         }
647         
648         this.previewEl.appendChild(this.canvasEl);
649         
650         this.setCanvasPosition();
651     },
652     
653     crop : function()
654     {
655         if(!this.canvasLoaded){
656             return;
657         }
658         var canvas = document.createElement("canvas");
659         
660         var context = canvas.getContext("2d");
661         
662         canvas.width = this.minWidth;
663         canvas.height = this.minHeight;
664         
665         var cropWidth = this.thumbEl.getWidth();
666         var cropHeight = this.thumbEl.getHeight();
667         
668         var x = this.thumbEl.getLeft(true) - this.previewEl.getLeft(true);
669         var y = this.thumbEl.getTop(true) - this.previewEl.getTop(true);
670         
671         if(this.canvasEl.width - cropWidth < x){
672             x = this.canvasEl.width - cropWidth;
673         }
674         
675         if(this.canvasEl.height - cropHeight < y){
676             y = this.canvasEl.height - cropHeight;
677         }
678         
679         x = x < 0 ? 0 : x;
680         y = y < 0 ? 0 : y;
681         
682         context.drawImage(this.canvasEl, x, y, cropWidth, cropHeight, 0, 0, canvas.width, canvas.height);
683         
684         this.cropData = canvas.toDataURL(this.cropType);
685         
686         this.fireEvent('crop', this, this.cropData);
687         
688     },
689     
690     setThumbBoxSize : function()
691     {
692         var height = 300;
693         var width = Math.ceil(this.minWidth * height / this.minHeight);
694         
695         if(this.minWidth > this.minHeight){
696             width = 300;
697             height = Math.ceil(this.minHeight * width / this.minWidth);
698         }
699         
700         this.thumbEl.setStyle({
701             width : width + 'px',
702             height : height + 'px'
703         });
704
705         return;
706             
707     },
708     
709     setThumbBoxPosition : function()
710     {
711         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
712         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
713         
714         this.thumbEl.setLeft(x);
715         this.thumbEl.setTop(y);
716         
717     },
718     
719     baseRotateLevel : function()
720     {
721         this.baseRotate = 1;
722         
723         if(
724                 typeof(this.exif) != 'undefined' &&
725                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
726                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
727         ){
728             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
729         }
730         
731         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
732         
733     },
734     
735     baseScaleLevel : function()
736     {
737         var width, height;
738         
739         if(this.baseRotate == 6 || this.baseRotate == 8){
740             
741             width = this.thumbEl.getHeight();
742             this.baseScale = height / this.imageEl.OriginHeight;
743             
744             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
745                 height = this.thumbEl.getWidth();
746                 this.baseScale = height / this.imageEl.OriginHeight;
747             }
748             
749             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
750                 height = this.thumbEl.getWidth();
751                 this.baseScale = height / this.imageEl.OriginHeight;
752                 
753                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
754                     width = this.thumbEl.getHeight();
755                     this.baseScale = width / this.imageEl.OriginWidth;
756                 }
757             }
758             
759             return;
760         }
761         
762         width = this.thumbEl.getWidth();
763         this.baseScale = width / this.imageEl.OriginWidth;
764         
765         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
766             height = this.thumbEl.getHeight();
767             this.baseScale = height / this.imageEl.OriginHeight;
768         }
769         
770         
771         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
772             
773             height = this.thumbEl.getHeight();
774             this.baseScale = height / this.imageEl.OriginHeight;
775             
776             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
777                 width = this.thumbEl.getWidth();
778                 this.baseScale = width / this.imageEl.OriginWidth;
779             }
780             
781         }
782         
783         return;
784     },
785     
786     getScaleLevel : function()
787     {
788         return this.baseScale * Math.pow(1.1, this.scale);
789     },
790     
791     onTouchStart : function(e)
792     {
793         if(!this.canvasLoaded){
794             this.beforeSelectFile(e);
795             return;
796         }
797         
798         var touches = e.browserEvent.touches;
799         
800         if(!touches){
801             return;
802         }
803         
804         if(touches.length == 1){
805             this.onMouseDown(e);
806             return;
807         }
808         
809         if(touches.length != 2){
810             return;
811         }
812         
813         var coords = [];
814         
815         for(var i = 0, finger; finger = touches[i]; i++){
816             coords.push(finger.pageX, finger.pageY);
817         }
818         
819         var x = Math.pow(coords[0] - coords[2], 2);
820         var y = Math.pow(coords[1] - coords[3], 2);
821         
822         this.startDistance = Math.sqrt(x + y);
823         
824         this.startScale = this.scale;
825         
826         this.pinching = true;
827         this.dragable = false;
828         
829     },
830     
831     onTouchMove : function(e)
832     {
833         if(!this.pinching && !this.dragable){
834             return;
835         }
836         
837         var touches = e.browserEvent.touches;
838         
839         if(!touches){
840             return;
841         }
842         
843         if(this.dragable){
844             this.onMouseMove(e);
845             return;
846         }
847         
848         var coords = [];
849         
850         for(var i = 0, finger; finger = touches[i]; i++){
851             coords.push(finger.pageX, finger.pageY);
852         }
853         
854         var x = Math.pow(coords[0] - coords[2], 2);
855         var y = Math.pow(coords[1] - coords[3], 2);
856         
857         this.endDistance = Math.sqrt(x + y);
858         
859         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
860         
861         if(!this.zoomable()){
862             this.scale = this.startScale;
863             return;
864         }
865         
866         this.draw();
867         
868     },
869     
870     onTouchEnd : function(e)
871     {
872         this.pinching = false;
873         this.dragable = false;
874         
875     },
876     
877     prepare : function(input)
878     {        
879         this.file = false;
880         this.exif = {};
881         
882         if(typeof(input) === 'string'){
883             this.loadCanvas(input);
884             return;
885         }
886         
887         if(!input.files || !input.files[0] || !this.urlAPI){
888             return;
889         }
890         
891         this.file = input.files[0];
892         this.cropType = this.file.type;
893         
894         var _this = this;
895         
896         if(this.fireEvent('prepare', this, this.file) != false){
897             
898             var reader = new FileReader();
899             
900             reader.onload = function (e) {
901                 if (e.target.error) {
902                     Roo.log(e.target.error);
903                     return;
904                 }
905                 
906                 var buffer = e.target.result,
907                     dataView = new DataView(buffer),
908                     offset = 2,
909                     maxOffset = dataView.byteLength - 4,
910                     markerBytes,
911                     markerLength;
912                 
913                 if (dataView.getUint16(0) === 0xffd8) {
914                     while (offset < maxOffset) {
915                         markerBytes = dataView.getUint16(offset);
916                         
917                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
918                             markerLength = dataView.getUint16(offset + 2) + 2;
919                             if (offset + markerLength > dataView.byteLength) {
920                                 Roo.log('Invalid meta data: Invalid segment size.');
921                                 break;
922                             }
923                             
924                             if(markerBytes == 0xffe1){
925                                 _this.parseExifData(
926                                     dataView,
927                                     offset,
928                                     markerLength
929                                 );
930                             }
931                             
932                             offset += markerLength;
933                             
934                             continue;
935                         }
936                         
937                         break;
938                     }
939                     
940                 }
941                 
942                 var url = _this.urlAPI.createObjectURL(_this.file);
943                 
944                 _this.loadCanvas(url);
945                 
946                 return;
947             }
948             
949             reader.readAsArrayBuffer(this.file);
950             
951         }
952         
953     },
954     
955     parseExifData : function(dataView, offset, length)
956     {
957         var tiffOffset = offset + 10,
958             littleEndian,
959             dirOffset;
960     
961         if (dataView.getUint32(offset + 4) !== 0x45786966) {
962             // No Exif data, might be XMP data instead
963             return;
964         }
965         
966         // Check for the ASCII code for "Exif" (0x45786966):
967         if (dataView.getUint32(offset + 4) !== 0x45786966) {
968             // No Exif data, might be XMP data instead
969             return;
970         }
971         if (tiffOffset + 8 > dataView.byteLength) {
972             Roo.log('Invalid Exif data: Invalid segment size.');
973             return;
974         }
975         // Check for the two null bytes:
976         if (dataView.getUint16(offset + 8) !== 0x0000) {
977             Roo.log('Invalid Exif data: Missing byte alignment offset.');
978             return;
979         }
980         // Check the byte alignment:
981         switch (dataView.getUint16(tiffOffset)) {
982         case 0x4949:
983             littleEndian = true;
984             break;
985         case 0x4D4D:
986             littleEndian = false;
987             break;
988         default:
989             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
990             return;
991         }
992         // Check for the TIFF tag marker (0x002A):
993         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
994             Roo.log('Invalid Exif data: Missing TIFF marker.');
995             return;
996         }
997         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
998         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
999         
1000         this.parseExifTags(
1001             dataView,
1002             tiffOffset,
1003             tiffOffset + dirOffset,
1004             littleEndian
1005         );
1006     },
1007     
1008     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
1009     {
1010         var tagsNumber,
1011             dirEndOffset,
1012             i;
1013         if (dirOffset + 6 > dataView.byteLength) {
1014             Roo.log('Invalid Exif data: Invalid directory offset.');
1015             return;
1016         }
1017         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
1018         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
1019         if (dirEndOffset + 4 > dataView.byteLength) {
1020             Roo.log('Invalid Exif data: Invalid directory size.');
1021             return;
1022         }
1023         for (i = 0; i < tagsNumber; i += 1) {
1024             this.parseExifTag(
1025                 dataView,
1026                 tiffOffset,
1027                 dirOffset + 2 + 12 * i, // tag offset
1028                 littleEndian
1029             );
1030         }
1031         // Return the offset to the next directory:
1032         return dataView.getUint32(dirEndOffset, littleEndian);
1033     },
1034     
1035     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
1036     {
1037         var tag = dataView.getUint16(offset, littleEndian);
1038         
1039         this.exif[tag] = this.getExifValue(
1040             dataView,
1041             tiffOffset,
1042             offset,
1043             dataView.getUint16(offset + 2, littleEndian), // tag type
1044             dataView.getUint32(offset + 4, littleEndian), // tag length
1045             littleEndian
1046         );
1047     },
1048     
1049     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
1050     {
1051         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
1052             tagSize,
1053             dataOffset,
1054             values,
1055             i,
1056             str,
1057             c;
1058     
1059         if (!tagType) {
1060             Roo.log('Invalid Exif data: Invalid tag type.');
1061             return;
1062         }
1063         
1064         tagSize = tagType.size * length;
1065         // Determine if the value is contained in the dataOffset bytes,
1066         // or if the value at the dataOffset is a pointer to the actual data:
1067         dataOffset = tagSize > 4 ?
1068                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
1069         if (dataOffset + tagSize > dataView.byteLength) {
1070             Roo.log('Invalid Exif data: Invalid data offset.');
1071             return;
1072         }
1073         if (length === 1) {
1074             return tagType.getValue(dataView, dataOffset, littleEndian);
1075         }
1076         values = [];
1077         for (i = 0; i < length; i += 1) {
1078             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
1079         }
1080         
1081         if (tagType.ascii) {
1082             str = '';
1083             // Concatenate the chars:
1084             for (i = 0; i < values.length; i += 1) {
1085                 c = values[i];
1086                 // Ignore the terminating NULL byte(s):
1087                 if (c === '\u0000') {
1088                     break;
1089                 }
1090                 str += c;
1091             }
1092             return str;
1093         }
1094         return values;
1095     }
1096     
1097 });
1098
1099 Roo.apply(Roo.bootstrap.UploadCropbox, {
1100     tags : {
1101         'Orientation': 0x0112
1102     },
1103     
1104     Orientation: {
1105             1: 0, //'top-left',
1106 //            2: 'top-right',
1107             3: 180, //'bottom-right',
1108 //            4: 'bottom-left',
1109 //            5: 'left-top',
1110             6: 90, //'right-top',
1111 //            7: 'right-bottom',
1112             8: 270 //'left-bottom'
1113     },
1114     
1115     exifTagTypes : {
1116         // byte, 8-bit unsigned int:
1117         1: {
1118             getValue: function (dataView, dataOffset) {
1119                 return dataView.getUint8(dataOffset);
1120             },
1121             size: 1
1122         },
1123         // ascii, 8-bit byte:
1124         2: {
1125             getValue: function (dataView, dataOffset) {
1126                 return String.fromCharCode(dataView.getUint8(dataOffset));
1127             },
1128             size: 1,
1129             ascii: true
1130         },
1131         // short, 16 bit int:
1132         3: {
1133             getValue: function (dataView, dataOffset, littleEndian) {
1134                 return dataView.getUint16(dataOffset, littleEndian);
1135             },
1136             size: 2
1137         },
1138         // long, 32 bit int:
1139         4: {
1140             getValue: function (dataView, dataOffset, littleEndian) {
1141                 return dataView.getUint32(dataOffset, littleEndian);
1142             },
1143             size: 4
1144         },
1145         // rational = two long values, first is numerator, second is denominator:
1146         5: {
1147             getValue: function (dataView, dataOffset, littleEndian) {
1148                 return dataView.getUint32(dataOffset, littleEndian) /
1149                     dataView.getUint32(dataOffset + 4, littleEndian);
1150             },
1151             size: 8
1152         },
1153         // slong, 32 bit signed int:
1154         9: {
1155             getValue: function (dataView, dataOffset, littleEndian) {
1156                 return dataView.getInt32(dataOffset, littleEndian);
1157             },
1158             size: 4
1159         },
1160         // srational, two slongs, first is numerator, second is denominator:
1161         10: {
1162             getValue: function (dataView, dataOffset, littleEndian) {
1163                 return dataView.getInt32(dataOffset, littleEndian) /
1164                     dataView.getInt32(dataOffset + 4, littleEndian);
1165             },
1166             size: 8
1167         }
1168     },
1169     
1170     footer : {
1171         STANDARD : [
1172             {
1173                 tag : 'div',
1174                 cls : 'btn-group roo-upload-cropbox-rotate-left',
1175                 action : 'rotate-left',
1176                 cn : [
1177                     {
1178                         tag : 'button',
1179                         cls : 'btn btn-default',
1180                         html : '<i class="fa fa-undo"></i>'
1181                     }
1182                 ]
1183             },
1184             {
1185                 tag : 'div',
1186                 cls : 'btn-group roo-upload-cropbox-picture',
1187                 action : 'picture',
1188                 cn : [
1189                     {
1190                         tag : 'button',
1191                         cls : 'btn btn-default',
1192                         html : '<i class="fa fa-picture-o"></i>'
1193                     }
1194                 ]
1195             },
1196             {
1197                 tag : 'div',
1198                 cls : 'btn-group roo-upload-cropbox-rotate-right',
1199                 action : 'rotate-right',
1200                 cn : [
1201                     {
1202                         tag : 'button',
1203                         cls : 'btn btn-default',
1204                         html : '<i class="fa fa-repeat"></i>'
1205                     }
1206                 ]
1207             }
1208         ],
1209         DOCUMENT : [
1210             {
1211                 tag : 'div',
1212                 cls : 'btn-group roo-upload-cropbox-rotate-left',
1213                 action : 'rotate-left',
1214                 cn : [
1215                     {
1216                         tag : 'button',
1217                         cls : 'btn btn-default',
1218                         html : '<i class="fa fa-undo"></i>'
1219                     }
1220                 ]
1221             },
1222             {
1223                 tag : 'div',
1224                 cls : 'btn-group roo-upload-cropbox-download',
1225                 action : 'download',
1226                 cn : [
1227                     {
1228                         tag : 'button',
1229                         cls : 'btn btn-default',
1230                         html : '<i class="fa fa-download"></i>'
1231                     }
1232                 ]
1233             },
1234             {
1235                 tag : 'div',
1236                 cls : 'btn-group roo-upload-cropbox-crop',
1237                 action : 'crop',
1238                 cn : [
1239                     {
1240                         tag : 'button',
1241                         cls : 'btn btn-default',
1242                         html : '<i class="fa fa-crop"></i>'
1243                     }
1244                 ]
1245             },
1246             {
1247                 tag : 'div',
1248                 cls : 'btn-group roo-upload-cropbox-trash',
1249                 action : 'trash',
1250                 cn : [
1251                     {
1252                         tag : 'button',
1253                         cls : 'btn btn-default',
1254                         html : '<i class="fa fa-trash"></i>'
1255                     }
1256                 ]
1257             },
1258             {
1259                 tag : 'div',
1260                 cls : 'btn-group roo-upload-cropbox-rotate-right',
1261                 action : 'rotate-right',
1262                 cn : [
1263                     {
1264                         tag : 'button',
1265                         cls : 'btn btn-default',
1266                         html : '<i class="fa fa-repeat"></i>'
1267                     }
1268                 ]
1269             }
1270         ]
1271     }
1272 });