Fix #7418 - radio button set value
[roojs1] / roojs-debug.js
index 3b02a08..29bfd4a 100644 (file)
@@ -1205,12 +1205,35 @@ document.write(dt.format(Date.patterns.ShortDate));
 /**
  Returns the number of milliseconds between this date and date
  @param {Date} date (optional) Defaults to now
- @return {Number} The diff in milliseconds
+ @param {String} interval (optional) Default Date.MILLI, A valid date interval enum value (eg. Date.DAY) 
+ @return {Number} The diff in milliseconds or units of interval
  @member Date getElapsed
  */
-Date.prototype.getElapsed = function(date) {
-       return Math.abs((date || new Date()).getTime()-this.getTime());
+Date.prototype.getElapsed = function(date, interval)
+{
+    date = date ||  new Date();
+    var ret = Math.abs(date.getTime()-this.getTime());
+    switch (interval) {
+       
+        case  Date.SECOND:
+            return Math.floor(ret / (1000));
+        case  Date.MINUTE:
+            return Math.floor(ret / (1000*60));
+        case  Date.HOUR:
+            return Math.floor(ret / (1000*60*60));
+        case  Date.DAY:
+            return Math.floor(ret / (1000*60*60*24));
+        case  Date.MONTH: // this does not give exact number...??
+            return ((date.format("Y") - this.format("Y")) * 12) + (date.format("m") - this.format("m"));
+        case  Date.YEAR: // this does not give exact number...??
+            return (date.format("Y") - this.format("Y"));
+       
+        case  Date.MILLI:
+        default:
+            return ret;
+    }
 };
 // was in date file..
 
 
@@ -5490,7 +5513,7 @@ Roo.DomHelper = function(){
         if (typeof(o) == 'string') {
             return parentNode.appendChild(document.createTextNode(o));
         }
-        o.tag = o.tag || div;
+        o.tag = o.tag || 'div';
         if (o.ns && Roo.isIE) {
             ns = false;
             o.tag = o.ns + ':' + o.tag;
@@ -16969,6 +16992,13 @@ Roo.extend(Roo.Component, Roo.util.Observable, {
      */
     actionMode : "el",
 
+        /**
+     * @cfg {String} style
+     * css styles to add to component
+     * eg. text-align:right;
+     */
+    style : false,
+       
     /** @private */
     getActionEl : function(){
         return this[this.actionMode];
@@ -25863,14 +25893,16 @@ Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
  */
 /**
  * @class Roo.data.MemoryProxy
+ * @extends Roo.data.DataProxy
  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
  * to the Reader when its load method is called.
  * @constructor
- * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
+ * @param {Object} config  A config object containing the objects needed for the Store to access data,
  */
-Roo.data.MemoryProxy = function(data){
-    if (data.data) {
-        data = data.data;
+Roo.data.MemoryProxy = function(config){
+    var data = config;
+    if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
+        data = config.data;
     }
     Roo.data.MemoryProxy.superclass.constructor.call(this);
     this.data = data;
@@ -25878,6 +25910,9 @@ Roo.data.MemoryProxy = function(data){
 
 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
     
+    /**
+     *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
+     */
     /**
      * Load data from the requested source (in this case an in-memory
      * data object passed to the constructor), read the data object into
@@ -31111,7 +31146,23 @@ Roo.extend(Roo.Button, Roo.util.Observable, {
             this.hide();
         }
     },
-    
+    /**
+        * Similar to toggle, but does not trigger event.
+        * @param {Boolean} state [required] Force a particular state
+        */
+       setPressed : function(state)
+       {
+           if(state != this.pressed){
+            if(state){
+                this.el.addClass("x-btn-pressed");
+                this.pressed = true;
+            }else{
+                this.el.removeClass("x-btn-pressed");
+                this.pressed = false;
+            }
+        }
+       },
+       
     /**
      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
      * @param {Boolean} state (optional) Force a particular state
@@ -31134,6 +31185,8 @@ Roo.extend(Roo.Button, Roo.util.Observable, {
         }
     },
     
+       
+       
     /**
      * Focus the button
      */
@@ -44972,7 +45025,9 @@ Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
             this.fireEvent('check', this, state);
         }
         this.inSetChecked = true;
-        this.el.dom.value = state ? this.inputValue : this.valueOff;
+                
+               this.el.dom.value = state ? this.inputValue : this.valueOff;
+                
         this.inSetChecked = false;
         
     },
@@ -45063,9 +45118,49 @@ Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
             this.el.dom.checked =   'checked' ;
         }
          
+    },
+    /**
+     * Sets the checked state of the checkbox.
+     * On is always based on a string comparison between inputValue and the param.
+     * @param {Boolean/String} value - the value to set 
+     * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
+     */
+    setValue : function(v,suppressEvent){
+        
+        
+        //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
+        //if(this.el && this.el.dom){
+        //    this.el.dom.checked = this.checked;
+        //    this.el.dom.defaultChecked = this.checked;
+        //}
+        this.setChecked(String(v) === String(this.inputValue), suppressEvent);
+        
+        this.el.dom.form[this.name].value = v;
+     
+        //this.fireEvent("check", this, this.checked);
+    },
+    // private..
+    setChecked : function(state,suppressEvent)
+    {
+         
+        if(this.wrap){
+            this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
+        }
+        this.checked = state;
+        if(suppressEvent !== true){
+            this.fireEvent('check', this, state);
+        }
+                
+                 
+       
+        
+    },
+    reset : function(){
+        // this.setValue(this.resetValue);
+        //this.originalValue = this.getValue();
+        this.clearInvalid();
     } 
     
-    
 });Roo.rtf = {}; // namespace
 Roo.rtf.Hex = function(hex)
 {
@@ -45351,7 +45446,7 @@ Roo.rtf.Parser.prototype = {
         this.hexStore.push(cmd);
     },
     cmderror : function(cmd) {
-        throw new Exception (cmd.value);
+        throw cmd.value;
     },
     
     /*
@@ -45481,7 +45576,8 @@ Roo.rtf.Parser.prototype = {
     {
         this.emitText();
         if (this.controlWord === '') {
-            this.emitError('empty control word');
+            // do we want to track this - it seems just to cause problems.
+            //this.emitError('empty control word');
         } else {
             this.push({
                   type: 'controlword',
@@ -45605,6 +45701,8 @@ Roo.htmleditor.Filter.prototype = {
                     return;
                 
                 case this.tag === true: // everything
+                case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
+                case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
                     if (this.replaceTag && false === this.replaceTag(e)) {
@@ -45623,6 +45721,21 @@ Roo.htmleditor.Filter.prototype = {
             
         }, this);
         
+    },
+    
+    
+    removeNodeKeepChildren : function( node)
+    {
+    
+        ar = Array.from(node.childNodes);
+        for (var i = 0; i < ar.length; i++) {
+         
+            node.removeChild(ar[i]);
+            // what if we need to walk these???
+            node.parentNode.insertBefore(ar[i], node);
+           
+        }
+        node.parentNode.removeChild(node);
     }
 }; 
 
@@ -45835,25 +45948,36 @@ Roo.htmleditor.FilterKeepChildren = function(cfg)
     if (this.tag === false) {
         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
     }
+    // hacky?
+    if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
+        this.cleanNamespace = true;
+    }
+        
     this.walk(cfg.node);
 }
 
 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
 {
-    
+    cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
   
     replaceTag : function(node)
     {
         // walk children...
-        //Roo.log(node);
+        //Roo.log(node.tagName);
         var ar = Array.from(node.childNodes);
         //remove first..
+        
         for (var i = 0; i < ar.length; i++) {
-            if (ar[i].nodeType == 1) {
+            var e = ar[i];
+            if (e.nodeType == 1) {
                 if (
-                    (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
+                    (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
                     || // array and it matches
-                    (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
+                    (typeof(this.tag) == 'string' && this.tag == e.tagName)
+                    ||
+                    (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
+                    ||
+                    (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
                 ) {
                     this.replaceTag(ar[i]); // child is blacklisted as well...
                     continue;
@@ -45871,6 +45995,7 @@ Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
                 
             }
         }
+        //Roo.log("REMOVE:" + node.tagName);
         node.parentNode.removeChild(node);
         return false; // don't walk children
         
@@ -46030,7 +46155,13 @@ Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
 Roo.htmleditor.FilterWord = function(cfg)
 {
     // no need to apply config.
-    this.walk(cfg.node);
+    this.replaceDocBullets(cfg.node);
+    
+    this.replaceAname(cfg.node);
+    // this is disabled as the removal is done by other filters;
+   // this.walk(cfg.node);
+    
+    
 }
 
 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
@@ -46080,7 +46211,10 @@ Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
                 node.removeChild(cn);
                 node.parentNode.insertBefore(cn, node);
                 // move node to parent - and clean it..
-                this.replaceTag(cn);
+                if (cn.nodeType == 1) {
+                    this.replaceTag(cn);
+                }
+                
             }
             node.parentNode.removeChild(node);
             /// no need to iterate chidlren = it's got none..
@@ -46132,7 +46266,246 @@ Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
         
         
         
+    },
+    
+    styleToObject: function(node)
+    {
+        var styles = (node.getAttribute("style") || '').split(";");
+        var ret = {};
+        Roo.each(styles, function(s) {
+            if (!s.match(/:/)) {
+                return;
+            }
+            var kv = s.split(":");
+             
+            // what ever is left... we allow.
+            ret[kv[0].trim()] = kv[1];
+        });
+        return ret;
+    },
+    
+    
+    replaceAname : function (doc)
+    {
+        // replace all the a/name without..
+        var aa = Array.from(doc.getElementsByTagName('a'));
+        for (var i = 0; i  < aa.length; i++) {
+            var a = aa[i];
+            if (a.hasAttribute("name")) {
+                a.removeAttribute("name");
+            }
+            if (a.hasAttribute("href")) {
+                continue;
+            }
+            // reparent children.
+            this.removeNodeKeepChildren(a);
+            
+        }
+        
+        
+        
+    },
+
+    
+    
+    replaceDocBullets : function(doc)
+    {
+        // this is a bit odd - but it appears some indents use ql-indent-1
+         //Roo.log(doc.innerHTML);
+        
+        var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
+        for( var i = 0; i < listpara.length; i ++) {
+            listpara[i].className = "MsoListParagraph";
+        }
+        
+        listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
+        for( var i = 0; i < listpara.length; i ++) {
+            listpara[i].className = "MsoListParagraph";
+        }
+        listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
+        for( var i = 0; i < listpara.length; i ++) {
+            listpara[i].className = "MsoListParagraph";
+        }
+        listpara =  Array.from(doc.getElementsByClassName('ql-indent-1'));
+        for( var i = 0; i < listpara.length; i ++) {
+            listpara[i].className = "MsoListParagraph";
+        }
+        
+        // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
+        var htwo =  Array.from(doc.getElementsByTagName('h2'));
+        for( var i = 0; i < htwo.length; i ++) {
+            if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
+                htwo[i].className = "MsoListParagraph";
+            }
+        }
+        listpara =  Array.from(doc.getElementsByClassName('MsoNormal'));
+        for( var i = 0; i < listpara.length; i ++) {
+            if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
+                listpara[i].className = "MsoListParagraph";
+            } else {
+                listpara[i].className = "MsoNormalx";
+            }
+        }
+       
+        listpara = doc.getElementsByClassName('MsoListParagraph');
+        // Roo.log(doc.innerHTML);
+        
+        
+        
+        while(listpara.length) {
+            
+            this.replaceDocBullet(listpara.item(0));
+        }
+      
+    },
+    
+     
+    
+    replaceDocBullet : function(p)
+    {
+        // gather all the siblings.
+        var ns = p,
+            parent = p.parentNode,
+            doc = parent.ownerDocument,
+            items = [];
+            
+        var listtype = 'ul';   
+        while (ns) {
+            if (ns.nodeType != 1) {
+                ns = ns.nextSibling;
+                continue;
+            }
+            if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
+                break;
+            }
+            var spans = ns.getElementsByTagName('span');
+            if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
+                items.push(ns);
+                ns = ns.nextSibling;
+                has_list = true;
+                if (spans.length && spans[0].hasAttribute('style')) {
+                    var  style = this.styleToObject(spans[0]);
+                    if (typeof(style['font-family']) != 'undefined' && !style['font-family'].match(/Symbol/)) {
+                        listtype = 'ol';
+                    }
+                }
+                
+                continue;
+            }
+            var spans = ns.getElementsByTagName('span');
+            if (!spans.length) {
+                break;
+            }
+            var has_list  = false;
+            for(var i = 0; i < spans.length; i++) {
+                if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
+                    has_list = true;
+                    break;
+                }
+            }
+            if (!has_list) {
+                break;
+            }
+            items.push(ns);
+            ns = ns.nextSibling;
+            
+            
+        }
+        if (!items.length) {
+            ns.className = "";
+            return;
+        }
+        
+        var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
+        parent.insertBefore(ul, p);
+        var lvl = 0;
+        var stack = [ ul ];
+        var last_li = false;
+        
+        var margin_to_depth = {};
+        max_margins = -1;
+        
+        items.forEach(function(n, ipos) {
+            //Roo.log("got innertHMLT=" + n.innerHTML);
+            
+            var spans = n.getElementsByTagName('span');
+            if (!spans.length) {
+                //Roo.log("No spans found");
+                 
+                parent.removeChild(n);
+                
+                
+                return; // skip it...
+            }
+           
+                
+            var num = 1;
+            var style = {};
+            for(var i = 0; i < spans.length; i++) {
+            
+                style = this.styleToObject(spans[i]);
+                if (typeof(style['mso-list']) == 'undefined') {
+                    continue;
+                }
+                if (listtype == 'ol') {
+                   num = spans[i].innerText.replace(/[^0-9]+]/g,'')  * 1;
+                }
+                spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
+                break;
+            }
+            //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
+            style = this.styleToObject(n); // mo-list is from the parent node.
+            if (typeof(style['mso-list']) == 'undefined') {
+                //Roo.log("parent is missing level");
+                  
+                parent.removeChild(n);
+                 
+                return;
+            }
+            
+            var margin = style['margin-left'];
+            if (typeof(margin_to_depth[margin]) == 'undefined') {
+                max_margins++;
+                margin_to_depth[margin] = max_margins;
+            }
+            nlvl = margin_to_depth[margin] ;
+             
+            if (nlvl > lvl) {
+                //new indent
+                var nul = doc.createElement(listtype); // what about number lists...
+                if (!last_li) {
+                    last_li = doc.createElement('li');
+                    stack[lvl].appendChild(last_li);
+                }
+                last_li.appendChild(nul);
+                stack[nlvl] = nul;
+                
+            }
+            lvl = nlvl;
+            
+            // not starting at 1..
+            if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
+                stack[nlvl].setAttribute("start", num);
+            }
+            
+            var nli = stack[nlvl].appendChild(doc.createElement('li'));
+            last_li = nli;
+            nli.innerHTML = n.innerHTML;
+            //Roo.log("innerHTML = " + n.innerHTML);
+            parent.removeChild(n);
+            
+             
+             
+            
+        },this);
+        
+        
+        
+        
     }
+    
+    
+    
 });
 /**
  * @class Roo.htmleditor.FilterStyleToTag
@@ -46620,12 +46993,14 @@ Roo.htmleditor.TidyWriter.prototype = {
      * @param {String} text String to write out.
      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
      */
-    text: function(text, node)
+    text: function(in_text, node)
     {
         // if not in whitespace critical
-        if (text.length < 1) {
+        if (in_text.length < 1) {
             return;
         }
+        var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
+        
         if (this.in_pre) {
             this.html[this.html.length] =  text;
             return;   
@@ -47623,7 +47998,17 @@ Roo.htmleditor.KeyEnter.prototype = {
         var pc = range.closest([ 'ol', 'ul']);
         var pli = range.closest('li');
         if (!pc || e.ctrlKey) {
-            sel.insertNode('br', 'after'); 
+            // on it list, or ctrl pressed.
+            if (!e.ctrlKey) {
+                sel.insertNode('br', 'after'); 
+            } else {
+                // only do this if we have ctrl key..
+                var br = doc.createElement('br');
+                br.className = 'clear';
+                br.setAttribute('style', 'clear: both');
+                sel.insertNode(br, 'after'); 
+            }
+            
          
             this.core.undoManager.addEvent();
             this.core.fireEditorEvent(e);
@@ -47987,10 +48372,11 @@ Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
                 store : {
                     xtype : 'SimpleStore',
                     data : [
-                        ['auto'],
-                        ['50%'],
+                        ['100%'],
                         ['80%'],
-                        ['100%']
+                        ['50%'],
+                        ['20%'],
+                        ['10%']
                     ],
                     fields : [ 'val'],
                     xns : Roo.data
@@ -48044,7 +48430,10 @@ Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
                 pressed : false,
                 enableToggle : true,
                 setValue : function(v) {
-                    this.toggle(v == 'block' ? false : true);
+                    // this trigger toggle.
+                     
+                    this.setText(v ? "Hide Caption" : "Show Caption");
+                    this.setPressed(v != 'block');
                 },
                 listeners : {
                     toggle: function (btn, state)
@@ -48083,8 +48472,7 @@ Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
             style: {
                 width : iw,
                 maxWidth : iw + ' !important', // this is not getting rendered?
-                margin : m 
-                
+                margin : m  
                 
             }
         };
@@ -48125,10 +48513,11 @@ Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
         
-        return  {
+  
+        var ret =   {
             tag: 'figure',
             'data-block' : 'Figure',
-            
+            'data-width' : this.width, 
             contenteditable : 'false',
             
             style : {
@@ -48137,7 +48526,7 @@ Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
                 width : this.align == 'center' ? '100%' : this.width,
                 margin:  '0px',
-                padding: '10px',
+                padding: this.align == 'center' ? '0' : '0 10px' ,
                 textAlign : this.align   // seems to work for email..
                 
             },
@@ -48155,9 +48544,9 @@ Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
                         fontSize : '16px',
                         lineHeight : '24px',
                         display : this.caption_display,
-                        maxWidth : this.width + ' !important',
+                        maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
                         margin: m,
-                        width: this.width
+                        width: this.align == 'center' ?  this.width : '100%' 
                     
                          
                     },
@@ -48186,6 +48575,7 @@ Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
                 }
             ]
         };
+        return ret;
          
     },
     
@@ -48201,11 +48591,14 @@ Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
          
         this.align = this.getVal(node, 'figure', 'align');
         var figcaption = this.getVal(node, 'figcaption', false);
-        this.caption = this.getVal(figcaption, 'i', 'html');
+        if (figcaption !== '') {
+            this.caption = this.getVal(figcaption, 'i', 'html');
+        }
+        
 
         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
-        this.width = this.getVal(node, 'figcaption', 'style', 'width');
+        this.width = this.getVal(node, true, 'data-width');
         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
         
     },
@@ -49053,7 +49446,6 @@ Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
      */
     toObject : function()
     {
-        
         var ret = {
             tag : 'td',
             contenteditable : 'true', // this stops cell selection from picking the table.
@@ -49094,6 +49486,9 @@ Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
         this.html = node.innerHTML;
+        if (node.style.textAlign != '') {
+            this.textAlign = node.style.textAlign;
+        }
         
         
     },
@@ -49151,7 +49546,7 @@ Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
                     c.col = cn;
                 }
                 
-                if (typeof(this.colWidths[cn]) == 'undefined') {
+                if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
                     this.colWidths[cn] =   ce.style.width;
                     if (this.colWidths[cn] != '') {
                         all_auto = false;
@@ -49220,6 +49615,9 @@ Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
         this.colspan += rc.colspan;
         this.node.setAttribute('colspan', this.colspan);
 
+        var table = this.toTableArray();
+        this.normalizeWidths(table);
+        this.updateWidths(table);
     },
     
     
@@ -49254,19 +49652,17 @@ Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
         this.colspan = 1;
         
         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
-            
-            
+             
             
             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
                 if (r == cd.row && c == cd.col) {
                     this.node.removeAttribute('rowspan');
                     this.node.removeAttribute('colspan');
-                    continue;
                 }
                  
                 var ntd = this.node.cloneNode(); // which col/row should be 0..
-                ntd.removeAttribute('id'); //
-                //ntd.style.width  = '';
+                ntd.removeAttribute('id'); 
+                ntd.style.width  = this.colWidths[c];
                 ntd.innerHTML = '';
                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
             }
@@ -49274,8 +49670,6 @@ Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
         }
         this.redrawAllCells(table);
         
-         
-        
     },
     
     
@@ -49324,13 +49718,21 @@ Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
                     el.width = Math.floor(this.colWidths[c])  +'%';
                     el.updateElement(el.node);
                 }
+                if (this.colWidths[0] != false && table[r][c].colspan > 1) {
+                    var el = Roo.htmleditor.Block.factory(table[r][c].cell);
+                    var width = 0;
+                    for(var i = 0; i < table[r][c].colspan; i ++) {
+                        width += Math.floor(this.colWidths[c + i]);
+                    }
+                    el.width = width  +'%';
+                    el.updateElement(el.node);
+                }
                 table[r][c].cell = false; // done
             }
         }
     },
     normalizeWidths : function(table)
     {
-    
         if (this.colWidths[0] === false) {
             var nw = 100.0 / this.colWidths.length;
             this.colWidths.forEach(function(w,i) {
@@ -49818,7 +50220,9 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
         if(this.initialized){
             
-            this.undoManager.addEvent();
+            if (this.undoManager) {
+                this.undoManager.addEvent();
+            }
 
             
             var bd = (this.doc.body || this.doc.documentElement);
@@ -49838,11 +50242,42 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
             if (this.enableBlocks) {
                 new Roo.htmleditor.FilterBlock({ node : div });
             }
+            
+            var html = div.innerHTML;
+            
             //?? tidy?
-            var tidy = new Roo.htmleditor.TidySerializer({
-                inner:  true
-            });
-            var html  = tidy.serialize(div);
+            if (this.autoClean) {
+                
+                new Roo.htmleditor.FilterAttributes({
+                    node : div,
+                    attrib_white : [
+                            'href',
+                            'src',
+                            'name',
+                            'align',
+                            'colspan',
+                            'rowspan',
+                            'data-display',
+                            'data-width',
+                            'start' ,
+                            'style',
+                            // youtube embed.
+                            'class',
+                            'allowfullscreen',
+                            'frameborder',
+                            'width',
+                            'height',
+                            'alt'
+                            ],
+                    attrib_clean : ['href', 'src' ] 
+                });
+                
+                var tidy = new Roo.htmleditor.TidySerializer({
+                    inner:  true
+                });
+                html  = tidy.serialize(div);
+                
+            }
             
             
             if(Roo.isSafari){
@@ -50049,14 +50484,14 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
         }
-        Roo.log(images);
+        //Roo.log(images);
         //Roo.log(imgs);
         // fixme..
         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
                        .map(function(g) { return g.toDataURL(); })
                        .filter(function(g) { return g != 'about:blank'; });
         
-        
+        //Roo.log(html);
         html = this.cleanWordChars(html);
         
         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
@@ -50074,24 +50509,39 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
             return false;
         }
         
+        
+        
         if (images.length > 0) {
+            // replace all v:imagedata - with img.
+            var ar = Array.from(d.getElementsByTagName('v:imagedata'));
+            Roo.each(ar, function(node) {
+                node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
+                node.parentNode.removeChild(node);
+            });
+            
+            
             Roo.each(d.getElementsByTagName('img'), function(img, i) {
                 img.setAttribute('src', images[i]);
             });
         }
         if (this.autoClean) {
+            new Roo.htmleditor.FilterWord({ node : d });
+            
             new Roo.htmleditor.FilterStyleToTag({ node : d });
             new Roo.htmleditor.FilterAttributes({
                 node : d,
-                attrib_white : ['href', 'src', 'name', 'align'],
+                attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
                 attrib_clean : ['href', 'src' ] 
             });
             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
             // should be fonts..
-            new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', 'O:P' ]} );
+            new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
             new Roo.htmleditor.FilterParagraph({ node : d });
             new Roo.htmleditor.FilterSpan({ node : d });
             new Roo.htmleditor.FilterLongBr({ node : d });
+            new Roo.htmleditor.FilterComment({ node : d });
+            
+            
         }
         if (this.enableBlocks) {
                 
@@ -50112,7 +50562,7 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
         if (this.enableBlocks) {
             Roo.htmleditor.Block.initAll(this.doc.body);
         }
-        
+         
         
         e.preventDefault();
         return false;
@@ -50775,6 +51225,7 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
     cleanWord : function(node)
     {
         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
+        new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
         
     },
    
@@ -53881,6 +54332,9 @@ clientValidation  Boolean          Applies to submit only.  Pass true to call fo
             for(id in values){
                 if(typeof values[id] != 'function' && (field = this.findField(id))){
                     
+                    
+                    
+                    
                     if (field.setFromData && 
                         field.valueField && 
                         field.displayField &&
@@ -53895,6 +54349,9 @@ clientValidation  Boolean          Applies to submit only.  Pass true to call fo
                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
                         field.setFromData(sd);
                         
+                    } else if (field.inputType && field.inputType == 'radio') {
+                        
+                        field.setValue(values[id]);
                     } else {
                         field.setValue(values[id]);
                     }
@@ -53920,7 +54377,7 @@ clientValidation  Boolean          Applies to submit only.  Pass true to call fo
     /**
      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
      * they are returned as an array.
-     * @param {Boolean} asString
+     * @param {Boolean} asString (def)
      * @return {Object}
      */
     getValues : function(asString)
@@ -54343,7 +54800,7 @@ Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
      */
     /**
-     * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
+     * @cfg {String} (left|center|right) buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
      */
     buttonAlign:'center',
 
@@ -54353,7 +54810,7 @@ Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
     minButtonWidth:75,
 
     /**
-     * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
+     * @cfg {String} labelAlign (left|top|right) Valid values are "left," "top" and "right" (defaults to "left").
      * This property cascades to child containers if not set.
      */
     labelAlign:'left',
@@ -55103,7 +55560,7 @@ Roo.extend(Roo.form.Layout, Roo.Component, {
      * a function which returns such a specification.
      */
     /**
-     * @cfg {String} labelAlign
+     * @cfg {String} labelAlign (left|top|right)
      * Valid values are "left," "top" and "right" (defaults to "left")
      */
     /**
@@ -55223,6 +55680,7 @@ Roo.extend(Roo.form.Layout, Roo.Component, {
     }
 });
 
+
 /**
  * @class Roo.form.Column
  * @extends Roo.form.Layout
@@ -55257,7 +55715,6 @@ Roo.extend(Roo.form.Column, Roo.form.Layout, {
     }
 });
 
-
 /**
  * @class Roo.form.Row
  * @extends Roo.form.Layout
@@ -60216,7 +60673,6 @@ Roo.LayoutStateManager.prototype = {
  */
 Roo.ContentPanel = function(el, config, content){
     
-     
     /*
     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
         config = el;
@@ -60600,6 +61056,14 @@ layout.addxtype({
      */
     
     addxtype : function(cfg) {
+        if(cfg.xtype.match(/^UploadCropbox$/)) {
+
+            this.cropbox = new Roo.factory(cfg);
+
+            this.cropbox.render(this.el);
+
+            return this.cropbox;
+        }
         // add form..
         if (cfg.xtype.match(/^Form$/)) {
             
@@ -61414,7 +61878,7 @@ Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
         * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
         */
        /**
-        * @cfg {Roo.grid.Store} ds The data store for the grid
+        * @cfg {Roo.data.Store} ds The data store for the grid
         */
        /**
         * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
@@ -64279,7 +64743,7 @@ Roo.grid.ColumnModel = function(config){
 };
 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
     /**
-     * @cfg {String} header The header text to display in the Grid view.
+     * @cfg {String} header [required] The header text to display in the Grid view.
      */
        /**
      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
@@ -64297,66 +64761,66 @@ Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
      * @cfg {String} xlHeader Header at Bootsrap extra Large width
      */
     /**
-     * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
+     * @cfg {String} dataIndex  The name of the field in the grid's {@link Roo.data.Store}'s
      * {@link Roo.data.Record} definition from which to draw the column's value. If not
      * specified, the column's index is used as an index into the Record's data Array.
      */
     /**
-     * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
+     * @cfg {Number} width  The initial width in pixels of the column. Using this
      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
      */
     /**
-     * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
+     * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
      * Defaults to the value of the {@link #defaultSortable} property.
      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
      */
     /**
-     * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
+     * @cfg {Boolean} locked  True to lock the column in place while scrolling the Grid.  Defaults to false.
      */
     /**
-     * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
+     * @cfg {Boolean} fixed  True if the column width cannot be changed.  Defaults to false.
      */
     /**
-     * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
+     * @cfg {Boolean} resizable  False to disable column resizing. Defaults to true.
      */
     /**
-     * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
+     * @cfg {Boolean} hidden  True to hide the column. Defaults to false.
      */
     /**
-     * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
+     * @cfg {Function} renderer A function used to generate HTML markup for a cell
      * given the cell's data value. See {@link #setRenderer}. If not specified, the
      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
      */
        /**
-     * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
+     * @cfg {Roo.grid.GridEditor} editor  For grid editors - returns the grid editor 
      */
     /**
-     * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
+     * @cfg {String} align (left|right) Set the CSS text-align property of the column.  Defaults to undefined (left).
      */
     /**
-     * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
+     * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined (middle)
      */
     /**
-     * @cfg {String} cursor (Optional)
+     * @cfg {String} cursor ( auto|default|none|context-menu|help|pointer|progress|wait|cell|crosshair|text|vertical-text|alias|copy|move|no-drop|not-allowed|e-resize|n-resize|ne-resize|nw-resize|s-resize|se-resize|sw-resize|w-resize|ew-resize|ns-resize|nesw-resize|nwse-resize|col-resize|row-resize|all-scroll|zoom-in|zoom-out|grab|grabbing)
      */
     /**
-     * @cfg {String} tooltip (Optional)
+     * @cfg {String} tooltip mouse over tooltip text
      */
     /**
-     * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
+     * @cfg {Number} xs  can be '0' for hidden at this size (number less than 12)
      */
     /**
-     * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
+     * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
      */
     /**
-     * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
+     * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
      */
     /**
-     * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
+     * @cfg {Number} lg   can be '0' for hidden at this size (number less than 12)
      */
        /**
-     * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
+     * @cfg {Number} xl   can be '0' for hidden at this size (number less than 12)
      */
     /**
      * Returns the id of the column at the specified index.
@@ -67852,4 +68316,1898 @@ Roo.extend(Roo.XTemplate, Roo.Template, {
 Roo.XTemplate.from = function(el){
     el = Roo.getDom(el);
     return new Roo.XTemplate(el.value || el.innerHTML);
-};
\ No newline at end of file
+};Roo.dialog = {};
+/*
+* Licence: LGPL
+*/
+
+/**
+ * @class Roo.dialog.UploadCropbox
+ * @extends Roo.BoxComponent
+ * Dialog UploadCropbox class
+ * @cfg {String} emptyText show when image has been loaded
+ * @cfg {String} rotateNotify show when image too small to rotate
+ * @cfg {Number} errorTimeout default 3000
+ * @cfg {Number} minWidth default 300
+ * @cfg {Number} minHeight default 300
+ * @cfg {Number} outputMaxWidth default 1200
+ * @cfg {Number} windowSize default 300
+ * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
+ * @cfg {Boolean} isDocument (true|false) default false
+ * @cfg {String} url action url
+ * @cfg {String} paramName default 'imageUpload'
+ * @cfg {String} method default POST
+ * @cfg {Boolean} loadMask (true|false) default true
+ * @cfg {Boolean} loadingText default 'Loading...'
+ * 
+ * @constructor
+ * Create a new UploadCropbox
+ * @param {Object} config The config object
+ */
+
+ Roo.dialog.UploadCropbox = function(config){
+    Roo.dialog.UploadCropbox.superclass.constructor.call(this, config);
+    
+    this.addEvents({
+        /**
+         * @event beforeselectfile
+         * Fire before select file
+         * @param {Roo.dialog.UploadCropbox} this
+         */
+        "beforeselectfile" : true,
+        /**
+         * @event initial
+         * Fire after initEvent
+         * @param {Roo.dialog.UploadCropbox} this
+         */
+        "initial" : true,
+        /**
+         * @event crop
+         * Fire after initEvent
+         * @param {Roo.dialog.UploadCropbox} this
+         * @param {String} data
+         */
+        "crop" : true,
+        /**
+         * @event prepare
+         * Fire when preparing the file data
+         * @param {Roo.dialog.UploadCropbox} this
+         * @param {Object} file
+         */
+        "prepare" : true,
+        /**
+         * @event exception
+         * Fire when get exception
+         * @param {Roo.dialog.UploadCropbox} this
+         * @param {XMLHttpRequest} xhr
+         */
+        "exception" : true,
+        /**
+         * @event beforeloadcanvas
+         * Fire before load the canvas
+         * @param {Roo.dialog.UploadCropbox} this
+         * @param {String} src
+         */
+        "beforeloadcanvas" : true,
+        /**
+         * @event trash
+         * Fire when trash image
+         * @param {Roo.dialog.UploadCropbox} this
+         */
+        "trash" : true,
+        /**
+         * @event download
+         * Fire when download the image
+         * @param {Roo.dialog.UploadCropbox} this
+         */
+        "download" : true,
+        /**
+         * @event footerbuttonclick
+         * Fire when footerbuttonclick
+         * @param {Roo.dialog.UploadCropbox} this
+         * @param {String} type
+         */
+        "footerbuttonclick" : true,
+        /**
+         * @event resize
+         * Fire when resize
+         * @param {Roo.dialog.UploadCropbox} this
+         */
+        "resize" : true,
+        /**
+         * @event rotate
+         * Fire when rotate the image
+         * @param {Roo.dialog.UploadCropbox} this
+         * @param {String} pos
+         */
+        "rotate" : true,
+        /**
+         * @event inspect
+         * Fire when inspect the file
+         * @param {Roo.dialog.UploadCropbox} this
+         * @param {Object} file
+         */
+        "inspect" : true,
+        /**
+         * @event upload
+         * Fire when xhr upload the file
+         * @param {Roo.dialog.UploadCropbox} this
+         * @param {Object} data
+         */
+        "upload" : true,
+        /**
+         * @event arrange
+         * Fire when arrange the file data
+         * @param {Roo.dialog.UploadCropbox} this
+         * @param {Object} formData
+         */
+        "arrange" : true,
+        /**
+         * @event loadcanvas
+         * Fire after load the canvas
+         * @param {Roo.dialog.UploadCropbox}
+         * @param {Object} imgEl
+         */
+        "loadcanvas" : true
+    });
+    
+    this.buttons = this.buttons || Roo.dialog.UploadCropbox.footer.STANDARD;
+};
+
+Roo.extend(Roo.dialog.UploadCropbox, Roo.Component,  {
+    
+    emptyText : 'Click to upload image',
+    rotateNotify : 'Image is too small to rotate',
+    errorTimeout : 3000,
+    scale : 0,
+    baseScale : 1,
+    rotate : 0,
+    dragable : false,
+    pinching : false,
+    mouseX : 0,
+    mouseY : 0,
+    cropData : false,
+    minWidth : 300,
+    minHeight : 300,
+    outputMaxWidth : 1200,
+    windowSize : 300,
+    file : false,
+    exif : {},
+    baseRotate : 1,
+    cropType : 'image/jpeg',
+    buttons : false,
+    canvasLoaded : false,
+    isDocument : false,
+    method : 'POST',
+    paramName : 'imageUpload',
+    loadMask : true,
+    loadingText : 'Loading...',
+    maskEl : false,
+    
+    getAutoCreate : function()
+    {
+        var cfg = {
+            tag : 'div',
+            cls : 'roo-upload-cropbox',
+            cn : [
+                {
+                    tag : 'input',
+                    cls : 'roo-upload-cropbox-selector',
+                    type : 'file'
+                },
+                {
+                    tag : 'div',
+                    cls : 'roo-upload-cropbox-body',
+                    style : 'cursor:pointer',
+                    cn : [
+                        {
+                            tag : 'div',
+                            cls : 'roo-upload-cropbox-preview'
+                        },
+                        {
+                            tag : 'div',
+                            cls : 'roo-upload-cropbox-thumb'
+                        },
+                        {
+                            tag : 'div',
+                            cls : 'roo-upload-cropbox-empty-notify',
+                            html : this.emptyText
+                        },
+                        {
+                            tag : 'div',
+                            cls : 'roo-upload-cropbox-error-notify alert alert-danger',
+                            html : this.rotateNotify
+                        }
+                    ]
+                },
+                {
+                    tag : 'div',
+                    cls : 'roo-upload-cropbox-footer',
+                    cn : {
+                        tag : 'div',
+                        cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
+                        cn : []
+                    }
+                }
+            ]
+        };
+        
+        return cfg;
+    },
+    
+    onRender : function(ct, position)
+    {
+        Roo.dialog.UploadCropbox.superclass.onRender.call(this, ct, position);
+
+        if(this.el){
+            if (this.el.attr('xtype')) {
+                this.el.attr('xtypex', this.el.attr('xtype'));
+                this.el.dom.removeAttribute('xtype');
+                
+                this.initEvents();
+            }
+        }
+        else {
+            var cfg = Roo.apply({},  this.getAutoCreate());
+        
+            cfg.id = this.id || Roo.id();
+            
+            if (this.cls) {
+                cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
+            }
+            
+            if (this.style) { // fixme needs to support more complex style data.
+                cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
+            }
+            
+            this.el = ct.createChild(cfg, position);
+            
+            this.initEvents();
+        }
+        
+        if (this.buttons.length) {
+            
+            Roo.each(this.buttons, function(bb) {
+                
+                var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
+                
+                btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
+                
+            }, this);
+        }
+        
+        if(this.loadMask){
+            this.maskEl = this.el;
+        }
+    },
+    
+    initEvents : function()
+    {
+        this.urlAPI = (window.createObjectURL && window) || 
+                                (window.URL && URL.revokeObjectURL && URL) || 
+                                (window.webkitURL && webkitURL);
+                        
+        this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
+        this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
+        
+        this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
+        this.selectorEl.hide();
+        
+        this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
+        this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
+        
+        this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
+        this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
+        this.thumbEl.hide();
+        
+        this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
+        this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
+        
+        this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
+        this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
+        this.errorEl.hide();
+        
+        this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
+        this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
+        this.footerEl.hide();
+        
+        this.setThumbBoxSize();
+        
+        this.bind();
+        
+        this.resize();
+        
+        this.fireEvent('initial', this);
+    },
+
+    bind : function()
+    {
+        var _this = this;
+        
+        window.addEventListener("resize", function() { _this.resize(); } );
+        
+        this.bodyEl.on('click', this.beforeSelectFile, this);
+        
+        if(Roo.isTouch){
+            this.bodyEl.on('touchstart', this.onTouchStart, this);
+            this.bodyEl.on('touchmove', this.onTouchMove, this);
+            this.bodyEl.on('touchend', this.onTouchEnd, this);
+        }
+        
+        if(!Roo.isTouch){
+            this.bodyEl.on('mousedown', this.onMouseDown, this);
+            this.bodyEl.on('mousemove', this.onMouseMove, this);
+            var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
+            this.bodyEl.on(mousewheel, this.onMouseWheel, this);
+            Roo.get(document).on('mouseup', this.onMouseUp, this);
+        }
+        
+        this.selectorEl.on('change', this.onFileSelected, this);
+    },
+    
+    reset : function()
+    {    
+        this.scale = 0;
+        this.baseScale = 1;
+        this.rotate = 0;
+        this.baseRotate = 1;
+        this.dragable = false;
+        this.pinching = false;
+        this.mouseX = 0;
+        this.mouseY = 0;
+        this.cropData = false;
+        this.notifyEl.dom.innerHTML = this.emptyText;
+        
+        // this.selectorEl.dom.value = '';
+        
+    },
+    
+    resize : function()
+    {
+        if(this.fireEvent('resize', this) != false){
+            this.setThumbBoxPosition();
+            this.setCanvasPosition();
+        }
+    },
+    
+    onFooterButtonClick : function(e, el, o, type)
+    {
+        switch (type) {
+            case 'rotate-left' :
+                this.onRotateLeft(e);
+                break;
+            case 'rotate-right' :
+                this.onRotateRight(e);
+                break;
+            case 'picture' :
+                this.beforeSelectFile(e);
+                break;
+            case 'trash' :
+                this.trash(e);
+                break;
+            case 'crop' :
+                this.crop(e);
+                break;
+            case 'download' :
+                this.download(e);
+                break;
+            case 'center' :
+                this.center(e);
+                break;
+            default :
+                break;
+        }
+        
+        this.fireEvent('footerbuttonclick', this, type);
+    },
+    
+    beforeSelectFile : function(e)
+    {
+        e.preventDefault();
+        
+        if(this.fireEvent('beforeselectfile', this) != false){
+            this.selectorEl.dom.click();
+        }
+    },
+    
+    onFileSelected : function(e)
+    {
+        e.preventDefault();
+        
+        if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
+            return;
+        }
+        
+        var file = this.selectorEl.dom.files[0];
+        
+        if(this.fireEvent('inspect', this, file) != false){
+            this.prepare(file);
+        }
+        
+    },
+    
+    trash : function(e)
+    {
+        this.fireEvent('trash', this);
+    },
+    
+    download : function(e)
+    {
+        this.fireEvent('download', this);
+    },
+
+    center : function(e)
+    {
+        this.setCanvasPosition();
+    },
+    
+    loadCanvas : function(src)
+    {   
+        if(this.fireEvent('beforeloadcanvas', this, src) != false){
+            
+            this.reset();
+            
+            this.imageEl = document.createElement('img');
+            
+            var _this = this;
+            
+            this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
+            
+            this.imageEl.src = src;
+        }
+    },
+    
+    onLoadCanvas : function()
+    {   
+        this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
+        this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
+
+        if(this.fireEvent('loadcanvas', this, this.imageEl) != false){
+        
+            this.bodyEl.un('click', this.beforeSelectFile, this);
+            
+            this.notifyEl.hide();
+            this.thumbEl.show();
+            this.footerEl.show();
+            
+            this.baseRotateLevel();
+            
+            if(this.isDocument){
+                this.setThumbBoxSize();
+            }
+            
+            this.setThumbBoxPosition();
+            
+            this.baseScaleLevel();
+            
+            this.draw();
+            
+            this.resize();
+            
+            this.canvasLoaded = true;
+        
+        }
+        
+        if(this.loadMask){
+            this.maskEl.unmask();
+        }
+        
+    },
+    
+    setCanvasPosition : function(center = true)
+    {   
+        if(!this.canvasEl){
+            return;
+        }
+
+        var newCenterLeft = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
+        var newCenterTop = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
+
+        if(center) {
+            this.previewEl.setLeft(newCenterLeft);
+            this.previewEl.setTop(newCenterTop);
+
+            return;
+        }
+        
+        var oldScaleLevel = this.baseScale * Math.pow(1.02, this.startScale);
+        var oldCanvasWidth = Math.floor(this.imageEl.OriginWidth * oldScaleLevel);
+        var oldCanvasHeight = Math.floor(this.imageEl.OriginHeight * oldScaleLevel);
+
+        var oldCenterLeft = Math.ceil((this.bodyEl.getWidth() - oldCanvasWidth) / 2);
+        var oldCenterTop = Math.ceil((this.bodyEl.getHeight() - oldCanvasHeight) / 2);
+
+        var leftDiff = newCenterLeft - oldCenterLeft;
+        var topDiff = newCenterTop - oldCenterTop;
+
+        var newPreviewLeft = this.previewEl.getLeft(true) + leftDiff;
+        var newPreviewTop = this.previewEl.getTop(true) + topDiff;
+
+        this.previewEl.setLeft(newPreviewLeft);
+        this.previewEl.setTop(newPreviewTop);
+        
+    },
+    
+    onMouseDown : function(e)
+    {   
+        e.stopEvent();
+        
+        this.dragable = true;
+        this.pinching = false;
+        
+        if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
+            this.dragable = false;
+            return;
+        }
+        
+        this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
+        this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
+        
+    },
+    
+    onMouseMove : function(e)
+    {   
+        e.stopEvent();
+        
+        if(!this.canvasLoaded){
+            return;
+        }
+        
+        if (!this.dragable){
+            return;
+        }
+
+        var maxPaddingLeft = this.canvasEl.width / 0.9 * 0.05;
+        var maxPaddingTop = maxPaddingLeft * this.minHeight / this.minWidth;
+
+        if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight <= this.minWidth / this.minHeight)) {
+            maxPaddingLeft = (this.canvasEl.height * this.minWidth / this.minHeight - this.canvasEl.width) / 2 + maxPaddingLeft;
+        }
+
+        if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight >= this.minWidth / this.minHeight)) {
+            maxPaddingTop = (this.canvasEl.width * this.minHeight / this.minWidth - this.canvasEl.height) / 2 + maxPaddingTop;
+        }
+        
+        var minX = Math.ceil(this.thumbEl.getLeft(true) + this.thumbEl.getWidth() - this.canvasEl.width - maxPaddingLeft);
+        var minY = Math.ceil(this.thumbEl.getTop(true) + this.thumbEl.getHeight() - this.canvasEl.height - maxPaddingTop);
+        
+        var maxX = Math.ceil(this.thumbEl.getLeft(true) + maxPaddingLeft);
+        var maxY = Math.ceil(this.thumbEl.getTop(true) +  maxPaddingTop);
+
+        if(minX > maxX) {
+            var tempX = minX;
+            minX = maxX;
+            maxX = tempX;
+        }
+
+        if(minY > maxY) {
+            var tempY = minY;
+            minY = maxY;
+            maxY = tempY;
+        }
+
+        var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
+        var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
+        
+        x = x - this.mouseX;
+        y = y - this.mouseY;
+
+        var bgX = Math.ceil(x + this.previewEl.getLeft(true));
+        var bgY = Math.ceil(y + this.previewEl.getTop(true));
+        
+        bgX = (bgX < minX) ? minX : ((bgX > maxX) ? maxX : bgX);
+        bgY = (bgY < minY) ? minY : ((bgY > maxY) ? maxY : bgY);
+        
+        this.previewEl.setLeft(bgX);
+        this.previewEl.setTop(bgY);
+        
+        this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
+        this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
+    },
+    
+    onMouseUp : function(e)
+    {   
+        e.stopEvent();
+        
+        this.dragable = false;
+    },
+    
+    onMouseWheel : function(e)
+    {   
+        e.stopEvent();
+        
+        this.startScale = this.scale;
+        this.scale = (e.getWheelDelta() > 0) ? (this.scale + 1) : (this.scale - 1);
+        
+        if(!this.zoomable()){
+            this.scale = this.startScale;
+            return;
+        }
+
+        
+        this.draw();
+        
+        return;
+    },
+    
+    zoomable : function()
+    {
+        var minScale = this.thumbEl.getWidth() / this.minWidth;
+        
+        if(this.minWidth < this.minHeight){
+            minScale = this.thumbEl.getHeight() / this.minHeight;
+        }
+        
+        var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
+        var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
+        var maxWidth = this.imageEl.OriginWidth;
+        var maxHeight = this.imageEl.OriginHeight;
+
+
+        var newCanvasWidth = Math.floor(this.imageEl.OriginWidth * this.getScaleLevel());
+        var newCanvasHeight = Math.floor(this.imageEl.OriginHeight * this.getScaleLevel());
+
+        var oldCenterLeft = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
+        var oldCenterTop = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
+
+        var newCenterLeft = Math.ceil((this.bodyEl.getWidth() - newCanvasWidth) / 2);
+        var newCenterTop = Math.ceil((this.bodyEl.getHeight() - newCanvasHeight) / 2);
+
+        var leftDiff = newCenterLeft - oldCenterLeft;
+        var topDiff = newCenterTop - oldCenterTop;
+
+        var newPreviewLeft = this.previewEl.getLeft(true) + leftDiff;
+        var newPreviewTop = this.previewEl.getTop(true) + topDiff;
+
+        var paddingLeft = newPreviewLeft - this.thumbEl.getLeft(true);
+        var paddingTop = newPreviewTop - this.thumbEl.getTop(true);
+
+        var paddingRight = this.thumbEl.getLeft(true) + this.thumbEl.getWidth() - newCanvasWidth - newPreviewLeft;
+        var paddingBottom = this.thumbEl.getTop(true) + this.thumbEl.getHeight() - newCanvasHeight - newPreviewTop;
+
+        var maxPaddingLeft = newCanvasWidth / 0.9 * 0.05;
+        var maxPaddingTop = maxPaddingLeft * this.minHeight / this.minWidth;
+
+        if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight <= this.minWidth / this.minHeight)) {
+            maxPaddingLeft = (newCanvasHeight * this.minWidth / this.minHeight - newCanvasWidth) / 2 + maxPaddingLeft;
+        }
+
+        if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight >= this.minWidth / this.minHeight)) {
+            maxPaddingTop = (newCanvasWidth * this.minHeight / this.minWidth - newCanvasHeight) / 2 + maxPaddingTop;
+        }
+        
+        if(
+                this.isDocument &&
+                (this.rotate == 0 || this.rotate == 180) && 
+                (
+                    width > this.imageEl.OriginWidth || 
+                    height > this.imageEl.OriginHeight ||
+                    (width < this.minWidth && height < this.minHeight)
+                )
+        ){
+            return false;
+        }
+        
+        if(
+                this.isDocument &&
+                (this.rotate == 90 || this.rotate == 270) && 
+                (
+                    width > this.imageEl.OriginWidth || 
+                    height > this.imageEl.OriginHeight ||
+                    (width < this.minHeight && height < this.minWidth)
+                )
+        ){
+            return false;
+        }
+        
+        if(
+                !this.isDocument &&
+                (this.rotate == 0 || this.rotate == 180) && 
+                (
+                    // for zoom out
+                    paddingLeft > maxPaddingLeft ||
+                    paddingRight > maxPaddingLeft ||
+                    paddingTop > maxPaddingTop ||
+                    paddingBottom > maxPaddingTop ||
+                    // for zoom in
+                    width > maxWidth ||
+                    height > maxHeight
+                )
+        ){
+            return false;
+        }
+        
+        if(
+                !this.isDocument &&
+                (this.rotate == 90 || this.rotate == 270) && 
+                (
+                    width < this.minHeight || 
+                    width > this.imageEl.OriginWidth || 
+                    height < this.minWidth || 
+                    height > this.imageEl.OriginHeight
+                )
+        ){
+            return false;
+        }
+        
+        return true;
+        
+    },
+    
+    onRotateLeft : function(e)
+    {   
+        if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
+            
+            var minScale = this.thumbEl.getWidth() / this.minWidth;
+            
+            var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
+            var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
+            
+            this.startScale = this.scale;
+            
+            while (this.getScaleLevel() < minScale){
+            
+                this.scale = this.scale + 1;
+                
+                if(!this.zoomable()){
+                    break;
+                }
+                
+                if(
+                        Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
+                        Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
+                ){
+                    continue;
+                }
+                
+                this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
+
+                this.draw();
+                
+                return;
+            }
+            
+            this.scale = this.startScale;
+            
+            this.onRotateFail();
+            
+            return false;
+        }
+        
+        this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
+
+        if(this.isDocument){
+            this.setThumbBoxSize();
+            this.setThumbBoxPosition();
+            this.setCanvasPosition();
+        }
+        
+        this.draw();
+        
+        this.fireEvent('rotate', this, 'left');
+        
+    },
+    
+    onRotateRight : function(e)
+    {
+        if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
+            
+            var minScale = this.thumbEl.getWidth() / this.minWidth;
+        
+            var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
+            var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
+            
+            this.startScale = this.scale;
+            
+            while (this.getScaleLevel() < minScale){
+            
+                this.scale = this.scale + 1;
+                
+                if(!this.zoomable()){
+                    break;
+                }
+                
+                if(
+                        Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
+                        Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
+                ){
+                    continue;
+                }
+                
+                this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
+
+                this.draw();
+                
+                return;
+            }
+            
+            this.scale = this.startScale;
+            
+            this.onRotateFail();
+            
+            return false;
+        }
+        
+        this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
+
+        if(this.isDocument){
+            this.setThumbBoxSize();
+            this.setThumbBoxPosition();
+            this.setCanvasPosition();
+        }
+        
+        this.draw();
+        
+        this.fireEvent('rotate', this, 'right');
+    },
+    
+    onRotateFail : function()
+    {
+        this.errorEl.show(true);
+        
+        var _this = this;
+        
+        (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
+    },
+    
+    draw : function()
+    {
+        this.previewEl.dom.innerHTML = '';
+        
+        var canvasEl = document.createElement("canvas");
+        
+        var contextEl = canvasEl.getContext("2d");
+        
+        canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
+        canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
+        var center = this.imageEl.OriginWidth / 2;
+        
+        if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
+            canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
+            canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
+            center = this.imageEl.OriginHeight / 2;
+        }
+        
+        contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
+        
+        contextEl.translate(center, center);
+        contextEl.rotate(this.rotate * Math.PI / 180);
+
+        contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
+        
+        this.canvasEl = document.createElement("canvas");
+        
+        this.contextEl = this.canvasEl.getContext("2d");
+        
+        switch (this.rotate) {
+            case 0 :
+                
+                this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
+                this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
+                
+                this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
+                
+                break;
+            case 90 : 
+                
+                this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
+                this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
+                
+                if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
+                    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);
+                    break;
+                }
+                
+                this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
+                
+                break;
+            case 180 :
+                
+                this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
+                this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
+                
+                if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
+                    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);
+                    break;
+                }
+                
+                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);
+                
+                break;
+            case 270 :
+                
+                this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
+                this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
+        
+                if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
+                    this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
+                    break;
+                }
+                
+                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);
+                
+                break;
+            default : 
+                break;
+        }
+        
+        this.previewEl.appendChild(this.canvasEl);
+        
+        this.setCanvasPosition(false);
+    },
+    
+    crop : function()
+    {
+        if(!this.canvasLoaded){
+            return;
+        }
+        
+        var imageCanvas = document.createElement("canvas");
+        
+        var imageContext = imageCanvas.getContext("2d");
+        
+        imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
+        imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
+        
+        var center = imageCanvas.width / 2;
+        
+        imageContext.translate(center, center);
+        
+        imageContext.rotate(this.rotate * Math.PI / 180);
+        
+        imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
+        
+        var canvas = document.createElement("canvas");
+        
+        var context = canvas.getContext("2d");
+
+        canvas.width = this.thumbEl.getWidth() / this.getScaleLevel();
+        
+        canvas.height = this.thumbEl.getHeight() / this.getScaleLevel();
+
+        switch (this.rotate) {
+            case 0 :
+                
+                var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
+                var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
+                
+                var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
+                var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
+                
+                var sx = this.thumbEl.getLeft(true) - this.previewEl.getLeft(true);
+                var sy = this.thumbEl.getTop(true) - this.previewEl.getTop(true);
+
+                sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
+                sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
+
+                if(canvas.width > this.outputMaxWidth) {
+                    var scale = this.outputMaxWidth / canvas.width;
+                    canvas.width = canvas.width * scale;
+                    canvas.height = canvas.height * scale;
+                    context.scale(scale, scale);
+                }
+
+                context.fillStyle = 'white';
+                context.fillRect(0, 0, this.thumbEl.getWidth() / this.getScaleLevel(), this.thumbEl.getHeight() / this.getScaleLevel());
+
+
+                context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
+                
+                break;
+            case 90 : 
+                
+                var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
+                var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
+                
+                var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
+                var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
+                
+                var targetWidth = this.minWidth - 2 * x;
+                var targetHeight = this.minHeight - 2 * y;
+                
+                var scale = 1;
+                
+                if((x == 0 && y == 0) || (x == 0 && y > 0)){
+                    scale = targetWidth / width;
+                }
+                
+                if(x > 0 && y == 0){
+                    scale = targetHeight / height;
+                }
+                
+                if(x > 0 && y > 0){
+                    scale = targetWidth / width;
+                    
+                    if(width < height){
+                        scale = targetHeight / height;
+                    }
+                }
+                
+                context.scale(scale, scale);
+                
+                var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
+                var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
+
+                sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
+                sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
+                
+                sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
+                
+                context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
+                
+                break;
+            case 180 :
+                
+                var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
+                var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
+                
+                var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
+                var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
+                
+                var targetWidth = this.minWidth - 2 * x;
+                var targetHeight = this.minHeight - 2 * y;
+                
+                var scale = 1;
+                
+                if((x == 0 && y == 0) || (x == 0 && y > 0)){
+                    scale = targetWidth / width;
+                }
+                
+                if(x > 0 && y == 0){
+                    scale = targetHeight / height;
+                }
+                
+                if(x > 0 && y > 0){
+                    scale = targetWidth / width;
+                    
+                    if(width < height){
+                        scale = targetHeight / height;
+                    }
+                }
+                
+                context.scale(scale, scale);
+                
+                var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
+                var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
+
+                sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
+                sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
+
+                sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
+                sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
+                
+                context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
+                
+                break;
+            case 270 :
+                
+                var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
+                var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
+                
+                var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
+                var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
+                
+                var targetWidth = this.minWidth - 2 * x;
+                var targetHeight = this.minHeight - 2 * y;
+                
+                var scale = 1;
+                
+                if((x == 0 && y == 0) || (x == 0 && y > 0)){
+                    scale = targetWidth / width;
+                }
+                
+                if(x > 0 && y == 0){
+                    scale = targetHeight / height;
+                }
+                
+                if(x > 0 && y > 0){
+                    scale = targetWidth / width;
+                    
+                    if(width < height){
+                        scale = targetHeight / height;
+                    }
+                }
+                
+                context.scale(scale, scale);
+                var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
+                var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
+
+                sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
+                sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
+                
+                sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
+                
+                context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
+                
+                break;
+            default : 
+                break;
+        }
+        
+        this.cropData = canvas.toDataURL(this.cropType);
+        
+        if(this.fireEvent('crop', this, this.cropData) !== false){
+            this.process(this.file, this.cropData);
+        }
+        
+        return;
+        
+    },
+    
+    setThumbBoxSize : function()
+    {
+        var width, height;
+        
+        if(this.isDocument && typeof(this.imageEl) != 'undefined'){
+            width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
+            height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
+            
+            this.minWidth = width;
+            this.minHeight = height;
+            
+            if(this.rotate == 90 || this.rotate == 270){
+                this.minWidth = height;
+                this.minHeight = width;
+            }
+        }
+        
+        height = this.windowSize;
+        width = Math.ceil(this.minWidth * height / this.minHeight);
+        
+        if(this.minWidth > this.minHeight){
+            width = this.windowSize;
+            height = Math.ceil(this.minHeight * width / this.minWidth);
+        }
+        
+        this.thumbEl.setStyle({
+            width : width + 'px',
+            height : height + 'px'
+        });
+
+        return;
+            
+    },
+    
+    setThumbBoxPosition : function()
+    {
+        var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
+        var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
+        
+        this.thumbEl.setLeft(x);
+        this.thumbEl.setTop(y);
+        
+    },
+    
+    baseRotateLevel : function()
+    {
+        this.baseRotate = 1;
+        
+        if(
+                typeof(this.exif) != 'undefined' &&
+                typeof(this.exif[Roo.dialog.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
+                [1, 3, 6, 8].indexOf(this.exif[Roo.dialog.UploadCropbox['tags']['Orientation']]) != -1
+        ){
+            this.baseRotate = this.exif[Roo.dialog.UploadCropbox['tags']['Orientation']];
+        }
+        
+        this.rotate = Roo.dialog.UploadCropbox['Orientation'][this.baseRotate];
+        
+    },
+    
+    baseScaleLevel : function()
+    {
+        var width, height;
+        
+        if(this.isDocument){
+            
+            if(this.baseRotate == 6 || this.baseRotate == 8){
+            
+                height = this.thumbEl.getHeight();
+                this.baseScale = height / this.imageEl.OriginWidth;
+
+                if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
+                    width = this.thumbEl.getWidth();
+                    this.baseScale = width / this.imageEl.OriginHeight;
+                }
+
+                return;
+            }
+
+            height = this.thumbEl.getHeight();
+            this.baseScale = height / this.imageEl.OriginHeight;
+
+            if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
+                width = this.thumbEl.getWidth();
+                this.baseScale = width / this.imageEl.OriginWidth;
+            }
+
+            return;
+        }
+        
+        if(this.baseRotate == 6 || this.baseRotate == 8){
+            
+            width = this.thumbEl.getHeight();
+            this.baseScale = width / this.imageEl.OriginHeight;
+            
+            if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
+                height = this.thumbEl.getWidth();
+                this.baseScale = height / this.imageEl.OriginHeight;
+            }
+            
+            if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
+                height = this.thumbEl.getWidth();
+                this.baseScale = height / this.imageEl.OriginHeight;
+                
+                if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
+                    width = this.thumbEl.getHeight();
+                    this.baseScale = width / this.imageEl.OriginWidth;
+                }
+            }
+            
+            return;
+        }
+        
+        width = this.thumbEl.getWidth();
+        this.baseScale = width / this.imageEl.OriginWidth;
+        
+        if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
+            height = this.thumbEl.getHeight();
+            this.baseScale = height / this.imageEl.OriginHeight;
+        }
+        
+        if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
+            
+            height = this.thumbEl.getHeight();
+            this.baseScale = height / this.imageEl.OriginHeight;
+            
+            if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
+                width = this.thumbEl.getWidth();
+                this.baseScale = width / this.imageEl.OriginWidth;
+            }
+            
+        }
+
+        if(this.imageEl.OriginWidth < this.minWidth || this.imageEl.OriginHeight < this.minHeight) {
+            this.baseScale = width / this.minWidth;
+        }
+
+        return;
+    },
+    
+    getScaleLevel : function()
+    {
+        return this.baseScale * Math.pow(1.02, this.scale);
+    },
+    
+    onTouchStart : function(e)
+    {
+        if(!this.canvasLoaded){
+            this.beforeSelectFile(e);
+            return;
+        }
+        
+        var touches = e.browserEvent.touches;
+        
+        if(!touches){
+            return;
+        }
+        
+        if(touches.length == 1){
+            this.onMouseDown(e);
+            return;
+        }
+        
+        if(touches.length != 2){
+            return;
+        }
+        
+        var coords = [];
+        
+        for(var i = 0, finger; finger = touches[i]; i++){
+            coords.push(finger.pageX, finger.pageY);
+        }
+        
+        var x = Math.pow(coords[0] - coords[2], 2);
+        var y = Math.pow(coords[1] - coords[3], 2);
+        
+        this.startDistance = Math.sqrt(x + y);
+        
+        this.startScale = this.scale;
+        
+        this.pinching = true;
+        this.dragable = false;
+        
+    },
+    
+    onTouchMove : function(e)
+    {
+        if(!this.pinching && !this.dragable){
+            return;
+        }
+        
+        var touches = e.browserEvent.touches;
+        
+        if(!touches){
+            return;
+        }
+        
+        if(this.dragable){
+            this.onMouseMove(e);
+            return;
+        }
+        
+        var coords = [];
+        
+        for(var i = 0, finger; finger = touches[i]; i++){
+            coords.push(finger.pageX, finger.pageY);
+        }
+        
+        var x = Math.pow(coords[0] - coords[2], 2);
+        var y = Math.pow(coords[1] - coords[3], 2);
+        
+        this.endDistance = Math.sqrt(x + y);
+        
+        this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
+        
+        if(!this.zoomable()){
+            this.scale = this.startScale;
+            return;
+        }
+        
+        this.draw();
+        
+    },
+    
+    onTouchEnd : function(e)
+    {
+        this.pinching = false;
+        this.dragable = false;
+        
+    },
+    
+    process : function(file, crop)
+    {
+        if(this.loadMask){
+            this.maskEl.mask(this.loadingText);
+        }
+        
+        this.xhr = new XMLHttpRequest();
+        
+        file.xhr = this.xhr;
+
+        this.xhr.open(this.method, this.url, true);
+        
+        var headers = {
+            "Accept": "application/json",
+            "Cache-Control": "no-cache",
+            "X-Requested-With": "XMLHttpRequest"
+        };
+        
+        for (var headerName in headers) {
+            var headerValue = headers[headerName];
+            if (headerValue) {
+                this.xhr.setRequestHeader(headerName, headerValue);
+            }
+        }
+        
+        var _this = this;
+        
+        this.xhr.onload = function()
+        {
+            _this.xhrOnLoad(_this.xhr);
+        }
+        
+        this.xhr.onerror = function()
+        {
+            _this.xhrOnError(_this.xhr);
+        }
+        
+        var formData = new FormData();
+
+        formData.append('returnHTML', 'NO');
+
+        if(crop){
+            formData.append('crop', crop);
+            var blobBin = atob(crop.split(',')[1]);
+            var array = [];
+            for(var i = 0; i < blobBin.length; i++) {
+                array.push(blobBin.charCodeAt(i));
+            }
+            var croppedFile =new Blob([new Uint8Array(array)], {type: this.cropType});
+            formData.append(this.paramName, croppedFile, file.name);
+        }
+        
+        if(typeof(file.filename) != 'undefined'){
+            formData.append('filename', file.filename);
+        }
+        
+        if(typeof(file.mimetype) != 'undefined'){
+            formData.append('mimetype', file.mimetype);
+        }
+
+        if(this.fireEvent('arrange', this, formData) != false){
+            this.xhr.send(formData);
+        };
+    },
+    
+    xhrOnLoad : function(xhr)
+    {
+        if(this.loadMask){
+            this.maskEl.unmask();
+        }
+        
+        if (xhr.readyState !== 4) {
+            this.fireEvent('exception', this, xhr);
+            return;
+        }
+
+        var response = Roo.decode(xhr.responseText);
+        
+        if(!response.success){
+            this.fireEvent('exception', this, xhr);
+            return;
+        }
+        
+        var response = Roo.decode(xhr.responseText);
+        
+        this.fireEvent('upload', this, response);
+        
+    },
+    
+    xhrOnError : function()
+    {
+        if(this.loadMask){
+            this.maskEl.unmask();
+        }
+        
+        Roo.log('xhr on error');
+        
+        var response = Roo.decode(xhr.responseText);
+          
+        Roo.log(response);
+        
+    },
+    
+    prepare : function(file)
+    {   
+        if(this.loadMask){
+            this.maskEl.mask(this.loadingText);
+        }
+        
+        this.file = false;
+        this.exif = {};
+        
+        if(typeof(file) === 'string'){
+            this.loadCanvas(file);
+            return;
+        }
+        
+        if(!file || !this.urlAPI){
+            return;
+        }
+        
+        this.file = file;
+        if(typeof(file.type) != 'undefined' && file.type.length != 0) {
+            this.cropType = file.type;
+        }
+        
+        var _this = this;
+        
+        if(this.fireEvent('prepare', this, this.file) != false){
+            
+            var reader = new FileReader();
+            
+            reader.onload = function (e) {
+                if (e.target.error) {
+                    Roo.log(e.target.error);
+                    return;
+                }
+                
+                var buffer = e.target.result,
+                    dataView = new DataView(buffer),
+                    offset = 2,
+                    maxOffset = dataView.byteLength - 4,
+                    markerBytes,
+                    markerLength;
+                
+                if (dataView.getUint16(0) === 0xffd8) {
+                    while (offset < maxOffset) {
+                        markerBytes = dataView.getUint16(offset);
+                        
+                        if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
+                            markerLength = dataView.getUint16(offset + 2) + 2;
+                            if (offset + markerLength > dataView.byteLength) {
+                                Roo.log('Invalid meta data: Invalid segment size.');
+                                break;
+                            }
+                            
+                            if(markerBytes == 0xffe1){
+                                _this.parseExifData(
+                                    dataView,
+                                    offset,
+                                    markerLength
+                                );
+                            }
+                            
+                            offset += markerLength;
+                            
+                            continue;
+                        }
+                        
+                        break;
+                    }
+                    
+                }
+                
+                var url = _this.urlAPI.createObjectURL(_this.file);
+                
+                _this.loadCanvas(url);
+                
+                return;
+            }
+            
+            reader.readAsArrayBuffer(this.file);
+            
+        }
+        
+    },
+    
+    parseExifData : function(dataView, offset, length)
+    {
+        var tiffOffset = offset + 10,
+            littleEndian,
+            dirOffset;
+    
+        if (dataView.getUint32(offset + 4) !== 0x45786966) {
+            // No Exif data, might be XMP data instead
+            return;
+        }
+        
+        // Check for the ASCII code for "Exif" (0x45786966):
+        if (dataView.getUint32(offset + 4) !== 0x45786966) {
+            // No Exif data, might be XMP data instead
+            return;
+        }
+        if (tiffOffset + 8 > dataView.byteLength) {
+            Roo.log('Invalid Exif data: Invalid segment size.');
+            return;
+        }
+        // Check for the two null bytes:
+        if (dataView.getUint16(offset + 8) !== 0x0000) {
+            Roo.log('Invalid Exif data: Missing byte alignment offset.');
+            return;
+        }
+        // Check the byte alignment:
+        switch (dataView.getUint16(tiffOffset)) {
+        case 0x4949:
+            littleEndian = true;
+            break;
+        case 0x4D4D:
+            littleEndian = false;
+            break;
+        default:
+            Roo.log('Invalid Exif data: Invalid byte alignment marker.');
+            return;
+        }
+        // Check for the TIFF tag marker (0x002A):
+        if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
+            Roo.log('Invalid Exif data: Missing TIFF marker.');
+            return;
+        }
+        // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
+        dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
+        
+        this.parseExifTags(
+            dataView,
+            tiffOffset,
+            tiffOffset + dirOffset,
+            littleEndian
+        );
+    },
+    
+    parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
+    {
+        var tagsNumber,
+            dirEndOffset,
+            i;
+        if (dirOffset + 6 > dataView.byteLength) {
+            Roo.log('Invalid Exif data: Invalid directory offset.');
+            return;
+        }
+        tagsNumber = dataView.getUint16(dirOffset, littleEndian);
+        dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
+        if (dirEndOffset + 4 > dataView.byteLength) {
+            Roo.log('Invalid Exif data: Invalid directory size.');
+            return;
+        }
+        for (i = 0; i < tagsNumber; i += 1) {
+            this.parseExifTag(
+                dataView,
+                tiffOffset,
+                dirOffset + 2 + 12 * i, // tag offset
+                littleEndian
+            );
+        }
+        // Return the offset to the next directory:
+        return dataView.getUint32(dirEndOffset, littleEndian);
+    },
+    
+    parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
+    {
+        var tag = dataView.getUint16(offset, littleEndian);
+        
+        this.exif[tag] = this.getExifValue(
+            dataView,
+            tiffOffset,
+            offset,
+            dataView.getUint16(offset + 2, littleEndian), // tag type
+            dataView.getUint32(offset + 4, littleEndian), // tag length
+            littleEndian
+        );
+    },
+    
+    getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
+    {
+        var tagType = Roo.dialog.UploadCropbox.exifTagTypes[type],
+            tagSize,
+            dataOffset,
+            values,
+            i,
+            str,
+            c;
+    
+        if (!tagType) {
+            Roo.log('Invalid Exif data: Invalid tag type.');
+            return;
+        }
+        
+        tagSize = tagType.size * length;
+        // Determine if the value is contained in the dataOffset bytes,
+        // or if the value at the dataOffset is a pointer to the actual data:
+        dataOffset = tagSize > 4 ?
+                tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
+        if (dataOffset + tagSize > dataView.byteLength) {
+            Roo.log('Invalid Exif data: Invalid data offset.');
+            return;
+        }
+        if (length === 1) {
+            return tagType.getValue(dataView, dataOffset, littleEndian);
+        }
+        values = [];
+        for (i = 0; i < length; i += 1) {
+            values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
+        }
+        
+        if (tagType.ascii) {
+            str = '';
+            // Concatenate the chars:
+            for (i = 0; i < values.length; i += 1) {
+                c = values[i];
+                // Ignore the terminating NULL byte(s):
+                if (c === '\u0000') {
+                    break;
+                }
+                str += c;
+            }
+            return str;
+        }
+        return values;
+    }
+    
+});
+
+Roo.apply(Roo.dialog.UploadCropbox, {
+    tags : {
+        'Orientation': 0x0112
+    },
+    
+    Orientation: {
+            1: 0, //'top-left',
+//            2: 'top-right',
+            3: 180, //'bottom-right',
+//            4: 'bottom-left',
+//            5: 'left-top',
+            6: 90, //'right-top',
+//            7: 'right-bottom',
+            8: 270 //'left-bottom'
+    },
+    
+    exifTagTypes : {
+        // byte, 8-bit unsigned int:
+        1: {
+            getValue: function (dataView, dataOffset) {
+                return dataView.getUint8(dataOffset);
+            },
+            size: 1
+        },
+        // ascii, 8-bit byte:
+        2: {
+            getValue: function (dataView, dataOffset) {
+                return String.fromCharCode(dataView.getUint8(dataOffset));
+            },
+            size: 1,
+            ascii: true
+        },
+        // short, 16 bit int:
+        3: {
+            getValue: function (dataView, dataOffset, littleEndian) {
+                return dataView.getUint16(dataOffset, littleEndian);
+            },
+            size: 2
+        },
+        // long, 32 bit int:
+        4: {
+            getValue: function (dataView, dataOffset, littleEndian) {
+                return dataView.getUint32(dataOffset, littleEndian);
+            },
+            size: 4
+        },
+        // rational = two long values, first is numerator, second is denominator:
+        5: {
+            getValue: function (dataView, dataOffset, littleEndian) {
+                return dataView.getUint32(dataOffset, littleEndian) /
+                    dataView.getUint32(dataOffset + 4, littleEndian);
+            },
+            size: 8
+        },
+        // slong, 32 bit signed int:
+        9: {
+            getValue: function (dataView, dataOffset, littleEndian) {
+                return dataView.getInt32(dataOffset, littleEndian);
+            },
+            size: 4
+        },
+        // srational, two slongs, first is numerator, second is denominator:
+        10: {
+            getValue: function (dataView, dataOffset, littleEndian) {
+                return dataView.getInt32(dataOffset, littleEndian) /
+                    dataView.getInt32(dataOffset + 4, littleEndian);
+            },
+            size: 8
+        }
+    },
+    
+    footer : {
+        STANDARD : [
+            {
+                tag : 'div',
+                cls : 'btn-group roo-upload-cropbox-rotate-left',
+                action : 'rotate-left',
+                cn : [
+                    {
+                        tag : 'button',
+                        cls : 'btn btn-default',
+                        html : '<i class="fa fa-undo"></i>'
+                    }
+                ]
+            },
+            {
+                tag : 'div',
+                cls : 'btn-group roo-upload-cropbox-picture',
+                action : 'picture',
+                cn : [
+                    {
+                        tag : 'button',
+                        cls : 'btn btn-default',
+                        html : '<i class="fa fa-picture-o"></i>'
+                    }
+                ]
+            },
+            {
+                tag : 'div',
+                cls : 'btn-group roo-upload-cropbox-rotate-right',
+                action : 'rotate-right',
+                cn : [
+                    {
+                        tag : 'button',
+                        cls : 'btn btn-default',
+                        html : '<i class="fa fa-repeat"></i>'
+                    }
+                ]
+            }
+        ],
+        DOCUMENT : [
+            {
+                tag : 'div',
+                cls : 'btn-group roo-upload-cropbox-rotate-left',
+                action : 'rotate-left',
+                cn : [
+                    {
+                        tag : 'button',
+                        cls : 'btn btn-default',
+                        html : '<i class="fa fa-undo"></i>'
+                    }
+                ]
+            },
+            {
+                tag : 'div',
+                cls : 'btn-group roo-upload-cropbox-download',
+                action : 'download',
+                cn : [
+                    {
+                        tag : 'button',
+                        cls : 'btn btn-default',
+                        html : '<i class="fa fa-download"></i>'
+                    }
+                ]
+            },
+            {
+                tag : 'div',
+                cls : 'btn-group roo-upload-cropbox-crop',
+                action : 'crop',
+                cn : [
+                    {
+                        tag : 'button',
+                        cls : 'btn btn-default',
+                        html : '<i class="fa fa-crop"></i>'
+                    }
+                ]
+            },
+            {
+                tag : 'div',
+                cls : 'btn-group roo-upload-cropbox-trash',
+                action : 'trash',
+                cn : [
+                    {
+                        tag : 'button',
+                        cls : 'btn btn-default',
+                        html : '<i class="fa fa-trash"></i>'
+                    }
+                ]
+            },
+            {
+                tag : 'div',
+                cls : 'btn-group roo-upload-cropbox-rotate-right',
+                action : 'rotate-right',
+                cn : [
+                    {
+                        tag : 'button',
+                        cls : 'btn btn-default',
+                        html : '<i class="fa fa-repeat"></i>'
+                    }
+                ]
+            }
+        ],
+        ROTATOR : [
+            {
+                tag : 'div',
+                cls : 'btn-group roo-upload-cropbox-rotate-left',
+                action : 'rotate-left',
+                cn : [
+                    {
+                        tag : 'button',
+                        cls : 'btn btn-default',
+                        html : '<i class="fa fa-undo"></i>'
+                    }
+                ]
+            },
+            {
+                tag : 'div',
+                cls : 'btn-group roo-upload-cropbox-rotate-right',
+                action : 'rotate-right',
+                cn : [
+                    {
+                        tag : 'button',
+                        cls : 'btn btn-default',
+                        html : '<i class="fa fa-repeat"></i>'
+                    }
+                ]
+            }
+        ],
+        CENTER : [
+            {
+                tag : 'div',
+                cls : 'btn-group roo-upload-cropbox-center',
+                action : 'center',
+                cn : [
+                    {
+                        tag : 'button',
+                        cls : 'btn btn-default',
+                        html : 'CENTER'
+                    }
+                ]
+            }
+        ]
+    }
+});