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