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