Roo/bootstrap/DocumentViewer.js
[roojs1] / Roo / bootstrap / DocumentViewer.js
1
2 /*
3 * Licence: LGPL
4 */
5
6 /**
7  * @class Roo.bootstrap.DocumentViewer
8  * @extends Roo.bootstrap.Component
9  * Bootstrap DocumentViewer class
10  * @cfg {Number} thumbWidth default 300
11  * @cfg {Number} thumbHeight default 300
12  * 
13  * @constructor
14  * Create a new DocumentViewer
15  * @param {Object} config The config object
16  */
17
18 Roo.bootstrap.DocumentViewer = function(config){
19     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
20     
21     this.addEvents({
22         /**
23          * @event initial
24          * Fire after initEvent
25          * @param {Roo.bootstrap.DocumentViewer} this
26          */
27         "initial" : true
28         
29     });
30 };
31
32 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33     
34     thumbWidth : 300,
35     thumbHeight : 300,
36     
37     getAutoCreate : function()
38     {
39         var cfg = {
40             tag : 'div',
41             cls : 'roo-document-viewer',
42             cn : [
43                 {
44                     tag : 'div',
45                     cls : 'roo-document-viewer-body',
46                     cn : [
47                         {
48                             tag : 'div',
49                             cls : 'roo-document-viewer-preview'
50                         },
51                         {
52                             tag : 'div',
53                             cls : 'roo-document-viewer-thumb',
54                             style : 'width: ' + this.thumbWidth + 'px; height: ' + this.thumbHeight + 'px;'
55                         }
56                     ]
57                 },
58                 {
59                     tag : 'div',
60                     cls : 'roo-document-viewer-footer',
61                     cn : {
62                         tag : 'div',
63                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
64                         cn : [
65                             {
66                                 tag : 'div',
67                                 cls : 'btn-group',
68                                 cn : [
69                                     {
70                                         tag : 'button',
71                                         cls : 'btn btn-default roo-document-viewer-delete',
72                                         html : '<i class="fa fa-trash"></i>'
73                                     }
74                                 ]
75                             }
76                         ]
77                     }
78                 }
79             ]
80         };
81         
82         return cfg;
83     },
84     
85     initEvents : function()
86     {
87         
88         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
89         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
90         
91         this.previewEl = this.el.select('.roo-document-viewer-preview', true).first();
92         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
93         
94         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
95         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
96         
97         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
98         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
99         
100         this.bind();
101         
102         this.fireEvent('initial', this);
103     },
104
105     bind : function()
106     {
107         var _this = this;
108         
109         window.addEventListener("resize", function() { _this.resize(); } );
110         
111         
112         this.bodyEl.on('click', this.onClick, this);
113         
114         this.pictureBtn.on('click', this.beforeSelectFile, this);
115         
116         this.rotateLeft.on('click', this.onRotateLeft, this);
117         
118         this.rotateRight.on('click', this.onRotateRight, this);
119         
120     },
121     
122     reset : function()
123     {    
124         this.scale = 0;
125         this.baseScale = 1;
126         this.rotate = 0;
127         this.baseRotate = 1;
128         this.dragable = false;
129         this.pinching = false;
130         this.mouseX = 0;
131         this.mouseY = 0;
132         this.cropData = false;
133         
134     },
135     
136     resize : function()
137     {
138         this.setThumbBoxPosition();
139         this.setCanvasPosition();
140     },
141     
142     beforeSelectFile : function(e)
143     {
144         e.preventDefault();
145         
146         this.fireEvent('beforeselectfile', this);
147     },
148     
149     loadCanvas : function(src)
150     {   
151         if(this.fireEvent('beforeloadcanvas', this, src) != false){
152             
153             this.reset();
154             
155             this.imageEl = document.createElement('img');
156             
157             var _this = this;
158             
159             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
160             
161             this.imageEl.src = src;
162         }
163     },
164     
165     onLoadCanvas : function()
166     {   
167         if(this.bodyHasOnClickEvent){
168             this.bodyEl.un('click', this.beforeSelectFile, this);
169             this.bodyHasOnClickEvent = false;
170         }
171         
172         this.notifyEl.hide();
173         this.thumbEl.show();
174         this.footerEl.show();
175         
176         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
177         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
178         
179         this.setThumbBoxPosition();
180         this.baseRotateLevel();
181         this.baseScaleLevel();
182         
183         this.draw();
184         
185     },
186     
187     setCanvasPosition : function()
188     {   
189         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
190         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
191         
192         this.previewEl.setLeft(pw);
193         this.previewEl.setTop(ph);
194     },
195     
196     onMouseDown : function(e)
197     {   
198         e.stopEvent();
199         
200         this.dragable = true;
201         this.pinching = false;
202         
203         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
204         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
205         
206     },
207     
208     onMouseMove : function(e)
209     {   
210         e.stopEvent();
211         
212         if (!this.dragable){
213             return;
214         }
215         
216         var minX = Math.ceil(this.thumbEl.getLeft(true));
217         var minY = Math.ceil(this.thumbEl.getTop(true));
218         
219         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
220         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
221         
222         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
223         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
224         
225         x = x - this.mouseX;
226         y = y - this.mouseY;
227         
228         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
229         var bgY = Math.ceil(y + this.previewEl.getTop(true));
230         
231         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
232         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
233         
234         this.previewEl.setLeft(bgX);
235         this.previewEl.setTop(bgY);
236         
237         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
238         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
239     },
240     
241     onMouseUp : function(e)
242     {   
243         e.stopEvent();
244         
245         this.dragable = false;
246     },
247     
248     onMouseWheel : function(e)
249     {   
250         e.stopEvent();
251         
252         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
253         
254         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel());
255         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel());
256         
257         if(
258                 e.getWheelDelta() == -1 &&
259                 (
260                     (
261                         (this.rotate == 0 || this.rotate == 180) && (width < this.thumbEl.getWidth() || height < this.thumbEl.getHeight())
262                     )
263                     ||
264                     (
265                         (this.rotate == 90 || this.rotate == 270) && (height < this.thumbEl.getWidth() || width < this.thumbEl.getHeight())
266                     )
267                 )
268         ){
269             this.scale = (e.getWheelDelta() == 1) ? (this.scale - 1) : (this.scale + 1);
270             return;
271         }
272         
273         this.draw();
274     },
275     
276     onRotateLeft : function(e)
277     {
278         e.stopEvent();
279         
280         if(
281                 (
282                     (this.rotate == 0 || this.rotate == 180) 
283                     &&
284                     (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())
285                 )
286                 ||
287                 (
288                     (this.rotate == 90 || this.rotate == 270) 
289                     &&
290                     (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())
291                 )
292                 
293         ){
294             return;
295         }
296         
297         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
298
299         this.draw();
300         
301     },
302     
303     onRotateRight : function(e)
304     {
305         e.stopEvent();
306         
307         if(
308                 (
309                     (this.rotate == 0 || this.rotate == 180) 
310                     &&
311                     (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())
312                 )
313                 ||
314                 (
315                     (this.rotate == 90 || this.rotate == 270) 
316                     &&
317                     (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())
318                 )
319                 
320         ){
321             return false;
322         }
323         
324         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
325
326         this.draw();
327         
328     },
329     
330     draw : function()
331     {
332         this.previewEl.dom.innerHTML = '';
333         
334         var canvasEl = document.createElement("canvas");
335         
336         var contextEl = canvasEl.getContext("2d");
337         
338         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
339         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
340         var center = this.imageEl.OriginWidth / 2;
341         
342         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
343             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
344             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
345             center = this.imageEl.OriginHeight / 2;
346         }
347         
348         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
349         
350         contextEl.translate(center, center);
351         contextEl.rotate(this.rotate * Math.PI / 180);
352
353         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
354         
355         this.canvasEl = document.createElement("canvas");
356         
357         this.contextEl = this.canvasEl.getContext("2d");
358         
359         switch (this.rotate) {
360             case 0 :
361                 
362                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
363                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
364                 
365                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
366                 
367                 break;
368             case 90 : 
369                 
370                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
371                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
372                 
373                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
374                     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);
375                     break;
376                 }
377                 
378                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
379                 
380                 break;
381             case 180 :
382                 
383                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
384                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
385                 
386                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
387                     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);
388                     break;
389                 }
390                 
391                 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);
392                 
393                 break;
394             case 270 :
395                 
396                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
397                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
398         
399                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
400                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
401                     break;
402                 }
403                 
404                 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);
405                 
406                 break;
407             default : 
408                 break;
409         }
410         
411         this.previewEl.appendChild(this.canvasEl);
412         
413         this.setCanvasPosition();
414     },
415     
416     crop : function()
417     {
418         var canvas = document.createElement("canvas");
419         
420         var context = canvas.getContext("2d");
421         
422         canvas.width = this.minWidth;
423         canvas.height = this.minHeight;
424         
425         var cropWidth = this.thumbEl.getWidth();
426         var cropHeight = this.thumbEl.getHeight();
427         
428         var x = this.thumbEl.getLeft(true) - this.previewEl.getLeft(true);
429         var y = this.thumbEl.getTop(true) - this.previewEl.getTop(true);
430         
431         if(this.canvasEl.width - cropWidth < x){
432             x = this.canvasEl.width - cropWidth;
433         }
434         
435         if(this.canvasEl.height - cropHeight < y){
436             y = this.canvasEl.height - cropHeight;
437         }
438         
439         x = x < 0 ? 0 : x;
440         y = y < 0 ? 0 : y;
441         
442         context.drawImage(this.canvasEl, x, y, cropWidth, cropHeight, 0, 0, canvas.width, canvas.height);
443         
444         this.cropData = canvas.toDataURL(this.cropType);
445         
446         this.fireEvent('crop', this, this.cropData);
447         
448     },
449     
450     setThumbBoxSize : function()
451     {
452         var height = 300;
453         var width = Math.ceil(this.minWidth * height / this.minHeight);
454         
455         if(this.minWidth > this.minHeight){
456             width = 300;
457             height = Math.ceil(this.minHeight * width / this.minWidth);
458         }
459         
460         this.thumbEl.setStyle({
461             width : width + 'px',
462             height : height + 'px'
463         });
464
465         return;
466             
467     },
468     
469     setThumbBoxPosition : function()
470     {
471         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
472         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
473         
474         this.thumbEl.setLeft(x);
475         this.thumbEl.setTop(y);
476         
477     },
478     
479     baseRotateLevel : function()
480     {
481         this.baseRotate = 1;
482         
483         if(
484                 typeof(this.exif) != 'undefined' &&
485                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
486                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
487         ){
488             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
489         }
490         
491         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
492         
493     },
494     
495     baseScaleLevel : function()
496     {
497         var width, height;
498         
499         if(this.baseRotate == 6 || this.baseRotate == 8){
500             
501             width = this.thumbEl.getHeight();
502             this.baseScale = height / this.imageEl.OriginHeight;
503             
504             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
505                 height = this.thumbEl.getWidth();
506                 this.baseScale = height / this.imageEl.OriginHeight;
507             }
508             
509             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
510                 height = this.thumbEl.getWidth();
511                 this.baseScale = height / this.imageEl.OriginHeight;
512                 
513                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
514                     width = this.thumbEl.getHeight();
515                     this.baseScale = width / this.imageEl.OriginWidth;
516                 }
517             }
518             
519             return;
520         }
521         
522         width = this.thumbEl.getWidth();
523         this.baseScale = width / this.imageEl.OriginWidth;
524         
525         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
526             height = this.thumbEl.getHeight();
527             this.baseScale = height / this.imageEl.OriginHeight;
528         }
529         
530         
531         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
532             
533             height = this.thumbEl.getHeight();
534             this.baseScale = height / this.imageEl.OriginHeight;
535             
536             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
537                 width = this.thumbEl.getWidth();
538                 this.baseScale = width / this.imageEl.OriginWidth;
539             }
540             
541         }
542         
543         return;
544     },
545     
546     getScaleLevel : function()
547     {
548         return this.baseScale * Math.pow(1.1, this.scale);
549     },
550     
551     onTouchStart : function(e)
552     {
553         e.stopEvent();
554         
555         var touches = e.browserEvent.touches;
556         
557         if(!touches){
558             return;
559         }
560         
561         if(touches.length == 1){
562             this.onMouseDown(e);
563             return;
564         }
565         
566         if(touches.length != 2){
567             return;
568         }
569         
570         var coords = [];
571         
572         for(var i = 0, finger; finger = touches[i]; i++){
573             coords.push(finger.pageX, finger.pageY);
574         }
575         
576         var x = Math.pow(coords[0] - coords[2], 2);
577         var y = Math.pow(coords[1] - coords[3], 2);
578         
579         this.startDistance = Math.sqrt(x + y);
580         
581         this.startScale = this.scale;
582         
583         this.pinching = true;
584         this.dragable = false;
585         
586     },
587     
588     onTouchMove : function(e)
589     {
590         e.stopEvent();
591         
592         if(!this.pinching && !this.dragable){
593             return;
594         }
595         
596         var touches = e.browserEvent.touches;
597         
598         if(!touches){
599             return;
600         }
601         
602         if(this.dragable){
603             this.onMouseMove(e);
604             return;
605         }
606         
607         var coords = [];
608         
609         for(var i = 0, finger; finger = touches[i]; i++){
610             coords.push(finger.pageX, finger.pageY);
611         }
612         
613         var x = Math.pow(coords[0] - coords[2], 2);
614         var y = Math.pow(coords[1] - coords[3], 2);
615         
616         this.endDistance = Math.sqrt(x + y);
617         
618         var scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
619         
620         var width = Math.ceil(this.imageEl.OriginWidth * this.baseScale * Math.pow(1.1, scale));
621         var height = Math.ceil(this.imageEl.OriginHeight * this.baseScale * Math.pow(1.1, scale));
622         
623         if(
624                 this.endDistance / this.startDistance < 1 &&
625                 (
626                     (
627                         (this.rotate == 0 || this.rotate == 180) && (width < this.thumbEl.getWidth() || height < this.thumbEl.getHeight())
628                     )
629                     ||
630                     (
631                         (this.rotate == 90 || this.rotate == 270) && (height < this.thumbEl.getWidth() || width < this.thumbEl.getHeight())
632                     )
633                 )
634         ){
635             return;
636         }
637         
638         this.scale = scale;
639         
640         this.draw();
641         
642     },
643     
644     onTouchEnd : function(e)
645     {
646         e.stopEvent();
647         
648         this.pinching = false;
649         this.dragable = false;
650         
651     },
652     
653     prepare : function(input)
654     {        
655         this.file = false;
656         this.exif = {};
657         
658         if(typeof(input) === 'string'){
659             this.loadCanvas(input);
660             return;
661         }
662         
663         if(!input.files || !input.files[0] || !this.urlAPI){
664             return;
665         }
666         
667         this.file = input.files[0];
668         this.cropType = this.file.type;
669         
670         var _this = this;
671         
672         if(this.fireEvent('prepare', this, this.file) != false){
673             
674             var reader = new FileReader();
675             
676             reader.onload = function (e) {
677                 if (e.target.error) {
678                     Roo.log(e.target.error);
679                     return;
680                 }
681                 
682                 var buffer = e.target.result,
683                     dataView = new DataView(buffer),
684                     offset = 2,
685                     maxOffset = dataView.byteLength - 4,
686                     markerBytes,
687                     markerLength;
688                 
689                 if (dataView.getUint16(0) === 0xffd8) {
690                     while (offset < maxOffset) {
691                         markerBytes = dataView.getUint16(offset);
692                         
693                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
694                             markerLength = dataView.getUint16(offset + 2) + 2;
695                             if (offset + markerLength > dataView.byteLength) {
696                                 Roo.log('Invalid meta data: Invalid segment size.');
697                                 break;
698                             }
699                             
700                             if(markerBytes == 0xffe1){
701                                 _this.parseExifData(
702                                     dataView,
703                                     offset,
704                                     markerLength
705                                 );
706                             }
707                             
708                             offset += markerLength;
709                             
710                             continue;
711                         }
712                         
713                         break;
714                     }
715                     
716                 }
717                 
718                 var url = _this.urlAPI.createObjectURL(_this.file);
719                 
720                 _this.loadCanvas(url);
721                 
722                 return;
723             }
724             
725             reader.readAsArrayBuffer(this.file);
726             
727         }
728         
729     },
730     
731     parseExifData : function(dataView, offset, length)
732     {
733         var tiffOffset = offset + 10,
734             littleEndian,
735             dirOffset;
736     
737         if (dataView.getUint32(offset + 4) !== 0x45786966) {
738             // No Exif data, might be XMP data instead
739             return;
740         }
741         
742         // Check for the ASCII code for "Exif" (0x45786966):
743         if (dataView.getUint32(offset + 4) !== 0x45786966) {
744             // No Exif data, might be XMP data instead
745             return;
746         }
747         if (tiffOffset + 8 > dataView.byteLength) {
748             Roo.log('Invalid Exif data: Invalid segment size.');
749             return;
750         }
751         // Check for the two null bytes:
752         if (dataView.getUint16(offset + 8) !== 0x0000) {
753             Roo.log('Invalid Exif data: Missing byte alignment offset.');
754             return;
755         }
756         // Check the byte alignment:
757         switch (dataView.getUint16(tiffOffset)) {
758         case 0x4949:
759             littleEndian = true;
760             break;
761         case 0x4D4D:
762             littleEndian = false;
763             break;
764         default:
765             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
766             return;
767         }
768         // Check for the TIFF tag marker (0x002A):
769         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
770             Roo.log('Invalid Exif data: Missing TIFF marker.');
771             return;
772         }
773         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
774         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
775         
776         this.parseExifTags(
777             dataView,
778             tiffOffset,
779             tiffOffset + dirOffset,
780             littleEndian
781         );
782     },
783     
784     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
785     {
786         var tagsNumber,
787             dirEndOffset,
788             i;
789         if (dirOffset + 6 > dataView.byteLength) {
790             Roo.log('Invalid Exif data: Invalid directory offset.');
791             return;
792         }
793         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
794         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
795         if (dirEndOffset + 4 > dataView.byteLength) {
796             Roo.log('Invalid Exif data: Invalid directory size.');
797             return;
798         }
799         for (i = 0; i < tagsNumber; i += 1) {
800             this.parseExifTag(
801                 dataView,
802                 tiffOffset,
803                 dirOffset + 2 + 12 * i, // tag offset
804                 littleEndian
805             );
806         }
807         // Return the offset to the next directory:
808         return dataView.getUint32(dirEndOffset, littleEndian);
809     },
810     
811     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
812     {
813         var tag = dataView.getUint16(offset, littleEndian);
814         
815         this.exif[tag] = this.getExifValue(
816             dataView,
817             tiffOffset,
818             offset,
819             dataView.getUint16(offset + 2, littleEndian), // tag type
820             dataView.getUint32(offset + 4, littleEndian), // tag length
821             littleEndian
822         );
823     },
824     
825     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
826     {
827         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
828             tagSize,
829             dataOffset,
830             values,
831             i,
832             str,
833             c;
834     
835         if (!tagType) {
836             Roo.log('Invalid Exif data: Invalid tag type.');
837             return;
838         }
839         
840         tagSize = tagType.size * length;
841         // Determine if the value is contained in the dataOffset bytes,
842         // or if the value at the dataOffset is a pointer to the actual data:
843         dataOffset = tagSize > 4 ?
844                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
845         if (dataOffset + tagSize > dataView.byteLength) {
846             Roo.log('Invalid Exif data: Invalid data offset.');
847             return;
848         }
849         if (length === 1) {
850             return tagType.getValue(dataView, dataOffset, littleEndian);
851         }
852         values = [];
853         for (i = 0; i < length; i += 1) {
854             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
855         }
856         
857         if (tagType.ascii) {
858             str = '';
859             // Concatenate the chars:
860             for (i = 0; i < values.length; i += 1) {
861                 c = values[i];
862                 // Ignore the terminating NULL byte(s):
863                 if (c === '\u0000') {
864                     break;
865                 }
866                 str += c;
867             }
868             return str;
869         }
870         return values;
871     }
872     
873 });
874
875 Roo.apply(Roo.bootstrap.UploadCropbox, {
876     tags : {
877         'Orientation': 0x0112
878     },
879     
880     Orientation: {
881             1: 0, //'top-left',
882 //            2: 'top-right',
883             3: 180, //'bottom-right',
884 //            4: 'bottom-left',
885 //            5: 'left-top',
886             6: 90, //'right-top',
887 //            7: 'right-bottom',
888             8: 270 //'left-bottom'
889     },
890     
891     exifTagTypes : {
892         // byte, 8-bit unsigned int:
893         1: {
894             getValue: function (dataView, dataOffset) {
895                 return dataView.getUint8(dataOffset);
896             },
897             size: 1
898         },
899         // ascii, 8-bit byte:
900         2: {
901             getValue: function (dataView, dataOffset) {
902                 return String.fromCharCode(dataView.getUint8(dataOffset));
903             },
904             size: 1,
905             ascii: true
906         },
907         // short, 16 bit int:
908         3: {
909             getValue: function (dataView, dataOffset, littleEndian) {
910                 return dataView.getUint16(dataOffset, littleEndian);
911             },
912             size: 2
913         },
914         // long, 32 bit int:
915         4: {
916             getValue: function (dataView, dataOffset, littleEndian) {
917                 return dataView.getUint32(dataOffset, littleEndian);
918             },
919             size: 4
920         },
921         // rational = two long values, first is numerator, second is denominator:
922         5: {
923             getValue: function (dataView, dataOffset, littleEndian) {
924                 return dataView.getUint32(dataOffset, littleEndian) /
925                     dataView.getUint32(dataOffset + 4, littleEndian);
926             },
927             size: 8
928         },
929         // slong, 32 bit signed int:
930         9: {
931             getValue: function (dataView, dataOffset, littleEndian) {
932                 return dataView.getInt32(dataOffset, littleEndian);
933             },
934             size: 4
935         },
936         // srational, two slongs, first is numerator, second is denominator:
937         10: {
938             getValue: function (dataView, dataOffset, littleEndian) {
939                 return dataView.getInt32(dataOffset, littleEndian) /
940                     dataView.getInt32(dataOffset + 4, littleEndian);
941             },
942             size: 8
943         }
944     }
945 });