Roo/Resizable.js
[roojs1] / roojs-core-debug.js
index 648d7b5..5d578b5 100644 (file)
@@ -68,7 +68,7 @@ Roo.apply = function(o, c, defaults){
             document.execCommand("BackgroundImageCache", false, true);
         }catch(e){}
     }
-
+    
     Roo.apply(Roo, {
         /**
          * True if the browser is in strict mode
@@ -120,7 +120,7 @@ Roo.apply = function(o, c, defaults){
         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
 
         emptyFn : function(){},
-
+        
         /**
          * Copies all the properties of config to obj if they don't already exist.
          * @param {Object} obj The receiver of the properties
@@ -336,16 +336,16 @@ Roo.factory(conf, Roo.data);
             }
             var buf = [];
             for(var key in o){
-                var ov = o[key], k = encodeURIComponent(key);
+                var ov = o[key], k = Roo.encodeURIComponent(key);
                 var type = typeof ov;
                 if(type == 'undefined'){
                     buf.push(k, "=&");
                 }else if(type != "function" && type != "object"){
-                    buf.push(k, "=", encodeURIComponent(ov), "&");
+                    buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
                 }else if(ov instanceof Array){
                     if (ov.length) {
                            for(var i = 0, len = ov.length; i < len; i++) {
-                               buf.push(k, "=", encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
+                               buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
                            }
                        } else {
                            buf.push(k, "=&");
@@ -354,6 +354,60 @@ Roo.factory(conf, Roo.data);
             }
             buf.pop();
             return buf.join("");
+        },
+         /**
+         * Safe version of encodeURIComponent
+         * @param {String} data 
+         * @return {String} 
+         */
+        
+        encodeURIComponent : function (data)
+        {
+            try {
+                return encodeURIComponent(data);
+            } catch(e) {} // should be an uri encode error.
+            
+            if (data == '' || data == null){
+               return '';
+            }
+            // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
+            function nibble_to_hex(nibble){
+                var chars = '0123456789ABCDEF';
+                return chars.charAt(nibble);
+            }
+            data = data.toString();
+            var buffer = '';
+            for(var i=0; i<data.length; i++){
+                var c = data.charCodeAt(i);
+                var bs = new Array();
+                if (c > 0x10000){
+                        // 4 bytes
+                    bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
+                    bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
+                    bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
+                    bs[3] = 0x80 | (c & 0x3F);
+                }else if (c > 0x800){
+                         // 3 bytes
+                    bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
+                    bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
+                    bs[2] = 0x80 | (c & 0x3F);
+                }else if (c > 0x80){
+                       // 2 bytes
+                    bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
+                    bs[1] = 0x80 | (c & 0x3F);
+                }else{
+                        // 1 byte
+                    bs[0] = c;
+                }
+                for(var j=0; j<bs.length; j++){
+                    var b = bs[j];
+                    var hex = nibble_to_hex((b & 0xF0) >>> 4) 
+                            + nibble_to_hex(b &0x0F);
+                    buffer += '%'+hex;
+               }
+            }
+            return buffer;    
+             
         },
 
         /**
@@ -952,7 +1006,8 @@ Format  Output      Description
   H      15         24-hour format of an hour with leading zeros
   i      05         Minutes with leading zeros
   s      01         Seconds, with leading zeros
-  O      -0600      Difference to Greenwich time (GMT) in hours
+  O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
+  P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
   T      CST        Timezone setting of the machine running the code
   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
 </pre>
@@ -1119,6 +1174,8 @@ Date.getFormatCode = function(character) {
         return "String.leftPad(this.getSeconds(), 2, '0') + ";
     case "O":
         return "this.getGMTOffset() + ";
+    case "P":
+       return "this.getGMTColonOffset() + ";
     case "T":
         return "this.getTimezone() + ";
     case "Z":
@@ -1330,6 +1387,19 @@ Date.formatCodeToRegex = function(character, currentGroup) {
                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
             ].join(""),
+            s:"([+\-]\\d{2,4})"};
+    
+    
+    case "P":
+       return {g:1,
+               c:[
+                  "o = results[", currentGroup, "];\n",
+                  "var sn = o.substring(0,1);\n",
+                  "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
+                  "var mn = o.substring(4,6) % 60;\n",
+                  "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
+                       "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
+            ].join(""),
             s:"([+\-]\\d{4})"};
     case "T":
         return {g:0,
@@ -1365,6 +1435,18 @@ Date.prototype.getGMTOffset = function() {
         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
 };
 
+/**
+ * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
+ * @return {String} 2-characters representing hours and 2-characters representing minutes
+ * seperated by a colon and prefixed with + or - (e.g. '-06:00')
+ */
+Date.prototype.getGMTColonOffset = function() {
+       return (this.getTimezoneOffset() > 0 ? "-" : "+")
+               + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
+               + ":"
+               + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
+}
+
 /**
  * Get the numeric day number of the year, adjusted for leap year.
  * @return {Number} 0 through 364 (365 in leap years)
@@ -1680,7 +1762,8 @@ Date.prototype.add = function(interval, value){
       break;
   }
   return d;
-};/*
+};
+/*
  * Based on:
  * Ext JS Library 1.1.1
  * Copyright(c) 2006-2007, Ext JS, LLC.
@@ -1691,11 +1774,28 @@ Date.prototype.add = function(interval, value){
  * <script type="text/javascript">
  */
 
+/**
+ * @class Roo.lib.Dom
+ * @static
+ * 
+ * Dom utils (from YIU afaik)
+ * 
+ **/
 Roo.lib.Dom = {
+    /**
+     * Get the view width
+     * @param {Boolean} full True will get the full document, otherwise it's the view width
+     * @return {Number} The width
+     */
+     
     getViewWidth : function(full) {
         return full ? this.getDocumentWidth() : this.getViewportWidth();
     },
-
+    /**
+     * Get the view height
+     * @param {Boolean} full True will get the full document, otherwise it's the view height
+     * @return {Number} The height
+     */
     getViewHeight : function(full) {
         return full ? this.getDocumentHeight() : this.getViewportHeight();
     },
@@ -2390,8 +2490,14 @@ Roo.lib.Event = function() {
  */
 
 (function() {
-    
+    /**
+     * @class Roo.lib.Ajax
+     *
+     */
     Roo.lib.Ajax = {
+        /**
+         * @static 
+         */
         request : function(method, uri, cb, data, options) {
             if(options){
                 var hs = options.headers;
@@ -2432,10 +2538,10 @@ Roo.lib.Event = function() {
                             for (var j = 0; j < el.options.length; j++) {
                                 if (el.options[j].selected) {
                                     if (Roo.isIE) {
-                                        data += encodeURIComponent(name) + '=' + encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
+                                        data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
                                     }
                                     else {
-                                        data += encodeURIComponent(name) + '=' + encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
+                                        data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
                                     }
                                 }
                             }
@@ -2443,7 +2549,7 @@ Roo.lib.Event = function() {
                         case 'radio':
                         case 'checkbox':
                             if (el.checked) {
-                                data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
+                                data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
                             }
                             break;
                         case 'file':
@@ -2457,12 +2563,12 @@ Roo.lib.Event = function() {
                             break;
                         case 'submit':
                             if(hasSubmit == false) {
-                                data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
+                                data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
                                 hasSubmit = true;
                             }
                             break;
                         default:
-                            data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
+                            data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
                             break;
                     }
                 }
@@ -2604,6 +2710,7 @@ Roo.lib.Event = function() {
             var oConn = this;
 
             if (callback && callback.timeout) {
+                
                 this.timeout[o.tId] = window.setTimeout(function() {
                     oConn.abort(o, callback, true);
                 }, callback.timeout);
@@ -3971,12 +4078,27 @@ Roo.lib.Easing = {
  * Fork - LGPL
  * <script type="text/javascript">
  */
+
+
+// nasty IE9 hack - what a pile of crap that is..
+
+ if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
+    Range.prototype.createContextualFragment = function (html) {
+        var doc = window.document;
+        var container = doc.createElement("div");
+        container.innerHTML = html;
+        var frag = doc.createDocumentFragment(), n;
+        while ((n = container.firstChild)) {
+            frag.appendChild(n);
+        }
+        return frag;
+    };
+}
 
 /**
  * @class Roo.DomHelper
  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
- * For more information see <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
+ * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
  * @singleton
  */
 Roo.DomHelper = function(){
@@ -4426,7 +4548,9 @@ var t = new Roo.Template({
 });
 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
 </code></pre>
-* For more information see this blog post with examples: <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">DomHelper - Create Elements using DOM, HTML fragments and Templates</a>. 
+* For more information see this blog post with examples:
+*  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
+     - Create Elements using DOM, HTML fragments and Templates</a>. 
 * @constructor
 * @param {Object} cfg - Configuration object.
 */
@@ -4445,11 +4569,18 @@ Roo.Template = function(cfg){
         // bc
         this.html = cfg;
     }
-    
+    if (this.url) {
+        this.load();
+    }
     
 };
 Roo.Template.prototype = {
     
+    /**
+     * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
+     *                    it should be fixed so that template is observable...
+     */
+    url : false,
     /**
      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
      */
@@ -4461,7 +4592,7 @@ Roo.Template.prototype = {
      */
     applyTemplate : function(values){
         try {
-            
+           
             if(this.compiled){
                 return this.compiled(values);
             }
@@ -4499,6 +4630,36 @@ Roo.Template.prototype = {
          
     },
     
+    loading : false,
+      
+    load : function ()
+    {
+         
+        if (this.loading) {
+            return;
+        }
+        var _t = this;
+        
+        this.loading = true;
+        this.compiled = false;
+        
+        var cx = new Roo.data.Connection();
+        cx.request({
+            url : this.url,
+            method : 'GET',
+            success : function (response) {
+                _t.loading = false;
+                _t.html = response.responseText;
+                _t.url = false;
+                _t.compile();
+             },
+            failure : function(response) {
+                Roo.log("Template failed to load from " + _t.url);
+                _t.loading = false;
+            }
+        });
+    },
+
     /**
      * Sets the HTML used as the template and optionally compiles it.
      * @param {String} html
@@ -8758,7 +8919,8 @@ if(opt.anim.isAnimated()){
          * @param {String} msgCls (optional) A css class to apply to the msg element
          * @return {Element} The mask  element
          */
-        mask : function(msg, msgCls){
+        mask : function(msg, msgCls)
+        {
             if(this.getStyle("position") == "static"){
                 this.setStyle("position", "relative");
             }
@@ -8767,6 +8929,23 @@ if(opt.anim.isAnimated()){
             }
             this.addClass("x-masked");
             this._mask.setDisplayed(true);
+            
+            // we wander
+            var z = 0;
+            var dom = this.dom
+            while (dom && dom.style) {
+                if (!isNaN(parseInt(dom.style.zIndex))) {
+                    z = Math.max(z, parseInt(dom.style.zIndex));
+                }
+                dom = dom.parentNode;
+            }
+            // if we are masking the body - then it hides everything..
+            if (this.dom == document.body) {
+                z = 1000000;
+                this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
+                this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
+            }
+           
             if(typeof msg == 'string'){
                 if(!this._maskMsg){
                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
@@ -8776,10 +8955,13 @@ if(opt.anim.isAnimated()){
                 mm.dom.firstChild.innerHTML = msg;
                 mm.setDisplayed(true);
                 mm.center(this);
+                mm.setStyle('z-index', z + 102);
             }
             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
                 this._mask.setHeight(this.getHeight());
             }
+            this._mask.setStyle('z-index', z + 100);
+            
             return this._mask;
         },
 
@@ -9372,7 +9554,28 @@ if(opt.anim.isAnimated()){
         } : function(ns, name){
             var d = this.dom;
             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
+        },
+        
+        
+        /**
+         * Sets or Returns the value the dom attribute value
+         * @param {String} name The attribute name
+         * @param {String} value (optional) The value to set the attribute to
+         * @return {String} The attribute value
+         */
+        attr : function(name){
+            if (arguments.length > 1) {
+                this.dom.setAttribute(name, arguments[1]);
+                return arguments[1];
+            }
+            if (!this.dom.hasAttribute(name)) {
+                return undefined;
+            }
+            return this.dom.getAttribute(name);
         }
+        
+        
+        
     };
 
     var ep = El.prototype;
@@ -11201,7 +11404,7 @@ Roo.extend(Roo.data.Connection, Roo.util.Observable, {
                 failure: this.handleFailure,
                 scope: this,
                 argument: {options: o},
-                timeout : this.timeout
+                timeout : o.timeout || this.timeout
             };
 
             var method = o.method||this.method||(p ? "POST" : "GET");
@@ -11360,87 +11563,7 @@ Roo.extend(Roo.data.Connection, Roo.util.Observable, {
         }
     }
 });
-
-/**
- * @class Roo.Ajax
- * @extends Roo.data.Connection
- * Global Ajax request class.
- *
- * @singleton
- */
-Roo.Ajax = new Roo.data.Connection({
-    // fix up the docs
-   /**
-     * @cfg {String} url @hide
-     */
-    /**
-     * @cfg {Object} extraParams @hide
-     */
-    /**
-     * @cfg {Object} defaultHeaders @hide
-     */
-    /**
-     * @cfg {String} method (Optional) @hide
-     */
-    /**
-     * @cfg {Number} timeout (Optional) @hide
-     */
-    /**
-     * @cfg {Boolean} autoAbort (Optional) @hide
-     */
-
-    /**
-     * @cfg {Boolean} disableCaching (Optional) @hide
-     */
-
-    /**
-     * @property  disableCaching
-     * True to add a unique cache-buster param to GET requests. (defaults to true)
-     * @type Boolean
-     */
-    /**
-     * @property  url
-     * The default URL to be used for requests to the server. (defaults to undefined)
-     * @type String
-     */
-    /**
-     * @property  extraParams
-     * An object containing properties which are used as
-     * extra parameters to each request made by this object. (defaults to undefined)
-     * @type Object
-     */
-    /**
-     * @property  defaultHeaders
-     * An object containing request headers which are added to each request made by this object. (defaults to undefined)
-     * @type Object
-     */
-    /**
-     * @property  method
-     * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
-     * @type String
-     */
-    /**
-     * @property  timeout
-     * The timeout in milliseconds to be used for requests. (defaults to 30000)
-     * @type Number
-     */
-
-    /**
-     * @property  autoAbort
-     * Whether a new request should abort any pending requests. (defaults to false)
-     * @type Boolean
-     */
-    autoAbort : false,
-
-    /**
-     * Serialize the passed form into a url encoded string
-     * @param {String/HTMLElement} form
-     * @return {String}
-     */
-    serializeForm : function(form){
-        return Roo.lib.Ajax.serializeForm(form);
-    }
-});/*
+/*
  * Based on:
  * Ext JS Library 1.1.1
  * Copyright(c) 2006-2007, Ext JS, LLC.
@@ -11452,84 +11575,31 @@ Roo.Ajax = new Roo.data.Connection({
  */
  
 /**
+ * Global Ajax request class.
+ * 
  * @class Roo.Ajax
  * @extends Roo.data.Connection
- * Global Ajax request class.
- *
- * @instanceOf  Roo.data.Connection
+ * @static
+ * 
+ * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
+ * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
+ * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
+ * @cfg {String} method (Optional)  The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
+ * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
+ * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
+ * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
  */
 Roo.Ajax = new Roo.data.Connection({
     // fix up the docs
-    
     /**
-     * fix up scoping
      * @scope Roo.Ajax
-     */
-    
-   /**
-     * @cfg {String} url @hide
-     */
-    /**
-     * @cfg {Object} extraParams @hide
-     */
-    /**
-     * @cfg {Object} defaultHeaders @hide
-     */
-    /**
-     * @cfg {String} method (Optional) @hide
-     */
-    /**
-     * @cfg {Number} timeout (Optional) @hide
-     */
-    /**
-     * @cfg {Boolean} autoAbort (Optional) @hide
-     */
-
-    /**
-     * @cfg {Boolean} disableCaching (Optional) @hide
-     */
-
-    /**
-     * @property  disableCaching
-     * True to add a unique cache-buster param to GET requests. (defaults to true)
-     * @type Boolean
-     */
-    /**
-     * @property  url
-     * The default URL to be used for requests to the server. (defaults to undefined)
-     * @type String
-     */
-    /**
-     * @property  extraParams
-     * An object containing properties which are used as
-     * extra parameters to each request made by this object. (defaults to undefined)
-     * @type Object
-     */
-    /**
-     * @property  defaultHeaders
-     * An object containing request headers which are added to each request made by this object. (defaults to undefined)
-     * @type Object
-     */
-    /**
-     * @property  method
-     * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
-     * @type String
-     */
-    /**
-     * @property  timeout
-     * The timeout in milliseconds to be used for requests. (defaults to 30000)
-     * @type Number
-     */
-
-    /**
-     * @property  autoAbort
-     * Whether a new request should abort any pending requests. (defaults to false)
-     * @type Boolean
+     * @type {Boolear} 
      */
     autoAbort : false,
 
     /**
      * Serialize the passed form into a url encoded string
+     * @scope Roo.Ajax
      * @param {String/HTMLElement} form
      * @return {String}
      */
@@ -11727,7 +11797,8 @@ um.update({<br/>
      */
     update : function(url, params, callback, discardUrl){
         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
-            var method = this.method, cfg;
+            var method = this.method,
+                cfg;
             if(typeof url == "object"){ // must be config object
                 cfg = url;
                 url = cfg.url;
@@ -11765,7 +11836,7 @@ um.update({<br/>
                 timeout: (this.timeout*1000),
                 argument: {"url": url, "form": null, "callback": callback, "params": params}
             });
-
+            Roo.log("updated manager called with timeout of " + o.timeout);
             this.transaction = Roo.Ajax.request(o);
         }
     },
@@ -12022,64 +12093,519 @@ Roo.UpdateManager.BasicRenderer.prototype = {
 };
 /*
  * Based on:
- * Ext JS Library 1.1.1
- * Copyright(c) 2006-2007, Ext JS, LLC.
- *
- * Originally Released Under LGPL - original licence link has changed is not relivant.
- *
- * Fork - LGPL
- * <script type="text/javascript">
+ * Roo JS
+ * (c)) Alan Knowles
+ * Licence : LGPL
  */
 
+
 /**
- * @class Roo.util.DelayedTask
- * Provides a convenient method of performing setTimeout where a new
- * timeout cancels the old timeout. An example would be performing validation on a keypress.
- * You can use this class to buffer
- * the keypress events for a certain number of milliseconds, and perform only if they stop
- * for that amount of time.
- * @constructor The parameters to this constructor serve as defaults and are not required.
- * @param {Function} fn (optional) The default function to timeout
- * @param {Object} scope (optional) The default scope of that timeout
- * @param {Array} args (optional) The default Array of arguments
+ * @class Roo.DomTemplate
+ * @extends Roo.Template
+ * An effort at a dom based template engine..
+ *
+ * Similar to XTemplate, except it uses dom parsing to create the template..
+ *
+ * Supported features:
+ *
+ *  Tags:
+
+<pre><code>
+      {a_variable} - output encoded.
+      {a_variable.format:("Y-m-d")} - call a method on the variable
+      {a_variable:raw} - unencoded output
+      {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
+      {a_variable:this.method_on_template(...)} - call a method on the template object.
+</code></pre>
+ *  The tpl tag:
+<pre><code>
+        &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
+        &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
+        &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
+        &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
+  
+</code></pre>
+ *      
  */
-Roo.util.DelayedTask = function(fn, scope, args){
-    var id = null, d, t;
+Roo.DomTemplate = function()
+{
+     Roo.DomTemplate.superclass.constructor.apply(this, arguments);
+     if (this.html) {
+        this.compile();
+     }
+};
 
-    var call = function(){
-        var now = new Date().getTime();
-        if(now - t >= d){
-            clearInterval(id);
-            id = null;
-            fn.apply(scope, args || []);
-        }
-    };
+
+Roo.extend(Roo.DomTemplate, Roo.Template, {
     /**
-     * Cancels any pending timeout and queues a new one
-     * @param {Number} delay The milliseconds to delay
-     * @param {Function} newFn (optional) Overrides function passed to constructor
-     * @param {Object} newScope (optional) Overrides scope passed to constructor
-     * @param {Array} newArgs (optional) Overrides args passed to constructor
+     * id counter for sub templates.
      */
-    this.delay = function(delay, newFn, newScope, newArgs){
-        if(id && delay != d){
-            this.cancel();
+    id : 0,
+    /**
+     * flag to indicate if dom parser is inside a pre,
+     * it will strip whitespace if not.
+     */
+    inPre : false,
+    
+    /**
+     * The various sub templates
+     */
+    tpls : false,
+    
+    
+    
+    /**
+     *
+     * basic tag replacing syntax
+     * WORD:WORD()
+     *
+     * // you can fake an object call by doing this
+     *  x.t:(test,tesT) 
+     * 
+     */
+    re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
+    //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
+    
+    iterChild : function (node, method) {
+        
+        var oldPre = this.inPre;
+        if (node.tagName == 'PRE') {
+            this.inPre = true;
         }
-        d = delay;
-        t = new Date().getTime();
-        fn = newFn || fn;
-        scope = newScope || scope;
-        args = newArgs || args;
-        if(!id){
-            id = setInterval(call, d);
+        for( var i = 0; i < node.childNodes.length; i++) {
+            method.call(this, node.childNodes[i]);
         }
-    };
-
+        this.inPre = oldPre;
+    },
+    
+    
+    
     /**
-     * Cancel the last queued timeout
+     * compile the template
+     *
+     * This is not recursive, so I'm not sure how nested templates are really going to be handled..
+     *
      */
-    this.cancel = function(){
-        if(id){
+    compile: function()
+    {
+        var s = this.html;
+        
+        // covert the html into DOM...
+        var doc = false;
+        var div =false;
+        try {
+            doc = document.implementation.createHTMLDocument("");
+            doc.documentElement.innerHTML =   this.html  ;
+            div = doc.documentElement;
+        } catch (e) {
+            // old IE... - nasty -- it causes all sorts of issues.. with
+            // images getting pulled from server..
+            div = document.createElement('div');
+            div.innerHTML = this.html;
+        }
+        //doc.documentElement.innerHTML = htmlBody
+         
+        
+        
+        this.tpls = [];
+        var _t = this;
+        this.iterChild(div, function(n) {_t.compileNode(n, true); });
+        
+        var tpls = this.tpls;
+        
+        // create a top level template from the snippet..
+        
+        //Roo.log(div.innerHTML);
+        
+        var tpl = {
+            uid : 'master',
+            id : this.id++,
+            attr : false,
+            value : false,
+            body : div.innerHTML,
+            
+            forCall : false,
+            execCall : false,
+            dom : div,
+            isTop : true
+            
+        };
+        tpls.unshift(tpl);
+        
+        
+        // compile them...
+        this.tpls = [];
+        Roo.each(tpls, function(tp){
+            this.compileTpl(tp);
+            this.tpls[tp.id] = tp;
+        }, this);
+        
+        this.master = tpls[0];
+        return this;
+        
+        
+    },
+    
+    compileNode : function(node, istop) {
+        // test for
+        //Roo.log(node);
+        
+        
+        // skip anything not a tag..
+        if (node.nodeType != 1) {
+            if (node.nodeType == 3 && !this.inPre) {
+                // reduce white space..
+                node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
+                
+            }
+            return;
+        }
+        
+        var tpl = {
+            uid : false,
+            id : false,
+            attr : false,
+            value : false,
+            body : '',
+            
+            forCall : false,
+            execCall : false,
+            dom : false,
+            isTop : istop
+            
+            
+        };
+        
+        
+        switch(true) {
+            case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
+            case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
+            case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
+            case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
+            // no default..
+        }
+        
+        
+        if (!tpl.attr) {
+            // just itterate children..
+            this.iterChild(node,this.compileNode);
+            return;
+        }
+        tpl.uid = this.id++;
+        tpl.value = node.getAttribute('roo-' +  tpl.attr);
+        node.removeAttribute('roo-'+ tpl.attr);
+        if (tpl.attr != 'name') {
+            var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
+            node.parentNode.replaceChild(placeholder,  node);
+        } else {
+            
+            var placeholder =  document.createElement('span');
+            placeholder.className = 'roo-tpl-' + tpl.value;
+            node.parentNode.replaceChild(placeholder,  node);
+        }
+        
+        // parent now sees '{domtplXXXX}
+        this.iterChild(node,this.compileNode);
+        
+        // we should now have node body...
+        var div = document.createElement('div');
+        div.appendChild(node);
+        tpl.dom = node;
+        // this has the unfortunate side effect of converting tagged attributes
+        // eg. href="{...}" into %7C...%7D
+        // this has been fixed by searching for those combo's although it's a bit hacky..
+        
+        
+        tpl.body = div.innerHTML;
+        
+        
+         
+        tpl.id = tpl.uid;
+        switch(tpl.attr) {
+            case 'for' :
+                switch (tpl.value) {
+                    case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
+                    case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
+                    default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
+                }
+                break;
+            
+            case 'exec':
+                tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
+                break;
+            
+            case 'if':     
+                tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
+                break;
+            
+            case 'name':
+                tpl.id  = tpl.value; // replace non characters???
+                break;
+            
+        }
+        
+        
+        this.tpls.push(tpl);
+        
+        
+        
+    },
+    
+    
+    
+    
+    /**
+     * Compile a segment of the template into a 'sub-template'
+     *
+     * 
+     * 
+     *
+     */
+    compileTpl : function(tpl)
+    {
+        var fm = Roo.util.Format;
+        var useF = this.disableFormats !== true;
+        
+        var sep = Roo.isGecko ? "+\n" : ",\n";
+        
+        var undef = function(str) {
+            Roo.debug && Roo.log("Property not found :"  + str);
+            return '';
+        };
+          
+        //Roo.log(tpl.body);
+        
+        
+        
+        var fn = function(m, lbrace, name, format, args)
+        {
+            //Roo.log("ARGS");
+            //Roo.log(arguments);
+            args = args ? args.replace(/\\'/g,"'") : args;
+            //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
+            if (typeof(format) == 'undefined') {
+                format =  'htmlEncode'; 
+            }
+            if (format == 'raw' ) {
+                format = false;
+            }
+            
+            if(name.substr(0, 6) == 'domtpl'){
+                return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
+            }
+            
+            // build an array of options to determine if value is undefined..
+            
+            // basically get 'xxxx.yyyy' then do
+            // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
+            //    (function () { Roo.log("Property not found"); return ''; })() :
+            //    ......
+            
+            var udef_ar = [];
+            var lookfor = '';
+            Roo.each(name.split('.'), function(st) {
+                lookfor += (lookfor.length ? '.': '') + st;
+                udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
+            });
+            
+            var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
+            
+            
+            if(format && useF){
+                
+                args = args ? ',' + args : "";
+                 
+                if(format.substr(0, 5) != "this."){
+                    format = "fm." + format + '(';
+                }else{
+                    format = 'this.call("'+ format.substr(5) + '", ';
+                    args = ", values";
+                }
+                
+                return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
+            }
+             
+            if (args && args.length) {
+                // called with xxyx.yuu:(test,test)
+                // change to ()
+                return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
+            }
+            // raw.. - :raw modifier..
+            return "'"+ sep + udef_st  + name + ")"+sep+"'";
+            
+        };
+        var body;
+        // branched to use + in gecko and [].join() in others
+        if(Roo.isGecko){
+            body = "tpl.compiled = function(values, parent){  with(values) { return '" +
+                   tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
+                    "';};};";
+        }else{
+            body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
+            body.push(tpl.body.replace(/(\r\n|\n)/g,
+                            '\\n').replace(/'/g, "\\'").replace(this.re, fn));
+            body.push("'].join('');};};");
+            body = body.join('');
+        }
+        
+        Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
+       
+        /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
+        eval(body);
+        
+        return this;
+    },
+     
+    /**
+     * same as applyTemplate, except it's done to one of the subTemplates
+     * when using named templates, you can do:
+     *
+     * var str = pl.applySubTemplate('your-name', values);
+     *
+     * 
+     * @param {Number} id of the template
+     * @param {Object} values to apply to template
+     * @param {Object} parent (normaly the instance of this object)
+     */
+    applySubTemplate : function(id, values, parent)
+    {
+        
+        
+        var t = this.tpls[id];
+        
+        
+        try { 
+            if(t.ifCall && !t.ifCall.call(this, values, parent)){
+                Roo.debug && Roo.log('if call on ' + t.value + ' return false');
+                return '';
+            }
+        } catch(e) {
+            Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
+            Roo.log(values);
+          
+            return '';
+        }
+        try { 
+            
+            if(t.execCall && t.execCall.call(this, values, parent)){
+                return '';
+            }
+        } catch(e) {
+            Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
+            Roo.log(values);
+            return '';
+        }
+        
+        try {
+            var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
+            parent = t.target ? values : parent;
+            if(t.forCall && vs instanceof Array){
+                var buf = [];
+                for(var i = 0, len = vs.length; i < len; i++){
+                    try {
+                        buf[buf.length] = t.compiled.call(this, vs[i], parent);
+                    } catch (e) {
+                        Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
+                        Roo.log(e.body);
+                        //Roo.log(t.compiled);
+                        Roo.log(vs[i]);
+                    }   
+                }
+                return buf.join('');
+            }
+        } catch (e) {
+            Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
+            Roo.log(values);
+            return '';
+        }
+        try {
+            return t.compiled.call(this, vs, parent);
+        } catch (e) {
+            Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
+            Roo.log(e.body);
+            //Roo.log(t.compiled);
+            Roo.log(values);
+            return '';
+        }
+    },
+
+   
+
+    applyTemplate : function(values){
+        return this.master.compiled.call(this, values, {});
+        //var s = this.subs;
+    },
+
+    apply : function(){
+        return this.applyTemplate.apply(this, arguments);
+    }
+
+ });
+
+Roo.DomTemplate.from = function(el){
+    el = Roo.getDom(el);
+    return new Roo.Domtemplate(el.value || el.innerHTML);
+};/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+
+/**
+ * @class Roo.util.DelayedTask
+ * Provides a convenient method of performing setTimeout where a new
+ * timeout cancels the old timeout. An example would be performing validation on a keypress.
+ * You can use this class to buffer
+ * the keypress events for a certain number of milliseconds, and perform only if they stop
+ * for that amount of time.
+ * @constructor The parameters to this constructor serve as defaults and are not required.
+ * @param {Function} fn (optional) The default function to timeout
+ * @param {Object} scope (optional) The default scope of that timeout
+ * @param {Array} args (optional) The default Array of arguments
+ */
+Roo.util.DelayedTask = function(fn, scope, args){
+    var id = null, d, t;
+
+    var call = function(){
+        var now = new Date().getTime();
+        if(now - t >= d){
+            clearInterval(id);
+            id = null;
+            fn.apply(scope, args || []);
+        }
+    };
+    /**
+     * Cancels any pending timeout and queues a new one
+     * @param {Number} delay The milliseconds to delay
+     * @param {Function} newFn (optional) Overrides function passed to constructor
+     * @param {Object} newScope (optional) Overrides scope passed to constructor
+     * @param {Array} newArgs (optional) Overrides args passed to constructor
+     */
+    this.delay = function(delay, newFn, newScope, newArgs){
+        if(id && delay != d){
+            this.cancel();
+        }
+        d = delay;
+        t = new Date().getTime();
+        fn = newFn || fn;
+        scope = newScope || scope;
+        args = newArgs || args;
+        if(!id){
+            id = setInterval(call, d);
+        }
+    };
+
+    /**
+     * Cancel the last queued timeout
+     */
+    this.cancel = function(){
+        if(id){
             clearInterval(id);
             id = null;
         }
@@ -13014,17 +13540,47 @@ Roo.util.Format = function(){
          * @return {String} The formatted currency string
          */
         usMoney : function(v){
-            v = (Math.round((v-0)*100))/100;
-            v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
+            return '$' + Roo.util.Format.number(v);
+        },
+        
+        /**
+         * Format a number
+         * eventually this should probably emulate php's number_format
+         * @param {Number/String} value The numeric value to format
+         * @param {Number} decimals number of decimal places
+         * @return {String} The formatted currency string
+         */
+        number : function(v,decimals)
+        {
+            // multiply and round.
+            decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
+            var mul = Math.pow(10, decimals);
+            var zero = String(mul).substring(1);
+            v = (Math.round((v-0)*mul))/mul;
+            
+            // if it's '0' number.. then
+            
+            //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
             v = String(v);
             var ps = v.split('.');
             var whole = ps[0];
-            var sub = ps[1] ? '.'+ ps[1] : '.00';
+            
+            
             var r = /(\d+)(\d{3})/;
+            // add comma's
             while (r.test(whole)) {
                 whole = whole.replace(r, '$1' + ',' + '$2');
             }
-            return "$" + whole + sub ;
+            
+            
+            var sub = ps[1] ?
+                    // has decimals..
+                    (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
+                    // does not have decimals
+                    (decimals ? ('.' + zero) : '');
+            
+            
+            return whole + sub ;
         },
         
         /**
@@ -13403,4 +13959,990 @@ Roo.util.CSS = function(){
                return false;
        }
    };  
-}();
\ No newline at end of file
+}();/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+
+
+/**
+ * @class Roo.util.ClickRepeater
+ * @extends Roo.util.Observable
+ * 
+ * A wrapper class which can be applied to any element. Fires a "click" event while the
+ * mouse is pressed. The interval between firings may be specified in the config but
+ * defaults to 10 milliseconds.
+ * 
+ * Optionally, a CSS class may be applied to the element during the time it is pressed.
+ * 
+ * @cfg {String/HTMLElement/Element} el The element to act as a button.
+ * @cfg {Number} delay The initial delay before the repeating event begins firing.
+ * Similar to an autorepeat key delay.
+ * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
+ * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
+ * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
+ *           "interval" and "delay" are ignored. "immediate" is honored.
+ * @cfg {Boolean} preventDefault True to prevent the default click event
+ * @cfg {Boolean} stopDefault True to stop the default click event
+ * 
+ * @history
+ *     2007-02-02 jvs Original code contributed by Nige "Animal" White
+ *     2007-02-02 jvs Renamed to ClickRepeater
+ *   2007-02-03 jvs Modifications for FF Mac and Safari 
+ *
+ *  @constructor
+ * @param {String/HTMLElement/Element} el The element to listen on
+ * @param {Object} config
+ **/
+Roo.util.ClickRepeater = function(el, config)
+{
+    this.el = Roo.get(el);
+    this.el.unselectable();
+
+    Roo.apply(this, config);
+
+    this.addEvents({
+    /**
+     * @event mousedown
+     * Fires when the mouse button is depressed.
+     * @param {Roo.util.ClickRepeater} this
+     */
+        "mousedown" : true,
+    /**
+     * @event click
+     * Fires on a specified interval during the time the element is pressed.
+     * @param {Roo.util.ClickRepeater} this
+     */
+        "click" : true,
+    /**
+     * @event mouseup
+     * Fires when the mouse key is released.
+     * @param {Roo.util.ClickRepeater} this
+     */
+        "mouseup" : true
+    });
+
+    this.el.on("mousedown", this.handleMouseDown, this);
+    if(this.preventDefault || this.stopDefault){
+        this.el.on("click", function(e){
+            if(this.preventDefault){
+                e.preventDefault();
+            }
+            if(this.stopDefault){
+                e.stopEvent();
+            }
+        }, this);
+    }
+
+    // allow inline handler
+    if(this.handler){
+        this.on("click", this.handler,  this.scope || this);
+    }
+
+    Roo.util.ClickRepeater.superclass.constructor.call(this);
+};
+
+Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
+    interval : 20,
+    delay: 250,
+    preventDefault : true,
+    stopDefault : false,
+    timer : 0,
+
+    // private
+    handleMouseDown : function(){
+        clearTimeout(this.timer);
+        this.el.blur();
+        if(this.pressClass){
+            this.el.addClass(this.pressClass);
+        }
+        this.mousedownTime = new Date();
+
+        Roo.get(document).on("mouseup", this.handleMouseUp, this);
+        this.el.on("mouseout", this.handleMouseOut, this);
+
+        this.fireEvent("mousedown", this);
+        this.fireEvent("click", this);
+        
+        this.timer = this.click.defer(this.delay || this.interval, this);
+    },
+
+    // private
+    click : function(){
+        this.fireEvent("click", this);
+        this.timer = this.click.defer(this.getInterval(), this);
+    },
+
+    // private
+    getInterval: function(){
+        if(!this.accelerate){
+            return this.interval;
+        }
+        var pressTime = this.mousedownTime.getElapsed();
+        if(pressTime < 500){
+            return 400;
+        }else if(pressTime < 1700){
+            return 320;
+        }else if(pressTime < 2600){
+            return 250;
+        }else if(pressTime < 3500){
+            return 180;
+        }else if(pressTime < 4400){
+            return 140;
+        }else if(pressTime < 5300){
+            return 80;
+        }else if(pressTime < 6200){
+            return 50;
+        }else{
+            return 10;
+        }
+    },
+
+    // private
+    handleMouseOut : function(){
+        clearTimeout(this.timer);
+        if(this.pressClass){
+            this.el.removeClass(this.pressClass);
+        }
+        this.el.on("mouseover", this.handleMouseReturn, this);
+    },
+
+    // private
+    handleMouseReturn : function(){
+        this.el.un("mouseover", this.handleMouseReturn);
+        if(this.pressClass){
+            this.el.addClass(this.pressClass);
+        }
+        this.click();
+    },
+
+    // private
+    handleMouseUp : function(){
+        clearTimeout(this.timer);
+        this.el.un("mouseover", this.handleMouseReturn);
+        this.el.un("mouseout", this.handleMouseOut);
+        Roo.get(document).un("mouseup", this.handleMouseUp);
+        this.el.removeClass(this.pressClass);
+        this.fireEvent("mouseup", this);
+    }
+});/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+
+/**
+ * @class Roo.KeyNav
+ * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
+ * navigation keys to function calls that will get called when the keys are pressed, providing an easy
+ * way to implement custom navigation schemes for any UI component.</p>
+ * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
+ * pageUp, pageDown, del, home, end.  Usage:</p>
+ <pre><code>
+var nav = new Roo.KeyNav("my-element", {
+    "left" : function(e){
+        this.moveLeft(e.ctrlKey);
+    },
+    "right" : function(e){
+        this.moveRight(e.ctrlKey);
+    },
+    "enter" : function(e){
+        this.save();
+    },
+    scope : this
+});
+</code></pre>
+ * @constructor
+ * @param {String/HTMLElement/Roo.Element} el The element to bind to
+ * @param {Object} config The config
+ */
+Roo.KeyNav = function(el, config){
+    this.el = Roo.get(el);
+    Roo.apply(this, config);
+    if(!this.disabled){
+        this.disabled = true;
+        this.enable();
+    }
+};
+
+Roo.KeyNav.prototype = {
+    /**
+     * @cfg {Boolean} disabled
+     * True to disable this KeyNav instance (defaults to false)
+     */
+    disabled : false,
+    /**
+     * @cfg {String} defaultEventAction
+     * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
+     * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
+     * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
+     */
+    defaultEventAction: "stopEvent",
+    /**
+     * @cfg {Boolean} forceKeyDown
+     * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
+     * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
+     * handle keydown instead of keypress.
+     */
+    forceKeyDown : false,
+
+    // private
+    prepareEvent : function(e){
+        var k = e.getKey();
+        var h = this.keyToHandler[k];
+        //if(h && this[h]){
+        //    e.stopPropagation();
+        //}
+        if(Roo.isSafari && h && k >= 37 && k <= 40){
+            e.stopEvent();
+        }
+    },
+
+    // private
+    relay : function(e){
+        var k = e.getKey();
+        var h = this.keyToHandler[k];
+        if(h && this[h]){
+            if(this.doRelay(e, this[h], h) !== true){
+                e[this.defaultEventAction]();
+            }
+        }
+    },
+
+    // private
+    doRelay : function(e, h, hname){
+        return h.call(this.scope || this, e);
+    },
+
+    // possible handlers
+    enter : false,
+    left : false,
+    right : false,
+    up : false,
+    down : false,
+    tab : false,
+    esc : false,
+    pageUp : false,
+    pageDown : false,
+    del : false,
+    home : false,
+    end : false,
+
+    // quick lookup hash
+    keyToHandler : {
+        37 : "left",
+        39 : "right",
+        38 : "up",
+        40 : "down",
+        33 : "pageUp",
+        34 : "pageDown",
+        46 : "del",
+        36 : "home",
+        35 : "end",
+        13 : "enter",
+        27 : "esc",
+        9  : "tab"
+    },
+
+       /**
+        * Enable this KeyNav
+        */
+       enable: function(){
+               if(this.disabled){
+            // ie won't do special keys on keypress, no one else will repeat keys with keydown
+            // the EventObject will normalize Safari automatically
+            if(this.forceKeyDown || Roo.isIE || Roo.isAir){
+                this.el.on("keydown", this.relay,  this);
+            }else{
+                this.el.on("keydown", this.prepareEvent,  this);
+                this.el.on("keypress", this.relay,  this);
+            }
+                   this.disabled = false;
+               }
+       },
+
+       /**
+        * Disable this KeyNav
+        */
+       disable: function(){
+               if(!this.disabled){
+                   if(this.forceKeyDown || Roo.isIE || Roo.isAir){
+                this.el.un("keydown", this.relay);
+            }else{
+                this.el.un("keydown", this.prepareEvent);
+                this.el.un("keypress", this.relay);
+            }
+                   this.disabled = true;
+               }
+       }
+};/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+
+/**
+ * @class Roo.KeyMap
+ * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
+ * The constructor accepts the same config object as defined by {@link #addBinding}.
+ * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
+ * combination it will call the function with this signature (if the match is a multi-key
+ * combination the callback will still be called only once): (String key, Roo.EventObject e)
+ * A KeyMap can also handle a string representation of keys.<br />
+ * Usage:
+ <pre><code>
+// map one key by key code
+var map = new Roo.KeyMap("my-element", {
+    key: 13, // or Roo.EventObject.ENTER
+    fn: myHandler,
+    scope: myObject
+});
+
+// map multiple keys to one action by string
+var map = new Roo.KeyMap("my-element", {
+    key: "a\r\n\t",
+    fn: myHandler,
+    scope: myObject
+});
+
+// map multiple keys to multiple actions by strings and array of codes
+var map = new Roo.KeyMap("my-element", [
+    {
+        key: [10,13],
+        fn: function(){ alert("Return was pressed"); }
+    }, {
+        key: "abc",
+        fn: function(){ alert('a, b or c was pressed'); }
+    }, {
+        key: "\t",
+        ctrl:true,
+        shift:true,
+        fn: function(){ alert('Control + shift + tab was pressed.'); }
+    }
+]);
+</code></pre>
+ * <b>Note: A KeyMap starts enabled</b>
+ * @constructor
+ * @param {String/HTMLElement/Roo.Element} el The element to bind to
+ * @param {Object} config The config (see {@link #addBinding})
+ * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
+ */
+Roo.KeyMap = function(el, config, eventName){
+    this.el  = Roo.get(el);
+    this.eventName = eventName || "keydown";
+    this.bindings = [];
+    if(config){
+        this.addBinding(config);
+    }
+    this.enable();
+};
+
+Roo.KeyMap.prototype = {
+    /**
+     * True to stop the event from bubbling and prevent the default browser action if the
+     * key was handled by the KeyMap (defaults to false)
+     * @type Boolean
+     */
+    stopEvent : false,
+
+    /**
+     * Add a new binding to this KeyMap. The following config object properties are supported:
+     * <pre>
+Property    Type             Description
+----------  ---------------  ----------------------------------------------------------------------
+key         String/Array     A single keycode or an array of keycodes to handle
+shift       Boolean          True to handle key only when shift is pressed (defaults to false)
+ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
+alt         Boolean          True to handle key only when alt is pressed (defaults to false)
+fn          Function         The function to call when KeyMap finds the expected key combination
+scope       Object           The scope of the callback function
+</pre>
+     *
+     * Usage:
+     * <pre><code>
+// Create a KeyMap
+var map = new Roo.KeyMap(document, {
+    key: Roo.EventObject.ENTER,
+    fn: handleKey,
+    scope: this
+});
+
+//Add a new binding to the existing KeyMap later
+map.addBinding({
+    key: 'abc',
+    shift: true,
+    fn: handleKey,
+    scope: this
+});
+</code></pre>
+     * @param {Object/Array} config A single KeyMap config or an array of configs
+     */
+       addBinding : function(config){
+        if(config instanceof Array){
+            for(var i = 0, len = config.length; i < len; i++){
+                this.addBinding(config[i]);
+            }
+            return;
+        }
+        var keyCode = config.key,
+            shift = config.shift, 
+            ctrl = config.ctrl, 
+            alt = config.alt,
+            fn = config.fn,
+            scope = config.scope;
+        if(typeof keyCode == "string"){
+            var ks = [];
+            var keyString = keyCode.toUpperCase();
+            for(var j = 0, len = keyString.length; j < len; j++){
+                ks.push(keyString.charCodeAt(j));
+            }
+            keyCode = ks;
+        }
+        var keyArray = keyCode instanceof Array;
+        var handler = function(e){
+            if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
+                var k = e.getKey();
+                if(keyArray){
+                    for(var i = 0, len = keyCode.length; i < len; i++){
+                        if(keyCode[i] == k){
+                          if(this.stopEvent){
+                              e.stopEvent();
+                          }
+                          fn.call(scope || window, k, e);
+                          return;
+                        }
+                    }
+                }else{
+                    if(k == keyCode){
+                        if(this.stopEvent){
+                           e.stopEvent();
+                        }
+                        fn.call(scope || window, k, e);
+                    }
+                }
+            }
+        };
+        this.bindings.push(handler);  
+       },
+
+    /**
+     * Shorthand for adding a single key listener
+     * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
+     * following options:
+     * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
+     * @param {Function} fn The function to call
+     * @param {Object} scope (optional) The scope of the function
+     */
+    on : function(key, fn, scope){
+        var keyCode, shift, ctrl, alt;
+        if(typeof key == "object" && !(key instanceof Array)){
+            keyCode = key.key;
+            shift = key.shift;
+            ctrl = key.ctrl;
+            alt = key.alt;
+        }else{
+            keyCode = key;
+        }
+        this.addBinding({
+            key: keyCode,
+            shift: shift,
+            ctrl: ctrl,
+            alt: alt,
+            fn: fn,
+            scope: scope
+        })
+    },
+
+    // private
+    handleKeyDown : function(e){
+           if(this.enabled){ //just in case
+           var b = this.bindings;
+           for(var i = 0, len = b.length; i < len; i++){
+               b[i].call(this, e);
+           }
+           }
+       },
+       
+       /**
+        * Returns true if this KeyMap is enabled
+        * @return {Boolean} 
+        */
+       isEnabled : function(){
+           return this.enabled;  
+       },
+       
+       /**
+        * Enables this KeyMap
+        */
+       enable: function(){
+               if(!this.enabled){
+                   this.el.on(this.eventName, this.handleKeyDown, this);
+                   this.enabled = true;
+               }
+       },
+
+       /**
+        * Disable this KeyMap
+        */
+       disable: function(){
+               if(this.enabled){
+                   this.el.removeListener(this.eventName, this.handleKeyDown, this);
+                   this.enabled = false;
+               }
+       }
+};/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+
+/**
+ * @class Roo.util.TextMetrics
+ * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
+ * wide, in pixels, a given block of text will be.
+ * @singleton
+ */
+Roo.util.TextMetrics = function(){
+    var shared;
+    return {
+        /**
+         * Measures the size of the specified text
+         * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
+         * that can affect the size of the rendered text
+         * @param {String} text The text to measure
+         * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
+         * in order to accurately measure the text height
+         * @return {Object} An object containing the text's size {width: (width), height: (height)}
+         */
+        measure : function(el, text, fixedWidth){
+            if(!shared){
+                shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
+            }
+            shared.bind(el);
+            shared.setFixedWidth(fixedWidth || 'auto');
+            return shared.getSize(text);
+        },
+
+        /**
+         * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
+         * the overhead of multiple calls to initialize the style properties on each measurement.
+         * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
+         * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
+         * in order to accurately measure the text height
+         * @return {Roo.util.TextMetrics.Instance} instance The new instance
+         */
+        createInstance : function(el, fixedWidth){
+            return Roo.util.TextMetrics.Instance(el, fixedWidth);
+        }
+    };
+}();
+
+
+Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
+    var ml = new Roo.Element(document.createElement('div'));
+    document.body.appendChild(ml.dom);
+    ml.position('absolute');
+    ml.setLeftTop(-1000, -1000);
+    ml.hide();
+
+    if(fixedWidth){
+        ml.setWidth(fixedWidth);
+    }
+     
+    var instance = {
+        /**
+         * Returns the size of the specified text based on the internal element's style and width properties
+         * @memberOf Roo.util.TextMetrics.Instance#
+         * @param {String} text The text to measure
+         * @return {Object} An object containing the text's size {width: (width), height: (height)}
+         */
+        getSize : function(text){
+            ml.update(text);
+            var s = ml.getSize();
+            ml.update('');
+            return s;
+        },
+
+        /**
+         * Binds this TextMetrics instance to an element from which to copy existing CSS styles
+         * that can affect the size of the rendered text
+         * @memberOf Roo.util.TextMetrics.Instance#
+         * @param {String/HTMLElement} el The element, dom node or id
+         */
+        bind : function(el){
+            ml.setStyle(
+                Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
+            );
+        },
+
+        /**
+         * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
+         * to set a fixed width in order to accurately measure the text height.
+         * @memberOf Roo.util.TextMetrics.Instance#
+         * @param {Number} width The width to set on the element
+         */
+        setFixedWidth : function(width){
+            ml.setWidth(width);
+        },
+
+        /**
+         * Returns the measured width of the specified text
+         * @memberOf Roo.util.TextMetrics.Instance#
+         * @param {String} text The text to measure
+         * @return {Number} width The width in pixels
+         */
+        getWidth : function(text){
+            ml.dom.style.width = 'auto';
+            return this.getSize(text).width;
+        },
+
+        /**
+         * Returns the measured height of the specified text.  For multiline text, be sure to call
+         * {@link #setFixedWidth} if necessary.
+         * @memberOf Roo.util.TextMetrics.Instance#
+         * @param {String} text The text to measure
+         * @return {Number} height The height in pixels
+         */
+        getHeight : function(text){
+            return this.getSize(text).height;
+        }
+    };
+
+    instance.bind(bindTo);
+
+    return instance;
+};
+
+// backwards compat
+Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+
+/**
+ * @class Roo.state.Provider
+ * Abstract base class for state provider implementations. This class provides methods
+ * for encoding and decoding <b>typed</b> variables including dates and defines the 
+ * Provider interface.
+ */
+Roo.state.Provider = function(){
+    /**
+     * @event statechange
+     * Fires when a state change occurs.
+     * @param {Provider} this This state provider
+     * @param {String} key The state key which was changed
+     * @param {String} value The encoded value for the state
+     */
+    this.addEvents({
+        "statechange": true
+    });
+    this.state = {};
+    Roo.state.Provider.superclass.constructor.call(this);
+};
+Roo.extend(Roo.state.Provider, Roo.util.Observable, {
+    /**
+     * Returns the current value for a key
+     * @param {String} name The key name
+     * @param {Mixed} defaultValue A default value to return if the key's value is not found
+     * @return {Mixed} The state data
+     */
+    get : function(name, defaultValue){
+        return typeof this.state[name] == "undefined" ?
+            defaultValue : this.state[name];
+    },
+    
+    /**
+     * Clears a value from the state
+     * @param {String} name The key name
+     */
+    clear : function(name){
+        delete this.state[name];
+        this.fireEvent("statechange", this, name, null);
+    },
+    
+    /**
+     * Sets the value for a key
+     * @param {String} name The key name
+     * @param {Mixed} value The value to set
+     */
+    set : function(name, value){
+        this.state[name] = value;
+        this.fireEvent("statechange", this, name, value);
+    },
+    
+    /**
+     * Decodes a string previously encoded with {@link #encodeValue}.
+     * @param {String} value The value to decode
+     * @return {Mixed} The decoded value
+     */
+    decodeValue : function(cookie){
+        var re = /^(a|n|d|b|s|o)\:(.*)$/;
+        var matches = re.exec(unescape(cookie));
+        if(!matches || !matches[1]) return; // non state cookie
+        var type = matches[1];
+        var v = matches[2];
+        switch(type){
+            case "n":
+                return parseFloat(v);
+            case "d":
+                return new Date(Date.parse(v));
+            case "b":
+                return (v == "1");
+            case "a":
+                var all = [];
+                var values = v.split("^");
+                for(var i = 0, len = values.length; i < len; i++){
+                    all.push(this.decodeValue(values[i]));
+                }
+                return all;
+           case "o":
+                var all = {};
+                var values = v.split("^");
+                for(var i = 0, len = values.length; i < len; i++){
+                    var kv = values[i].split("=");
+                    all[kv[0]] = this.decodeValue(kv[1]);
+                }
+                return all;
+           default:
+                return v;
+        }
+    },
+    
+    /**
+     * Encodes a value including type information.  Decode with {@link #decodeValue}.
+     * @param {Mixed} value The value to encode
+     * @return {String} The encoded value
+     */
+    encodeValue : function(v){
+        var enc;
+        if(typeof v == "number"){
+            enc = "n:" + v;
+        }else if(typeof v == "boolean"){
+            enc = "b:" + (v ? "1" : "0");
+        }else if(v instanceof Date){
+            enc = "d:" + v.toGMTString();
+        }else if(v instanceof Array){
+            var flat = "";
+            for(var i = 0, len = v.length; i < len; i++){
+                flat += this.encodeValue(v[i]);
+                if(i != len-1) flat += "^";
+            }
+            enc = "a:" + flat;
+        }else if(typeof v == "object"){
+            var flat = "";
+            for(var key in v){
+                if(typeof v[key] != "function"){
+                    flat += key + "=" + this.encodeValue(v[key]) + "^";
+                }
+            }
+            enc = "o:" + flat.substring(0, flat.length-1);
+        }else{
+            enc = "s:" + v;
+        }
+        return escape(enc);        
+    }
+});
+
+/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+/**
+ * @class Roo.state.Manager
+ * This is the global state manager. By default all components that are "state aware" check this class
+ * for state information if you don't pass them a custom state provider. In order for this class
+ * to be useful, it must be initialized with a provider when your application initializes.
+ <pre><code>
+// in your initialization function
+init : function(){
+   Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
+   ...
+   // supposed you have a {@link Roo.BorderLayout}
+   var layout = new Roo.BorderLayout(...);
+   layout.restoreState();
+   // or a {Roo.BasicDialog}
+   var dialog = new Roo.BasicDialog(...);
+   dialog.restoreState();
+ </code></pre>
+ * @singleton
+ */
+Roo.state.Manager = function(){
+    var provider = new Roo.state.Provider();
+    
+    return {
+        /**
+         * Configures the default state provider for your application
+         * @param {Provider} stateProvider The state provider to set
+         */
+        setProvider : function(stateProvider){
+            provider = stateProvider;
+        },
+        
+        /**
+         * Returns the current value for a key
+         * @param {String} name The key name
+         * @param {Mixed} defaultValue The default value to return if the key lookup does not match
+         * @return {Mixed} The state data
+         */
+        get : function(key, defaultValue){
+            return provider.get(key, defaultValue);
+        },
+        
+        /**
+         * Sets the value for a key
+         * @param {String} name The key name
+         * @param {Mixed} value The state data
+         */
+         set : function(key, value){
+            provider.set(key, value);
+        },
+        
+        /**
+         * Clears a value from the state
+         * @param {String} name The key name
+         */
+        clear : function(key){
+            provider.clear(key);
+        },
+        
+        /**
+         * Gets the currently configured state provider
+         * @return {Provider} The state provider
+         */
+        getProvider : function(){
+            return provider;
+        }
+    };
+}();
+/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+/**
+ * @class Roo.state.CookieProvider
+ * @extends Roo.state.Provider
+ * The default Provider implementation which saves state via cookies.
+ * <br />Usage:
+ <pre><code>
+   var cp = new Roo.state.CookieProvider({
+       path: "/cgi-bin/",
+       expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
+       domain: "roojs.com"
+   })
+   Roo.state.Manager.setProvider(cp);
+ </code></pre>
+ * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
+ * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
+ * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
+ * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
+ * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
+ * domain the page is running on including the 'www' like 'www.roojs.com')
+ * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
+ * @constructor
+ * Create a new CookieProvider
+ * @param {Object} config The configuration object
+ */
+Roo.state.CookieProvider = function(config){
+    Roo.state.CookieProvider.superclass.constructor.call(this);
+    this.path = "/";
+    this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
+    this.domain = null;
+    this.secure = false;
+    Roo.apply(this, config);
+    this.state = this.readCookies();
+};
+
+Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
+    // private
+    set : function(name, value){
+        if(typeof value == "undefined" || value === null){
+            this.clear(name);
+            return;
+        }
+        this.setCookie(name, value);
+        Roo.state.CookieProvider.superclass.set.call(this, name, value);
+    },
+
+    // private
+    clear : function(name){
+        this.clearCookie(name);
+        Roo.state.CookieProvider.superclass.clear.call(this, name);
+    },
+
+    // private
+    readCookies : function(){
+        var cookies = {};
+        var c = document.cookie + ";";
+        var re = /\s?(.*?)=(.*?);/g;
+       var matches;
+       while((matches = re.exec(c)) != null){
+            var name = matches[1];
+            var value = matches[2];
+            if(name && name.substring(0,3) == "ys-"){
+                cookies[name.substr(3)] = this.decodeValue(value);
+            }
+        }
+        return cookies;
+    },
+
+    // private
+    setCookie : function(name, value){
+        document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
+           ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
+           ((this.path == null) ? "" : ("; path=" + this.path)) +
+           ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
+           ((this.secure == true) ? "; secure" : "");
+    },
+
+    // private
+    clearCookie : function(name){
+        document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
+           ((this.path == null) ? "" : ("; path=" + this.path)) +
+           ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
+           ((this.secure == true) ? "; secure" : "");
+    }
+});
\ No newline at end of file