Roo/bootstrap/UploadCropbox.js
[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  * @cfg {Boolean} isDocument (true|false) default false
17  * @cfg {String} url action url
18  * 
19  * @constructor
20  * Create a new UploadCropbox
21  * @param {Object} config The config object
22  */
23
24 Roo.bootstrap.UploadCropbox = function(config){
25     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26     
27     this.addEvents({
28         /**
29          * @event beforeselectfile
30          * Fire before select file
31          * @param {Roo.bootstrap.UploadCropbox} this
32          */
33         "beforeselectfile" : true,
34         /**
35          * @event initial
36          * Fire after initEvent
37          * @param {Roo.bootstrap.UploadCropbox} this
38          */
39         "initial" : true,
40         /**
41          * @event crop
42          * Fire after initEvent
43          * @param {Roo.bootstrap.UploadCropbox} this
44          * @param {String} data
45          */
46         "crop" : true,
47         /**
48          * @event prepare
49          * Fire when preparing the file data
50          * @param {Roo.bootstrap.UploadCropbox} this
51          * @param {Object} file
52          */
53         "prepare" : true,
54         /**
55          * @event exception
56          * Fire when get exception
57          * @param {Roo.bootstrap.UploadCropbox} this
58          * @param {String} type
59          * @param {Object} exception
60          */
61         "exception" : true,
62         /**
63          * @event beforeloadcanvas
64          * Fire before load the canvas
65          * @param {Roo.bootstrap.UploadCropbox} this
66          * @param {String} src
67          */
68         "beforeloadcanvas" : true,
69         /**
70          * @event trash
71          * Fire when trash image
72          * @param {Roo.bootstrap.UploadCropbox} this
73          */
74         "trash" : true,
75         /**
76          * @event download
77          * Fire when download the image
78          * @param {Roo.bootstrap.UploadCropbox} this
79          */
80         "download" : true,
81         /**
82          * @event footerbuttonclick
83          * Fire when footerbuttonclick
84          * @param {Roo.bootstrap.UploadCropbox} this
85          * @param {String} type
86          */
87         "footerbuttonclick" : true,
88         /**
89          * @event resize
90          * Fire when resize
91          * @param {Roo.bootstrap.UploadCropbox} this
92          */
93         "resize" : true,
94         /**
95          * @event rotate
96          * Fire when rotate the image
97          * @param {Roo.bootstrap.UploadCropbox} this
98          * @param {String} pos
99          */
100         "rotate" : true,
101         /**
102          * @event inspect
103          * Fire when inspect the file
104          * @param {Roo.bootstrap.UploadCropbox} this
105          * @param {Object} file
106          */
107         "inspect" : true,
108         /**
109          * @event upload
110          * Fire when xhr upload the file
111          * @param {Roo.bootstrap.UploadCropbox} this
112          * @param {Object} data
113          */
114         "upload" : true,
115         /**
116          * @event arrange
117          * Fire when arrange the file data
118          * @param {Roo.bootstrap.UploadCropbox} this
119          * @param {Object} formData
120          */
121         "arrange" : true
122     });
123     
124     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
125 };
126
127 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
128     
129     emptyText : 'Click to upload image',
130     rotateNotify : 'Image is too small to rotate',
131     errorTimeout : 3000,
132     scale : 0,
133     baseScale : 1,
134     rotate : 0,
135     dragable : false,
136     pinching : false,
137     mouseX : 0,
138     mouseY : 0,
139     cropData : false,
140     minWidth : 300,
141     minHeight : 300,
142     file : false,
143     exif : {},
144     baseRotate : 1,
145     cropType : 'image/jpeg',
146     buttons : false,
147     canvasLoaded : false,
148     isDocument : false,
149     
150     getAutoCreate : function()
151     {
152         var cfg = {
153             tag : 'div',
154             cls : 'roo-upload-cropbox',
155             cn : [
156                 {
157                     tag : 'div',
158                     cls : 'roo-upload-cropbox-body',
159                     style : 'cursor:pointer',
160                     cn : [
161                         {
162                             tag : 'input',
163                             cls : 'roo-upload-cropbox-selector',
164                             type : 'file'
165                         },
166                         {
167                             tag : 'div',
168                             cls : 'roo-upload-cropbox-preview'
169                         },
170                         {
171                             tag : 'div',
172                             cls : 'roo-upload-cropbox-thumb'
173                         },
174                         {
175                             tag : 'div',
176                             cls : 'roo-upload-cropbox-empty-notify',
177                             html : this.emptyText
178                         },
179                         {
180                             tag : 'div',
181                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
182                             html : this.rotateNotify
183                         }
184                     ]
185                 },
186                 {
187                     tag : 'div',
188                     cls : 'roo-upload-cropbox-footer',
189                     cn : {
190                         tag : 'div',
191                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
192                         cn : []
193                     }
194                 }
195             ]
196         };
197         
198         return cfg;
199     },
200     
201     onRender : function(ct, position)
202     {
203         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
204         
205         if (this.buttons.length) {
206             
207             Roo.each(this.buttons, function(bb) {
208                 
209                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
210                 
211                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
212                 
213             }, this);
214         }
215     },
216     
217     initEvents : function()
218     {
219         this.urlAPI = (window.createObjectURL && window) || 
220                                 (window.URL && URL.revokeObjectURL && URL) || 
221                                 (window.webkitURL && webkitURL);
222                         
223         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
224         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
225         
226         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
227         this.selectorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
228         this.selectorEl.hide();
229         
230         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
231         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
232         
233         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
234         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
235         this.thumbEl.hide();
236         
237         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
238         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
239         
240         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
241         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
242         this.errorEl.hide();
243         
244         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
245         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
246         this.footerEl.hide();
247         
248         this.setThumbBoxSize();
249         
250         this.bind();
251         
252         this.resize();
253         
254         this.fireEvent('initial', this);
255     },
256
257     bind : function()
258     {
259         var _this = this;
260         
261         window.addEventListener("resize", function() { _this.resize(); } );
262         
263         this.bodyEl.on('click', this.beforeSelectFile, this);
264         
265         if(Roo.isTouch){
266             this.bodyEl.on('touchstart', this.onTouchStart, this);
267             this.bodyEl.on('touchmove', this.onTouchMove, this);
268             this.bodyEl.on('touchend', this.onTouchEnd, this);
269         }
270         
271         if(!Roo.isTouch){
272             this.bodyEl.on('mousedown', this.onMouseDown, this);
273             this.bodyEl.on('mousemove', this.onMouseMove, this);
274             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
275             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
276             Roo.get(document).on('mouseup', this.onMouseUp, this);
277         }
278         
279         this.selectorEl.on('change', this.onFileSelected, this);
280     },
281     
282     reset : function()
283     {    
284         this.scale = 0;
285         this.baseScale = 1;
286         this.rotate = 0;
287         this.baseRotate = 1;
288         this.dragable = false;
289         this.pinching = false;
290         this.mouseX = 0;
291         this.mouseY = 0;
292         this.cropData = false;
293         this.notifyEl.dom.innerHTML = this.emptyText;
294         
295         this.selectorEl.dom.value = '';
296         
297     },
298     
299     resize : function()
300     {
301         if(this.fireEvent('resize', this) != false){
302             this.setThumbBoxPosition();
303             this.setCanvasPosition();
304         }
305     },
306     
307     onFooterButtonClick : function(e, el, o, type)
308     {
309         switch (type) {
310             case 'rotate-left' :
311                 this.onRotateLeft(e);
312                 break;
313             case 'rotate-right' :
314                 this.onRotateRight(e);
315                 break;
316             case 'picture' :
317                 this.beforeSelectFile(e);
318                 break;
319             case 'trash' :
320                 this.trash(e);
321                 break;
322             case 'crop' :
323                 this.crop(e);
324                 break;
325             case 'download' :
326                 this.download(e);
327                 break;
328             default :
329                 break;
330         }
331         
332         this.fireEvent('footerbuttonclick', this, type);
333     },
334     
335     beforeSelectFile : function(e)
336     {
337         e.preventDefault();
338         
339         if(this.fireEvent('beforeselectfile', this) != false){
340             this.selectorEl.dom.click();
341         }
342     },
343     
344     onFileSelected : function(e)
345     {
346         e.preventDefault();
347         
348         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
349             return;
350         }
351         
352         var file = this.selectorEl.dom.files[0];
353         
354         if(this.fireEvent('inspect', this, file) != false){
355             this.prepare(file);
356         }
357         
358     },
359     
360     trash : function(e)
361     {
362         this.fireEvent('trash', this);
363     },
364     
365     download : function(e)
366     {
367         this.fireEvent('download', this);
368     },
369     
370     loadCanvas : function(src)
371     {   
372         if(this.fireEvent('beforeloadcanvas', this, src) != false){
373             
374             this.reset();
375             
376             this.imageEl = document.createElement('img');
377             
378             var _this = this;
379             
380             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
381             
382             this.imageEl.src = src;
383         }
384     },
385     
386     onLoadCanvas : function()
387     {   
388         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
389         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
390         
391         if(this.imageEl.OriginWidth < this.minWidth || this.imageEl.OriginHeight < this.minHeight){
392             this.reset();
393             this.fireEvent('exception', this, 'volume', this.imageEl);
394             return;
395         }
396         
397         this.bodyEl.un('click', this.beforeSelectFile, this);
398         
399         this.notifyEl.hide();
400         this.thumbEl.show();
401         this.footerEl.show();
402         
403         
404         
405         this.baseRotateLevel();
406         
407         if(this.isDocument){
408             this.setThumbBoxSize();
409         }
410         
411         this.setThumbBoxPosition();
412         
413         this.baseScaleLevel();
414         
415         this.draw();
416         
417         this.resize();
418         
419         this.canvasLoaded = true;
420         
421     },
422     
423     setCanvasPosition : function()
424     {   
425         if(!this.canvasEl){
426             return;
427         }
428         
429         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
430         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
431         
432         this.previewEl.setLeft(pw);
433         this.previewEl.setTop(ph);
434         
435     },
436     
437     onMouseDown : function(e)
438     {   
439         e.stopEvent();
440         
441         this.dragable = true;
442         this.pinching = false;
443         
444         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
445             this.dragable = false;
446             return;
447         }
448         
449         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
450         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
451         
452     },
453     
454     onMouseMove : function(e)
455     {   
456         e.stopEvent();
457         
458         if(!this.canvasLoaded){
459             return;
460         }
461         
462         if (!this.dragable){
463             return;
464         }
465         
466         var minX = Math.ceil(this.thumbEl.getLeft(true));
467         var minY = Math.ceil(this.thumbEl.getTop(true));
468         
469         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
470         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
471         
472         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
473         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
474         
475         x = x - this.mouseX;
476         y = y - this.mouseY;
477         
478         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
479         var bgY = Math.ceil(y + this.previewEl.getTop(true));
480         
481         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
482         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
483         
484         this.previewEl.setLeft(bgX);
485         this.previewEl.setTop(bgY);
486         
487         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
488         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
489     },
490     
491     onMouseUp : function(e)
492     {   
493         e.stopEvent();
494         
495         this.dragable = false;
496     },
497     
498     onMouseWheel : function(e)
499     {   
500         e.stopEvent();
501         
502         this.startScale = this.scale;
503         
504         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
505         
506         if(!this.zoomable()){
507             this.scale = this.startScale;
508             return;
509         }
510         
511         this.draw();
512         
513         return;
514     },
515     
516     zoomable : function()
517     {
518         var minScale = this.thumbEl.getWidth() / this.minWidth;
519         
520         if(this.minWidth < this.minHeight){
521             minScale = this.thumbEl.getHeight() / this.minHeight;
522         }
523         
524         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
525         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
526         
527         if(
528                 this.isDocument &&
529                 (this.rotate == 0 || this.rotate == 180) && 
530                 (
531                     width > this.imageEl.OriginWidth || 
532                     height > this.imageEl.OriginHeight ||
533                     (width < this.minWidth && height < this.minHeight)
534                 )
535         ){
536             return false;
537         }
538         
539         if(
540                 this.isDocument &&
541                 (this.rotate == 90 || this.rotate == 270) && 
542                 (
543                     width > this.imageEl.OriginWidth || 
544                     height > this.imageEl.OriginHeight ||
545                     (width < this.minHeight && height < this.minWidth)
546                 )
547         ){
548             return false;
549         }
550         
551         if(
552                 !this.isDocument &&
553                 (this.rotate == 0 || this.rotate == 180) && 
554                 (
555                     width < this.minWidth || 
556                     width > this.imageEl.OriginWidth || 
557                     height < this.minHeight || 
558                     height > this.imageEl.OriginHeight
559                 )
560         ){
561             return false;
562         }
563         
564         if(
565                 !this.isDocument &&
566                 (this.rotate == 90 || this.rotate == 270) && 
567                 (
568                     width < this.minHeight || 
569                     width > this.imageEl.OriginWidth || 
570                     height < this.minWidth || 
571                     height > this.imageEl.OriginHeight
572                 )
573         ){
574             return false;
575         }
576         
577         return true;
578         
579     },
580     
581     onRotateLeft : function(e)
582     {   
583         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
584             
585             var minScale = this.thumbEl.getWidth() / this.minWidth;
586             
587             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
588             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
589             
590             this.startScale = this.scale;
591             
592             while (this.getScaleLevel() < minScale){
593             
594                 this.scale = this.scale + 1;
595                 
596                 if(!this.zoomable()){
597                     break;
598                 }
599                 
600                 if(
601                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
602                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
603                 ){
604                     continue;
605                 }
606                 
607                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
608
609                 this.draw();
610                 
611                 return;
612             }
613             
614             this.scale = this.startScale;
615             
616             this.onRotateFail();
617             
618             return false;
619         }
620         
621         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
622
623         if(this.isDocument){
624             this.setThumbBoxSize();
625             this.setThumbBoxPosition();
626             this.setCanvasPosition();
627         }
628         
629         this.draw();
630         
631         this.fireEvent('rotate', this, 'left');
632         
633     },
634     
635     onRotateRight : function(e)
636     {
637         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
638             
639             var minScale = this.thumbEl.getWidth() / this.minWidth;
640         
641             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
642             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
643             
644             this.startScale = this.scale;
645             
646             while (this.getScaleLevel() < minScale){
647             
648                 this.scale = this.scale + 1;
649                 
650                 if(!this.zoomable()){
651                     break;
652                 }
653                 
654                 if(
655                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
656                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
657                 ){
658                     continue;
659                 }
660                 
661                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
662
663                 this.draw();
664                 
665                 return;
666             }
667             
668             this.scale = this.startScale;
669             
670             this.onRotateFail();
671             
672             return false;
673         }
674         
675         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
676
677         if(this.isDocument){
678             this.setThumbBoxSize();
679             this.setThumbBoxPosition();
680             this.setCanvasPosition();
681         }
682         
683         this.draw();
684         
685         this.fireEvent('rotate', this, 'right');
686     },
687     
688     onRotateFail : function()
689     {
690         this.errorEl.show(true);
691         
692         var _this = this;
693         
694         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
695     },
696     
697     draw : function()
698     {
699         this.previewEl.dom.innerHTML = '';
700         
701         var canvasEl = document.createElement("canvas");
702         
703         var contextEl = canvasEl.getContext("2d");
704         
705         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
706         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
707         var center = this.imageEl.OriginWidth / 2;
708         
709         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
710             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
711             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
712             center = this.imageEl.OriginHeight / 2;
713         }
714         
715         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
716         
717         contextEl.translate(center, center);
718         contextEl.rotate(this.rotate * Math.PI / 180);
719
720         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
721         
722         this.canvasEl = document.createElement("canvas");
723         
724         this.contextEl = this.canvasEl.getContext("2d");
725         
726         switch (this.rotate) {
727             case 0 :
728                 
729                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
730                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
731                 
732                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
733                 
734                 break;
735             case 90 : 
736                 
737                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
738                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
739                 
740                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
741                     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);
742                     break;
743                 }
744                 
745                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
746                 
747                 break;
748             case 180 :
749                 
750                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
751                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
752                 
753                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
754                     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);
755                     break;
756                 }
757                 
758                 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);
759                 
760                 break;
761             case 270 :
762                 
763                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
764                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
765         
766                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
767                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
768                     break;
769                 }
770                 
771                 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);
772                 
773                 break;
774             default : 
775                 break;
776         }
777         
778         this.previewEl.appendChild(this.canvasEl);
779         
780         this.setCanvasPosition();
781     },
782     
783     crop : function()
784     {
785         if(!this.canvasLoaded){
786             return;
787         }
788         
789         var imageCanvas = document.createElement("canvas");
790         
791         var imageContext = imageCanvas.getContext("2d");
792         
793         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
794         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
795         
796         var center = imageCanvas.width / 2;
797         
798         imageContext.translate(center, center);
799         
800         imageContext.rotate(this.rotate * Math.PI / 180);
801         
802         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
803         
804         var canvas = document.createElement("canvas");
805         
806         var context = canvas.getContext("2d");
807                 
808         canvas.width = this.minWidth;
809         canvas.height = this.minHeight;
810
811         switch (this.rotate) {
812             case 0 :
813                 
814                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
815                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
816                 
817                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
818                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
819                 
820                 var targetWidth = this.minWidth - 2 * x;
821                 var targetHeight = this.minHeight - 2 * y;
822                 
823                 var scale = 1;
824                 
825                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
826                     scale = targetWidth / width;
827                 }
828                 
829                 if(x > 0 && y == 0){
830                     scale = targetHeight / height;
831                 }
832                 
833                 if(x > 0 && y > 0){
834                     scale = targetWidth / width;
835                     
836                     if(width < height){
837                         scale = targetHeight / height;
838                     }
839                 }
840                 
841                 context.scale(scale, scale);
842                 
843                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
844                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
845
846                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
847                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
848
849                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
850                 
851                 break;
852             case 90 : 
853                 
854                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
855                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
856                 
857                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
858                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
859                 
860                 var targetWidth = this.minWidth - 2 * x;
861                 var targetHeight = this.minHeight - 2 * y;
862                 
863                 var scale = 1;
864                 
865                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
866                     scale = targetWidth / width;
867                 }
868                 
869                 if(x > 0 && y == 0){
870                     scale = targetHeight / height;
871                 }
872                 
873                 if(x > 0 && y > 0){
874                     scale = targetWidth / width;
875                     
876                     if(width < height){
877                         scale = targetHeight / height;
878                     }
879                 }
880                 
881                 context.scale(scale, scale);
882                 
883                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
884                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
885
886                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
887                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
888                 
889                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
890                 
891                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
892                 
893                 break;
894             case 180 :
895                 
896                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
897                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
898                 
899                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
900                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
901                 
902                 var targetWidth = this.minWidth - 2 * x;
903                 var targetHeight = this.minHeight - 2 * y;
904                 
905                 var scale = 1;
906                 
907                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
908                     scale = targetWidth / width;
909                 }
910                 
911                 if(x > 0 && y == 0){
912                     scale = targetHeight / height;
913                 }
914                 
915                 if(x > 0 && y > 0){
916                     scale = targetWidth / width;
917                     
918                     if(width < height){
919                         scale = targetHeight / height;
920                     }
921                 }
922                 
923                 context.scale(scale, scale);
924                 
925                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
926                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
927
928                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
929                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
930
931                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
932                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
933                 
934                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
935                 
936                 break;
937             case 270 :
938                 
939                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
940                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
941                 
942                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
943                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
944                 
945                 var targetWidth = this.minWidth - 2 * x;
946                 var targetHeight = this.minHeight - 2 * y;
947                 
948                 var scale = 1;
949                 
950                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
951                     scale = targetWidth / width;
952                 }
953                 
954                 if(x > 0 && y == 0){
955                     scale = targetHeight / height;
956                 }
957                 
958                 if(x > 0 && y > 0){
959                     scale = targetWidth / width;
960                     
961                     if(width < height){
962                         scale = targetHeight / height;
963                     }
964                 }
965                 
966                 context.scale(scale, scale);
967                 
968                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
969                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
970
971                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
972                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
973                 
974                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
975                 
976                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
977                 
978                 break;
979             default : 
980                 break;
981         }
982         
983         this.cropData = canvas.toDataURL(this.cropType);
984         
985         this.fireEvent('crop', this, this.cropData);
986         
987         return;
988         
989     },
990     
991     setThumbBoxSize : function()
992     {
993         var width, height;
994         
995         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
996             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
997             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
998             
999             this.minWidth = width;
1000             this.minHeight = height;
1001             
1002             if(this.rotate == 90 || this.rotate == 270){
1003                 this.minWidth = height;
1004                 this.minHeight = width;
1005             }
1006         }
1007         
1008         height = 300;
1009         width = Math.ceil(this.minWidth * height / this.minHeight);
1010         
1011         if(this.minWidth > this.minHeight){
1012             width = 300;
1013             height = Math.ceil(this.minHeight * width / this.minWidth);
1014         }
1015         
1016         this.thumbEl.setStyle({
1017             width : width + 'px',
1018             height : height + 'px'
1019         });
1020
1021         return;
1022             
1023     },
1024     
1025     setThumbBoxPosition : function()
1026     {
1027         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
1028         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
1029         
1030         this.thumbEl.setLeft(x);
1031         this.thumbEl.setTop(y);
1032         
1033     },
1034     
1035     baseRotateLevel : function()
1036     {
1037         this.baseRotate = 1;
1038         
1039         if(
1040                 typeof(this.exif) != 'undefined' &&
1041                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
1042                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
1043         ){
1044             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
1045         }
1046         
1047         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
1048         
1049     },
1050     
1051     baseScaleLevel : function()
1052     {
1053         var width, height;
1054         
1055         if(this.isDocument){
1056             
1057             if(this.baseRotate == 6 || this.baseRotate == 8){
1058             
1059                 height = this.thumbEl.getHeight();
1060                 this.baseScale = height / this.imageEl.OriginWidth;
1061
1062                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
1063                     width = this.thumbEl.getWidth();
1064                     this.baseScale = width / this.imageEl.OriginHeight;
1065                 }
1066
1067                 return;
1068             }
1069
1070             height = this.thumbEl.getHeight();
1071             this.baseScale = height / this.imageEl.OriginHeight;
1072
1073             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
1074                 width = this.thumbEl.getWidth();
1075                 this.baseScale = width / this.imageEl.OriginWidth;
1076             }
1077
1078             return;
1079         }
1080         
1081         if(this.baseRotate == 6 || this.baseRotate == 8){
1082             
1083             width = this.thumbEl.getHeight();
1084             this.baseScale = width / this.imageEl.OriginHeight;
1085             
1086             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
1087                 height = this.thumbEl.getWidth();
1088                 this.baseScale = height / this.imageEl.OriginHeight;
1089             }
1090             
1091             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
1092                 height = this.thumbEl.getWidth();
1093                 this.baseScale = height / this.imageEl.OriginHeight;
1094                 
1095                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
1096                     width = this.thumbEl.getHeight();
1097                     this.baseScale = width / this.imageEl.OriginWidth;
1098                 }
1099             }
1100             
1101             return;
1102         }
1103         
1104         width = this.thumbEl.getWidth();
1105         this.baseScale = width / this.imageEl.OriginWidth;
1106         
1107         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
1108             height = this.thumbEl.getHeight();
1109             this.baseScale = height / this.imageEl.OriginHeight;
1110         }
1111         
1112         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
1113             
1114             height = this.thumbEl.getHeight();
1115             this.baseScale = height / this.imageEl.OriginHeight;
1116             
1117             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
1118                 width = this.thumbEl.getWidth();
1119                 this.baseScale = width / this.imageEl.OriginWidth;
1120             }
1121             
1122         }
1123         
1124         return;
1125     },
1126     
1127     getScaleLevel : function()
1128     {
1129         return this.baseScale * Math.pow(1.1, this.scale);
1130     },
1131     
1132     onTouchStart : function(e)
1133     {
1134         if(!this.canvasLoaded){
1135             this.beforeSelectFile(e);
1136             return;
1137         }
1138         
1139         var touches = e.browserEvent.touches;
1140         
1141         if(!touches){
1142             return;
1143         }
1144         
1145         if(touches.length == 1){
1146             this.onMouseDown(e);
1147             return;
1148         }
1149         
1150         if(touches.length != 2){
1151             return;
1152         }
1153         
1154         var coords = [];
1155         
1156         for(var i = 0, finger; finger = touches[i]; i++){
1157             coords.push(finger.pageX, finger.pageY);
1158         }
1159         
1160         var x = Math.pow(coords[0] - coords[2], 2);
1161         var y = Math.pow(coords[1] - coords[3], 2);
1162         
1163         this.startDistance = Math.sqrt(x + y);
1164         
1165         this.startScale = this.scale;
1166         
1167         this.pinching = true;
1168         this.dragable = false;
1169         
1170     },
1171     
1172     onTouchMove : function(e)
1173     {
1174         if(!this.pinching && !this.dragable){
1175             return;
1176         }
1177         
1178         var touches = e.browserEvent.touches;
1179         
1180         if(!touches){
1181             return;
1182         }
1183         
1184         if(this.dragable){
1185             this.onMouseMove(e);
1186             return;
1187         }
1188         
1189         var coords = [];
1190         
1191         for(var i = 0, finger; finger = touches[i]; i++){
1192             coords.push(finger.pageX, finger.pageY);
1193         }
1194         
1195         var x = Math.pow(coords[0] - coords[2], 2);
1196         var y = Math.pow(coords[1] - coords[3], 2);
1197         
1198         this.endDistance = Math.sqrt(x + y);
1199         
1200         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
1201         
1202         if(!this.zoomable()){
1203             this.scale = this.startScale;
1204             return;
1205         }
1206         
1207         this.draw();
1208         
1209     },
1210     
1211     onTouchEnd : function(e)
1212     {
1213         this.pinching = false;
1214         this.dragable = false;
1215         
1216     },
1217     
1218     process : function(file, crop)
1219     {
1220         this.xhr = new XMLHttpRequest();
1221         
1222         if(typeof(this.file.id) != 'undefined' && this.file.id * 1 > 0){
1223             return;
1224         }
1225         
1226         file.xhr = this.xhr;
1227
1228         this.xhr.open(this.method, this.url, true);
1229         
1230         var headers = {
1231             "Accept": "application/json",
1232             "Cache-Control": "no-cache",
1233             "X-Requested-With": "XMLHttpRequest"
1234         };
1235         
1236         for (var headerName in headers) {
1237             var headerValue = headers[headerName];
1238             if (headerValue) {
1239                 this.xhr.setRequestHeader(headerName, headerValue);
1240             }
1241         }
1242         
1243         var _this = this;
1244         
1245         this.xhr.onload = function()
1246         {
1247             _this.xhrOnLoad(_this.xhr);
1248         }
1249         
1250         this.xhr.onerror = function()
1251         {
1252             _this.xhrOnError(_this.xhr);
1253         }
1254         
1255         var formData = new FormData();
1256
1257         formData.append('returnHTML', 'NO');
1258         
1259         if(crop){
1260             formData.append('crop', crop);
1261         }
1262         
1263         formData.append(this.paramName, file, file.name);
1264         
1265         if(this.fireEvent('arrange', this, formData) != false){
1266             this.xhr.send(formData);
1267         };
1268     },
1269     
1270     xhrOnLoad : function(xhr)
1271     {
1272         if (xhr.readyState !== 4) {
1273             this.fireEvent('exception', this, 'xhr', xhr);
1274             return;
1275         }
1276
1277         var response = Roo.decode(xhr.responseText);
1278         
1279         if(!response.success){
1280             this.fireEvent('exception', this, 'xhr', xhr);
1281             return;
1282         }
1283         
1284         var response = Roo.decode(xhr.responseText);
1285         
1286         this.fireEvent('upload', this, response);
1287         
1288     },
1289     
1290     xhrOnError : function()
1291     {
1292         Roo.log('xhr on error');
1293         
1294         var response = Roo.decode(xhr.responseText);
1295           
1296         Roo.log(response);
1297         
1298     },
1299     
1300     uploadFromSource : function(file, crop)
1301     {
1302         this.xhr = new XMLHttpRequest();
1303         
1304         this.xhr.open(this.method, this.url, true);
1305         
1306         var headers = {
1307             "Accept": "application/json",
1308             "Cache-Control": "no-cache",
1309             "X-Requested-With": "XMLHttpRequest"
1310         };
1311         
1312         for (var headerName in headers) {
1313             var headerValue = headers[headerName];
1314             if (headerValue) {
1315                 this.xhr.setRequestHeader(headerName, headerValue);
1316             }
1317         }
1318         
1319         var _this = this;
1320         
1321         this.xhr.onload = function()
1322         {
1323             _this.xhrOnLoad(_this.xhr);
1324         }
1325         
1326         this.xhr.onerror = function()
1327         {
1328             _this.xhrOnError(_this.xhr);
1329         }
1330         
1331         var formData = new FormData();
1332
1333         formData.append('returnHTML', 'NO');
1334         
1335         formData.append('crop', crop);
1336         
1337         if(typeof(file.filename) != 'undefined'){
1338             formData.append('filename', file.filename);
1339         }
1340         
1341         if(typeof(file.mimetype) != 'undefined'){
1342             formData.append('mimetype', file.mimetype);
1343         }
1344         
1345         if(this.fireEvent('arrange', this, formData) != false){
1346             this.xhr.send(formData);
1347         };
1348     },
1349     
1350     prepare : function(file)
1351     {   
1352         this.file = false;
1353         this.exif = {};
1354         
1355         if(typeof(file) === 'string'){
1356             this.loadCanvas(file);
1357             return;
1358         }
1359         
1360         if(!file || !this.urlAPI){
1361             return;
1362         }
1363         
1364         this.file = file;
1365         this.cropType = file.type;
1366         
1367         var _this = this;
1368         
1369         if(this.fireEvent('prepare', this, this.file) != false){
1370             
1371             var reader = new FileReader();
1372             
1373             reader.onload = function (e) {
1374                 if (e.target.error) {
1375                     Roo.log(e.target.error);
1376                     return;
1377                 }
1378                 
1379                 var buffer = e.target.result,
1380                     dataView = new DataView(buffer),
1381                     offset = 2,
1382                     maxOffset = dataView.byteLength - 4,
1383                     markerBytes,
1384                     markerLength;
1385                 
1386                 if (dataView.getUint16(0) === 0xffd8) {
1387                     while (offset < maxOffset) {
1388                         markerBytes = dataView.getUint16(offset);
1389                         
1390                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
1391                             markerLength = dataView.getUint16(offset + 2) + 2;
1392                             if (offset + markerLength > dataView.byteLength) {
1393                                 Roo.log('Invalid meta data: Invalid segment size.');
1394                                 break;
1395                             }
1396                             
1397                             if(markerBytes == 0xffe1){
1398                                 _this.parseExifData(
1399                                     dataView,
1400                                     offset,
1401                                     markerLength
1402                                 );
1403                             }
1404                             
1405                             offset += markerLength;
1406                             
1407                             continue;
1408                         }
1409                         
1410                         break;
1411                     }
1412                     
1413                 }
1414                 
1415                 var url = _this.urlAPI.createObjectURL(_this.file);
1416                 
1417                 _this.loadCanvas(url);
1418                 
1419                 return;
1420             }
1421             
1422             reader.readAsArrayBuffer(this.file);
1423             
1424         }
1425         
1426     },
1427     
1428     parseExifData : function(dataView, offset, length)
1429     {
1430         var tiffOffset = offset + 10,
1431             littleEndian,
1432             dirOffset;
1433     
1434         if (dataView.getUint32(offset + 4) !== 0x45786966) {
1435             // No Exif data, might be XMP data instead
1436             return;
1437         }
1438         
1439         // Check for the ASCII code for "Exif" (0x45786966):
1440         if (dataView.getUint32(offset + 4) !== 0x45786966) {
1441             // No Exif data, might be XMP data instead
1442             return;
1443         }
1444         if (tiffOffset + 8 > dataView.byteLength) {
1445             Roo.log('Invalid Exif data: Invalid segment size.');
1446             return;
1447         }
1448         // Check for the two null bytes:
1449         if (dataView.getUint16(offset + 8) !== 0x0000) {
1450             Roo.log('Invalid Exif data: Missing byte alignment offset.');
1451             return;
1452         }
1453         // Check the byte alignment:
1454         switch (dataView.getUint16(tiffOffset)) {
1455         case 0x4949:
1456             littleEndian = true;
1457             break;
1458         case 0x4D4D:
1459             littleEndian = false;
1460             break;
1461         default:
1462             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
1463             return;
1464         }
1465         // Check for the TIFF tag marker (0x002A):
1466         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
1467             Roo.log('Invalid Exif data: Missing TIFF marker.');
1468             return;
1469         }
1470         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
1471         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
1472         
1473         this.parseExifTags(
1474             dataView,
1475             tiffOffset,
1476             tiffOffset + dirOffset,
1477             littleEndian
1478         );
1479     },
1480     
1481     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
1482     {
1483         var tagsNumber,
1484             dirEndOffset,
1485             i;
1486         if (dirOffset + 6 > dataView.byteLength) {
1487             Roo.log('Invalid Exif data: Invalid directory offset.');
1488             return;
1489         }
1490         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
1491         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
1492         if (dirEndOffset + 4 > dataView.byteLength) {
1493             Roo.log('Invalid Exif data: Invalid directory size.');
1494             return;
1495         }
1496         for (i = 0; i < tagsNumber; i += 1) {
1497             this.parseExifTag(
1498                 dataView,
1499                 tiffOffset,
1500                 dirOffset + 2 + 12 * i, // tag offset
1501                 littleEndian
1502             );
1503         }
1504         // Return the offset to the next directory:
1505         return dataView.getUint32(dirEndOffset, littleEndian);
1506     },
1507     
1508     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
1509     {
1510         var tag = dataView.getUint16(offset, littleEndian);
1511         
1512         this.exif[tag] = this.getExifValue(
1513             dataView,
1514             tiffOffset,
1515             offset,
1516             dataView.getUint16(offset + 2, littleEndian), // tag type
1517             dataView.getUint32(offset + 4, littleEndian), // tag length
1518             littleEndian
1519         );
1520     },
1521     
1522     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
1523     {
1524         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
1525             tagSize,
1526             dataOffset,
1527             values,
1528             i,
1529             str,
1530             c;
1531     
1532         if (!tagType) {
1533             Roo.log('Invalid Exif data: Invalid tag type.');
1534             return;
1535         }
1536         
1537         tagSize = tagType.size * length;
1538         // Determine if the value is contained in the dataOffset bytes,
1539         // or if the value at the dataOffset is a pointer to the actual data:
1540         dataOffset = tagSize > 4 ?
1541                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
1542         if (dataOffset + tagSize > dataView.byteLength) {
1543             Roo.log('Invalid Exif data: Invalid data offset.');
1544             return;
1545         }
1546         if (length === 1) {
1547             return tagType.getValue(dataView, dataOffset, littleEndian);
1548         }
1549         values = [];
1550         for (i = 0; i < length; i += 1) {
1551             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
1552         }
1553         
1554         if (tagType.ascii) {
1555             str = '';
1556             // Concatenate the chars:
1557             for (i = 0; i < values.length; i += 1) {
1558                 c = values[i];
1559                 // Ignore the terminating NULL byte(s):
1560                 if (c === '\u0000') {
1561                     break;
1562                 }
1563                 str += c;
1564             }
1565             return str;
1566         }
1567         return values;
1568     }
1569     
1570 });
1571
1572 Roo.apply(Roo.bootstrap.UploadCropbox, {
1573     tags : {
1574         'Orientation': 0x0112
1575     },
1576     
1577     Orientation: {
1578             1: 0, //'top-left',
1579 //            2: 'top-right',
1580             3: 180, //'bottom-right',
1581 //            4: 'bottom-left',
1582 //            5: 'left-top',
1583             6: 90, //'right-top',
1584 //            7: 'right-bottom',
1585             8: 270 //'left-bottom'
1586     },
1587     
1588     exifTagTypes : {
1589         // byte, 8-bit unsigned int:
1590         1: {
1591             getValue: function (dataView, dataOffset) {
1592                 return dataView.getUint8(dataOffset);
1593             },
1594             size: 1
1595         },
1596         // ascii, 8-bit byte:
1597         2: {
1598             getValue: function (dataView, dataOffset) {
1599                 return String.fromCharCode(dataView.getUint8(dataOffset));
1600             },
1601             size: 1,
1602             ascii: true
1603         },
1604         // short, 16 bit int:
1605         3: {
1606             getValue: function (dataView, dataOffset, littleEndian) {
1607                 return dataView.getUint16(dataOffset, littleEndian);
1608             },
1609             size: 2
1610         },
1611         // long, 32 bit int:
1612         4: {
1613             getValue: function (dataView, dataOffset, littleEndian) {
1614                 return dataView.getUint32(dataOffset, littleEndian);
1615             },
1616             size: 4
1617         },
1618         // rational = two long values, first is numerator, second is denominator:
1619         5: {
1620             getValue: function (dataView, dataOffset, littleEndian) {
1621                 return dataView.getUint32(dataOffset, littleEndian) /
1622                     dataView.getUint32(dataOffset + 4, littleEndian);
1623             },
1624             size: 8
1625         },
1626         // slong, 32 bit signed int:
1627         9: {
1628             getValue: function (dataView, dataOffset, littleEndian) {
1629                 return dataView.getInt32(dataOffset, littleEndian);
1630             },
1631             size: 4
1632         },
1633         // srational, two slongs, first is numerator, second is denominator:
1634         10: {
1635             getValue: function (dataView, dataOffset, littleEndian) {
1636                 return dataView.getInt32(dataOffset, littleEndian) /
1637                     dataView.getInt32(dataOffset + 4, littleEndian);
1638             },
1639             size: 8
1640         }
1641     },
1642     
1643     footer : {
1644         STANDARD : [
1645             {
1646                 tag : 'div',
1647                 cls : 'btn-group roo-upload-cropbox-rotate-left',
1648                 action : 'rotate-left',
1649                 cn : [
1650                     {
1651                         tag : 'button',
1652                         cls : 'btn btn-default',
1653                         html : '<i class="fa fa-undo"></i>'
1654                     }
1655                 ]
1656             },
1657             {
1658                 tag : 'div',
1659                 cls : 'btn-group roo-upload-cropbox-picture',
1660                 action : 'picture',
1661                 cn : [
1662                     {
1663                         tag : 'button',
1664                         cls : 'btn btn-default',
1665                         html : '<i class="fa fa-picture-o"></i>'
1666                     }
1667                 ]
1668             },
1669             {
1670                 tag : 'div',
1671                 cls : 'btn-group roo-upload-cropbox-rotate-right',
1672                 action : 'rotate-right',
1673                 cn : [
1674                     {
1675                         tag : 'button',
1676                         cls : 'btn btn-default',
1677                         html : '<i class="fa fa-repeat"></i>'
1678                     }
1679                 ]
1680             }
1681         ],
1682         DOCUMENT : [
1683             {
1684                 tag : 'div',
1685                 cls : 'btn-group roo-upload-cropbox-rotate-left',
1686                 action : 'rotate-left',
1687                 cn : [
1688                     {
1689                         tag : 'button',
1690                         cls : 'btn btn-default',
1691                         html : '<i class="fa fa-undo"></i>'
1692                     }
1693                 ]
1694             },
1695             {
1696                 tag : 'div',
1697                 cls : 'btn-group roo-upload-cropbox-download',
1698                 action : 'download',
1699                 cn : [
1700                     {
1701                         tag : 'button',
1702                         cls : 'btn btn-default',
1703                         html : '<i class="fa fa-download"></i>'
1704                     }
1705                 ]
1706             },
1707             {
1708                 tag : 'div',
1709                 cls : 'btn-group roo-upload-cropbox-crop',
1710                 action : 'crop',
1711                 cn : [
1712                     {
1713                         tag : 'button',
1714                         cls : 'btn btn-default',
1715                         html : '<i class="fa fa-crop"></i>'
1716                     }
1717                 ]
1718             },
1719             {
1720                 tag : 'div',
1721                 cls : 'btn-group roo-upload-cropbox-trash',
1722                 action : 'trash',
1723                 cn : [
1724                     {
1725                         tag : 'button',
1726                         cls : 'btn btn-default',
1727                         html : '<i class="fa fa-trash"></i>'
1728                     }
1729                 ]
1730             },
1731             {
1732                 tag : 'div',
1733                 cls : 'btn-group roo-upload-cropbox-rotate-right',
1734                 action : 'rotate-right',
1735                 cn : [
1736                     {
1737                         tag : 'button',
1738                         cls : 'btn btn-default',
1739                         html : '<i class="fa fa-repeat"></i>'
1740                     }
1741                 ]
1742             }
1743         ],
1744         ROTATOR : [
1745             {
1746                 tag : 'div',
1747                 cls : 'btn-group roo-upload-cropbox-rotate-left',
1748                 action : 'rotate-left',
1749                 cn : [
1750                     {
1751                         tag : 'button',
1752                         cls : 'btn btn-default',
1753                         html : '<i class="fa fa-undo"></i>'
1754                     }
1755                 ]
1756             },
1757             {
1758                 tag : 'div',
1759                 cls : 'btn-group roo-upload-cropbox-rotate-right',
1760                 action : 'rotate-right',
1761                 cn : [
1762                     {
1763                         tag : 'button',
1764                         cls : 'btn btn-default',
1765                         html : '<i class="fa fa-repeat"></i>'
1766                     }
1767                 ]
1768             }
1769         ]
1770     }
1771 });