Fix #7284 - more issues with bullet point detection
[roojs1] / roojs-debug.js
index 1cda431..bbbe6a8 100644 (file)
@@ -19,7 +19,7 @@ window["undefined"] = window["undefined"];
 /**
  * @class Roo
  * Roo core utilities and functions.
- * @singleton
+ * @static
  */
 var Roo = {}; 
 /**
@@ -668,7 +668,30 @@ Roo.factory(conf, Roo.data);
         {
             var node = Roo.DomQuery.selectNode(selector,root);
             return node ? Roo.get(node) : new Roo.Element(false);
-        }
+        },
+               /**
+                * Find the current bootstrap width Grid size
+                * Note xs is the default for smaller.. - this is currently used by grids to render correct columns
+                * @returns {String} (xs|sm|md|lg|xl)
+                */
+               
+               getGridSize : function()
+               {
+                       var w = Roo.lib.Dom.getViewWidth();
+                       switch(true) {
+                               case w > 1200:
+                                       return 'xl';
+                               case w > 992:
+                                       return 'lg';
+                               case w > 768:
+                                       return 'md';
+                               case w > 576:
+                                       return 'sm';
+                               default:
+                                       return 'xs'
+                       }
+                       
+               } 
         
     });
 
@@ -677,9 +700,8 @@ Roo.factory(conf, Roo.data);
 
 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
-                "Roo.app", "Roo.ux",
-                "Roo.bootstrap",
-                "Roo.bootstrap.dash");
+                "Roo.app", "Roo.ux" 
+               );
 /*
  * Based on:
  * Ext JS Library 1.1.1
@@ -932,6 +954,16 @@ String.prototype.unicodeClean = function () {
     );
 };
   
+
+/**
+  * Make the first letter of a string uppercase
+  *
+  * @return {String} The new string.
+  */
+String.prototype.toUpperCaseFirst = function () {
+    return this.charAt(0).toUpperCase() + this.slice(1);
+};  
+  
 /*
  * Based on:
  * Ext JS Library 1.1.1
@@ -1015,12 +1047,58 @@ Roo.applyIf(Array.prototype, {
         }
 
         return res;
-    }
+    },
+    /**
+     * equals
+     * @param {Array} o The array to compare to
+     * @returns {Boolean} true if the same
+     */
+    equals : function(b)
+    {
+            // https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
+        if (this === b) {
+            return true;
+        }
+        if (b == null) {
+            return false;
+        }
+        if (this.length !== b.length) {
+            return false;
+        }
+          
+        // sort?? a.sort().equals(b.sort());
+          
+        for (var i = 0; i < this.length; ++i) {
+            if (this[i] !== b[i]) {
+            return false;
+            }
+        }
+        return true;
+    } 
+    
+    
+    
     
 });
 
-
+Roo.applyIf(Array, {
+ /**
+     * from
+     * @static
+     * @param {Array} o Or Array like object (eg. nodelist)
+     * @returns {Array} 
+     */
+    from : function(o)
+    {
+        var ret= [];
+    
+        for (var i =0; i < o.length; i++) { 
+            ret[i] = o[i];
+        }
+        return ret;
+      
+    }
+});
 /*
  * Based on:
  * Ext JS Library 1.1.1
@@ -1127,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..
 
 
@@ -1324,17 +1425,17 @@ Date.createParser = function(format) {
     }
 
     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
-        + "{v = new Date(y, m, d, h, i, s);}\n"
+        + "{v = new Date(y, m, d, h, i, s); v.setFullYear(y);}\n"
         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
-        + "{v = new Date(y, m, d, h, i);}\n"
+        + "{v = new Date(y, m, d, h, i); v.setFullYear(y);}\n"
         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
-        + "{v = new Date(y, m, d, h);}\n"
+        + "{v = new Date(y, m, d, h); v.setFullYear(y);}\n"
         + "else if (y >= 0 && m >= 0 && d > 0)\n"
-        + "{v = new Date(y, m, d);}\n"
+        + "{v = new Date(y, m, d); v.setFullYear(y);}\n"
         + "else if (y >= 0 && m >= 0)\n"
-        + "{v = new Date(y, m);}\n"
+        + "{v = new Date(y, m); v.setFullYear(y);}\n"
         + "else if (y >= 0)\n"
-        + "{v = new Date(y);}\n"
+        + "{v = new Date(y); v.setFullYear(y);}\n"
         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
@@ -1394,7 +1495,7 @@ Date.formatCodeToRegex = function(character, currentGroup) {
             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
     case "m":
         return {g:1,
-            c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
+            c:"m = Math.max(0,parseInt(results[" + currentGroup + "], 10) - 1);\n",
             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
     case "t":
         return {g:0,
@@ -1829,22 +1930,13 @@ Date.prototype.add = function(interval, value){
   }
   return d;
 };
-/*
- * 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.lib.Dom
+ * @licence LGPL
  * @static
  * 
  * Dom utils (from YIU afaik)
+ *
  * 
  **/
 Roo.lib.Dom = {
@@ -1865,17 +1957,26 @@ Roo.lib.Dom = {
     getViewHeight : function(full) {
         return full ? this.getDocumentHeight() : this.getViewportHeight();
     },
-
+    /**
+     * Get the Full Document height 
+     * @return {Number} The height
+     */
     getDocumentHeight: function() {
         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
         return Math.max(scrollHeight, this.getViewportHeight());
     },
-
+    /**
+     * Get the Full Document width
+     * @return {Number} The width
+     */
     getDocumentWidth: function() {
         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
         return Math.max(scrollWidth, this.getViewportWidth());
     },
-
+    /**
+     * Get the Window Viewport height
+     * @return {Number} The height
+     */
     getViewportHeight: function() {
         var height = self.innerHeight;
         var mode = document.compatMode;
@@ -1888,7 +1989,10 @@ Roo.lib.Dom = {
 
         return height;
     },
-
+    /**
+     * Get the Window Viewport width
+     * @return {Number} The width
+     */
     getViewportWidth: function() {
         var width = self.innerWidth;
         var mode = document.compatMode;
@@ -2552,18 +2656,20 @@ Roo.lib.Event = function() {
     E._tryPreloadAttach();
 })();
 
-/*
- * Portions of this file are based on pieces of Yahoo User Interface Library
- * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
- * YUI licensed under the BSD License:
- * http://developer.yahoo.net/yui/license.txt
- * <script type="text/javascript">
- *
- */
 
 (function() {
     /**
      * @class Roo.lib.Ajax
+     *
+     * provide a simple Ajax request utility functions
+     * 
+     * Portions of this file are based on pieces of Yahoo User Interface Library
+    * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
+    * YUI licensed under the BSD License:
+    * http://developer.yahoo.net/yui/license.txt
+    * <script type="text/javascript">
+    *
      *
      */
     Roo.lib.Ajax = {
@@ -2589,7 +2695,13 @@ Roo.lib.Event = function() {
 
             return this.asyncRequest(method, uri, cb, data);
         },
-
+        /**
+         * serialize a form
+         *
+         * @static
+         * @param {DomForm} form element
+         * @return {String} urlencode form output.
+         */
         serializeForm : function(form) {
             if(typeof form == 'string') {
                 form = (document.getElementById(form) || document.forms[form]);
@@ -3611,7 +3723,683 @@ Roo.lib.Bezier = new function() {
             return [ tmp[0][0], tmp[0][1] ];
 
         };
-    };/*
+    }; 
+
+/**
+ * @class Roo.lib.Color
+ * @constructor
+ * An abstract Color implementation. Concrete Color implementations should use
+ * an instance of this function as their prototype, and implement the getRGB and
+ * getHSL functions. getRGB should return an object representing the RGB
+ * components of this Color, with the red, green, and blue components in the
+ * range [0,255] and the alpha component in the range [0,100]. getHSL should
+ * return an object representing the HSL components of this Color, with the hue
+ * component in the range [0,360), the saturation and lightness components in
+ * the range [0,100], and the alpha component in the range [0,1].
+ *
+ *
+ * Color.js
+ *
+ * Functions for Color handling and processing.
+ *
+ * http://www.safalra.com/web-design/javascript/Color-handling-and-processing/
+ *
+ * The author of this program, Safalra (Stephen Morley), irrevocably releases all
+ * rights to this program, with the intention of it becoming part of the public
+ * domain. Because this program is released into the public domain, it comes with
+ * no warranty either expressed or implied, to the extent permitted by law.
+ * 
+ * For more free and public domain JavaScript code by the same author, visit:
+ * http://www.safalra.com/web-design/javascript/
+ * 
+ */
+Roo.lib.Color = function() { }
+
+
+Roo.apply(Roo.lib.Color.prototype, {
+  
+  rgb : null,
+  hsv : null,
+  hsl : null,
+  
+  /**
+   * getIntegerRGB
+   * @return {Object} an object representing the RGBA components of this Color. The red,
+   * green, and blue components are converted to integers in the range [0,255].
+   * The alpha is a value in the range [0,1].
+   */
+  getIntegerRGB : function(){
+
+    // get the RGB components of this Color
+    var rgb = this.getRGB();
+
+    // return the integer components
+    return {
+      'r' : Math.round(rgb.r),
+      'g' : Math.round(rgb.g),
+      'b' : Math.round(rgb.b),
+      'a' : rgb.a
+    };
+
+  },
+
+  /**
+   * getPercentageRGB
+   * @return {Object} an object representing the RGBA components of this Color. The red,
+   * green, and blue components are converted to numbers in the range [0,100].
+   * The alpha is a value in the range [0,1].
+   */
+  getPercentageRGB : function(){
+
+    // get the RGB components of this Color
+    var rgb = this.getRGB();
+
+    // return the percentage components
+    return {
+      'r' : 100 * rgb.r / 255,
+      'g' : 100 * rgb.g / 255,
+      'b' : 100 * rgb.b / 255,
+      'a' : rgb.a
+    };
+
+  },
+
+  /**
+   * getCSSHexadecimalRGB
+   * @return {String} a string representing this Color as a CSS hexadecimal RGB Color
+   * value - that is, a string of the form #RRGGBB where each of RR, GG, and BB
+   * are two-digit hexadecimal numbers.
+   */
+  getCSSHexadecimalRGB : function()
+  {
+
+    // get the integer RGB components
+    var rgb = this.getIntegerRGB();
+
+    // determine the hexadecimal equivalents
+    var r16 = rgb.r.toString(16);
+    var g16 = rgb.g.toString(16);
+    var b16 = rgb.b.toString(16);
+
+    // return the CSS RGB Color value
+    return '#'
+        + (r16.length == 2 ? r16 : '0' + r16)
+        + (g16.length == 2 ? g16 : '0' + g16)
+        + (b16.length == 2 ? b16 : '0' + b16);
+
+  },
+
+  /**
+   * getCSSIntegerRGB
+   * @return {String} a string representing this Color as a CSS integer RGB Color
+   * value - that is, a string of the form rgb(r,g,b) where each of r, g, and b
+   * are integers in the range [0,255].
+   */
+  getCSSIntegerRGB : function(){
+
+    // get the integer RGB components
+    var rgb = this.getIntegerRGB();
+
+    // return the CSS RGB Color value
+    return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')';
+
+  },
+
+  /**
+   * getCSSIntegerRGBA
+   * @return {String} Returns a string representing this Color as a CSS integer RGBA Color
+   * value - that is, a string of the form rgba(r,g,b,a) where each of r, g, and
+   * b are integers in the range [0,255] and a is in the range [0,1].
+   */
+  getCSSIntegerRGBA : function(){
+
+    // get the integer RGB components
+    var rgb = this.getIntegerRGB();
+
+    // return the CSS integer RGBA Color value
+    return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + rgb.a + ')';
+
+  },
+
+  /**
+   * getCSSPercentageRGB
+   * @return {String} a string representing this Color as a CSS percentage RGB Color
+   * value - that is, a string of the form rgb(r%,g%,b%) where each of r, g, and
+   * b are in the range [0,100].
+   */
+  getCSSPercentageRGB : function(){
+
+    // get the percentage RGB components
+    var rgb = this.getPercentageRGB();
+
+    // return the CSS RGB Color value
+    return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%)';
+
+  },
+
+  /**
+   * getCSSPercentageRGBA
+   * @return {String} a string representing this Color as a CSS percentage RGBA Color
+   * value - that is, a string of the form rgba(r%,g%,b%,a) where each of r, g,
+   * and b are in the range [0,100] and a is in the range [0,1].
+   */
+  getCSSPercentageRGBA : function(){
+
+    // get the percentage RGB components
+    var rgb = this.getPercentageRGB();
+
+    // return the CSS percentage RGBA Color value
+    return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%,' + rgb.a + ')';
+
+  },
+
+  /**
+   * getCSSHSL
+   * @return {String} a string representing this Color as a CSS HSL Color value - that
+   * is, a string of the form hsl(h,s%,l%) where h is in the range [0,100] and
+   * s and l are in the range [0,100].
+   */
+  getCSSHSL : function(){
+
+    // get the HSL components
+    var hsl = this.getHSL();
+
+    // return the CSS HSL Color value
+    return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%)';
+
+  },
+
+  /**
+   * getCSSHSLA
+   * @return {String} a string representing this Color as a CSS HSLA Color value - that
+   * is, a string of the form hsla(h,s%,l%,a) where h is in the range [0,100],
+   * s and l are in the range [0,100], and a is in the range [0,1].
+   */
+  getCSSHSLA : function(){
+
+    // get the HSL components
+    var hsl = this.getHSL();
+
+    // return the CSS HSL Color value
+    return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%,' + hsl.a + ')';
+
+  },
+
+  /**
+   * Sets the Color of the specified node to this Color. This functions sets
+   * the CSS 'color' property for the node. The parameter is:
+   * 
+   * @param {DomElement} node - the node whose Color should be set
+   */
+  setNodeColor : function(node){
+
+    // set the Color of the node
+    node.style.color = this.getCSSHexadecimalRGB();
+
+  },
+
+  /**
+   * Sets the background Color of the specified node to this Color. This
+   * functions sets the CSS 'background-color' property for the node. The
+   * parameter is:
+   *
+   * @param {DomElement} node - the node whose background Color should be set
+   */
+  setNodeBackgroundColor : function(node){
+
+    // set the background Color of the node
+    node.style.backgroundColor = this.getCSSHexadecimalRGB();
+
+  },
+  // convert between formats..
+  toRGB: function()
+  {
+    var r = this.getIntegerRGB();
+    return new Roo.lib.RGBColor(r.r,r.g,r.b,r.a);
+    
+  },
+  toHSL : function()
+  {
+     var hsl = this.getHSL();
+  // return the CSS HSL Color value
+    return new Roo.lib.HSLColor(hsl.h,  hsl.s, hsl.l ,  hsl.a );
+    
+  },
+  
+  toHSV : function()
+  {
+    var rgb = this.toRGB();
+    var hsv = rgb.getHSV();
+   // return the CSS HSL Color value
+    return new Roo.lib.HSVColor(hsv.h,  hsv.s, hsv.v ,  hsv.a );
+    
+  },
+  
+  // modify  v = 0 ... 1 (eg. 0.5)
+  saturate : function(v)
+  {
+      var rgb = this.toRGB();
+      var hsv = rgb.getHSV();
+      return new Roo.lib.HSVColor(hsv.h,  hsv.s * v, hsv.v ,  hsv.a );
+      
+  
+  },
+  
+   
+  /**
+   * getRGB
+   * @return {Object} the RGB and alpha components of this Color as an object with r,
+   * g, b, and a properties. r, g, and b are in the range [0,255] and a is in
+   * the range [0,1].
+   */
+  getRGB: function(){
+   
+    // return the RGB components
+    return {
+      'r' : this.rgb.r,
+      'g' : this.rgb.g,
+      'b' : this.rgb.b,
+      'a' : this.alpha
+    };
+
+  },
+
+  /**
+   * getHSV
+   * @return {Object} the HSV and alpha components of this Color as an object with h,
+   * s, v, and a properties. h is in the range [0,360), s and v are in the range
+   * [0,100], and a is in the range [0,1].
+   */
+  getHSV : function()
+  {
+    
+    // calculate the HSV components if necessary
+    if (this.hsv == null) {
+      this.calculateHSV();
+    }
+
+    // return the HSV components
+    return {
+      'h' : this.hsv.h,
+      's' : this.hsv.s,
+      'v' : this.hsv.v,
+      'a' : this.alpha
+    };
+
+  },
+
+  /**
+   * getHSL
+   * @return {Object} the HSL and alpha components of this Color as an object with h,
+   * s, l, and a properties. h is in the range [0,360), s and l are in the range
+   * [0,100], and a is in the range [0,1].
+   */
+  getHSL : function(){
+    
+     
+    // calculate the HSV components if necessary
+    if (this.hsl == null) { this.calculateHSL(); }
+
+    // return the HSL components
+    return {
+      'h' : this.hsl.h,
+      's' : this.hsl.s,
+      'l' : this.hsl.l,
+      'a' : this.alpha
+    };
+
+  }
+  
+
+});
+
+
+/**
+ * @class Roo.lib.RGBColor
+ * @extends Roo.lib.Color
+ * Creates a Color specified in the RGB Color space, with an optional alpha
+ * component. The parameters are:
+ * @constructor
+ * 
+
+ * @param {Number} r - the red component, clipped to the range [0,255]
+ * @param {Number} g - the green component, clipped to the range [0,255]
+ * @param {Number} b - the blue component, clipped to the range [0,255]
+ * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
+ *     optional and defaults to 1
+ */
+Roo.lib.RGBColor = function (r, g, b, a){
+
+  // store the alpha component after clipping it if necessary
+  this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
+
+  // store the RGB components after clipping them if necessary
+  this.rgb =
+      {
+        'r' : Math.max(0, Math.min(255, r)),
+        'g' : Math.max(0, Math.min(255, g)),
+        'b' : Math.max(0, Math.min(255, b))
+      };
+
+  // initialise the HSV and HSL components to null
+  
+
+  /* 
+   * //private returns the HSV or HSL hue component of this RGBColor. The hue is in the
+   * range [0,360). The parameters are:
+   *
+   * maximum - the maximum of the RGB component values
+   * range   - the range of the RGB component values
+   */
+   
+
+}
+// this does an 'exteds'
+Roo.extend(Roo.lib.RGBColor, Roo.lib.Color, {
+
+  
+    getHue  : function(maximum, range)
+    {
+      var rgb = this.rgb;
+       
+      // check whether the range is zero
+      if (range == 0){
+  
+        // set the hue to zero (any hue is acceptable as the Color is grey)
+        var hue = 0;
+  
+      }else{
+  
+        // determine which of the components has the highest value and set the hue
+        switch (maximum){
+  
+          // red has the highest value
+          case rgb.r:
+            var hue = (rgb.g - rgb.b) / range * 60;
+            if (hue < 0) { hue += 360; }
+            break;
+  
+          // green has the highest value
+          case rgb.g:
+            var hue = (rgb.b - rgb.r) / range * 60 + 120;
+            break;
+  
+          // blue has the highest value
+          case rgb.b:
+            var hue = (rgb.r - rgb.g) / range * 60 + 240;
+            break;
+  
+        }
+  
+      }
+  
+      // return the hue
+      return hue;
+  
+    },
+
+  /* //private Calculates and stores the HSV components of this RGBColor so that they can
+   * be returned be the getHSV function.
+   */
+   calculateHSV : function(){
+    var rgb = this.rgb;
+    // get the maximum and range of the RGB component values
+    var maximum = Math.max(rgb.r, rgb.g, rgb.b);
+    var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
+
+    // store the HSV components
+    this.hsv =
+        {
+          'h' : this.getHue(maximum, range),
+          's' : (maximum == 0 ? 0 : 100 * range / maximum),
+          'v' : maximum / 2.55
+        };
+
+  },
+
+  /* //private Calculates and stores the HSL components of this RGBColor so that they can
+   * be returned be the getHSL function.
+   */
+   calculateHSL : function(){
+    var rgb = this.rgb;
+    // get the maximum and range of the RGB component values
+    var maximum = Math.max(rgb.r, rgb.g, rgb.b);
+    var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
+
+    // determine the lightness in the range [0,1]
+    var l = maximum / 255 - range / 510;
+
+    // store the HSL components
+    this.hsl =
+        {
+          'h' : this.getHue(maximum, range),
+          's' : (range == 0 ? 0 : range / 2.55 / (l < 0.5 ? l * 2 : 2 - l * 2)),
+          'l' : 100 * l
+        };
+
+  }
+
+});
+
+/**
+ * @class Roo.lib.HSVColor
+ * @extends Roo.lib.Color
+ * Creates a Color specified in the HSV Color space, with an optional alpha
+ * component. The parameters are:
+ * @constructor
+ *
+ * @param {Number} h - the hue component, wrapped to the range [0,360)
+ * @param {Number} s - the saturation component, clipped to the range [0,100]
+ * @param {Number} v - the value component, clipped to the range [0,100]
+ * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
+ *     optional and defaults to 1
+ */
+Roo.lib.HSVColor = function (h, s, v, a){
+
+  // store the alpha component after clipping it if necessary
+  this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
+
+  // store the HSV components after clipping or wrapping them if necessary
+  this.hsv =
+      {
+        'h' : (h % 360 + 360) % 360,
+        's' : Math.max(0, Math.min(100, s)),
+        'v' : Math.max(0, Math.min(100, v))
+      };
+
+  // initialise the RGB and HSL components to null
+  this.rgb = null;
+  this.hsl = null;
+}
+
+Roo.extend(Roo.lib.HSVColor, Roo.lib.Color, {
+  /* Calculates and stores the RGB components of this HSVColor so that they can
+   * be returned be the getRGB function.
+   */
+  calculateRGB: function ()
+  {
+    var hsv = this.hsv;
+    // check whether the saturation is zero
+    if (hsv.s == 0){
+
+      // set the Color to the appropriate shade of grey
+      var r = hsv.v;
+      var g = hsv.v;
+      var b = hsv.v;
+
+    }else{
+
+      // set some temporary values
+      var f  = hsv.h / 60 - Math.floor(hsv.h / 60);
+      var p  = hsv.v * (1 - hsv.s / 100);
+      var q  = hsv.v * (1 - hsv.s / 100 * f);
+      var t  = hsv.v * (1 - hsv.s / 100 * (1 - f));
+
+      // set the RGB Color components to their temporary values
+      switch (Math.floor(hsv.h / 60)){
+        case 0: var r = hsv.v; var g = t; var b = p; break;
+        case 1: var r = q; var g = hsv.v; var b = p; break;
+        case 2: var r = p; var g = hsv.v; var b = t; break;
+        case 3: var r = p; var g = q; var b = hsv.v; break;
+        case 4: var r = t; var g = p; var b = hsv.v; break;
+        case 5: var r = hsv.v; var g = p; var b = q; break;
+      }
+
+    }
+
+    // store the RGB components
+    this.rgb =
+        {
+          'r' : r * 2.55,
+          'g' : g * 2.55,
+          'b' : b * 2.55
+        };
+
+  },
+
+  /* Calculates and stores the HSL components of this HSVColor so that they can
+   * be returned be the getHSL function.
+   */
+  calculateHSL : function (){
+
+    var hsv = this.hsv;
+    // determine the lightness in the range [0,100]
+    var l = (2 - hsv.s / 100) * hsv.v / 2;
+
+    // store the HSL components
+    this.hsl =
+        {
+          'h' : hsv.h,
+          's' : hsv.s * hsv.v / (l < 50 ? l * 2 : 200 - l * 2),
+          'l' : l
+        };
+
+    // correct a division-by-zero error
+    if (isNaN(hsl.s)) { hsl.s = 0; }
+
+  } 
+
+});
+
+/**
+ * @class Roo.lib.HSLColor
+ * @extends Roo.lib.Color
+ *
+ * @constructor
+ * Creates a Color specified in the HSL Color space, with an optional alpha
+ * component. The parameters are:
+ *
+ * @param {Number} h - the hue component, wrapped to the range [0,360)
+ * @param {Number} s - the saturation component, clipped to the range [0,100]
+ * @param {Number} l - the lightness component, clipped to the range [0,100]
+ * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
+ *     optional and defaults to 1
+ */
+
+Roo.lib.HSLColor = function(h, s, l, a){
+
+  // store the alpha component after clipping it if necessary
+  this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
+
+  // store the HSL components after clipping or wrapping them if necessary
+  this.hsl =
+      {
+        'h' : (h % 360 + 360) % 360,
+        's' : Math.max(0, Math.min(100, s)),
+        'l' : Math.max(0, Math.min(100, l))
+      };
+
+  // initialise the RGB and HSV components to null
+}
+
+Roo.extend(Roo.lib.HSLColor, Roo.lib.Color, {
+
+  /* Calculates and stores the RGB components of this HSLColor so that they can
+   * be returned be the getRGB function.
+   */
+  calculateRGB: function (){
+
+    // check whether the saturation is zero
+    if (this.hsl.s == 0){
+
+      // store the RGB components representing the appropriate shade of grey
+      this.rgb =
+          {
+            'r' : this.hsl.l * 2.55,
+            'g' : this.hsl.l * 2.55,
+            'b' : this.hsl.l * 2.55
+          };
+
+    }else{
+
+      // set some temporary values
+      var p = this.hsl.l < 50
+            ? this.hsl.l * (1 + hsl.s / 100)
+            : this.hsl.l + hsl.s - hsl.l * hsl.s / 100;
+      var q = 2 * hsl.l - p;
+
+      // initialise the RGB components
+      this.rgb =
+          {
+            'r' : (h + 120) / 60 % 6,
+            'g' : h / 60,
+            'b' : (h + 240) / 60 % 6
+          };
+
+      // loop over the RGB components
+      for (var key in this.rgb){
+
+        // ensure that the property is not inherited from the root object
+        if (this.rgb.hasOwnProperty(key)){
+
+          // set the component to its value in the range [0,100]
+          if (this.rgb[key] < 1){
+            this.rgb[key] = q + (p - q) * this.rgb[key];
+          }else if (this.rgb[key] < 3){
+            this.rgb[key] = p;
+          }else if (this.rgb[key] < 4){
+            this.rgb[key] = q + (p - q) * (4 - this.rgb[key]);
+          }else{
+            this.rgb[key] = q;
+          }
+
+          // set the component to its value in the range [0,255]
+          this.rgb[key] *= 2.55;
+
+        }
+
+      }
+
+    }
+
+  },
+
+  /* Calculates and stores the HSV components of this HSLColor so that they can
+   * be returned be the getHSL function.
+   */
+   calculateHSV : function(){
+
+    // set a temporary value
+    var t = this.hsl.s * (this.hsl.l < 50 ? this.hsl.l : 100 - this.hsl.l) / 100;
+
+    // store the HSV components
+    this.hsv =
+        {
+          'h' : this.hsl.h,
+          's' : 200 * t / (this.hsl.l + t),
+          'v' : t + this.hsl.l
+        };
+
+    // correct a division-by-zero error
+    if (isNaN(this.hsv.s)) { this.hsv.s = 0; }
+
+  }
+
+});
+/*
  * Portions of this file are based on pieces of Yahoo User Interface Library
  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
  * YUI licensed under the BSD License:
@@ -4142,7 +4930,468 @@ Roo.lib.Easing = {
             }
         };
     })();
-/*
+/**
+ * Originally based of this code... - refactored for Roo...
+ * https://github.com/aaalsaleh/undo-manager
+ * undo-manager.js
+ * @author  Abdulrahman Alsaleh 
+ * @copyright 2015 Abdulrahman Alsaleh 
+ * @license  MIT License (c) 
+ *
+ * Hackily modifyed by alan@roojs.com
+ *
+ *
+ *  
+ *
+ *  TOTALLY UNTESTED...
+ *
+ *  Documentation to be done....
+ */
+
+/**
+* @class Roo.lib.UndoManager
+* An undo manager implementation in JavaScript. It follows the W3C UndoManager and DOM Transaction
+* Draft and the undocumented and disabled Mozilla Firefox's UndoManager implementation.
+
+ * Usage:
+ * <pre><code>
+
+
+editor.undoManager = new Roo.lib.UndoManager(1000, editor);
+</code></pre>
+
+* 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 {Number} limit how far back to go ... use 1000?
+* @param {Object} scope usually use document..
+*/
+
+Roo.lib.UndoManager = function (limit, undoScopeHost)
+{
+    this.stack = [];
+    this.limit = limit;
+    this.scope = undoScopeHost;
+    this.fireEvent = typeof CustomEvent != 'undefined' && undoScopeHost && undoScopeHost.dispatchEvent;
+    if (this.fireEvent) {
+        this.bindEvents();
+    }
+    this.reset();
+    
+};
+        
+Roo.lib.UndoManager.prototype = {
+    
+    limit : false,
+    stack : false,
+    scope :  false,
+    fireEvent : false,
+    position : 0,
+    length : 0,
+    
+    
+     /**
+     * To push and execute a transaction, the method undoManager.transact
+     * must be called by passing a transaction object as the first argument, and a merge
+     * flag as the second argument. A transaction object has the following properties:
+     *
+     * Usage:
+<pre><code>
+undoManager.transact({
+    label: 'Typing',
+    execute: function() { ... },
+    undo: function() { ... },
+    // redo same as execute
+    redo: function() { this.execute(); }
+}, false);
+
+// merge transaction
+undoManager.transact({
+    label: 'Typing',
+    execute: function() { ... },  // this will be run...
+    undo: function() { ... }, // what to do when undo is run.
+    // redo same as execute
+    redo: function() { this.execute(); }
+}, true); 
+</code></pre> 
+     *
+     * 
+     * @param {Object} transaction The transaction to add to the stack.
+     * @return {String} The HTML fragment
+     */
+    
+    
+    transact : function (transaction, merge)
+    {
+        if (arguments.length < 2) {
+            throw new TypeError('Not enough arguments to UndoManager.transact.');
+        }
+
+        transaction.execute();
+
+        this.stack.splice(0, this.position);
+        if (merge && this.length) {
+            this.stack[0].push(transaction);
+        } else {
+            this.stack.unshift([transaction]);
+        }
+    
+        this.position = 0;
+
+        if (this.limit && this.stack.length > this.limit) {
+            this.length = this.stack.length = this.limit;
+        } else {
+            this.length = this.stack.length;
+        }
+
+        if (this.fireEvent) {
+            this.scope.dispatchEvent(
+                new CustomEvent('DOMTransaction', {
+                    detail: {
+                        transactions: this.stack[0].slice()
+                    },
+                    bubbles: true,
+                    cancelable: false
+                })
+            );
+        }
+        
+        //Roo.log("transaction: pos:" + this.position + " len: " + this.length + " slen:" + this.stack.length);
+      
+        
+    },
+
+    undo : function ()
+    {
+        //Roo.log("undo: pos:" + this.position + " len: " + this.length + " slen:" + this.stack.length);
+        
+        if (this.position < this.length) {
+            for (var i = this.stack[this.position].length - 1; i >= 0; i--) {
+                this.stack[this.position][i].undo();
+            }
+            this.position++;
+
+            if (this.fireEvent) {
+                this.scope.dispatchEvent(
+                    new CustomEvent('undo', {
+                        detail: {
+                            transactions: this.stack[this.position - 1].slice()
+                        },
+                        bubbles: true,
+                        cancelable: false
+                    })
+                );
+            }
+        }
+    },
+
+    redo : function ()
+    {
+        if (this.position > 0) {
+            for (var i = 0, n = this.stack[this.position - 1].length; i < n; i++) {
+                this.stack[this.position - 1][i].redo();
+            }
+            this.position--;
+
+            if (this.fireEvent) {
+                this.scope.dispatchEvent(
+                    new CustomEvent('redo', {
+                        detail: {
+                            transactions: this.stack[this.position].slice()
+                        },
+                        bubbles: true,
+                        cancelable: false
+                    })
+                );
+            }
+        }
+    },
+
+    item : function (index)
+    {
+        if (index >= 0 && index < this.length) {
+            return this.stack[index].slice();
+        }
+        return null;
+    },
+
+    clearUndo : function () {
+        this.stack.length = this.length = this.position;
+    },
+
+    clearRedo : function () {
+        this.stack.splice(0, this.position);
+        this.position = 0;
+        this.length = this.stack.length;
+    },
+    /**
+     * Reset the undo - probaly done on load to clear all history.
+     */
+    reset : function()
+    {
+        this.stack = [];
+        this.position = 0;
+        this.length = 0;
+        this.current_html = this.scope.innerHTML;
+        if (this.timer !== false) {
+            clearTimeout(this.timer);
+        }
+        this.timer = false;
+        this.merge = false;
+        this.addEvent();
+        
+    },
+    current_html : '',
+    timer : false,
+    merge : false,
+    
+    
+    // this will handle the undo/redo on the element.?
+    bindEvents : function()
+    {
+        var el  = this.scope;
+        el.undoManager = this;
+        
+        
+        this.scope.addEventListener('keydown', function(e) {
+            if ((e.ctrlKey || e.metaKey) && e.keyCode === 90) {
+                if (e.shiftKey) {
+                    el.undoManager.redo(); // Ctrl/Command + Shift + Z
+                } else {
+                    el.undoManager.undo(); // Ctrl/Command + Z
+                }
+        
+                e.preventDefault();
+            }
+        });
+        /// ignore keyup..
+        this.scope.addEventListener('keyup', function(e) {
+            if ((e.ctrlKey || e.metaKey) && e.keyCode === 90) {
+                e.preventDefault();
+            }
+        });
+        
+        
+        
+        var t = this;
+        
+        el.addEventListener('input', function(e) {
+            if(el.innerHTML == t.current_html) {
+                return;
+            }
+            // only record events every second.
+            if (t.timer !== false) {
+               clearTimeout(t.timer);
+               t.timer = false;
+            }
+            t.timer = setTimeout(function() { t.merge = false; }, 1000);
+            
+            t.addEvent(t.merge);
+            t.merge = true; // ignore changes happening every second..
+        });
+       },
+    /**
+     * Manually add an event.
+     * Normall called without arguements - and it will just get added to the stack.
+     * 
+     */
+    
+    addEvent : function(merge)
+    {
+        //Roo.log("undomanager +" + (merge ? 'Y':'n'));
+        // not sure if this should clear the timer 
+        merge = typeof(merge) == 'undefined' ? false : merge; 
+        
+        this.scope.undoManager.transact({
+            scope : this.scope,
+            oldHTML: this.current_html,
+            newHTML: this.scope.innerHTML,
+            // nothing to execute (content already changed when input is fired)
+            execute: function() { },
+            undo: function() {
+                this.scope.innerHTML = this.current_html = this.oldHTML;
+            },
+            redo: function() {
+                this.scope.innerHTML = this.current_html = this.newHTML;
+            }
+        }, false); //merge);
+        
+        this.merge = merge;
+        
+        this.current_html = this.scope.innerHTML;
+    }
+    
+    
+     
+    
+    
+    
+};
+/**
+ * @class Roo.lib.Range
+ * @constructor
+ * This is a toolkit, normally used to copy features into a Dom Range element
+ * Roo.lib.Range.wrap(x);
+ *
+ *
+ *
+ */
+Roo.lib.Range = function() { };
+
+/**
+ * Wrap a Dom Range object, to give it new features...
+ * @static
+ * @param {Range} the range to wrap
+ */
+Roo.lib.Range.wrap = function(r) {
+    return Roo.apply(r, Roo.lib.Range.prototype);
+};
+/**
+ * find a parent node eg. LI / OL
+ * @param {string|Array} node name or array of nodenames
+ * @return {DomElement|false}
+ */
+Roo.apply(Roo.lib.Range.prototype,
+{
+    
+    closest : function(str)
+    {
+        if (typeof(str) != 'string') {
+            // assume it's a array.
+            for(var i = 0;i < str.length;i++) {
+                var r = this.closest(str[i]);
+                if (r !== false) {
+                    return r;
+                }
+                
+            }
+            return false;
+        }
+        str = str.toLowerCase();
+        var n = this.commonAncestorContainer; // might not be a node
+        while (n.nodeType != 1) {
+            n = n.parentNode;
+        }
+        
+        if (n.nodeName.toLowerCase() == str ) {
+            return n;
+        }
+        if (n.nodeName.toLowerCase() == 'body') {
+            return false;
+        }
+            
+        return n.closest(str) || false;
+        
+    },
+    cloneRange : function()
+    {
+        return Roo.lib.Range.wrap(Range.prototype.cloneRange.call(this));
+    }
+});/**
+ * @class Roo.lib.Selection
+ * @constructor
+ * This is a toolkit, normally used to copy features into a Dom Selection element
+ * Roo.lib.Selection.wrap(x);
+ *
+ *
+ *
+ */
+Roo.lib.Selection = function() { };
+
+/**
+ * Wrap a Dom Range object, to give it new features...
+ * @static
+ * @param {Range} the range to wrap
+ */
+Roo.lib.Selection.wrap = function(r, doc) {
+    Roo.apply(r, Roo.lib.Selection.prototype);
+    r.ownerDocument = doc; // usefull so we dont have to keep referening to it.
+    return r;
+};
+/**
+ * find a parent node eg. LI / OL
+ * @param {string|Array} node name or array of nodenames
+ * @return {DomElement|false}
+ */
+Roo.apply(Roo.lib.Selection.prototype,
+{
+    /**
+     * the owner document
+     */
+    ownerDocument : false,
+    
+    getRangeAt : function(n)
+    {
+        return Roo.lib.Range.wrap(Selection.prototype.getRangeAt.call(this,n));
+    },
+    
+    /**
+     * insert node at selection 
+     * @param {DomElement|string} node
+     * @param {string} cursor (after|in|none) where to place the cursor after inserting.
+     */
+    insertNode: function(node, cursor)
+    {
+        if (typeof(node) == 'string') {
+            node = this.ownerDocument.createElement(node);
+            if (cursor == 'in') {
+                node.innerHTML = '&nbsp;';
+            }
+        }
+        
+        var range = this.getRangeAt(0);
+        
+        if (this.type != 'Caret') {
+            range.deleteContents();
+        }
+        var sn = node.childNodes[0]; // select the contents.
+
+        
+        
+        range.insertNode(node);
+        if (cursor == 'after') {
+            node.insertAdjacentHTML('afterend', '&nbsp;');
+            sn = node.nextSibling;
+        }
+        
+        if (cursor == 'none') {
+            return;
+        }
+        
+        this.cursorText(sn);
+    },
+    
+    cursorText : function(n)
+    {
+       
+        //var range = this.getRangeAt(0);
+        range = Roo.lib.Range.wrap(new Range());
+        //range.selectNode(n);
+        
+        var ix = Array.from(n.parentNode.childNodes).indexOf(n);
+        range.setStart(n.parentNode,ix);
+        range.setEnd(n.parentNode,ix+1);
+        //range.collapse(false);
+         
+        this.removeAllRanges();
+        this.addRange(range);
+        
+        Roo.log([n, range, this,this.baseOffset,this.extentOffset, this.type]);
+    },
+    cursorAfter : function(n)
+    {
+        if (!n.nextSibling || n.nextSibling.nodeValue != '&nbsp;') {
+            n.insertAdjacentHTML('afterend', '&nbsp;');
+        }
+        this.cursorText (n.nextSibling);
+    }
+        
+    
+});/*
  * Based on:
  * Ext JS Library 1.1.1
  * Copyright(c) 2006-2007, Ext JS, LLC.
@@ -4173,7 +5422,7 @@ Roo.lib.Easing = {
  * @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://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
+ * @static
  */
 Roo.DomHelper = function(){
     var tempTableEl = null;
@@ -4310,7 +5559,7 @@ Roo.DomHelper = function(){
     var ieTable = function(depth, s, h, e){
         tempTableEl.innerHTML = [s, h, e].join('');
         var i = -1, el = tempTableEl;
-        while(++i < depth){
+        while(++i < depth && el.firstChild){
             el = el.firstChild;
         }
         return el;
@@ -4389,212 +5638,320 @@ Roo.DomHelper = function(){
         el.insertBefore(node, before);
         return node;
     };
+    
+    // this is a bit like the react update code...
+    // 
+    
+    var updateNode = function(from, to)
+    {
+        // should we handle non-standard elements?
+        Roo.log(["UpdateNode" , from, to]);
+        if (from.nodeType != to.nodeType) {
+            Roo.log(["ReplaceChild - mismatch notType" , to, from ]);
+            from.parentNode.replaceChild(to, from);
+        }
+        
+        if (from.nodeType == 3) {
+            // assume it's text?!
+            if (from.data == to.data) {
+                return;
+            }
+            from.data = to.data;
+            return;
+        }
+        if (!from.parentNode) {
+            // not sure why this is happening?
+            return;
+        }
+        // assume 'to' doesnt have '1/3 nodetypes!
+        // not sure why, by from, parent node might not exist?
+        if (from.nodeType !=1 || from.tagName != to.tagName) {
+            Roo.log(["ReplaceChild" , from, to ]);
+            
+            from.parentNode.replaceChild(to, from);
+            return;
+        }
+        // compare attributes
+        var ar = Array.from(from.attributes);
+        for(var i = 0; i< ar.length;i++) {
+            if (to.hasAttribute(ar[i].name)) {
+                continue;
+            }
+            if (ar[i].name == 'id') { // always keep ids?
+               continue;
+            }
+            //if (ar[i].name == 'style') {
+            //   throw "style removed?";
+            //}
+            Roo.log("removeAttribute" + ar[i].name);
+            from.removeAttribute(ar[i].name);
+        }
+        ar = to.attributes;
+        for(var i = 0; i< ar.length;i++) {
+            if (from.getAttribute(ar[i].name) == to.getAttribute(ar[i].name)) {
+                Roo.log("skipAttribute " + ar[i].name  + '=' + to.getAttribute(ar[i].name));
+                continue;
+            }
+            Roo.log("updateAttribute " + ar[i].name + '=>' + to.getAttribute(ar[i].name));
+            from.setAttribute(ar[i].name, to.getAttribute(ar[i].name));
+        }
+        // children
+        var far = Array.from(from.childNodes);
+        var tar = Array.from(to.childNodes);
+        // if the lengths are different.. then it's probably a editable content change, rather than
+        // a change of the block definition..
+        
+        // this did notwork , as our rebuilt nodes did not include ID's so did not match at all.
+         /*if (from.innerHTML == to.innerHTML) {
+            return;
+        }
+        if (far.length != tar.length) {
+            from.innerHTML = to.innerHTML;
+            return;
+        }
+        */
+        
+        for(var i = 0; i < Math.max(tar.length, far.length); i++) {
+            if (i >= far.length) {
+                from.appendChild(tar[i]);
+                Roo.log(["add", tar[i]]);
+                
+            } else if ( i  >= tar.length) {
+                from.removeChild(far[i]);
+                Roo.log(["remove", far[i]]);
+            } else {
+                
+                updateNode(far[i], tar[i]);
+            }    
+        }
+        
+        
+        
+        
+    };
+    
+    
 
     return {
-    /** True to force the use of DOM instead of html fragments @type Boolean */
-    useDom : false,
-
-    /**
-     * Returns the markup for the passed Element(s) config
-     * @param {Object} o The Dom object spec (and children)
-     * @return {String}
-     */
-    markup : function(o){
-        return createHtml(o);
-    },
-
-    /**
-     * Applies a style specification to an element
-     * @param {String/HTMLElement} el The element to apply styles to
-     * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
-     * a function which returns such a specification.
-     */
-    applyStyles : function(el, styles){
-        if(styles){
-           el = Roo.fly(el);
-           if(typeof styles == "string"){
-               var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
-               var matches;
-               while ((matches = re.exec(styles)) != null){
-                   el.setStyle(matches[1], matches[2]);
-               }
-           }else if (typeof styles == "object"){
-               for (var style in styles){
-                  el.setStyle(style, styles[style]);
+        /** True to force the use of DOM instead of html fragments @type Boolean */
+        useDom : false,
+    
+        /**
+         * Returns the markup for the passed Element(s) config
+         * @param {Object} o The Dom object spec (and children)
+         * @return {String}
+         */
+        markup : function(o){
+            return createHtml(o);
+        },
+    
+        /**
+         * Applies a style specification to an element
+         * @param {String/HTMLElement} el The element to apply styles to
+         * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
+         * a function which returns such a specification.
+         */
+        applyStyles : function(el, styles){
+            if(styles){
+               el = Roo.fly(el);
+               if(typeof styles == "string"){
+                   var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
+                   var matches;
+                   while ((matches = re.exec(styles)) != null){
+                       el.setStyle(matches[1], matches[2]);
+                   }
+               }else if (typeof styles == "object"){
+                   for (var style in styles){
+                      el.setStyle(style, styles[style]);
+                   }
+               }else if (typeof styles == "function"){
+                    Roo.DomHelper.applyStyles(el, styles.call());
                }
-           }else if (typeof styles == "function"){
-                Roo.DomHelper.applyStyles(el, styles.call());
-           }
-        }
-    },
-
-    /**
-     * Inserts an HTML fragment into the Dom
-     * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
-     * @param {HTMLElement} el The context element
-     * @param {String} html The HTML fragmenet
-     * @return {HTMLElement} The new node
-     */
-    insertHtml : function(where, el, html){
-        where = where.toLowerCase();
-        if(el.insertAdjacentHTML){
-            if(tableRe.test(el.tagName)){
-                var rs;
-                if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
-                    return rs;
+            }
+        },
+    
+        /**
+         * Inserts an HTML fragment into the Dom
+         * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
+         * @param {HTMLElement} el The context element
+         * @param {String} html The HTML fragmenet
+         * @return {HTMLElement} The new node
+         */
+        insertHtml : function(where, el, html){
+            where = where.toLowerCase();
+            if(el.insertAdjacentHTML){
+                if(tableRe.test(el.tagName)){
+                    var rs;
+                    if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
+                        return rs;
+                    }
+                }
+                switch(where){
+                    case "beforebegin":
+                        el.insertAdjacentHTML('BeforeBegin', html);
+                        return el.previousSibling;
+                    case "afterbegin":
+                        el.insertAdjacentHTML('AfterBegin', html);
+                        return el.firstChild;
+                    case "beforeend":
+                        el.insertAdjacentHTML('BeforeEnd', html);
+                        return el.lastChild;
+                    case "afterend":
+                        el.insertAdjacentHTML('AfterEnd', html);
+                        return el.nextSibling;
                 }
+                throw 'Illegal insertion point -> "' + where + '"';
             }
+            var range = el.ownerDocument.createRange();
+            var frag;
             switch(where){
-                case "beforebegin":
-                    el.insertAdjacentHTML('BeforeBegin', html);
+                 case "beforebegin":
+                    range.setStartBefore(el);
+                    frag = range.createContextualFragment(html);
+                    el.parentNode.insertBefore(frag, el);
                     return el.previousSibling;
-                case "afterbegin":
-                    el.insertAdjacentHTML('AfterBegin', html);
-                    return el.firstChild;
+                 case "afterbegin":
+                    if(el.firstChild){
+                        range.setStartBefore(el.firstChild);
+                        frag = range.createContextualFragment(html);
+                        el.insertBefore(frag, el.firstChild);
+                        return el.firstChild;
+                    }else{
+                        el.innerHTML = html;
+                        return el.firstChild;
+                    }
                 case "beforeend":
-                    el.insertAdjacentHTML('BeforeEnd', html);
-                    return el.lastChild;
+                    if(el.lastChild){
+                        range.setStartAfter(el.lastChild);
+                        frag = range.createContextualFragment(html);
+                        el.appendChild(frag);
+                        return el.lastChild;
+                    }else{
+                        el.innerHTML = html;
+                        return el.lastChild;
+                    }
                 case "afterend":
-                    el.insertAdjacentHTML('AfterEnd', html);
-                    return el.nextSibling;
-            }
-            throw 'Illegal insertion point -> "' + where + '"';
-        }
-        var range = el.ownerDocument.createRange();
-        var frag;
-        switch(where){
-             case "beforebegin":
-                range.setStartBefore(el);
-                frag = range.createContextualFragment(html);
-                el.parentNode.insertBefore(frag, el);
-                return el.previousSibling;
-             case "afterbegin":
-                if(el.firstChild){
-                    range.setStartBefore(el.firstChild);
+                    range.setStartAfter(el);
                     frag = range.createContextualFragment(html);
-                    el.insertBefore(frag, el.firstChild);
-                    return el.firstChild;
-                }else{
-                    el.innerHTML = html;
-                    return el.firstChild;
+                    el.parentNode.insertBefore(frag, el.nextSibling);
+                    return el.nextSibling;
                 }
-            case "beforeend":
-                if(el.lastChild){
-                    range.setStartAfter(el.lastChild);
-                    frag = range.createContextualFragment(html);
-                    el.appendChild(frag);
-                    return el.lastChild;
-                }else{
-                    el.innerHTML = html;
-                    return el.lastChild;
+                throw 'Illegal insertion point -> "' + where + '"';
+        },
+    
+        /**
+         * Creates new Dom element(s) and inserts them before el
+         * @param {String/HTMLElement/Element} el The context element
+         * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
+         * @param {Boolean} returnElement (optional) true to return a Roo.Element
+         * @return {HTMLElement/Roo.Element} The new node
+         */
+        insertBefore : function(el, o, returnElement){
+            return this.doInsert(el, o, returnElement, "beforeBegin");
+        },
+    
+        /**
+         * Creates new Dom element(s) and inserts them after el
+         * @param {String/HTMLElement/Element} el The context element
+         * @param {Object} o The Dom object spec (and children)
+         * @param {Boolean} returnElement (optional) true to return a Roo.Element
+         * @return {HTMLElement/Roo.Element} The new node
+         */
+        insertAfter : function(el, o, returnElement){
+            return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
+        },
+    
+        /**
+         * Creates new Dom element(s) and inserts them as the first child of el
+         * @param {String/HTMLElement/Element} el The context element
+         * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
+         * @param {Boolean} returnElement (optional) true to return a Roo.Element
+         * @return {HTMLElement/Roo.Element} The new node
+         */
+        insertFirst : function(el, o, returnElement){
+            return this.doInsert(el, o, returnElement, "afterBegin");
+        },
+    
+        // private
+        doInsert : function(el, o, returnElement, pos, sibling){
+            el = Roo.getDom(el);
+            var newNode;
+            if(this.useDom || o.ns){
+                newNode = createDom(o, null);
+                el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
+            }else{
+                var html = createHtml(o);
+                newNode = this.insertHtml(pos, el, html);
+            }
+            return returnElement ? Roo.get(newNode, true) : newNode;
+        },
+    
+        /**
+         * Creates new Dom element(s) and appends them to el
+         * @param {String/HTMLElement/Element} el The context element
+         * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
+         * @param {Boolean} returnElement (optional) true to return a Roo.Element
+         * @return {HTMLElement/Roo.Element} The new node
+         */
+        append : function(el, o, returnElement){
+            el = Roo.getDom(el);
+            var newNode;
+            if(this.useDom || o.ns){
+                newNode = createDom(o, null);
+                el.appendChild(newNode);
+            }else{
+                var html = createHtml(o);
+                newNode = this.insertHtml("beforeEnd", el, html);
+            }
+            return returnElement ? Roo.get(newNode, true) : newNode;
+        },
+    
+        /**
+         * Creates new Dom element(s) and overwrites the contents of el with them
+         * @param {String/HTMLElement/Element} el The context element
+         * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
+         * @param {Boolean} returnElement (optional) true to return a Roo.Element
+         * @return {HTMLElement/Roo.Element} The new node
+         */
+        overwrite : function(el, o, returnElement)
+        {
+            el = Roo.getDom(el);
+            if (o.ns) {
+              
+                while (el.childNodes.length) {
+                    el.removeChild(el.firstChild);
                 }
-            case "afterend":
-                range.setStartAfter(el);
-                frag = range.createContextualFragment(html);
-                el.parentNode.insertBefore(frag, el.nextSibling);
-                return el.nextSibling;
+                createDom(o, el);
+            } else {
+                el.innerHTML = createHtml(o);   
             }
-            throw 'Illegal insertion point -> "' + where + '"';
-    },
-
-    /**
-     * Creates new Dom element(s) and inserts them before el
-     * @param {String/HTMLElement/Element} el The context element
-     * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
-     * @param {Boolean} returnElement (optional) true to return a Roo.Element
-     * @return {HTMLElement/Roo.Element} The new node
-     */
-    insertBefore : function(el, o, returnElement){
-        return this.doInsert(el, o, returnElement, "beforeBegin");
-    },
-
-    /**
-     * Creates new Dom element(s) and inserts them after el
-     * @param {String/HTMLElement/Element} el The context element
-     * @param {Object} o The Dom object spec (and children)
-     * @param {Boolean} returnElement (optional) true to return a Roo.Element
-     * @return {HTMLElement/Roo.Element} The new node
-     */
-    insertAfter : function(el, o, returnElement){
-        return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
-    },
-
-    /**
-     * Creates new Dom element(s) and inserts them as the first child of el
-     * @param {String/HTMLElement/Element} el The context element
-     * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
-     * @param {Boolean} returnElement (optional) true to return a Roo.Element
-     * @return {HTMLElement/Roo.Element} The new node
-     */
-    insertFirst : function(el, o, returnElement){
-        return this.doInsert(el, o, returnElement, "afterBegin");
-    },
-
-    // private
-    doInsert : function(el, o, returnElement, pos, sibling){
-        el = Roo.getDom(el);
-        var newNode;
-        if(this.useDom || o.ns){
-            newNode = createDom(o, null);
-            el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
-        }else{
-            var html = createHtml(o);
-            newNode = this.insertHtml(pos, el, html);
-        }
-        return returnElement ? Roo.get(newNode, true) : newNode;
-    },
-
-    /**
-     * Creates new Dom element(s) and appends them to el
-     * @param {String/HTMLElement/Element} el The context element
-     * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
-     * @param {Boolean} returnElement (optional) true to return a Roo.Element
-     * @return {HTMLElement/Roo.Element} The new node
-     */
-    append : function(el, o, returnElement){
-        el = Roo.getDom(el);
-        var newNode;
-        if(this.useDom || o.ns){
-            newNode = createDom(o, null);
-            el.appendChild(newNode);
-        }else{
+            
+            return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
+        },
+    
+        /**
+         * Creates a new Roo.DomHelper.Template from the Dom object spec
+         * @param {Object} o The Dom object spec (and children)
+         * @return {Roo.DomHelper.Template} The new template
+         */
+        createTemplate : function(o){
             var html = createHtml(o);
-            newNode = this.insertHtml("beforeEnd", el, html);
-        }
-        return returnElement ? Roo.get(newNode, true) : newNode;
-    },
-
-    /**
-     * Creates new Dom element(s) and overwrites the contents of el with them
-     * @param {String/HTMLElement/Element} el The context element
-     * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
-     * @param {Boolean} returnElement (optional) true to return a Roo.Element
-     * @return {HTMLElement/Roo.Element} The new node
-     */
-    overwrite : function(el, o, returnElement){
-        el = Roo.getDom(el);
-        if (o.ns) {
-          
-            while (el.childNodes.length) {
-                el.removeChild(el.firstChild);
-            }
-            createDom(o, el);
-        } else {
-            el.innerHTML = createHtml(o);   
+            return new Roo.Template(html);
+        },
+         /**
+         * Updates the first element with the spec from the o (replacing if necessary)
+         * This iterates through the children, and updates attributes / children etc..
+         * @param {String/HTMLElement/Element} el The context element
+         * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
+         */
+        
+        update : function(el, o)
+        {
+            updateNode(Roo.getDom(el), createDom(o));
+            
         }
         
-        return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
-    },
-
-    /**
-     * Creates a new Roo.DomHelper.Template from the Dom object spec
-     * @param {Object} o The Dom object spec (and children)
-     * @return {Roo.DomHelper.Template} The new template
-     */
-    createTemplate : function(o){
-        var html = createHtml(o);
-        return new Roo.Template(html);
-    }
+        
     };
 }();
 /*
@@ -4652,6 +6009,12 @@ Roo.Template = function(cfg){
 };
 Roo.Template.prototype = {
     
+    /**
+     * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
+     */
+    onLoad : false,
+    
+    
     /**
      * @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...
@@ -4661,13 +6024,20 @@ Roo.Template.prototype = {
      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
      */
     html : '',
+    
+    
+    compiled : false,
+    loaded : false,
     /**
      * Returns an HTML fragment of this template with the specified values applied.
      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
      * @return {String} The HTML fragment
      */
+    
+   
+    
     applyTemplate : function(values){
-        Roo.log(["applyTemplate", values]);
+        //Roo.log(["applyTemplate", values]);
         try {
            
             if(this.compiled){
@@ -4726,9 +6096,13 @@ Roo.Template.prototype = {
             method : 'GET',
             success : function (response) {
                 _t.loading = false;
-                _t.html = response.responseText;
                 _t.url = false;
-                _t.compile();
+                
+                _t.set(response.responseText,true);
+                _t.loaded = true;
+                if (_t.onLoad) {
+                    _t.onLoad();
+                }
              },
             failure : function(response) {
                 Roo.log("Template failed to load from " + _t.url);
@@ -4745,7 +6119,7 @@ Roo.Template.prototype = {
      */
     set : function(html, compile){
         this.html = html;
-        this.compiled = null;
+        this.compiled = false;
         if(compile){
             this.compile();
         }
@@ -4968,7 +6342,7 @@ All selectors, attribute filters and pseudos below can be combined infinitely in
     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
 </ul>
- * @singleton
+ * @static
  */
 Roo.DomQuery = function(){
     var cache = {}, simpleCache = {}, valueCache = {};
@@ -5023,7 +6397,11 @@ Roo.DomQuery = function(){
         }
         var r = [], ri = -1, cn;
         for(var i = 0, ci; ci = c[i]; i++){
-            if((' '+ci.className+' ').indexOf(v) != -1){
+           
+           
+            if((' '+
+               ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
+                +' ').indexOf(v) != -1){
                 r[++ri] = ci;
             }
         }
@@ -5041,7 +6419,7 @@ Roo.DomQuery = function(){
             return n.htmlFor;
         }
         if(attr == "class" || attr == "className"){
-            return n.className;
+           return (n instanceof SVGElement) ? n.className.baseVal : n.className;
         }
         return n.getAttribute(attr) || n[attr];
 
@@ -5145,7 +6523,7 @@ Roo.DomQuery = function(){
                 a = Roo.DomQuery.getStyle(ci, attr);
             }
             else if(attr == "class" || attr == "className"){
-                a = ci.className;
+                a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
             }else if(attr == "for"){
                 a = ci.htmlFor;
             }else if(attr == "href"){
@@ -5906,12 +7284,15 @@ Roo.util.Observable.prototype = {
     relayEvents : function(o, events){
         var createHandler = function(ename){
             return function(){
+                
                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
             };
         };
         for(var i = 0, len = events.length; i < len; i++){
             var ename = events[i];
-            if(!this.events[ename]){ this.events[ename] = true; };
+            if(!this.events[ename]){
+               this.events[ename] = true;
+           };
             o.on(ename, createHandler(ename), this);
         }
     },
@@ -6148,7 +7529,7 @@ Roo.extend(Roo.Document, Roo.util.Observable, {});/*
  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
  * several useful events directly.
  * See {@link Roo.EventObject} for more details on normalized event objects.
- * @singleton
+ * @static
  */
 Roo.EventManager = function(){
     var docReadyEvent, docReadyProcId, docReadyState = false;
@@ -6256,8 +7637,10 @@ Roo.EventManager = function(){
         }
     }
     
+  
 
-    var listen = function(element, ename, opt, fn, scope){
+    var listen = function(element, ename, opt, fn, scope)
+    {
         var o = (!opt || typeof opt == "boolean") ? {} : opt;
         fn = fn || o.fn; scope = scope || o.scope;
         var el = Roo.getDom(element);
@@ -6314,7 +7697,9 @@ Roo.EventManager = function(){
         
         
          
-        E.on(el, ename, h);
+        E.on(el, ename, h); // this adds the actuall listener to the object..
+        
+        
         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
             el.addEventListener("DOMMouseScroll", h, false);
             E.on(window, 'unload', function(){
@@ -6500,16 +7885,18 @@ el.on({
          * @param {Object}   scope    An object that becomes the scope of the handler
          * @param {boolean}  options
          */
-        onWindowResize : function(fn, scope, options){
+        onWindowResize : function(fn, scope, options)
+        {
             if(!resizeEvent){
                 resizeEvent = new Roo.util.Event();
                 resizeTask = new Roo.util.DelayedTask(function(){
                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
                 });
-                E.on(window, "resize", function(){
-                    if(Roo.isIE){
+                E.on(window, "resize", function()
+                {
+                    if (Roo.isIE) {
                         resizeTask.delay(50);
-                    }else{
+                    } else {
                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
                     }
                 });
@@ -6706,7 +8093,7 @@ Roo.onReady(function(){
  Roo.EventManager.on("myDiv", 'click', handleClick);
  Roo.EventManager.addListener("myDiv", 'click', handleClick);
  </code></pre>
- * @singleton
+ * @static
  */
 Roo.EventObject = function(){
     
@@ -7099,9 +8486,13 @@ if(opt.anim.isAnimated()){
  * @param {String/HTMLElement} element
  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
  */
-    Roo.Element = function(element, forceNew){
+    Roo.Element = function(element, forceNew)
+    {
         var dom = typeof element == "string" ?
                 document.getElementById(element) : element;
+        
+        this.listeners = {};
+        
         if(!dom){ // invalid id/element
             return null;
         }
@@ -7121,18 +8512,22 @@ if(opt.anim.isAnimated()){
          * @type String
          */
         this.id = id || Roo.id(dom);
+        
+        return this; // assumed for cctor?
     };
 
     var El = Roo.Element;
 
     El.prototype = {
         /**
-         * The element's default display mode  (defaults to "")
+         * The element's default display mode  (defaults to "") 
          * @type String
          */
         originalDisplay : "",
 
-        visibilityMode : 1,
+        
+        // note this is overridden in BS version..
+        visibilityMode : 1, 
         /**
          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
          * @type String
@@ -7626,7 +9021,11 @@ if(opt.anim.isAnimated()){
                 }
             }else{
                 if(className && !this.hasClass(className)){
-                    this.dom.className = this.dom.className + " " + className;
+                    if (this.dom instanceof SVGElement) {
+                        this.dom.className.baseVal =this.dom.className.baseVal  + " " + className;
+                    } else {
+                        this.dom.className = this.dom.className + " " + className;
+                    }
                 }
             }
             return this;
@@ -7655,7 +9054,9 @@ if(opt.anim.isAnimated()){
          * @return {Roo.Element} this
          */
         removeClass : function(className){
-            if(!className || !this.dom.className){
+            
+            var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
+            if(!className || !cn){
                 return this;
             }
             if(className instanceof Array){
@@ -7669,8 +9070,11 @@ if(opt.anim.isAnimated()){
                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
                        this.classReCache[className] = re;
                     }
-                    this.dom.className =
-                        this.dom.className.replace(re, " ");
+                    if (this.dom instanceof SVGElement) {
+                        this.dom.className.baseVal = cn.replace(re, " ");
+                    } else {
+                        this.dom.className = cn.replace(re, " ");
+                    }
                 }
             }
             return this;
@@ -7699,6 +9103,9 @@ if(opt.anim.isAnimated()){
          * @return {Boolean} True if the class exists, else false
          */
         hasClass : function(className){
+            if (this.dom instanceof SVGElement) {
+                return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1; 
+            } 
             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
         },
 
@@ -8180,20 +9587,67 @@ if(opt.anim.isAnimated()){
          * @param {Object} scope       (optional) The scope (this object) of the fn
          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
          */
-        addListener : function(eventName, fn, scope, options){
-            if (this.dom) {
-                Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
+        addListener : function(eventName, fn, scope, options)
+        {
+            if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
+                this.addListener('touchstart', this.onTapHandler, this);
+            }
+            
+            // we need to handle a special case where dom element is a svg element.
+            // in this case we do not actua
+            if (!this.dom) {
+                return;
+            }
+            
+            if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
+                if (typeof(this.listeners[eventName]) == 'undefined') {
+                    this.listeners[eventName] =  new Roo.util.Event(this, eventName);
+                }
+                this.listeners[eventName].addListener(fn, scope, options);
+                return;
             }
+            
+                
+            Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
+            
+            
         },
-
+        tapedTwice : false,
+        onTapHandler : function(event)
+        {
+            if(!this.tapedTwice) {
+                this.tapedTwice = true;
+                var s = this;
+                setTimeout( function() {
+                    s.tapedTwice = false;
+                }, 300 );
+                return;
+            }
+            event.preventDefault();
+            var revent = new MouseEvent('dblclick',  {
+                view: window,
+                bubbles: true,
+                cancelable: true
+            });
+             
+            this.dom.dispatchEvent(revent);
+            //action on double tap goes below
+             
+        }, 
         /**
          * Removes an event handler from this element
          * @param {String} eventName the type of event to remove
          * @param {Function} fn the method the event invokes
+         * @param {Function} scope (needed for svg fake listeners)
          * @return {Roo.Element} this
          */
-        removeListener : function(eventName, fn){
+        removeListener : function(eventName, fn, scope){
             Roo.EventManager.removeListener(this.dom,  eventName, fn);
+            if (typeof(this.listeners) == 'undefined'  || typeof(this.listeners[eventName]) == 'undefined') {
+                return this;
+            }
+            this.listeners[eventName].removeListener(fn, scope);
             return this;
         },
 
@@ -8203,6 +9657,7 @@ if(opt.anim.isAnimated()){
          */
         removeAllListeners : function(){
             E.purgeElement(this.dom);
+            this.listeners = {};
             return this;
         },
 
@@ -8212,6 +9667,7 @@ if(opt.anim.isAnimated()){
             });
         },
 
+        
         /**
          * Set the opacity of the element
          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
@@ -8560,7 +10016,8 @@ if(opt.anim.isAnimated()){
          * @param {Array} offsets (optional) Offset the positioning by [x, y]
          * @return {Array} [x, y]
          */
-        getAlignToXY : function(el, p, o){
+        getAlignToXY : function(el, p, o)
+        {
             el = Roo.get(el);
             var d = this.dom;
             if(!el.dom){
@@ -8601,7 +10058,7 @@ if(opt.anim.isAnimated()){
                 //otherwise swap the aligned el to the opposite border of the target.
                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
-               var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
+               var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
 
                var doc = document;
@@ -9115,7 +10572,7 @@ if(opt.anim.isAnimated()){
          * Puts a mask over this element to disable user interaction. Requires core.css.
          * This method can only be applied to elements which accept child nodes.
          * @param {String} msg (optional) A message to display in the mask
-         * @param {String} msgCls (optional) A css class to apply to the msg element
+         * @param {String} msgCls (optional) A css class to apply to the msg element - use no-spinner to hide the spinner on bootstrap
          * @return {Element} The mask  element
          */
         mask : function(msg, msgCls)
@@ -9315,7 +10772,7 @@ if(opt.anim.isAnimated()){
         /**
          * @private
          */
-      fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
+        fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
 
         /**
          * Sizes this element to its parent element's dimensions performing
@@ -9328,7 +10785,7 @@ if(opt.anim.isAnimated()){
           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
-            return;
+            return this;
           }
           var p = Roo.get(targetParent || this.dom.parentNode);
           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
@@ -11616,7 +13073,7 @@ Roo.extend(Roo.data.Connection, Roo.util.Observable, {
                 var enctype = form.getAttribute("enctype");
                 
                 if (o.formData) {
-                    return this.doFormDataUpload(o,p,url);
+                    return this.doFormDataUpload(o, url);
                 }
                 
                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
@@ -11625,6 +13082,16 @@ Roo.extend(Roo.data.Connection, Roo.util.Observable, {
                 var f = Roo.lib.Ajax.serializeForm(form);
                 p = p ? (p + '&' + f) : f;
             }
+            
+            if (!o.form && o.formData) {
+                o.formData = o.formData === true ? new FormData() : o.formData;
+                for (var k in o.params) {
+                    o.formData.append(k,o.params[k]);
+                }
+                    
+                return this.doFormDataUpload(o, url);
+            }
+            
 
             var hs = o.headers;
             if(this.defaultHeaders){
@@ -11802,11 +13269,17 @@ Roo.extend(Roo.data.Connection, Roo.util.Observable, {
     // this is a 'formdata version???'
     
     
-    doFormDataUpload : function(o, ps, url)
+    doFormDataUpload : function(o,  url)
     {
-        var form = Roo.getDom(o.form);
-        form.enctype = form.encoding = 'multipart/form-data';
-        var formData = o.formData === true ? new FormData(form) : o.formData;
+        var formData;
+        if (o.form) {
+            var form =  Roo.getDom(o.form);
+            form.enctype = form.encoding = 'multipart/form-data';
+            formData = o.formData === true ? new FormData(form) : o.formData;
+        } else {
+            formData = o.formData === true ? new FormData() : o.formData;
+        }
+        
       
         var cb = {
             success: this.handleResponse,
@@ -11826,7 +13299,7 @@ Roo.extend(Roo.data.Connection, Roo.util.Observable, {
 
         //Roo.lib.Ajax.defaultPostHeader = null;
         Roo.lib.Ajax.useDefaultHeader = false;
-        this.transId = Roo.lib.Ajax.request( "POST", url, cb, o.formData, o);
+        this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
         Roo.lib.Ajax.useDefaultHeader = true;
  
          
@@ -12890,7 +14363,12 @@ Roo.util.DelayedTask = function(fn, scope, args){
  * Fork - LGPL
  * <script type="text/javascript">
  */
+/**
+ * @class Roo.util.TaskRunner
+ * Manage background tasks - not sure why this is better that setInterval?
+ * @static
+ *
+ */
  
 Roo.util.TaskRunner = function(interval){
     interval = interval || 10;
@@ -12950,6 +14428,12 @@ Roo.util.TaskRunner = function(interval){
     /**
      * Queues a new task.
      * @param {Object} task
+     *
+     * Task property : interval = how frequent to run.
+     * Task object should implement
+     * function run()
+     * Task object may implement
+     * function onStop()
      */
     this.start = function(task){
         tasks.push(task);
@@ -12959,12 +14443,17 @@ Roo.util.TaskRunner = function(interval){
         startThread();
         return task;
     };
-
+    /**
+     * Stop  new task.
+     * @param {Object} task
+     */
     this.stop = function(task){
         removeTask(task);
         return task;
     };
-
+    /**
+     * Stop all Tasks
+     */
     this.stopAll = function(){
         stopThread();
         for(var i = 0, len = tasks.length; i < len; i++){
@@ -13518,7 +15007,7 @@ Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item
  * Modified version of Douglas Crockford"s json.js that doesn"t
  * mess with the Object prototype 
  * http://www.json.org/js.html
- * @singleton
+ * @static
  */
 Roo.util.JSON = new (function(){
     var useHasOwn = {}.hasOwnProperty ? true : false;
@@ -13666,7 +15155,7 @@ Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.J
 /**
  * @class Roo.util.Format
  * Reusable data formatting functions
- * @singleton
+ * @static
  */
 Roo.util.Format = function(){
     var trimRe = /^\s+|\s+$/g;
@@ -13899,7 +15388,26 @@ Roo.util.Format = function(){
          */
         stripTags : function(v){
             return !v ? v : String(v).replace(this.stripTagsRE, "");
+        },
+        
+        /**
+         * Size in Mb,Gb etc.
+         * @param {Number} value The number to be formated
+         * @param {number} decimals how many decimal places
+         * @return {String} the formated string
+         */
+        size : function(value, decimals)
+        {
+            var sizes = ['b', 'k', 'M', 'G', 'T'];
+            if (value == 0) {
+                return 0;
+            }
+            var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
+            return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals)   + sizes[i];
         }
+        
+        
+        
     };
 }();
 Roo.util.Format.defaults = {
@@ -14071,7 +15579,8 @@ Roo.MasterTemplate.from = function(el, config){
 /**
  * @class Roo.util.CSS
  * Utility class for manipulating CSS rules
- * @singleton
+ * @static
+
  */
 Roo.util.CSS = function(){
        var rules = null;
@@ -14414,7 +15923,44 @@ Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
         this.el.removeClass(this.pressClass);
         this.fireEvent("mouseup", this);
     }
-});/*
+});/**
+ * @class Roo.util.Clipboard
+ * @static
+ * 
+ * Clipboard UTILS
+ * 
+ **/
+Roo.util.Clipboard = {
+    /**
+     * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
+     * @param {String} text to copy to clipboard
+     */
+    write : function(text) {
+        // navigator clipboard api needs a secure context (https)
+        if (navigator.clipboard && window.isSecureContext) {
+            // navigator clipboard api method'
+            navigator.clipboard.writeText(text);
+            return ;
+        } 
+        // text area method
+        var ta = document.createElement("textarea");
+        ta.value = text;
+        // make the textarea out of viewport
+        ta.style.position = "fixed";
+        ta.style.left = "-999999px";
+        ta.style.top = "-999999px";
+        document.body.appendChild(ta);
+        ta.focus();
+        ta.select();
+        document.execCommand('copy');
+        (function() {
+            ta.remove();
+        }).defer(100);
+        
+    }
+        
+}
+    /*
  * Based on:
  * Ext JS Library 1.1.1
  * Copyright(c) 2006-2007, Ext JS, LLC.
@@ -14807,7 +16353,7 @@ map.addBinding({
  * @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
+ * @static
  */
 Roo.util.TextMetrics = function(){
     var shared;
@@ -14844,9 +16390,17 @@ Roo.util.TextMetrics = function(){
     };
 }();
 
+/**
+ * @class Roo.util.TextMetrics.Instance
+ * Instance of  TextMetrics Calcuation
+ * @constructor
+ * Create a new TextMetrics Instance
+ * @param {Object} bindto
+ * @param {Boolean} fixedWidth
+ */
 
-Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
+Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth)
+{
     var ml = new Roo.Element(document.createElement('div'));
     document.body.appendChild(ml.dom);
     ml.position('absolute');
@@ -14860,7 +16414,6 @@ Roo.util.TextMetrics.Instance = function(bindTo, 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)}
          */
@@ -14874,7 +16427,6 @@ Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
         /**
          * 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){
@@ -14886,7 +16438,6 @@ Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
         /**
          * 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){
@@ -14895,7 +16446,6 @@ Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
 
         /**
          * 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
          */
@@ -14907,7 +16457,6 @@ Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
         /**
          * 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
          */
@@ -15088,7 +16637,7 @@ init : function(){
    var dialog = new Roo.BasicDialog(...);
    dialog.restoreState();
  </code></pre>
- * @singleton
+ * @static
  */
 Roo.state.Manager = function(){
     var provider = new Roo.state.Provider();
@@ -15246,7 +16795,7 @@ Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
 /**
  * @class Roo.ComponentMgr
  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
- * @singleton
+ * @static
  */
 Roo.ComponentMgr = function(){
     var all = new Roo.util.MixedCollection();
@@ -15737,7 +17286,7 @@ Roo.extend(Roo.Component, Roo.util.Observable, {
  * @extends Roo.Component
  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
- * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
+ * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
  * layout containers.
  * @constructor
  * @param {Roo.Element/String/Object} config The configuration options.
@@ -16004,1383 +17553,1840 @@ Roo.extend(Roo.BoxComponent, Roo.Component, {
         return {x : x, y: y};
     }
 });/*
- * Original code for Roojs - LGPL
+ * 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">
  */
+ (function(){ 
 /**
- * @class Roo.XComponent
- * A delayed Element creator...
- * Or a way to group chunks of interface together.
- * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
- *  used in conjunction with XComponent.build() it will create an instance of each element,
- *  then call addxtype() to build the User interface.
- * 
- * Mypart.xyx = new Roo.XComponent({
-
-    parent : 'Mypart.xyz', // empty == document.element.!!
-    order : '001',
-    name : 'xxxx'
-    region : 'xxxx'
-    disabled : function() {} 
-     
-    tree : function() { // return an tree of xtype declared components
-        var MODULE = this;
-        return 
-        {
-            xtype : 'NestedLayoutPanel',
-            // technicall
-        }
-     ]
- *})
- *
- *
- * It can be used to build a big heiracy, with parent etc.
- * or you can just use this to render a single compoent to a dom element
- * MYPART.render(Roo.Element | String(id) | dom_element )
- *
- *
- * Usage patterns.
- *
- * Classic Roo
- *
- * Roo is designed primarily as a single page application, so the UI build for a standard interface will
- * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
- *
- * Each sub module is expected to have a parent pointing to the class name of it's parent module.
- *
- * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
- * - if mulitple topModules exist, the last one is defined as the top module.
- *
- * Embeded Roo
- * 
- * When the top level or multiple modules are to embedded into a existing HTML page,
- * the parent element can container '#id' of the element where the module will be drawn.
- *
- * Bootstrap Roo
- *
- * Unlike classic Roo, the bootstrap tends not to be used as a single page.
- * it relies more on a include mechanism, where sub modules are included into an outer page.
- * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
- * 
- * Bootstrap Roo Included elements
- *
- * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
- * hence confusing the component builder as it thinks there are multiple top level elements. 
- *
- * String Over-ride & Translations
- *
- * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
- * and also the 'overlaying of string values - needed when different versions of the same application with different text content
- * are needed. @see Roo.XComponent.overlayString  
- * 
- * 
- * 
- * @extends Roo.util.Observable
+ * @class Roo.Layer
+ * @extends Roo.Element
+ * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
+ * automatic maintaining of shadow/shim positions.
+ * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
+ * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
+ * you can pass a string with a CSS class name. False turns off the shadow.
+ * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
+ * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
+ * @cfg {String} cls CSS class to add to the element
+ * @cfg {Number} zindex Starting z-index (defaults to 11000)
+ * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
  * @constructor
- * @param cfg {Object} configuration of component
- * 
+ * @param {Object} config An object with config options.
+ * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
  */
-Roo.XComponent = function(cfg) {
-    Roo.apply(this, cfg);
-    this.addEvents({ 
-        /**
-            * @event built
-            * Fires when this the componnt is built
-            * @param {Roo.XComponent} c the component
-            */
-        'built' : true
-        
-    });
-    this.region = this.region || 'center'; // default..
-    Roo.XComponent.register(this);
-    this.modules = false;
-    this.el = false; // where the layout goes..
-    
-    
-}
-Roo.extend(Roo.XComponent, Roo.util.Observable, {
-    /**
-     * @property el
-     * The created element (with Roo.factory())
-     * @type {Roo.Layout}
-     */
-    el  : false,
-    
-    /**
-     * @property el
-     * for BC  - use el in new code
-     * @type {Roo.Layout}
-     */
-    panel : false,
-    
-    /**
-     * @property layout
-     * for BC  - use el in new code
-     * @type {Roo.Layout}
-     */
-    layout : false,
-    
-     /**
-     * @cfg {Function|boolean} disabled
-     * If this module is disabled by some rule, return true from the funtion
-     */
-    disabled : false,
-    
-    /**
-     * @cfg {String} parent 
-     * Name of parent element which it get xtype added to..
-     */
-    parent: false,
-    
-    /**
-     * @cfg {String} order
-     * Used to set the order in which elements are created (usefull for multiple tabs)
-     */
-    
-    order : false,
-    /**
-     * @cfg {String} name
-     * String to display while loading.
-     */
-    name : false,
-    /**
-     * @cfg {String} region
-     * Region to render component to (defaults to center)
-     */
-    region : 'center',
-    
-    /**
-     * @cfg {Array} items
-     * A single item array - the first element is the root of the tree..
-     * It's done this way to stay compatible with the Xtype system...
-     */
-    items : false,
-    
-    /**
-     * @property _tree
-     * The method that retuns the tree of parts that make up this compoennt 
-     * @type {function}
-     */
-    _tree  : false,
-    
-     /**
-     * render
-     * render element to dom or tree
-     * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
-     */
-    
-    render : function(el)
-    {
-        
-        el = el || false;
-        var hp = this.parent ? 1 : 0;
-        Roo.debug &&  Roo.log(this);
-        
-        var tree = this._tree ? this._tree() : this.tree();
 
-        
-        if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
-            // if parent is a '#.....' string, then let's use that..
-            var ename = this.parent.substr(1);
-            this.parent = false;
-            Roo.debug && Roo.log(ename);
-            switch (ename) {
-                case 'bootstrap-body':
-                    if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
-                        // this is the BorderLayout standard?
-                       this.parent = { el : true };
-                       break;
-                    }
-                    if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
-                        // need to insert stuff...
-                        this.parent =  {
-                             el : new Roo.bootstrap.layout.Border({
-                                 el : document.body, 
-                     
-                                 center: {
-                                    titlebar: false,
-                                    autoScroll:false,
-                                    closeOnTab: true,
-                                    tabPosition: 'top',
-                                      //resizeTabs: true,
-                                    alwaysShowTabs: true,
-                                    hideTabs: false
-                                     //minTabWidth: 140
-                                 }
-                             })
-                        
-                         };
-                         break;
-                    }
-                         
-                    if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
-                        this.parent = { el :  new  Roo.bootstrap.Body() };
-                        Roo.debug && Roo.log("setting el to doc body");
-                         
-                    } else {
-                        throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
-                    }
-                    break;
-                case 'bootstrap':
-                    this.parent = { el : true};
-                    // fall through
-                default:
-                    el = Roo.get(ename);
-                    if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
-                        this.parent = { el : true};
-                    }
-                    
-                    break;
-            }
-                
-            
-            if (!el && !this.parent) {
-                Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
-                return;
-            }
-        }
-        
-        Roo.debug && Roo.log("EL:");
-        Roo.debug && Roo.log(el);
-        Roo.debug && Roo.log("this.parent.el:");
-        Roo.debug && Roo.log(this.parent.el);
-        
+Roo.Layer = function(config, existingEl){
+    config = config || {};
+    var dh = Roo.DomHelper;
+    var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
+    if(existingEl){
+        this.dom = Roo.getDom(existingEl);
+    }
+    if(!this.dom){
+        var o = config.dh || {tag: "div", cls: "x-layer"};
+        this.dom = dh.append(pel, o);
+    }
+    if(config.cls){
+        this.addClass(config.cls);
+    }
+    this.constrain = config.constrain !== false;
+    this.visibilityMode = Roo.Element.VISIBILITY;
+    if(config.id){
+        this.id = this.dom.id = config.id;
+    }else{
+        this.id = Roo.id(this.dom);
+    }
+    this.zindex = config.zindex || this.getZIndex();
+    this.position("absolute", this.zindex);
+    if(config.shadow){
+        this.shadowOffset = config.shadowOffset || 4;
+        this.shadow = new Roo.Shadow({
+            offset : this.shadowOffset,
+            mode : config.shadow
+        });
+    }else{
+        this.shadowOffset = 0;
+    }
+    this.useShim = config.shim !== false && Roo.useShims;
+    this.useDisplay = config.useDisplay;
+    this.hide();
+};
 
-        // altertive root elements ??? - we need a better way to indicate these.
-        var is_alt = Roo.XComponent.is_alt ||
-                    (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
-                    (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
-                    (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
-        
-        
-        
-        if (!this.parent && is_alt) {
-            //el = Roo.get(document.body);
-            this.parent = { el : true };
+var supr = Roo.Element.prototype;
+
+// shims are shared among layer to keep from having 100 iframes
+var shims = [];
+
+Roo.extend(Roo.Layer, Roo.Element, {
+
+    getZIndex : function(){
+        return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
+    },
+
+    getShim : function(){
+        if(!this.useShim){
+            return null;
         }
-            
-            
-        
-        if (!this.parent) {
-            
-            Roo.debug && Roo.log("no parent - creating one");
-            
-            el = el ? Roo.get(el) : false;     
-            
-            if (typeof(Roo.BorderLayout) == 'undefined' ) {
-                
-                this.parent =  {
-                    el : new Roo.bootstrap.layout.Border({
-                        el: el || document.body,
-                    
-                        center: {
-                            titlebar: false,
-                            autoScroll:false,
-                            closeOnTab: true,
-                            tabPosition: 'top',
-                             //resizeTabs: true,
-                            alwaysShowTabs: false,
-                            hideTabs: true,
-                            minTabWidth: 140,
-                            overflow: 'visible'
-                         }
-                     })
-                };
-            } else {
-            
-                // it's a top level one..
-                this.parent =  {
-                    el : new Roo.BorderLayout(el || document.body, {
-                        center: {
-                            titlebar: false,
-                            autoScroll:false,
-                            closeOnTab: true,
-                            tabPosition: 'top',
-                             //resizeTabs: true,
-                            alwaysShowTabs: el && hp? false :  true,
-                            hideTabs: el || !hp ? true :  false,
-                            minTabWidth: 140
-                         }
-                    })
-                };
-            }
+        if(this.shim){
+            return this.shim;
         }
-        
-        if (!this.parent.el) {
-                // probably an old style ctor, which has been disabled.
-                return;
-
+        var shim = shims.shift();
+        if(!shim){
+            shim = this.createShim();
+            shim.enableDisplayMode('block');
+            shim.dom.style.display = 'none';
+            shim.dom.style.visibility = 'visible';
         }
-               // The 'tree' method is  '_tree now' 
-            
-        tree.region = tree.region || this.region;
-        var is_body = false;
-        if (this.parent.el === true) {
-            // bootstrap... - body..
-            if (el) {
-                tree.el = el;
-            }
-            this.parent.el = Roo.factory(tree);
-            is_body = true;
+        var pn = this.dom.parentNode;
+        if(shim.dom.parentNode != pn){
+            pn.insertBefore(shim.dom, this.dom);
         }
-        
-        this.el = this.parent.el.addxtype(tree, undefined, is_body);
-        this.fireEvent('built', this);
-        
-        this.panel = this.el;
-        this.layout = this.panel.layout;
-        this.parentLayout = this.parent.layout  || false;  
-         
-    }
-    
-});
-
-Roo.apply(Roo.XComponent, {
-    /**
-     * @property  hideProgress
-     * true to disable the building progress bar.. usefull on single page renders.
-     * @type Boolean
-     */
-    hideProgress : false,
-    /**
-     * @property  buildCompleted
-     * True when the builder has completed building the interface.
-     * @type Boolean
-     */
-    buildCompleted : false,
-     
-    /**
-     * @property  topModule
-     * the upper most module - uses document.element as it's constructor.
-     * @type Object
-     */
-     
-    topModule  : false,
-      
-    /**
-     * @property  modules
-     * array of modules to be created by registration system.
-     * @type {Array} of Roo.XComponent
-     */
-    
-    modules : [],
-    /**
-     * @property  elmodules
-     * array of modules to be created by which use #ID 
-     * @type {Array} of Roo.XComponent
-     */
-     
-    elmodules : [],
+        shim.setStyle('z-index', this.getZIndex()-2);
+        this.shim = shim;
+        return shim;
+    },
 
-     /**
-     * @property  is_alt
-     * Is an alternative Root - normally used by bootstrap or other systems,
-     *    where the top element in the tree can wrap 'body' 
-     * @type {boolean}  (default false)
-     */
-     
-    is_alt : false,
-    /**
-     * @property  build_from_html
-     * Build elements from html - used by bootstrap HTML stuff 
-     *    - this is cleared after build is completed
-     * @type {boolean}    (default false)
-     */
-     
-    build_from_html : false,
-    /**
-     * Register components to be built later.
-     *
-     * This solves the following issues
-     * - Building is not done on page load, but after an authentication process has occured.
-     * - Interface elements are registered on page load
-     * - Parent Interface elements may not be loaded before child, so this handles that..
-     * 
-     *
-     * example:
-     * 
-     * MyApp.register({
-          order : '000001',
-          module : 'Pman.Tab.projectMgr',
-          region : 'center',
-          parent : 'Pman.layout',
-          disabled : false,  // or use a function..
-        })
-     
-     * * @param {Object} details about module
-     */
-    register : function(obj) {
-               
-        Roo.XComponent.event.fireEvent('register', obj);
-        switch(typeof(obj.disabled) ) {
-                
-            case 'undefined':
-                break;
-            
-            case 'function':
-                if ( obj.disabled() ) {
-                        return;
+    hideShim : function(){
+        if(this.shim){
+            this.shim.setDisplayed(false);
+            shims.push(this.shim);
+            delete this.shim;
+        }
+    },
+
+    disableShadow : function(){
+        if(this.shadow){
+            this.shadowDisabled = true;
+            this.shadow.hide();
+            this.lastShadowOffset = this.shadowOffset;
+            this.shadowOffset = 0;
+        }
+    },
+
+    enableShadow : function(show){
+        if(this.shadow){
+            this.shadowDisabled = false;
+            this.shadowOffset = this.lastShadowOffset;
+            delete this.lastShadowOffset;
+            if(show){
+                this.sync(true);
+            }
+        }
+    },
+
+    // private
+    // this code can execute repeatedly in milliseconds (i.e. during a drag) so
+    // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
+    sync : function(doShow){
+        var sw = this.shadow;
+        if(!this.updating && this.isVisible() && (sw || this.useShim)){
+            var sh = this.getShim();
+
+            var w = this.getWidth(),
+                h = this.getHeight();
+
+            var l = this.getLeft(true),
+                t = this.getTop(true);
+
+            if(sw && !this.shadowDisabled){
+                if(doShow && !sw.isVisible()){
+                    sw.show(this);
+                }else{
+                    sw.realign(l, t, w, h);
                 }
-                break;
-            
-            default:
-                if (obj.disabled || obj.region == '#disabled') {
-                        return;
+                if(sh){
+                    if(doShow){
+                       sh.show();
+                    }
+                    // fit the shim behind the shadow, so it is shimmed too
+                    var a = sw.adjusts, s = sh.dom.style;
+                    s.left = (Math.min(l, l+a.l))+"px";
+                    s.top = (Math.min(t, t+a.t))+"px";
+                    s.width = (w+a.w)+"px";
+                    s.height = (h+a.h)+"px";
                 }
-                break;
+            }else if(sh){
+                if(doShow){
+                   sh.show();
+                }
+                sh.setSize(w, h);
+                sh.setLeftTop(l, t);
+            }
+            
         }
-               
-        this.modules.push(obj);
-         
     },
-    /**
-     * convert a string to an object..
-     * eg. 'AAA.BBB' -> finds AAA.BBB
 
-     */
-    
-    toObject : function(str)
-    {
-        if (!str || typeof(str) == 'object') {
-            return str;
+    // private
+    destroy : function(){
+        this.hideShim();
+        if(this.shadow){
+            this.shadow.hide();
         }
-        if (str.substring(0,1) == '#') {
-            return str;
+        this.removeAllListeners();
+        var pn = this.dom.parentNode;
+        if(pn){
+            pn.removeChild(this.dom);
         }
+        Roo.Element.uncache(this.id);
+    },
 
-        var ar = str.split('.');
-        var rt, o;
-        rt = ar.shift();
-            /** eval:var:o */
-        try {
-            eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
-        } catch (e) {
-            throw "Module not found : " + str;
-        }
-        
-        if (o === false) {
-            throw "Module not found : " + str;
+    remove : function(){
+        this.destroy();
+    },
+
+    // private
+    beginUpdate : function(){
+        this.updating = true;
+    },
+
+    // private
+    endUpdate : function(){
+        this.updating = false;
+        this.sync(true);
+    },
+
+    // private
+    hideUnders : function(negOffset){
+        if(this.shadow){
+            this.shadow.hide();
         }
-        Roo.each(ar, function(e) {
-            if (typeof(o[e]) == 'undefined') {
-                throw "Module not found : " + str;
-            }
-            o = o[e];
-        });
-        
-        return o;
-        
+        this.hideShim();
     },
-    
-    
-    /**
-     * move modules into their correct place in the tree..
-     * 
-     */
-    preBuild : function ()
-    {
-        var _t = this;
-        Roo.each(this.modules , function (obj)
-        {
-            Roo.XComponent.event.fireEvent('beforebuild', obj);
-            
-            var opar = obj.parent;
-            try { 
-                obj.parent = this.toObject(opar);
-            } catch(e) {
-                Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
-                return;
-            }
-            
-            if (!obj.parent) {
-                Roo.debug && Roo.log("GOT top level module");
-                Roo.debug && Roo.log(obj);
-                obj.modules = new Roo.util.MixedCollection(false, 
-                    function(o) { return o.order + '' }
-                );
-                this.topModule = obj;
-                return;
+
+    // private
+    constrainXY : function(){
+        if(this.constrain){
+            var vw = Roo.lib.Dom.getViewWidth(),
+                vh = Roo.lib.Dom.getViewHeight();
+            var s = Roo.get(document).getScroll();
+
+            var xy = this.getXY();
+            var x = xy[0], y = xy[1];   
+            var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
+            // only move it if it needs it
+            var moved = false;
+            // first validate right/bottom
+            if((x + w) > vw+s.left){
+                x = vw - w - this.shadowOffset;
+                moved = true;
             }
-                       // parent is a string (usually a dom element name..)
-            if (typeof(obj.parent) == 'string') {
-                this.elmodules.push(obj);
-                return;
+            if((y + h) > vh+s.top){
+                y = vh - h - this.shadowOffset;
+                moved = true;
             }
-            if (obj.parent.constructor != Roo.XComponent) {
-                Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
+            // then make sure top/left isn't negative
+            if(x < s.left){
+                x = s.left;
+                moved = true;
             }
-            if (!obj.parent.modules) {
-                obj.parent.modules = new Roo.util.MixedCollection(false, 
-                    function(o) { return o.order + '' }
-                );
+            if(y < s.top){
+                y = s.top;
+                moved = true;
             }
-            if (obj.parent.disabled) {
-                obj.disabled = true;
+            if(moved){
+                if(this.avoidY){
+                    var ay = this.avoidY;
+                    if(y <= ay && (y+h) >= ay){
+                        y = ay-h-5;   
+                    }
+                }
+                xy = [x, y];
+                this.storeXY(xy);
+                supr.setXY.call(this, xy);
+                this.sync();
             }
-            obj.parent.modules.add(obj);
-        }, this);
-    },
-    
-     /**
-     * make a list of modules to build.
-     * @return {Array} list of modules. 
-     */ 
-    
-    buildOrder : function()
-    {
-        var _this = this;
-        var cmp = function(a,b) {   
-            return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
-        };
-        if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
-            throw "No top level modules to build";
         }
-        
-        // make a flat list in order of modules to build.
-        var mods = this.topModule ? [ this.topModule ] : [];
-               
-        
-       // elmodules (is a list of DOM based modules )
-        Roo.each(this.elmodules, function(e) {
-            mods.push(e);
-            if (!this.topModule &&
-                typeof(e.parent) == 'string' &&
-                e.parent.substring(0,1) == '#' &&
-                Roo.get(e.parent.substr(1))
-               ) {
-                
-                _this.topModule = e;
-            }
-            
-        });
+    },
 
-        
-        // add modules to their parents..
-        var addMod = function(m) {
-            Roo.debug && Roo.log("build Order: add: " + m.name);
-                
-            mods.push(m);
-            if (m.modules && !m.disabled) {
-                Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
-                m.modules.keySort('ASC',  cmp );
-                Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
-    
-                m.modules.each(addMod);
-            } else {
-                Roo.debug && Roo.log("build Order: no child modules");
-            }
-            // not sure if this is used any more..
-            if (m.finalize) {
-                m.finalize.name = m.name + " (clean up) ";
-                mods.push(m.finalize);
-            }
-            
-        }
-        if (this.topModule && this.topModule.modules) { 
-            this.topModule.modules.keySort('ASC',  cmp );
-            this.topModule.modules.each(addMod);
-        } 
-        return mods;
+    isVisible : function(){
+        return this.visible;    
     },
-    
-     /**
-     * Build the registered modules.
-     * @param {Object} parent element.
-     * @param {Function} optional method to call after module has been added.
-     * 
-     */ 
-   
-    build : function(opts) 
-    {
-        
-        if (typeof(opts) != 'undefined') {
-            Roo.apply(this,opts);
+
+    // private
+    showAction : function(){
+        this.visible = true; // track visibility to prevent getStyle calls
+        if(this.useDisplay === true){
+            this.setDisplayed("");
+        }else if(this.lastXY){
+            supr.setXY.call(this, this.lastXY);
+        }else if(this.lastLT){
+            supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
         }
-        
-        this.preBuild();
-        var mods = this.buildOrder();
-      
-        //this.allmods = mods;
-        //Roo.debug && Roo.log(mods);
-        //return;
-        if (!mods.length) { // should not happen
-            throw "NO modules!!!";
+    },
+
+    // private
+    hideAction : function(){
+        this.visible = false;
+        if(this.useDisplay === true){
+            this.setDisplayed(false);
+        }else{
+            this.setLeftTop(-10000,-10000);
         }
-        
-        
-        var msg = "Building Interface...";
-        // flash it up as modal - so we store the mask!?
-        if (!this.hideProgress && Roo.MessageBox) {
-            Roo.MessageBox.show({ title: 'loading' });
-            Roo.MessageBox.show({
-               title: "Please wait...",
-               msg: msg,
-               width:450,
-               progress:true,
-              buttons : false,
-               closable:false,
-               modal: false
-              
-            });
+    },
+
+    // overridden Element method
+    setVisible : function(v, a, d, c, e){
+        if(v){
+            this.showAction();
         }
-        var total = mods.length;
-        
-        var _this = this;
-        var progressRun = function() {
-            if (!mods.length) {
-                Roo.debug && Roo.log('hide?');
-                if (!this.hideProgress && Roo.MessageBox) {
-                    Roo.MessageBox.hide();
+        if(a && v){
+            var cb = function(){
+                this.sync(true);
+                if(c){
+                    c();
                 }
-                Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
-                
-                Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
-                
-                // THE END...
-                return false;   
+            }.createDelegate(this);
+            supr.setVisible.call(this, true, true, d, cb, e);
+        }else{
+            if(!v){
+                this.hideUnders(true);
             }
-            
-            var m = mods.shift();
-            
-            
-            Roo.debug && Roo.log(m);
-            // not sure if this is supported any more.. - modules that are are just function
-            if (typeof(m) == 'function') { 
-                m.call(this);
-                return progressRun.defer(10, _this);
-            } 
-            
-            
-            msg = "Building Interface " + (total  - mods.length) + 
-                    " of " + total + 
-                    (m.name ? (' - ' + m.name) : '');
-                       Roo.debug && Roo.log(msg);
-            if (!_this.hideProgress &&  Roo.MessageBox) { 
-                Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
+            var cb = c;
+            if(a){
+                cb = function(){
+                    this.hideAction();
+                    if(c){
+                        c();
+                    }
+                }.createDelegate(this);
             }
-            
-         
-            // is the module disabled?
-            var disabled = (typeof(m.disabled) == 'function') ?
-                m.disabled.call(m.module.disabled) : m.disabled;    
-            
-            
-            if (disabled) {
-                return progressRun(); // we do not update the display!
+            supr.setVisible.call(this, v, a, d, cb, e);
+            if(v){
+                this.sync(true);
+            }else if(!a){
+                this.hideAction();
             }
-            
-            // now build 
-            
-                       
-                       
-            m.render();
-            // it's 10 on top level, and 1 on others??? why...
-            return progressRun.defer(10, _this);
-             
         }
-        progressRun.defer(1, _this);
-     
-        
-        
     },
-    /**
-     * Overlay a set of modified strings onto a component
-     * This is dependant on our builder exporting the strings and 'named strings' elements.
-     * 
-     * @param {Object} element to overlay on - eg. Pman.Dialog.Login
-     * @param {Object} associative array of 'named' string and it's new value.
-     * 
-     */
-       overlayStrings : function( component, strings )
-    {
-        if (typeof(component['_named_strings']) == 'undefined') {
-            throw "ERROR: component does not have _named_strings";
+
+    storeXY : function(xy){
+        delete this.lastLT;
+        this.lastXY = xy;
+    },
+
+    storeLeftTop : function(left, top){
+        delete this.lastXY;
+        this.lastLT = [left, top];
+    },
+
+    // private
+    beforeFx : function(){
+        this.beforeAction();
+        return Roo.Layer.superclass.beforeFx.apply(this, arguments);
+    },
+
+    // private
+    afterFx : function(){
+        Roo.Layer.superclass.afterFx.apply(this, arguments);
+        this.sync(this.isVisible());
+    },
+
+    // private
+    beforeAction : function(){
+        if(!this.updating && this.shadow){
+            this.shadow.hide();
         }
-        for ( var k in strings ) {
-            var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
-            if (md !== false) {
-                component['_strings'][md] = strings[k];
-            } else {
-                Roo.log('could not find named string: ' + k + ' in');
-                Roo.log(component);
+    },
+
+    // overridden Element method
+    setLeft : function(left){
+        this.storeLeftTop(left, this.getTop(true));
+        supr.setLeft.apply(this, arguments);
+        this.sync();
+    },
+
+    setTop : function(top){
+        this.storeLeftTop(this.getLeft(true), top);
+        supr.setTop.apply(this, arguments);
+        this.sync();
+    },
+
+    setLeftTop : function(left, top){
+        this.storeLeftTop(left, top);
+        supr.setLeftTop.apply(this, arguments);
+        this.sync();
+    },
+
+    setXY : function(xy, a, d, c, e){
+        this.fixDisplay();
+        this.beforeAction();
+        this.storeXY(xy);
+        var cb = this.createCB(c);
+        supr.setXY.call(this, xy, a, d, cb, e);
+        if(!a){
+            cb();
+        }
+    },
+
+    // private
+    createCB : function(c){
+        var el = this;
+        return function(){
+            el.constrainXY();
+            el.sync(true);
+            if(c){
+                c();
             }
-            
+        };
+    },
+
+    // overridden Element method
+    setX : function(x, a, d, c, e){
+        this.setXY([x, this.getY()], a, d, c, e);
+    },
+
+    // overridden Element method
+    setY : function(y, a, d, c, e){
+        this.setXY([this.getX(), y], a, d, c, e);
+    },
+
+    // overridden Element method
+    setSize : function(w, h, a, d, c, e){
+        this.beforeAction();
+        var cb = this.createCB(c);
+        supr.setSize.call(this, w, h, a, d, cb, e);
+        if(!a){
+            cb();
         }
-        
+    },
+
+    // overridden Element method
+    setWidth : function(w, a, d, c, e){
+        this.beforeAction();
+        var cb = this.createCB(c);
+        supr.setWidth.call(this, w, a, d, cb, e);
+        if(!a){
+            cb();
+        }
+    },
+
+    // overridden Element method
+    setHeight : function(h, a, d, c, e){
+        this.beforeAction();
+        var cb = this.createCB(c);
+        supr.setHeight.call(this, h, a, d, cb, e);
+        if(!a){
+            cb();
+        }
+    },
+
+    // overridden Element method
+    setBounds : function(x, y, w, h, a, d, c, e){
+        this.beforeAction();
+        var cb = this.createCB(c);
+        if(!a){
+            this.storeXY([x, y]);
+            supr.setXY.call(this, [x, y]);
+            supr.setSize.call(this, w, h, a, d, cb, e);
+            cb();
+        }else{
+            supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
+        }
+        return this;
     },
     
-       
-       /**
-        * Event Object.
-        *
-        *
-        */
-       event: false, 
     /**
-        * wrapper for event.on - aliased later..  
-        * Typically use to register a event handler for register:
-        *
-        * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
-        *
-        */
-    on : false
-   
-    
-    
-});
-
-Roo.XComponent.event = new Roo.util.Observable({
-               events : { 
-                       /**
-                        * @event register
-                        * Fires when an Component is registered,
-                        * set the disable property on the Component to stop registration.
-                        * @param {Roo.XComponent} c the component being registerd.
-                        * 
-                        */
-                       'register' : true,
-            /**
-                        * @event beforebuild
-                        * Fires before each Component is built
-                        * can be used to apply permissions.
-                        * @param {Roo.XComponent} c the component being registerd.
-                        * 
-                        */
-                       'beforebuild' : true,
-                       /**
-                        * @event buildcomplete
-                        * Fires on the top level element when all elements have been built
-                        * @param {Roo.XComponent} the top level component.
-                        */
-                       'buildcomplete' : true
-                       
-               }
+     * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
+     * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
+     * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
+     * @param {Number} zindex The new z-index to set
+     * @return {this} The Layer
+     */
+    setZIndex : function(zindex){
+        this.zindex = zindex;
+        this.setStyle("z-index", zindex + 2);
+        if(this.shadow){
+            this.shadow.setZIndex(zindex + 1);
+        }
+        if(this.shim){
+            this.shim.setStyle("z-index", zindex);
+        }
+    }
 });
-
-Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
- //
- /**
- * marked - a markdown parser
- * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
- * https://github.com/chjj/marked
+})();/*
+ * Original code for Roojs - LGPL
+ * <script type="text/javascript">
  */
-
-
 /**
+ * @class Roo.XComponent
+ * A delayed Element creator...
+ * Or a way to group chunks of interface together.
+ * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
+ *  used in conjunction with XComponent.build() it will create an instance of each element,
+ *  then call addxtype() to build the User interface.
+ * 
+ * Mypart.xyx = new Roo.XComponent({
+
+    parent : 'Mypart.xyz', // empty == document.element.!!
+    order : '001',
+    name : 'xxxx'
+    region : 'xxxx'
+    disabled : function() {} 
+     
+    tree : function() { // return an tree of xtype declared components
+        var MODULE = this;
+        return 
+        {
+            xtype : 'NestedLayoutPanel',
+            // technicall
+        }
+     ]
+ *})
  *
- * Roo.Markdown - is a very crude wrapper around marked..
  *
- * usage:
+ * It can be used to build a big heiracy, with parent etc.
+ * or you can just use this to render a single compoent to a dom element
+ * MYPART.render(Roo.Element | String(id) | dom_element )
+ *
+ *
+ * Usage patterns.
+ *
+ * Classic Roo
+ *
+ * Roo is designed primarily as a single page application, so the UI build for a standard interface will
+ * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
+ *
+ * Each sub module is expected to have a parent pointing to the class name of it's parent module.
+ *
+ * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
+ * - if mulitple topModules exist, the last one is defined as the top module.
+ *
+ * Embeded Roo
  * 
- * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
+ * When the top level or multiple modules are to embedded into a existing HTML page,
+ * the parent element can container '#id' of the element where the module will be drawn.
+ *
+ * Bootstrap Roo
+ *
+ * Unlike classic Roo, the bootstrap tends not to be used as a single page.
+ * it relies more on a include mechanism, where sub modules are included into an outer page.
+ * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
  * 
- * Note: move the sample code to the bottom of this
- * file before uncommenting it.
+ * Bootstrap Roo Included elements
+ *
+ * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
+ * hence confusing the component builder as it thinks there are multiple top level elements. 
+ *
+ * String Over-ride & Translations
  *
+ * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
+ * and also the 'overlaying of string values - needed when different versions of the same application with different text content
+ * are needed. @see Roo.XComponent.overlayString  
+ * 
+ * 
+ * 
+ * @extends Roo.util.Observable
+ * @constructor
+ * @param cfg {Object} configuration of component
+ * 
  */
-
-Roo.Markdown = {};
-Roo.Markdown.toHtml = function(text) {
-    
-    var c = new Roo.Markdown.marked.setOptions({
-            renderer: new Roo.Markdown.marked.Renderer(),
-            gfm: true,
-            tables: true,
-            breaks: false,
-            pedantic: false,
-            sanitize: false,
-            smartLists: true,
-            smartypants: false
-          });
-    // A FEW HACKS!!?
+Roo.XComponent = function(cfg) {
+    Roo.apply(this, cfg);
+    this.addEvents({ 
+        /**
+            * @event built
+            * Fires when this the componnt is built
+            * @param {Roo.XComponent} c the component
+            */
+        'built' : true
+        
+    });
+    this.region = this.region || 'center'; // default..
+    Roo.XComponent.register(this);
+    this.modules = false;
+    this.el = false; // where the layout goes..
     
-    text = text.replace(/\\\n/g,' ');
-    return Roo.Markdown.marked(text);
-};
-//
-// converter
-//
-// Wraps all "globals" so that the only thing
-// exposed is makeHtml().
-//
-(function() {
     
-     /**
-         * eval:var:escape
-         * eval:var:unescape
-         * eval:var:replace
-         */
-      
+}
+Roo.extend(Roo.XComponent, Roo.util.Observable, {
     /**
-     * Helpers
+     * @property el
+     * The created element (with Roo.factory())
+     * @type {Roo.Layout}
      */
-    
-    var escape = function (html, encode) {
-      return html
-        .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
-        .replace(/</g, '&lt;')
-        .replace(/>/g, '&gt;')
-        .replace(/"/g, '&quot;')
-        .replace(/'/g, '&#39;');
-    }
-    
-    var unescape = function (html) {
-        // explicitly match decimal, hex, and named HTML entities 
-      return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
-        n = n.toLowerCase();
-        if (n === 'colon') { return ':'; }
-        if (n.charAt(0) === '#') {
-          return n.charAt(1) === 'x'
-            ? String.fromCharCode(parseInt(n.substring(2), 16))
-            : String.fromCharCode(+n.substring(1));
-        }
-        return '';
-      });
-    }
-    
-    var replace = function (regex, opt) {
-      regex = regex.source;
-      opt = opt || '';
-      return function self(name, val) {
-        if (!name) { return new RegExp(regex, opt); }
-        val = val.source || val;
-        val = val.replace(/(^|[^\[])\^/g, '$1');
-        regex = regex.replace(name, val);
-        return self;
-      };
-    }
-
-
-         /**
-         * eval:var:noop
-    */
-    var noop = function () {}
-    noop.exec = noop;
-    
-         /**
-         * eval:var:merge
-    */
-    var merge = function (obj) {
-      var i = 1
-        , target
-        , key;
-    
-      for (; i < arguments.length; i++) {
-        target = arguments[i];
-        for (key in target) {
-          if (Object.prototype.hasOwnProperty.call(target, key)) {
-            obj[key] = target[key];
-          }
-        }
-      }
-    
-      return obj;
-    }
-    
+    el  : false,
     
     /**
-     * Block-Level Grammar
+     * @property el
+     * for BC  - use el in new code
+     * @type {Roo.Layout}
      */
-    
-    
-    
-    
-    var block = {
-      newline: /^\n+/,
-      code: /^( {4}[^\n]+\n*)+/,
-      fences: noop,
-      hr: /^( *[-*_]){3,} *(?:\n+|$)/,
-      heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
-      nptable: noop,
-      lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
-      blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
-      list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
-      html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
-      def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
-      table: noop,
-      paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
-      text: /^[^\n]+/
-    };
-    
-    block.bullet = /(?:[*+-]|\d+\.)/;
-    block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
-    block.item = replace(block.item, 'gm')
-      (/bull/g, block.bullet)
-      ();
-    
-    block.list = replace(block.list)
-      (/bull/g, block.bullet)
-      ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
-      ('def', '\\n+(?=' + block.def.source + ')')
-      ();
-    
-    block.blockquote = replace(block.blockquote)
-      ('def', block.def)
-      ();
-    
-    block._tag = '(?!(?:'
-      + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
-      + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
-      + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
-    
-    block.html = replace(block.html)
-      ('comment', /<!--[\s\S]*?-->/)
-      ('closed', /<(tag)[\s\S]+?<\/\1>/)
-      ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
-      (/tag/g, block._tag)
-      ();
-    
-    block.paragraph = replace(block.paragraph)
-      ('hr', block.hr)
-      ('heading', block.heading)
-      ('lheading', block.lheading)
-      ('blockquote', block.blockquote)
-      ('tag', '<' + block._tag)
-      ('def', block.def)
-      ();
+    panel : false,
     
     /**
-     * Normal Block Grammar
+     * @property layout
+     * for BC  - use el in new code
+     * @type {Roo.Layout}
      */
+    layout : false,
     
-    block.normal = merge({}, block);
+     /**
+     * @cfg {Function|boolean} disabled
+     * If this module is disabled by some rule, return true from the funtion
+     */
+    disabled : false,
     
     /**
-     * GFM Block Grammar
+     * @cfg {String} parent 
+     * Name of parent element which it get xtype added to..
      */
-    
-    block.gfm = merge({}, block.normal, {
-      fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
-      paragraph: /^/,
-      heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
-    });
-    
-    block.gfm.paragraph = replace(block.paragraph)
-      ('(?!', '(?!'
-        + block.gfm.fences.source.replace('\\1', '\\2') + '|'
-        + block.list.source.replace('\\1', '\\3') + '|')
-      ();
+    parent: false,
     
     /**
-     * GFM + Tables Block Grammar
+     * @cfg {String} order
+     * Used to set the order in which elements are created (usefull for multiple tabs)
      */
     
-    block.tables = merge({}, block.gfm, {
-      nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
-      table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
-    });
-    
+    order : false,
     /**
-     * Block Lexer
+     * @cfg {String} name
+     * String to display while loading.
+     */
+    name : false,
+    /**
+     * @cfg {String} region
+     * Region to render component to (defaults to center)
      */
+    region : 'center',
     
-    var Lexer = function (options) {
-      this.tokens = [];
-      this.tokens.links = {};
-      this.options = options || marked.defaults;
-      this.rules = block.normal;
+    /**
+     * @cfg {Array} items
+     * A single item array - the first element is the root of the tree..
+     * It's done this way to stay compatible with the Xtype system...
+     */
+    items : false,
     
-      if (this.options.gfm) {
-        if (this.options.tables) {
-          this.rules = block.tables;
-        } else {
-          this.rules = block.gfm;
+    /**
+     * @property _tree
+     * The method that retuns the tree of parts that make up this compoennt 
+     * @type {function}
+     */
+    _tree  : false,
+    
+     /**
+     * render
+     * render element to dom or tree
+     * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
+     */
+    
+    render : function(el)
+    {
+        
+        el = el || false;
+        var hp = this.parent ? 1 : 0;
+        Roo.debug &&  Roo.log(this);
+        
+        var tree = this._tree ? this._tree() : this.tree();
+
+        
+        if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
+            // if parent is a '#.....' string, then let's use that..
+            var ename = this.parent.substr(1);
+            this.parent = false;
+            Roo.debug && Roo.log(ename);
+            switch (ename) {
+                case 'bootstrap-body':
+                    if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
+                        // this is the BorderLayout standard?
+                       this.parent = { el : true };
+                       break;
+                    }
+                    if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
+                        // need to insert stuff...
+                        this.parent =  {
+                             el : new Roo.bootstrap.layout.Border({
+                                 el : document.body, 
+                     
+                                 center: {
+                                    titlebar: false,
+                                    autoScroll:false,
+                                    closeOnTab: true,
+                                    tabPosition: 'top',
+                                      //resizeTabs: true,
+                                    alwaysShowTabs: true,
+                                    hideTabs: false
+                                     //minTabWidth: 140
+                                 }
+                             })
+                        
+                         };
+                         break;
+                    }
+                         
+                    if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
+                        this.parent = { el :  new  Roo.bootstrap.Body() };
+                        Roo.debug && Roo.log("setting el to doc body");
+                         
+                    } else {
+                        throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
+                    }
+                    break;
+                case 'bootstrap':
+                    this.parent = { el : true};
+                    // fall through
+                default:
+                    el = Roo.get(ename);
+                    if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
+                        this.parent = { el : true};
+                    }
+                    
+                    break;
+            }
+                
+            
+            if (!el && !this.parent) {
+                Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
+                return;
+            }
         }
-      }
+        
+        Roo.debug && Roo.log("EL:");
+        Roo.debug && Roo.log(el);
+        Roo.debug && Roo.log("this.parent.el:");
+        Roo.debug && Roo.log(this.parent.el);
+        
+
+        // altertive root elements ??? - we need a better way to indicate these.
+        var is_alt = Roo.XComponent.is_alt ||
+                    (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
+                    (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
+                    (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
+        
+        
+        
+        if (!this.parent && is_alt) {
+            //el = Roo.get(document.body);
+            this.parent = { el : true };
+        }
+            
+            
+        
+        if (!this.parent) {
+            
+            Roo.debug && Roo.log("no parent - creating one");
+            
+            el = el ? Roo.get(el) : false;     
+            
+            if (typeof(Roo.BorderLayout) == 'undefined' ) {
+                
+                this.parent =  {
+                    el : new Roo.bootstrap.layout.Border({
+                        el: el || document.body,
+                    
+                        center: {
+                            titlebar: false,
+                            autoScroll:false,
+                            closeOnTab: true,
+                            tabPosition: 'top',
+                             //resizeTabs: true,
+                            alwaysShowTabs: false,
+                            hideTabs: true,
+                            minTabWidth: 140,
+                            overflow: 'visible'
+                         }
+                     })
+                };
+            } else {
+            
+                // it's a top level one..
+                this.parent =  {
+                    el : new Roo.BorderLayout(el || document.body, {
+                        center: {
+                            titlebar: false,
+                            autoScroll:false,
+                            closeOnTab: true,
+                            tabPosition: 'top',
+                             //resizeTabs: true,
+                            alwaysShowTabs: el && hp? false :  true,
+                            hideTabs: el || !hp ? true :  false,
+                            minTabWidth: 140
+                         }
+                    })
+                };
+            }
+        }
+        
+        if (!this.parent.el) {
+                // probably an old style ctor, which has been disabled.
+                return;
+
+        }
+               // The 'tree' method is  '_tree now' 
+            
+        tree.region = tree.region || this.region;
+        var is_body = false;
+        if (this.parent.el === true) {
+            // bootstrap... - body..
+            if (el) {
+                tree.el = el;
+            }
+            this.parent.el = Roo.factory(tree);
+            is_body = true;
+        }
+        
+        this.el = this.parent.el.addxtype(tree, undefined, is_body);
+        this.fireEvent('built', this);
+        
+        this.panel = this.el;
+        this.layout = this.panel.layout;
+        this.parentLayout = this.parent.layout  || false;  
+         
     }
     
+});
+
+Roo.apply(Roo.XComponent, {
     /**
-     * Expose Block Rules
+     * @property  hideProgress
+     * true to disable the building progress bar.. usefull on single page renders.
+     * @type Boolean
      */
-    
-    Lexer.rules = block;
-    
+    hideProgress : false,
     /**
-     * Static Lex Method
+     * @property  buildCompleted
+     * True when the builder has completed building the interface.
+     * @type Boolean
      */
-    
-    Lexer.lex = function(src, options) {
-      var lexer = new Lexer(options);
-      return lexer.lex(src);
-    };
-    
+    buildCompleted : false,
+     
     /**
-     * Preprocessing
+     * @property  topModule
+     * the upper most module - uses document.element as it's constructor.
+     * @type Object
      */
-    
-    Lexer.prototype.lex = function(src) {
-      src = src
-        .replace(/\r\n|\r/g, '\n')
-        .replace(/\t/g, '    ')
-        .replace(/\u00a0/g, ' ')
-        .replace(/\u2424/g, '\n');
-    
-      return this.token(src, true);
-    };
-    
+     
+    topModule  : false,
+      
     /**
-     * Lexing
+     * @property  modules
+     * array of modules to be created by registration system.
+     * @type {Array} of Roo.XComponent
      */
     
-    Lexer.prototype.token = function(src, top, bq) {
-      var src = src.replace(/^ +$/gm, '')
-        , next
-        , loose
-        , cap
-        , bull
-        , b
-        , item
-        , space
-        , i
-        , l;
-    
-      while (src) {
-        // newline
-        if (cap = this.rules.newline.exec(src)) {
-          src = src.substring(cap[0].length);
-          if (cap[0].length > 1) {
-            this.tokens.push({
-              type: 'space'
-            });
-          }
+    modules : [],
+    /**
+     * @property  elmodules
+     * array of modules to be created by which use #ID 
+     * @type {Array} of Roo.XComponent
+     */
+     
+    elmodules : [],
+
+     /**
+     * @property  is_alt
+     * Is an alternative Root - normally used by bootstrap or other systems,
+     *    where the top element in the tree can wrap 'body' 
+     * @type {boolean}  (default false)
+     */
+     
+    is_alt : false,
+    /**
+     * @property  build_from_html
+     * Build elements from html - used by bootstrap HTML stuff 
+     *    - this is cleared after build is completed
+     * @type {boolean}    (default false)
+     */
+     
+    build_from_html : false,
+    /**
+     * Register components to be built later.
+     *
+     * This solves the following issues
+     * - Building is not done on page load, but after an authentication process has occured.
+     * - Interface elements are registered on page load
+     * - Parent Interface elements may not be loaded before child, so this handles that..
+     * 
+     *
+     * example:
+     * 
+     * MyApp.register({
+          order : '000001',
+          module : 'Pman.Tab.projectMgr',
+          region : 'center',
+          parent : 'Pman.layout',
+          disabled : false,  // or use a function..
+        })
+     
+     * * @param {Object} details about module
+     */
+    register : function(obj) {
+               
+        Roo.XComponent.event.fireEvent('register', obj);
+        switch(typeof(obj.disabled) ) {
+                
+            case 'undefined':
+                break;
+            
+            case 'function':
+                if ( obj.disabled() ) {
+                        return;
+                }
+                break;
+            
+            default:
+                if (obj.disabled || obj.region == '#disabled') {
+                        return;
+                }
+                break;
         }
+               
+        this.modules.push(obj);
+         
+    },
+    /**
+     * convert a string to an object..
+     * eg. 'AAA.BBB' -> finds AAA.BBB
+
+     */
     
-        // code
-        if (cap = this.rules.code.exec(src)) {
-          src = src.substring(cap[0].length);
-          cap = cap[0].replace(/^ {4}/gm, '');
-          this.tokens.push({
-            type: 'code',
-            text: !this.options.pedantic
-              ? cap.replace(/\n+$/, '')
-              : cap
-          });
-          continue;
+    toObject : function(str)
+    {
+        if (!str || typeof(str) == 'object') {
+            return str;
         }
-    
-        // fences (gfm)
-        if (cap = this.rules.fences.exec(src)) {
-          src = src.substring(cap[0].length);
-          this.tokens.push({
-            type: 'code',
-            lang: cap[2],
-            text: cap[3] || ''
-          });
-          continue;
+        if (str.substring(0,1) == '#') {
+            return str;
         }
-    
-        // heading
-        if (cap = this.rules.heading.exec(src)) {
-          src = src.substring(cap[0].length);
-          this.tokens.push({
-            type: 'heading',
-            depth: cap[1].length,
-            text: cap[2]
-          });
-          continue;
+
+        var ar = str.split('.');
+        var rt, o;
+        rt = ar.shift();
+            /** eval:var:o */
+        try {
+            eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
+        } catch (e) {
+            throw "Module not found : " + str;
         }
-    
-        // table no leading pipe (gfm)
-        if (top && (cap = this.rules.nptable.exec(src))) {
-          src = src.substring(cap[0].length);
-    
-          item = {
-            type: 'table',
-            header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
-            align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
-            cells: cap[3].replace(/\n$/, '').split('\n')
-          };
-    
-          for (i = 0; i < item.align.length; i++) {
-            if (/^ *-+: *$/.test(item.align[i])) {
-              item.align[i] = 'right';
-            } else if (/^ *:-+: *$/.test(item.align[i])) {
-              item.align[i] = 'center';
-            } else if (/^ *:-+ *$/.test(item.align[i])) {
-              item.align[i] = 'left';
-            } else {
-              item.align[i] = null;
+        
+        if (o === false) {
+            throw "Module not found : " + str;
+        }
+        Roo.each(ar, function(e) {
+            if (typeof(o[e]) == 'undefined') {
+                throw "Module not found : " + str;
             }
-          }
+            o = o[e];
+        });
+        
+        return o;
+        
+    },
     
-          for (i = 0; i < item.cells.length; i++) {
-            item.cells[i] = item.cells[i].split(/ *\| */);
-          }
     
-          this.tokens.push(item);
+    /**
+     * move modules into their correct place in the tree..
+     * 
+     */
+    preBuild : function ()
+    {
+        var _t = this;
+        Roo.each(this.modules , function (obj)
+        {
+            Roo.XComponent.event.fireEvent('beforebuild', obj);
+            
+            var opar = obj.parent;
+            try { 
+                obj.parent = this.toObject(opar);
+            } catch(e) {
+                Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
+                return;
+            }
+            
+            if (!obj.parent) {
+                Roo.debug && Roo.log("GOT top level module");
+                Roo.debug && Roo.log(obj);
+                obj.modules = new Roo.util.MixedCollection(false, 
+                    function(o) { return o.order + '' }
+                );
+                this.topModule = obj;
+                return;
+            }
+                       // parent is a string (usually a dom element name..)
+            if (typeof(obj.parent) == 'string') {
+                this.elmodules.push(obj);
+                return;
+            }
+            if (obj.parent.constructor != Roo.XComponent) {
+                Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
+            }
+            if (!obj.parent.modules) {
+                obj.parent.modules = new Roo.util.MixedCollection(false, 
+                    function(o) { return o.order + '' }
+                );
+            }
+            if (obj.parent.disabled) {
+                obj.disabled = true;
+            }
+            obj.parent.modules.add(obj);
+        }, this);
+    },
     
-          continue;
-        }
+     /**
+     * make a list of modules to build.
+     * @return {Array} list of modules. 
+     */ 
     
-        // lheading
-        if (cap = this.rules.lheading.exec(src)) {
-          src = src.substring(cap[0].length);
-          this.tokens.push({
-            type: 'heading',
-            depth: cap[2] === '=' ? 1 : 2,
-            text: cap[1]
-          });
-          continue;
+    buildOrder : function()
+    {
+        var _this = this;
+        var cmp = function(a,b) {   
+            return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
+        };
+        if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
+            throw "No top level modules to build";
         }
+        
+        // make a flat list in order of modules to build.
+        var mods = this.topModule ? [ this.topModule ] : [];
+               
+        
+       // elmodules (is a list of DOM based modules )
+        Roo.each(this.elmodules, function(e) {
+            mods.push(e);
+            if (!this.topModule &&
+                typeof(e.parent) == 'string' &&
+                e.parent.substring(0,1) == '#' &&
+                Roo.get(e.parent.substr(1))
+               ) {
+                
+                _this.topModule = e;
+            }
+            
+        });
+
+        
+        // add modules to their parents..
+        var addMod = function(m) {
+            Roo.debug && Roo.log("build Order: add: " + m.name);
+                
+            mods.push(m);
+            if (m.modules && !m.disabled) {
+                Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
+                m.modules.keySort('ASC',  cmp );
+                Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
     
-        // hr
-        if (cap = this.rules.hr.exec(src)) {
-          src = src.substring(cap[0].length);
-          this.tokens.push({
-            type: 'hr'
-          });
-          continue;
+                m.modules.each(addMod);
+            } else {
+                Roo.debug && Roo.log("build Order: no child modules");
+            }
+            // not sure if this is used any more..
+            if (m.finalize) {
+                m.finalize.name = m.name + " (clean up) ";
+                mods.push(m.finalize);
+            }
+            
         }
+        if (this.topModule && this.topModule.modules) { 
+            this.topModule.modules.keySort('ASC',  cmp );
+            this.topModule.modules.each(addMod);
+        } 
+        return mods;
+    },
     
-        // blockquote
-        if (cap = this.rules.blockquote.exec(src)) {
-          src = src.substring(cap[0].length);
-    
-          this.tokens.push({
-            type: 'blockquote_start'
-          });
-    
-          cap = cap[0].replace(/^ *> ?/gm, '');
-    
-          // Pass `top` to keep the current
-          // "toplevel" state. This is exactly
-          // how markdown.pl works.
-          this.token(cap, top, true);
-    
-          this.tokens.push({
-            type: 'blockquote_end'
-          });
-    
-          continue;
+     /**
+     * Build the registered modules.
+     * @param {Object} parent element.
+     * @param {Function} optional method to call after module has been added.
+     * 
+     */ 
+   
+    build : function(opts) 
+    {
+        
+        if (typeof(opts) != 'undefined') {
+            Roo.apply(this,opts);
         }
-    
-        // list
-        if (cap = this.rules.list.exec(src)) {
-          src = src.substring(cap[0].length);
-          bull = cap[2];
-    
-          this.tokens.push({
-            type: 'list_start',
-            ordered: bull.length > 1
-          });
-    
-          // Get each top-level item.
-          cap = cap[0].match(this.rules.item);
-    
-          next = false;
-          l = cap.length;
-          i = 0;
-    
-          for (; i < l; i++) {
-            item = cap[i];
-    
-            // Remove the list item's bullet
-            // so it is seen as the next token.
-            space = item.length;
-            item = item.replace(/^ *([*+-]|\d+\.) +/, '');
-    
-            // Outdent whatever the
-            // list item contains. Hacky.
-            if (~item.indexOf('\n ')) {
-              space -= item.length;
-              item = !this.options.pedantic
-                ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
-                : item.replace(/^ {1,4}/gm, '');
+        
+        this.preBuild();
+        var mods = this.buildOrder();
+      
+        //this.allmods = mods;
+        //Roo.debug && Roo.log(mods);
+        //return;
+        if (!mods.length) { // should not happen
+            throw "NO modules!!!";
+        }
+        
+        
+        var msg = "Building Interface...";
+        // flash it up as modal - so we store the mask!?
+        if (!this.hideProgress && Roo.MessageBox) {
+            Roo.MessageBox.show({ title: 'loading' });
+            Roo.MessageBox.show({
+               title: "Please wait...",
+               msg: msg,
+               width:450,
+               progress:true,
+              buttons : false,
+               closable:false,
+               modal: false
+              
+            });
+        }
+        var total = mods.length;
+        
+        var _this = this;
+        var progressRun = function() {
+            if (!mods.length) {
+                Roo.debug && Roo.log('hide?');
+                if (!this.hideProgress && Roo.MessageBox) {
+                    Roo.MessageBox.hide();
+                }
+                Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
+                
+                Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
+                
+                // THE END...
+                return false;   
             }
-    
-            // Determine whether the next list item belongs here.
-            // Backpedal if it does not belong in this list.
-            if (this.options.smartLists && i !== l - 1) {
-              b = block.bullet.exec(cap[i + 1])[0];
-              if (bull !== b && !(bull.length > 1 && b.length > 1)) {
-                src = cap.slice(i + 1).join('\n') + src;
-                i = l - 1;
-              }
+            
+            var m = mods.shift();
+            
+            
+            Roo.debug && Roo.log(m);
+            // not sure if this is supported any more.. - modules that are are just function
+            if (typeof(m) == 'function') { 
+                m.call(this);
+                return progressRun.defer(10, _this);
+            } 
+            
+            
+            msg = "Building Interface " + (total  - mods.length) + 
+                    " of " + total + 
+                    (m.name ? (' - ' + m.name) : '');
+                       Roo.debug && Roo.log(msg);
+            if (!_this.hideProgress &&  Roo.MessageBox) { 
+                Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
             }
-    
-            // Determine whether item is loose or not.
-            // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
-            // for discount behavior.
-            loose = next || /\n\n(?!\s*$)/.test(item);
-            if (i !== l - 1) {
-              next = item.charAt(item.length - 1) === '\n';
-              if (!loose) { loose = next; }
+            
+         
+            // is the module disabled?
+            var disabled = (typeof(m.disabled) == 'function') ?
+                m.disabled.call(m.module.disabled) : m.disabled;    
+            
+            
+            if (disabled) {
+                return progressRun(); // we do not update the display!
             }
-    
-            this.tokens.push({
-              type: loose
-                ? 'loose_item_start'
-                : 'list_item_start'
-            });
-    
-            // Recurse.
-            this.token(item, false, bq);
-    
-            this.tokens.push({
-              type: 'list_item_end'
-            });
-          }
-    
-          this.tokens.push({
-            type: 'list_end'
-          });
-    
-          continue;
-        }
-    
-        // html
-        if (cap = this.rules.html.exec(src)) {
-          src = src.substring(cap[0].length);
-          this.tokens.push({
-            type: this.options.sanitize
-              ? 'paragraph'
-              : 'html',
-            pre: !this.options.sanitizer
-              && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
-            text: cap[0]
-          });
-          continue;
+            
+            // now build 
+            
+                       
+                       
+            m.render();
+            // it's 10 on top level, and 1 on others??? why...
+            return progressRun.defer(10, _this);
+             
         }
-    
-        // def
-        if ((!bq && top) && (cap = this.rules.def.exec(src))) {
-          src = src.substring(cap[0].length);
-          this.tokens.links[cap[1].toLowerCase()] = {
-            href: cap[2],
-            title: cap[3]
-          };
-          continue;
+        progressRun.defer(1, _this);
+     
+        
+        
+    },
+    /**
+     * Overlay a set of modified strings onto a component
+     * This is dependant on our builder exporting the strings and 'named strings' elements.
+     * 
+     * @param {Object} element to overlay on - eg. Pman.Dialog.Login
+     * @param {Object} associative array of 'named' string and it's new value.
+     * 
+     */
+       overlayStrings : function( component, strings )
+    {
+        if (typeof(component['_named_strings']) == 'undefined') {
+            throw "ERROR: component does not have _named_strings";
         }
-    
-        // table (gfm)
-        if (top && (cap = this.rules.table.exec(src))) {
-          src = src.substring(cap[0].length);
-    
-          item = {
-            type: 'table',
-            header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
-            align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
-            cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
-          };
-    
-          for (i = 0; i < item.align.length; i++) {
-            if (/^ *-+: *$/.test(item.align[i])) {
-              item.align[i] = 'right';
-            } else if (/^ *:-+: *$/.test(item.align[i])) {
-              item.align[i] = 'center';
-            } else if (/^ *:-+ *$/.test(item.align[i])) {
-              item.align[i] = 'left';
+        for ( var k in strings ) {
+            var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
+            if (md !== false) {
+                component['_strings'][md] = strings[k];
             } else {
-              item.align[i] = null;
+                Roo.log('could not find named string: ' + k + ' in');
+                Roo.log(component);
             }
-          }
-    
-          for (i = 0; i < item.cells.length; i++) {
-            item.cells[i] = item.cells[i]
-              .replace(/^ *\| *| *\| *$/g, '')
-              .split(/ *\| */);
-          }
+            
+        }
+        
+    },
     
-          this.tokens.push(item);
+       
+       /**
+        * Event Object.
+        *
+        *
+        */
+       event: false, 
+    /**
+        * wrapper for event.on - aliased later..  
+        * Typically use to register a event handler for register:
+        *
+        * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
+        *
+        */
+    on : false
+   
     
-          continue;
-        }
     
-        // top-level paragraph
-        if (top && (cap = this.rules.paragraph.exec(src))) {
-          src = src.substring(cap[0].length);
-          this.tokens.push({
-            type: 'paragraph',
-            text: cap[1].charAt(cap[1].length - 1) === '\n'
-              ? cap[1].slice(0, -1)
-              : cap[1]
-          });
-          continue;
-        }
+});
+
+Roo.XComponent.event = new Roo.util.Observable({
+               events : { 
+                       /**
+                        * @event register
+                        * Fires when an Component is registered,
+                        * set the disable property on the Component to stop registration.
+                        * @param {Roo.XComponent} c the component being registerd.
+                        * 
+                        */
+                       'register' : true,
+            /**
+                        * @event beforebuild
+                        * Fires before each Component is built
+                        * can be used to apply permissions.
+                        * @param {Roo.XComponent} c the component being registerd.
+                        * 
+                        */
+                       'beforebuild' : true,
+                       /**
+                        * @event buildcomplete
+                        * Fires on the top level element when all elements have been built
+                        * @param {Roo.XComponent} the top level component.
+                        */
+                       'buildcomplete' : true
+                       
+               }
+});
+
+Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
+ //
+ /**
+ * marked - a markdown parser
+ * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
+ * https://github.com/chjj/marked
+ */
+
+
+/**
+ *
+ * Roo.Markdown - is a very crude wrapper around marked..
+ *
+ * usage:
+ * 
+ * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
+ * 
+ * Note: move the sample code to the bottom of this
+ * file before uncommenting it.
+ *
+ */
+
+Roo.Markdown = {};
+Roo.Markdown.toHtml = function(text) {
     
-        // text
-        if (cap = this.rules.text.exec(src)) {
-          // Top-level should never reach here.
-          src = src.substring(cap[0].length);
-          this.tokens.push({
-            type: 'text',
-            text: cap[0]
+    var c = new Roo.Markdown.marked.setOptions({
+            renderer: new Roo.Markdown.marked.Renderer(),
+            gfm: true,
+            tables: true,
+            breaks: false,
+            pedantic: false,
+            sanitize: false,
+            smartLists: true,
+            smartypants: false
           });
-          continue;
-        }
-    
-        if (src) {
-          throw new
-            Error('Infinite loop on byte: ' + src.charCodeAt(0));
-        }
-      }
+    // A FEW HACKS!!?
     
-      return this.tokens;
-    };
+    text = text.replace(/\\\n/g,' ');
+    return Roo.Markdown.marked(text);
+};
+//
+// converter
+//
+// Wraps all "globals" so that the only thing
+// exposed is makeHtml().
+//
+(function() {
     
+     /**
+         * eval:var:escape
+         * eval:var:unescape
+         * eval:var:replace
+         */
+      
     /**
-     * Inline-Level Grammar
+     * Helpers
      */
     
-    var inline = {
-      escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
-      autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
-      url: noop,
-      tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
-      link: /^!?\[(inside)\]\(href\)/,
-      reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
-      nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
-      strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
-      em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
-      code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
-      br: /^ {2,}\n(?!\s*$)/,
-      del: noop,
-      text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
-    };
+    var escape = function (html, encode) {
+      return html
+        .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
+        .replace(/</g, '&lt;')
+        .replace(/>/g, '&gt;')
+        .replace(/"/g, '&quot;')
+        .replace(/'/g, '&#39;');
+    }
     
-    inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
-    inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
+    var unescape = function (html) {
+        // explicitly match decimal, hex, and named HTML entities 
+      return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
+        n = n.toLowerCase();
+        if (n === 'colon') { return ':'; }
+        if (n.charAt(0) === '#') {
+          return n.charAt(1) === 'x'
+            ? String.fromCharCode(parseInt(n.substring(2), 16))
+            : String.fromCharCode(+n.substring(1));
+        }
+        return '';
+      });
+    }
     
-    inline.link = replace(inline.link)
-      ('inside', inline._inside)
-      ('href', inline._href)
-      ();
+    var replace = function (regex, opt) {
+      regex = regex.source;
+      opt = opt || '';
+      return function self(name, val) {
+        if (!name) { return new RegExp(regex, opt); }
+        val = val.source || val;
+        val = val.replace(/(^|[^\[])\^/g, '$1');
+        regex = regex.replace(name, val);
+        return self;
+      };
+    }
+
+
+         /**
+         * eval:var:noop
+    */
+    var noop = function () {}
+    noop.exec = noop;
     
-    inline.reflink = replace(inline.reflink)
-      ('inside', inline._inside)
-      ();
+         /**
+         * eval:var:merge
+    */
+    var merge = function (obj) {
+      var i = 1
+        , target
+        , key;
     
-    /**
-     * Normal Inline Grammar
-     */
+      for (; i < arguments.length; i++) {
+        target = arguments[i];
+        for (key in target) {
+          if (Object.prototype.hasOwnProperty.call(target, key)) {
+            obj[key] = target[key];
+          }
+        }
+      }
+    
+      return obj;
+    }
     
-    inline.normal = merge({}, inline);
     
     /**
-     * Pedantic Inline Grammar
+     * Block-Level Grammar
      */
     
-    inline.pedantic = merge({}, inline.normal, {
-      strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
-      em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
-    });
     
-    /**
-     * GFM Inline Grammar
-     */
     
-    inline.gfm = merge({}, inline.normal, {
-      escape: replace(inline.escape)('])', '~|])')(),
-      url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
-      del: /^~~(?=\S)([\s\S]*?\S)~~/,
-      text: replace(inline.text)
-        (']|', '~]|')
-        ('|', '|https?://|')
-        ()
-    });
     
-    /**
+    var block = {
+      newline: /^\n+/,
+      code: /^( {4}[^\n]+\n*)+/,
+      fences: noop,
+      hr: /^( *[-*_]){3,} *(?:\n+|$)/,
+      heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
+      nptable: noop,
+      lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
+      blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
+      list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
+      html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
+      def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
+      table: noop,
+      paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
+      text: /^[^\n]+/
+    };
+    
+    block.bullet = /(?:[*+-]|\d+\.)/;
+    block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
+    block.item = replace(block.item, 'gm')
+      (/bull/g, block.bullet)
+      ();
+    
+    block.list = replace(block.list)
+      (/bull/g, block.bullet)
+      ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
+      ('def', '\\n+(?=' + block.def.source + ')')
+      ();
+    
+    block.blockquote = replace(block.blockquote)
+      ('def', block.def)
+      ();
+    
+    block._tag = '(?!(?:'
+      + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
+      + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
+      + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
+    
+    block.html = replace(block.html)
+      ('comment', /<!--[\s\S]*?-->/)
+      ('closed', /<(tag)[\s\S]+?<\/\1>/)
+      ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
+      (/tag/g, block._tag)
+      ();
+    
+    block.paragraph = replace(block.paragraph)
+      ('hr', block.hr)
+      ('heading', block.heading)
+      ('lheading', block.lheading)
+      ('blockquote', block.blockquote)
+      ('tag', '<' + block._tag)
+      ('def', block.def)
+      ();
+    
+    /**
+     * Normal Block Grammar
+     */
+    
+    block.normal = merge({}, block);
+    
+    /**
+     * GFM Block Grammar
+     */
+    
+    block.gfm = merge({}, block.normal, {
+      fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
+      paragraph: /^/,
+      heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
+    });
+    
+    block.gfm.paragraph = replace(block.paragraph)
+      ('(?!', '(?!'
+        + block.gfm.fences.source.replace('\\1', '\\2') + '|'
+        + block.list.source.replace('\\1', '\\3') + '|')
+      ();
+    
+    /**
+     * GFM + Tables Block Grammar
+     */
+    
+    block.tables = merge({}, block.gfm, {
+      nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
+      table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
+    });
+    
+    /**
+     * Block Lexer
+     */
+    
+    var Lexer = function (options) {
+      this.tokens = [];
+      this.tokens.links = {};
+      this.options = options || marked.defaults;
+      this.rules = block.normal;
+    
+      if (this.options.gfm) {
+        if (this.options.tables) {
+          this.rules = block.tables;
+        } else {
+          this.rules = block.gfm;
+        }
+      }
+    }
+    
+    /**
+     * Expose Block Rules
+     */
+    
+    Lexer.rules = block;
+    
+    /**
+     * Static Lex Method
+     */
+    
+    Lexer.lex = function(src, options) {
+      var lexer = new Lexer(options);
+      return lexer.lex(src);
+    };
+    
+    /**
+     * Preprocessing
+     */
+    
+    Lexer.prototype.lex = function(src) {
+      src = src
+        .replace(/\r\n|\r/g, '\n')
+        .replace(/\t/g, '    ')
+        .replace(/\u00a0/g, ' ')
+        .replace(/\u2424/g, '\n');
+    
+      return this.token(src, true);
+    };
+    
+    /**
+     * Lexing
+     */
+    
+    Lexer.prototype.token = function(src, top, bq) {
+      var src = src.replace(/^ +$/gm, '')
+        , next
+        , loose
+        , cap
+        , bull
+        , b
+        , item
+        , space
+        , i
+        , l;
+    
+      while (src) {
+        // newline
+        if (cap = this.rules.newline.exec(src)) {
+          src = src.substring(cap[0].length);
+          if (cap[0].length > 1) {
+            this.tokens.push({
+              type: 'space'
+            });
+          }
+        }
+    
+        // code
+        if (cap = this.rules.code.exec(src)) {
+          src = src.substring(cap[0].length);
+          cap = cap[0].replace(/^ {4}/gm, '');
+          this.tokens.push({
+            type: 'code',
+            text: !this.options.pedantic
+              ? cap.replace(/\n+$/, '')
+              : cap
+          });
+          continue;
+        }
+    
+        // fences (gfm)
+        if (cap = this.rules.fences.exec(src)) {
+          src = src.substring(cap[0].length);
+          this.tokens.push({
+            type: 'code',
+            lang: cap[2],
+            text: cap[3] || ''
+          });
+          continue;
+        }
+    
+        // heading
+        if (cap = this.rules.heading.exec(src)) {
+          src = src.substring(cap[0].length);
+          this.tokens.push({
+            type: 'heading',
+            depth: cap[1].length,
+            text: cap[2]
+          });
+          continue;
+        }
+    
+        // table no leading pipe (gfm)
+        if (top && (cap = this.rules.nptable.exec(src))) {
+          src = src.substring(cap[0].length);
+    
+          item = {
+            type: 'table',
+            header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
+            align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
+            cells: cap[3].replace(/\n$/, '').split('\n')
+          };
+    
+          for (i = 0; i < item.align.length; i++) {
+            if (/^ *-+: *$/.test(item.align[i])) {
+              item.align[i] = 'right';
+            } else if (/^ *:-+: *$/.test(item.align[i])) {
+              item.align[i] = 'center';
+            } else if (/^ *:-+ *$/.test(item.align[i])) {
+              item.align[i] = 'left';
+            } else {
+              item.align[i] = null;
+            }
+          }
+    
+          for (i = 0; i < item.cells.length; i++) {
+            item.cells[i] = item.cells[i].split(/ *\| */);
+          }
+    
+          this.tokens.push(item);
+    
+          continue;
+        }
+    
+        // lheading
+        if (cap = this.rules.lheading.exec(src)) {
+          src = src.substring(cap[0].length);
+          this.tokens.push({
+            type: 'heading',
+            depth: cap[2] === '=' ? 1 : 2,
+            text: cap[1]
+          });
+          continue;
+        }
+    
+        // hr
+        if (cap = this.rules.hr.exec(src)) {
+          src = src.substring(cap[0].length);
+          this.tokens.push({
+            type: 'hr'
+          });
+          continue;
+        }
+    
+        // blockquote
+        if (cap = this.rules.blockquote.exec(src)) {
+          src = src.substring(cap[0].length);
+    
+          this.tokens.push({
+            type: 'blockquote_start'
+          });
+    
+          cap = cap[0].replace(/^ *> ?/gm, '');
+    
+          // Pass `top` to keep the current
+          // "toplevel" state. This is exactly
+          // how markdown.pl works.
+          this.token(cap, top, true);
+    
+          this.tokens.push({
+            type: 'blockquote_end'
+          });
+    
+          continue;
+        }
+    
+        // list
+        if (cap = this.rules.list.exec(src)) {
+          src = src.substring(cap[0].length);
+          bull = cap[2];
+    
+          this.tokens.push({
+            type: 'list_start',
+            ordered: bull.length > 1
+          });
+    
+          // Get each top-level item.
+          cap = cap[0].match(this.rules.item);
+    
+          next = false;
+          l = cap.length;
+          i = 0;
+    
+          for (; i < l; i++) {
+            item = cap[i];
+    
+            // Remove the list item's bullet
+            // so it is seen as the next token.
+            space = item.length;
+            item = item.replace(/^ *([*+-]|\d+\.) +/, '');
+    
+            // Outdent whatever the
+            // list item contains. Hacky.
+            if (~item.indexOf('\n ')) {
+              space -= item.length;
+              item = !this.options.pedantic
+                ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
+                : item.replace(/^ {1,4}/gm, '');
+            }
+    
+            // Determine whether the next list item belongs here.
+            // Backpedal if it does not belong in this list.
+            if (this.options.smartLists && i !== l - 1) {
+              b = block.bullet.exec(cap[i + 1])[0];
+              if (bull !== b && !(bull.length > 1 && b.length > 1)) {
+                src = cap.slice(i + 1).join('\n') + src;
+                i = l - 1;
+              }
+            }
+    
+            // Determine whether item is loose or not.
+            // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
+            // for discount behavior.
+            loose = next || /\n\n(?!\s*$)/.test(item);
+            if (i !== l - 1) {
+              next = item.charAt(item.length - 1) === '\n';
+              if (!loose) { loose = next; }
+            }
+    
+            this.tokens.push({
+              type: loose
+                ? 'loose_item_start'
+                : 'list_item_start'
+            });
+    
+            // Recurse.
+            this.token(item, false, bq);
+    
+            this.tokens.push({
+              type: 'list_item_end'
+            });
+          }
+    
+          this.tokens.push({
+            type: 'list_end'
+          });
+    
+          continue;
+        }
+    
+        // html
+        if (cap = this.rules.html.exec(src)) {
+          src = src.substring(cap[0].length);
+          this.tokens.push({
+            type: this.options.sanitize
+              ? 'paragraph'
+              : 'html',
+            pre: !this.options.sanitizer
+              && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
+            text: cap[0]
+          });
+          continue;
+        }
+    
+        // def
+        if ((!bq && top) && (cap = this.rules.def.exec(src))) {
+          src = src.substring(cap[0].length);
+          this.tokens.links[cap[1].toLowerCase()] = {
+            href: cap[2],
+            title: cap[3]
+          };
+          continue;
+        }
+    
+        // table (gfm)
+        if (top && (cap = this.rules.table.exec(src))) {
+          src = src.substring(cap[0].length);
+    
+          item = {
+            type: 'table',
+            header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
+            align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
+            cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
+          };
+    
+          for (i = 0; i < item.align.length; i++) {
+            if (/^ *-+: *$/.test(item.align[i])) {
+              item.align[i] = 'right';
+            } else if (/^ *:-+: *$/.test(item.align[i])) {
+              item.align[i] = 'center';
+            } else if (/^ *:-+ *$/.test(item.align[i])) {
+              item.align[i] = 'left';
+            } else {
+              item.align[i] = null;
+            }
+          }
+    
+          for (i = 0; i < item.cells.length; i++) {
+            item.cells[i] = item.cells[i]
+              .replace(/^ *\| *| *\| *$/g, '')
+              .split(/ *\| */);
+          }
+    
+          this.tokens.push(item);
+    
+          continue;
+        }
+    
+        // top-level paragraph
+        if (top && (cap = this.rules.paragraph.exec(src))) {
+          src = src.substring(cap[0].length);
+          this.tokens.push({
+            type: 'paragraph',
+            text: cap[1].charAt(cap[1].length - 1) === '\n'
+              ? cap[1].slice(0, -1)
+              : cap[1]
+          });
+          continue;
+        }
+    
+        // text
+        if (cap = this.rules.text.exec(src)) {
+          // Top-level should never reach here.
+          src = src.substring(cap[0].length);
+          this.tokens.push({
+            type: 'text',
+            text: cap[0]
+          });
+          continue;
+        }
+    
+        if (src) {
+          throw new
+            Error('Infinite loop on byte: ' + src.charCodeAt(0));
+        }
+      }
+    
+      return this.tokens;
+    };
+    
+    /**
+     * Inline-Level Grammar
+     */
+    
+    var inline = {
+      escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
+      autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
+      url: noop,
+      tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
+      link: /^!?\[(inside)\]\(href\)/,
+      reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
+      nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
+      strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
+      em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
+      code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
+      br: /^ {2,}\n(?!\s*$)/,
+      del: noop,
+      text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
+    };
+    
+    inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
+    inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
+    
+    inline.link = replace(inline.link)
+      ('inside', inline._inside)
+      ('href', inline._href)
+      ();
+    
+    inline.reflink = replace(inline.reflink)
+      ('inside', inline._inside)
+      ();
+    
+    /**
+     * Normal Inline Grammar
+     */
+    
+    inline.normal = merge({}, inline);
+    
+    /**
+     * Pedantic Inline Grammar
+     */
+    
+    inline.pedantic = merge({}, inline.normal, {
+      strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
+      em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
+    });
+    
+    /**
+     * GFM Inline Grammar
+     */
+    
+    inline.gfm = merge({}, inline.normal, {
+      escape: replace(inline.escape)('])', '~|])')(),
+      url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
+      del: /^~~(?=\S)([\s\S]*?\S)~~/,
+      text: replace(inline.text)
+        (']|', '~]|')
+        ('|', '|https?://|')
+        ()
+    });
+    
+    /**
      * GFM + Line Breaks Inline Grammar
      */
     
@@ -19315,7 +21321,7 @@ if (!Roo.dd.DragDropMgr) {
  * all DragDrop items in the window.  Generally, you will not call
  * this class directly, but it does have helper methods that could
  * be useful in your DragDrop implementations.
- * @singleton
+ * @static
  */
 Roo.dd.DragDropMgr = function() {
 
@@ -21159,7 +23165,7 @@ Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
  * @class Roo.dd.ScrollManager
  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
- * @singleton
+ * @static
  */
 Roo.dd.ScrollManager = function(){
     var ddm = Roo.dd.DragDropMgr;
@@ -21345,7 +23351,7 @@ Roo.dd.ScrollManager = function(){
  * @class Roo.dd.Registry
  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
- * @singleton
+ * @static
  */
 Roo.dd.Registry = function(){
     var elements = {}; 
@@ -22045,7 +24051,7 @@ Roo.dd.DropTarget = function(el, config){
          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
          * 
-         * IMPORTANT : it should set this.overClass and this.dropAllowed
+         * IMPORTANT : it should set  this.valid to true|false
          * 
          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
          * @param {Event} e The event
@@ -22059,7 +24065,7 @@ Roo.dd.DropTarget = function(el, config){
          * This method will be called on every mouse movement while the drag source is over the drop target.
          * This default implementation simply returns the dropAllowed config value.
          * 
-         * IMPORTANT : it should set this.dropAllowed
+         * IMPORTANT : it should set  this.valid to true|false
          * 
          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
          * @param {Event} e The event
@@ -22073,6 +24079,7 @@ Roo.dd.DropTarget = function(el, config){
          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
          * overClass (if any) from the drop element.
          * 
+         * 
          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
          * @param {Event} e The event
          * @param {Object} data An object containing arbitrary data supplied by the drag source
@@ -22497,7 +24504,7 @@ Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
 
 /**
  * @class Roo.data.SortTypes
- * @singleton
+ * @static
  * Defines the default sorting (casting?) comparison functions used when sorting data.
  */
 Roo.data.SortTypes = {
@@ -22996,13 +25003,13 @@ Roo.extend(Roo.data.Store, Roo.util.Observable, {
     */
     
     /**
-    * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
+    * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
     */
     /**
     * @cfg {Array} data Inline data to be loaded when the store is initialized.
     */
     /**
-    * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
+    * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
     */
     /**
@@ -23151,6 +25158,16 @@ Roo.extend(Roo.data.Store, Roo.util.Observable, {
      * <p>
      * @param {Object} options An object containing properties which control loading options:<ul>
      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
+     * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
+     * <pre>
+                {
+                    data : data,  // array of key=>value data like JsonReader
+                    total : data.length,
+                    success : true
+                    
+                }
+        </pre>
+            }.</li>
      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
      * passed the following arguments:<ul>
      * <li>r : Roo.data.Record[]</li>
@@ -23197,7 +25214,8 @@ Roo.extend(Roo.data.Store, Roo.util.Observable, {
     // private
     // Called as a callback by the Reader during a load operation.
     loadRecords : function(o, options, success){
-        if(!o || success === false){
+         
+        if(!o){
             if(success !== false){
                 this.fireEvent("load", this, [], options, o);
             }
@@ -23585,6 +25603,8 @@ Roo.extend(Roo.data.Store, Roo.util.Observable, {
  * @cfg {Array} fields An array of field definition objects, or field name strings.
  * @cfg {Object} an existing reader (eg. copied from another store)
  * @cfg {Array} data The multi-dimensional array of data
+ * @cfg {Roo.data.DataProxy} proxy [not-required]  
+ * @cfg {Roo.data.Reader} reader  [not-required] 
  * @constructor
  * @param {Object} config
  */
@@ -23756,6 +25776,7 @@ Roo.data.Field.prototype = {
 
 /**
  * @class Roo.data.DataReader
+ * @abstract
  * Base class for reading structured data from a data source.  This class is intended to be
  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
  */
@@ -23806,7 +25827,8 @@ Roo.data.DataReader.prototype = {
 
 /**
  * @class Roo.data.DataProxy
- * @extends Roo.data.Observable
+ * @extends Roo.util.Observable
+ * @abstract
  * This class is an abstract base class for implementations which provide retrieval of
  * unformatted data objects.<br>
  * <p>
@@ -23864,14 +25886,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;
@@ -23879,6 +25903,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
@@ -24044,8 +26071,10 @@ Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
         try {
             result = o.reader.read(response);
         }catch(e){
+            o.success = false;
+            o.raw = { errorMsg : response.responseText };
             this.fireEvent("loadexception", this, o, response, e);
-            o.request.callback.call(o.request.scope, null, o.request.arg, false);
+            o.request.callback.call(o.request.scope, o, o.request.arg, false);
             return;
         }
         
@@ -24451,19 +26480,27 @@ Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
         }
         var records = [];
         for(var i = 0; i < c; i++){
-                var n = root[i];
+            var n = root[i];
             var values = {};
             var id = this.getId(n);
             for(var j = 0; j < fl; j++){
                 f = fi[j];
-            var v = this.ef[j](n);
-            if (!f.convert) {
-                Roo.log('missing convert for ' + f.name);
-                Roo.log(f);
-                continue;
-            }
-            values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
-            }
+                               var v = this.ef[j](n);
+                               if (!f.convert) {
+                                       Roo.log('missing convert for ' + f.name);
+                                       Roo.log(f);
+                                       continue;
+                               }
+                               values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
+            }
+                       if (!Record) {
+                               return {
+                                       raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
+                                       success : false,
+                                       records : [],
+                                       totalRecords : 0
+                               };
+                       }
             var record = new Record(values, id);
             record.json = n;
             records[i] = record;
@@ -24682,35 +26719,35 @@ Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
     readRecords : function(o)
     {
         var sid = this.meta ? this.meta.id : null;
-       var recordType = this.recordType, fields = recordType.prototype.fields;
-       var records = [];
-       var root = o;
-       for(var i = 0; i < root.length; i++){
-               var n = root[i];
-           var values = {};
-           var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
-           for(var j = 0, jlen = fields.length; j < jlen; j++){
-               var f = fields.items[j];
-               var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
-               var v = n[k] !== undefined ? n[k] : f.defaultValue;
-               v = f.convert(v);
-               values[f.name] = v;
-           }
-           var record = new recordType(values, id);
-           record.json = n;
-           records[records.length] = record;
-       }
-       return {
-           records : records,
-           totalRecords : records.length
-       };
+        var recordType = this.recordType, fields = recordType.prototype.fields;
+        var records = [];
+        var root = o;
+        for(var i = 0; i < root.length; i++){
+            var n = root[i];
+            var values = {};
+            var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
+            for(var j = 0, jlen = fields.length; j < jlen; j++){
+                var f = fields.items[j];
+                var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
+                var v = n[k] !== undefined ? n[k] : f.defaultValue;
+                v = f.convert(v);
+                values[f.name] = v;
+            }
+            var record = new recordType(values, id);
+            record.json = n;
+            records[records.length] = record;
+        }
+        return {
+            records : records,
+            totalRecords : records.length
+        };
     },
     // used when loading children.. @see loadDataFromChildren
     toLoadData: function(rec)
     {
-       // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
-       return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
-       
+        // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
+        return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
+        
     }
     
     
@@ -25469,463 +27506,6 @@ Roo.extend(Roo.data.Node, Roo.util.Observable, {
  * Fork - LGPL
  * <script type="text/javascript">
  */
- (function(){ 
-/**
- * @class Roo.Layer
- * @extends Roo.Element
- * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
- * automatic maintaining of shadow/shim positions.
- * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
- * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
- * you can pass a string with a CSS class name. False turns off the shadow.
- * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
- * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
- * @cfg {String} cls CSS class to add to the element
- * @cfg {Number} zindex Starting z-index (defaults to 11000)
- * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
- * @constructor
- * @param {Object} config An object with config options.
- * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
- */
-
-Roo.Layer = function(config, existingEl){
-    config = config || {};
-    var dh = Roo.DomHelper;
-    var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
-    if(existingEl){
-        this.dom = Roo.getDom(existingEl);
-    }
-    if(!this.dom){
-        var o = config.dh || {tag: "div", cls: "x-layer"};
-        this.dom = dh.append(pel, o);
-    }
-    if(config.cls){
-        this.addClass(config.cls);
-    }
-    this.constrain = config.constrain !== false;
-    this.visibilityMode = Roo.Element.VISIBILITY;
-    if(config.id){
-        this.id = this.dom.id = config.id;
-    }else{
-        this.id = Roo.id(this.dom);
-    }
-    this.zindex = config.zindex || this.getZIndex();
-    this.position("absolute", this.zindex);
-    if(config.shadow){
-        this.shadowOffset = config.shadowOffset || 4;
-        this.shadow = new Roo.Shadow({
-            offset : this.shadowOffset,
-            mode : config.shadow
-        });
-    }else{
-        this.shadowOffset = 0;
-    }
-    this.useShim = config.shim !== false && Roo.useShims;
-    this.useDisplay = config.useDisplay;
-    this.hide();
-};
-
-var supr = Roo.Element.prototype;
-
-// shims are shared among layer to keep from having 100 iframes
-var shims = [];
-
-Roo.extend(Roo.Layer, Roo.Element, {
-
-    getZIndex : function(){
-        return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
-    },
-
-    getShim : function(){
-        if(!this.useShim){
-            return null;
-        }
-        if(this.shim){
-            return this.shim;
-        }
-        var shim = shims.shift();
-        if(!shim){
-            shim = this.createShim();
-            shim.enableDisplayMode('block');
-            shim.dom.style.display = 'none';
-            shim.dom.style.visibility = 'visible';
-        }
-        var pn = this.dom.parentNode;
-        if(shim.dom.parentNode != pn){
-            pn.insertBefore(shim.dom, this.dom);
-        }
-        shim.setStyle('z-index', this.getZIndex()-2);
-        this.shim = shim;
-        return shim;
-    },
-
-    hideShim : function(){
-        if(this.shim){
-            this.shim.setDisplayed(false);
-            shims.push(this.shim);
-            delete this.shim;
-        }
-    },
-
-    disableShadow : function(){
-        if(this.shadow){
-            this.shadowDisabled = true;
-            this.shadow.hide();
-            this.lastShadowOffset = this.shadowOffset;
-            this.shadowOffset = 0;
-        }
-    },
-
-    enableShadow : function(show){
-        if(this.shadow){
-            this.shadowDisabled = false;
-            this.shadowOffset = this.lastShadowOffset;
-            delete this.lastShadowOffset;
-            if(show){
-                this.sync(true);
-            }
-        }
-    },
-
-    // private
-    // this code can execute repeatedly in milliseconds (i.e. during a drag) so
-    // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
-    sync : function(doShow){
-        var sw = this.shadow;
-        if(!this.updating && this.isVisible() && (sw || this.useShim)){
-            var sh = this.getShim();
-
-            var w = this.getWidth(),
-                h = this.getHeight();
-
-            var l = this.getLeft(true),
-                t = this.getTop(true);
-
-            if(sw && !this.shadowDisabled){
-                if(doShow && !sw.isVisible()){
-                    sw.show(this);
-                }else{
-                    sw.realign(l, t, w, h);
-                }
-                if(sh){
-                    if(doShow){
-                       sh.show();
-                    }
-                    // fit the shim behind the shadow, so it is shimmed too
-                    var a = sw.adjusts, s = sh.dom.style;
-                    s.left = (Math.min(l, l+a.l))+"px";
-                    s.top = (Math.min(t, t+a.t))+"px";
-                    s.width = (w+a.w)+"px";
-                    s.height = (h+a.h)+"px";
-                }
-            }else if(sh){
-                if(doShow){
-                   sh.show();
-                }
-                sh.setSize(w, h);
-                sh.setLeftTop(l, t);
-            }
-            
-        }
-    },
-
-    // private
-    destroy : function(){
-        this.hideShim();
-        if(this.shadow){
-            this.shadow.hide();
-        }
-        this.removeAllListeners();
-        var pn = this.dom.parentNode;
-        if(pn){
-            pn.removeChild(this.dom);
-        }
-        Roo.Element.uncache(this.id);
-    },
-
-    remove : function(){
-        this.destroy();
-    },
-
-    // private
-    beginUpdate : function(){
-        this.updating = true;
-    },
-
-    // private
-    endUpdate : function(){
-        this.updating = false;
-        this.sync(true);
-    },
-
-    // private
-    hideUnders : function(negOffset){
-        if(this.shadow){
-            this.shadow.hide();
-        }
-        this.hideShim();
-    },
-
-    // private
-    constrainXY : function(){
-        if(this.constrain){
-            var vw = Roo.lib.Dom.getViewWidth(),
-                vh = Roo.lib.Dom.getViewHeight();
-            var s = Roo.get(document).getScroll();
-
-            var xy = this.getXY();
-            var x = xy[0], y = xy[1];   
-            var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
-            // only move it if it needs it
-            var moved = false;
-            // first validate right/bottom
-            if((x + w) > vw+s.left){
-                x = vw - w - this.shadowOffset;
-                moved = true;
-            }
-            if((y + h) > vh+s.top){
-                y = vh - h - this.shadowOffset;
-                moved = true;
-            }
-            // then make sure top/left isn't negative
-            if(x < s.left){
-                x = s.left;
-                moved = true;
-            }
-            if(y < s.top){
-                y = s.top;
-                moved = true;
-            }
-            if(moved){
-                if(this.avoidY){
-                    var ay = this.avoidY;
-                    if(y <= ay && (y+h) >= ay){
-                        y = ay-h-5;   
-                    }
-                }
-                xy = [x, y];
-                this.storeXY(xy);
-                supr.setXY.call(this, xy);
-                this.sync();
-            }
-        }
-    },
-
-    isVisible : function(){
-        return this.visible;    
-    },
-
-    // private
-    showAction : function(){
-        this.visible = true; // track visibility to prevent getStyle calls
-        if(this.useDisplay === true){
-            this.setDisplayed("");
-        }else if(this.lastXY){
-            supr.setXY.call(this, this.lastXY);
-        }else if(this.lastLT){
-            supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
-        }
-    },
-
-    // private
-    hideAction : function(){
-        this.visible = false;
-        if(this.useDisplay === true){
-            this.setDisplayed(false);
-        }else{
-            this.setLeftTop(-10000,-10000);
-        }
-    },
-
-    // overridden Element method
-    setVisible : function(v, a, d, c, e){
-        if(v){
-            this.showAction();
-        }
-        if(a && v){
-            var cb = function(){
-                this.sync(true);
-                if(c){
-                    c();
-                }
-            }.createDelegate(this);
-            supr.setVisible.call(this, true, true, d, cb, e);
-        }else{
-            if(!v){
-                this.hideUnders(true);
-            }
-            var cb = c;
-            if(a){
-                cb = function(){
-                    this.hideAction();
-                    if(c){
-                        c();
-                    }
-                }.createDelegate(this);
-            }
-            supr.setVisible.call(this, v, a, d, cb, e);
-            if(v){
-                this.sync(true);
-            }else if(!a){
-                this.hideAction();
-            }
-        }
-    },
-
-    storeXY : function(xy){
-        delete this.lastLT;
-        this.lastXY = xy;
-    },
-
-    storeLeftTop : function(left, top){
-        delete this.lastXY;
-        this.lastLT = [left, top];
-    },
-
-    // private
-    beforeFx : function(){
-        this.beforeAction();
-        return Roo.Layer.superclass.beforeFx.apply(this, arguments);
-    },
-
-    // private
-    afterFx : function(){
-        Roo.Layer.superclass.afterFx.apply(this, arguments);
-        this.sync(this.isVisible());
-    },
-
-    // private
-    beforeAction : function(){
-        if(!this.updating && this.shadow){
-            this.shadow.hide();
-        }
-    },
-
-    // overridden Element method
-    setLeft : function(left){
-        this.storeLeftTop(left, this.getTop(true));
-        supr.setLeft.apply(this, arguments);
-        this.sync();
-    },
-
-    setTop : function(top){
-        this.storeLeftTop(this.getLeft(true), top);
-        supr.setTop.apply(this, arguments);
-        this.sync();
-    },
-
-    setLeftTop : function(left, top){
-        this.storeLeftTop(left, top);
-        supr.setLeftTop.apply(this, arguments);
-        this.sync();
-    },
-
-    setXY : function(xy, a, d, c, e){
-        this.fixDisplay();
-        this.beforeAction();
-        this.storeXY(xy);
-        var cb = this.createCB(c);
-        supr.setXY.call(this, xy, a, d, cb, e);
-        if(!a){
-            cb();
-        }
-    },
-
-    // private
-    createCB : function(c){
-        var el = this;
-        return function(){
-            el.constrainXY();
-            el.sync(true);
-            if(c){
-                c();
-            }
-        };
-    },
-
-    // overridden Element method
-    setX : function(x, a, d, c, e){
-        this.setXY([x, this.getY()], a, d, c, e);
-    },
-
-    // overridden Element method
-    setY : function(y, a, d, c, e){
-        this.setXY([this.getX(), y], a, d, c, e);
-    },
-
-    // overridden Element method
-    setSize : function(w, h, a, d, c, e){
-        this.beforeAction();
-        var cb = this.createCB(c);
-        supr.setSize.call(this, w, h, a, d, cb, e);
-        if(!a){
-            cb();
-        }
-    },
-
-    // overridden Element method
-    setWidth : function(w, a, d, c, e){
-        this.beforeAction();
-        var cb = this.createCB(c);
-        supr.setWidth.call(this, w, a, d, cb, e);
-        if(!a){
-            cb();
-        }
-    },
-
-    // overridden Element method
-    setHeight : function(h, a, d, c, e){
-        this.beforeAction();
-        var cb = this.createCB(c);
-        supr.setHeight.call(this, h, a, d, cb, e);
-        if(!a){
-            cb();
-        }
-    },
-
-    // overridden Element method
-    setBounds : function(x, y, w, h, a, d, c, e){
-        this.beforeAction();
-        var cb = this.createCB(c);
-        if(!a){
-            this.storeXY([x, y]);
-            supr.setXY.call(this, [x, y]);
-            supr.setSize.call(this, w, h, a, d, cb, e);
-            cb();
-        }else{
-            supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
-        }
-        return this;
-    },
-    
-    /**
-     * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
-     * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
-     * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
-     * @param {Number} zindex The new z-index to set
-     * @return {this} The Layer
-     */
-    setZIndex : function(zindex){
-        this.zindex = zindex;
-        this.setStyle("z-index", zindex + 2);
-        if(this.shadow){
-            this.shadow.setZIndex(zindex + 1);
-        }
-        if(this.shim){
-            this.shim.setStyle("z-index", zindex);
-        }
-    }
-});
-})();/*
- * 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">
- */
 
 
 /**
@@ -25997,6 +27577,7 @@ Roo.Shadow.prototype = {
      * frame: Shadow displays equally on all four sides<br />
      * drop: Traditional bottom-right drop shadow (default)
      */
+    mode: false,
     /**
      * @cfg {String} offset
      * The number of pixels to offset the shadow from the element (defaults to 4)
@@ -29310,7 +30891,7 @@ Roo.extend(Roo.Button, Roo.util.Observable, {
      */
     enableToggle: false,
     /**
-     * @cfg {Mixed} menu
+     * @cfg {Roo.menu.Menu} menu
      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
      */
     menu : undefined,
@@ -29558,7 +31139,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
@@ -29581,6 +31178,8 @@ Roo.extend(Roo.Button, Roo.util.Observable, {
         }
     },
     
+       
+       
     /**
      * Focus the button
      */
@@ -29932,6 +31531,7 @@ Roo.MenuButton = Roo.SplitButton;/*
 
 /**
  * @class Roo.Toolbar
+ * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field 
  * Basic Toolbar class.
  * @constructor
  * Creates a new Toolbar
@@ -29973,7 +31573,7 @@ Roo.Toolbar.prototype = {
      * @cfg {Array} items
      * array of button configs or elements to add (will be converted to a MixedCollection)
      */
-    
+    items: false,
     /**
      * @cfg {String/HTMLElement/Element} container
      * The id or element that will contain the toolbar
@@ -30444,7 +32044,7 @@ Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
  * A simple class that renders text directly into a toolbar.
  * @constructor
  * Creates a new TextItem
- * @param {String} text
+ * @cfg {string} text 
  */
 Roo.Toolbar.TextItem = function(cfg){
     var  text = cfg || "";
@@ -30467,7 +32067,23 @@ Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
      
     enable:Roo.emptyFn,
     disable:Roo.emptyFn,
-    focus:Roo.emptyFn
+    focus:Roo.emptyFn,
+     /**
+     * Shows this button
+     */
+    show: function(){
+        this.hidden = false;
+        this.el.style.display = "";
+    },
+    
+    /**
+     * Hides this button
+     */
+    hide: function(){
+        this.hidden = true;
+        this.el.style.display = "none";
+    }
+    
 });
 
 /**
@@ -30481,7 +32097,10 @@ Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
 Roo.Toolbar.Button = function(config){
     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
 };
-Roo.extend(Roo.Toolbar.Button, Roo.Button, {
+Roo.extend(Roo.Toolbar.Button, Roo.Button,
+{
+    
+    
     render : function(td){
         this.td = td;
         Roo.Toolbar.Button.superclass.render.call(this, td);
@@ -30587,6 +32206,7 @@ Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
 /**
  * @class Roo.PagingToolbar
  * @extends Roo.Toolbar
+ * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
  * @constructor
  * Create a new PagingToolbar
@@ -30622,10 +32242,7 @@ Roo.PagingToolbar = function(el, ds, config)
 };
 
 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
-    /**
-     * @cfg {Roo.data.Store} dataSource
-     * The underlying data store providing the paged data
-     */
+   
     /**
      * @cfg {String/HTMLElement/Element} container
      * container The id or element that will contain the toolbar
@@ -30634,6 +32251,8 @@ Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
      * @cfg {Boolean} displayInfo
      * True to display the displayMsg (defaults to false)
      */
+    
+    
     /**
      * @cfg {Number} pageSize
      * The number of records to display per page (defaults to 20)
@@ -30830,7 +32449,11 @@ Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
             this.loading.disable();
         }
     },
-
+    /**
+     * event that occurs when you click on the navigation buttons - can be used to trigger load of a grid.
+     * @param {String} which (first|prev|next|last|refresh)  which button to press.
+     *
+     */
     // private
     onClick : function(which){
         var ds = this.ds;
@@ -31920,6 +33543,7 @@ Roo.extend(Roo.Editor, Roo.Component, {
 /**
  * @class Roo.BasicDialog
  * @extends Roo.util.Observable
+ * @parent none builder
  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
  * <pre><code>
 var dlg = new Roo.BasicDialog("my-dlg", {
@@ -33029,6 +34653,8 @@ Roo.DialogManager = function(){
 /**
  * @class Roo.LayoutDialog
  * @extends Roo.BasicDialog
+ * @children Roo.ContentPanel
+ * @parent builder none
  * Dialog which provides adjustments for working with a layout in a Dialog.
  * Add your necessary layout config options to the dialog's config.<br>
  * Example usage (including a nested layout):
@@ -33114,6 +34740,28 @@ Roo.LayoutDialog = function(el, cfg){
     
 };
 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
+    
+    
+    /**
+     * @cfg {Roo.LayoutRegion} east  
+     */
+    /**
+     * @cfg {Roo.LayoutRegion} west
+     */
+    /**
+     * @cfg {Roo.LayoutRegion} south
+     */
+    /**
+     * @cfg {Roo.LayoutRegion} north
+     */
+    /**
+     * @cfg {Roo.LayoutRegion} center
+     */
+    /**
+     * @cfg {Roo.Button} buttons[]  Bottom buttons..
+     */
+    
+    
     /**
      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
      * @deprecated
@@ -33173,6 +34821,7 @@ Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
  
 /**
  * @class Roo.MessageBox
+ * @static
  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
  * Example usage:
  *<pre><code>
@@ -33195,7 +34844,7 @@ Roo.Msg.show({
    animEl: 'elId'
 });
 </code></pre>
- * @singleton
+ * @static
  */
 Roo.MessageBox = function(){
     var dlg, opt, mask, waitTimer;
@@ -33282,6 +34931,7 @@ Roo.MessageBox = function(){
                         }
                     }
                 });
+              
                 dlg.on("hide", handleHide);
                 mask = dlg.mask;
                 dlg.addKeyListener(27, handleEsc);
@@ -33525,6 +35175,7 @@ Roo.Msg.show({
                 d.animateTarget = null;
                 d.show(options.animEl);
             }
+            dlg.toFront();
             return this;
         },
 
@@ -33724,7 +35375,7 @@ Roo.Msg = Roo.MessageBox;/*
 /**
  * @class Roo.QuickTips
  * Provides attractive and customizable tooltips for any element.
- * @singleton
+ * @static
  */
 Roo.QuickTips = function(){
     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
@@ -34126,7 +35777,7 @@ Roo.QuickTips.tips = Roo.QuickTips.register;/*
 /**
  * @class Roo.tree.TreePanel
  * @extends Roo.data.Tree
-
+ * @cfg {Roo.tree.TreeNode} root The root node
  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
  * @cfg {Boolean} enableDD true to enable drag and drop
@@ -34143,8 +35794,8 @@ Roo.QuickTips.tips = Roo.QuickTips.register;/*
  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
- * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
- * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
+ * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
+ * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
@@ -34371,223 +36022,2668 @@ Roo.tree.TreePanel = function(el, config){
         this.editor = Roo.factory(this.editor, Roo.tree);
     }
     
-    if (this.selModel) {
-        this.selModel = Roo.factory(this.selModel, Roo.tree);
-    }
-   
-};
-Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
-    rootVisible : true,
-    animate: Roo.enableFx,
-    lines : true,
-    enableDD : false,
-    hlDrop : Roo.enableFx,
-  
-    renderer: false,
+    if (this.selModel) {
+        this.selModel = Roo.factory(this.selModel, Roo.tree);
+    }
+   
+};
+Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
+    rootVisible : true,
+    animate: Roo.enableFx,
+    lines : true,
+    enableDD : false,
+    hlDrop : Roo.enableFx,
+  
+    renderer: false,
+    
+    rendererTip: false,
+    // private
+    restrictExpand : function(node){
+        var p = node.parentNode;
+        if(p){
+            if(p.expandedChild && p.expandedChild.parentNode == p){
+                p.expandedChild.collapse();
+            }
+            p.expandedChild = node;
+        }
+    },
+
+    // private override
+    setRootNode : function(node){
+        Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
+        if(!this.rootVisible){
+            node.ui = new Roo.tree.RootTreeNodeUI(node);
+        }
+        return node;
+    },
+
+    /**
+     * Returns the container element for this TreePanel
+     */
+    getEl : function(){
+        return this.el;
+    },
+
+    /**
+     * Returns the default TreeLoader for this TreePanel
+     */
+    getLoader : function(){
+        return this.loader;
+    },
+
+    /**
+     * Expand all nodes
+     */
+    expandAll : function(){
+        this.root.expand(true);
+    },
+
+    /**
+     * Collapse all nodes
+     */
+    collapseAll : function(){
+        this.root.collapse(true);
+    },
+
+    /**
+     * Returns the selection model used by this TreePanel
+     */
+    getSelectionModel : function(){
+        if(!this.selModel){
+            this.selModel = new Roo.tree.DefaultSelectionModel();
+        }
+        return this.selModel;
+    },
+
+    /**
+     * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
+     * @param {String} attribute (optional) Defaults to null (return the actual nodes)
+     * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
+     * @return {Array}
+     */
+    getChecked : function(a, startNode){
+        startNode = startNode || this.root;
+        var r = [];
+        var f = function(){
+            if(this.attributes.checked){
+                r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
+            }
+        }
+        startNode.cascade(f);
+        return r;
+    },
+
+    /**
+     * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
+     * @param {String} path
+     * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
+     * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
+     * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
+     */
+    expandPath : function(path, attr, callback){
+        attr = attr || "id";
+        var keys = path.split(this.pathSeparator);
+        var curNode = this.root;
+        if(curNode.attributes[attr] != keys[1]){ // invalid root
+            if(callback){
+                callback(false, null);
+            }
+            return;
+        }
+        var index = 1;
+        var f = function(){
+            if(++index == keys.length){
+                if(callback){
+                    callback(true, curNode);
+                }
+                return;
+            }
+            var c = curNode.findChild(attr, keys[index]);
+            if(!c){
+                if(callback){
+                    callback(false, curNode);
+                }
+                return;
+            }
+            curNode = c;
+            c.expand(false, false, f);
+        };
+        curNode.expand(false, false, f);
+    },
+
+    /**
+     * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
+     * @param {String} path
+     * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
+     * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
+     * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
+     */
+    selectPath : function(path, attr, callback){
+        attr = attr || "id";
+        var keys = path.split(this.pathSeparator);
+        var v = keys.pop();
+        if(keys.length > 0){
+            var f = function(success, node){
+                if(success && node){
+                    var n = node.findChild(attr, v);
+                    if(n){
+                        n.select();
+                        if(callback){
+                            callback(true, n);
+                        }
+                    }else if(callback){
+                        callback(false, n);
+                    }
+                }else{
+                    if(callback){
+                        callback(false, n);
+                    }
+                }
+            };
+            this.expandPath(keys.join(this.pathSeparator), attr, f);
+        }else{
+            this.root.select();
+            if(callback){
+                callback(true, this.root);
+            }
+        }
+    },
+
+    getTreeEl : function(){
+        return this.el;
+    },
+
+    /**
+     * Trigger rendering of this TreePanel
+     */
+    render : function(){
+        if (this.innerCt) {
+            return this; // stop it rendering more than once!!
+        }
+        
+        this.innerCt = this.el.createChild({tag:"ul",
+               cls:"x-tree-root-ct " +
+               (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
+
+        if(this.containerScroll){
+            Roo.dd.ScrollManager.register(this.el);
+        }
+        if((this.enableDD || this.enableDrop) && !this.dropZone){
+           /**
+            * The dropZone used by this tree if drop is enabled
+            * @type Roo.tree.TreeDropZone
+            */
+             this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
+               ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
+           });
+        }
+        if((this.enableDD || this.enableDrag) && !this.dragZone){
+           /**
+            * The dragZone used by this tree if drag is enabled
+            * @type Roo.tree.TreeDragZone
+            */
+            this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
+               ddGroup: this.ddGroup || "TreeDD",
+               scroll: this.ddScroll
+           });
+        }
+        this.getSelectionModel().init(this);
+        if (!this.root) {
+            Roo.log("ROOT not set in tree");
+            return this;
+        }
+        this.root.render();
+        if(!this.rootVisible){
+            this.root.renderChildren();
+        }
+        return 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.tree.DefaultSelectionModel
+ * @extends Roo.util.Observable
+ * The default single selection for a TreePanel.
+ * @param {Object} cfg Configuration
+ */
+Roo.tree.DefaultSelectionModel = function(cfg){
+   this.selNode = null;
+   
+   
+   
+   this.addEvents({
+       /**
+        * @event selectionchange
+        * Fires when the selected node changes
+        * @param {DefaultSelectionModel} this
+        * @param {TreeNode} node the new selection
+        */
+       "selectionchange" : true,
+
+       /**
+        * @event beforeselect
+        * Fires before the selected node changes, return false to cancel the change
+        * @param {DefaultSelectionModel} this
+        * @param {TreeNode} node the new selection
+        * @param {TreeNode} node the old selection
+        */
+       "beforeselect" : true
+   });
+   
+    Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
+};
+
+Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
+    init : function(tree){
+        this.tree = tree;
+        tree.getTreeEl().on("keydown", this.onKeyDown, this);
+        tree.on("click", this.onNodeClick, this);
+    },
+    
+    onNodeClick : function(node, e){
+        if (e.ctrlKey && this.selNode == node)  {
+            this.unselect(node);
+            return;
+        }
+        this.select(node);
+    },
+    
+    /**
+     * Select a node.
+     * @param {TreeNode} node The node to select
+     * @return {TreeNode} The selected node
+     */
+    select : function(node){
+        var last = this.selNode;
+        if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
+            if(last){
+                last.ui.onSelectedChange(false);
+            }
+            this.selNode = node;
+            node.ui.onSelectedChange(true);
+            this.fireEvent("selectionchange", this, node, last);
+        }
+        return node;
+    },
+    
+    /**
+     * Deselect a node.
+     * @param {TreeNode} node The node to unselect
+     */
+    unselect : function(node){
+        if(this.selNode == node){
+            this.clearSelections();
+        }    
+    },
+    
+    /**
+     * Clear all selections
+     */
+    clearSelections : function(){
+        var n = this.selNode;
+        if(n){
+            n.ui.onSelectedChange(false);
+            this.selNode = null;
+            this.fireEvent("selectionchange", this, null);
+        }
+        return n;
+    },
+    
+    /**
+     * Get the selected node
+     * @return {TreeNode} The selected node
+     */
+    getSelectedNode : function(){
+        return this.selNode;    
+    },
+    
+    /**
+     * Returns true if the node is selected
+     * @param {TreeNode} node The node to check
+     * @return {Boolean}
+     */
+    isSelected : function(node){
+        return this.selNode == node;  
+    },
+
+    /**
+     * Selects the node above the selected node in the tree, intelligently walking the nodes
+     * @return TreeNode The new selection
+     */
+    selectPrevious : function(){
+        var s = this.selNode || this.lastSelNode;
+        if(!s){
+            return null;
+        }
+        var ps = s.previousSibling;
+        if(ps){
+            if(!ps.isExpanded() || ps.childNodes.length < 1){
+                return this.select(ps);
+            } else{
+                var lc = ps.lastChild;
+                while(lc && lc.isExpanded() && lc.childNodes.length > 0){
+                    lc = lc.lastChild;
+                }
+                return this.select(lc);
+            }
+        } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
+            return this.select(s.parentNode);
+        }
+        return null;
+    },
+
+    /**
+     * Selects the node above the selected node in the tree, intelligently walking the nodes
+     * @return TreeNode The new selection
+     */
+    selectNext : function(){
+        var s = this.selNode || this.lastSelNode;
+        if(!s){
+            return null;
+        }
+        if(s.firstChild && s.isExpanded()){
+             return this.select(s.firstChild);
+         }else if(s.nextSibling){
+             return this.select(s.nextSibling);
+         }else if(s.parentNode){
+            var newS = null;
+            s.parentNode.bubble(function(){
+                if(this.nextSibling){
+                    newS = this.getOwnerTree().selModel.select(this.nextSibling);
+                    return false;
+                }
+            });
+            return newS;
+         }
+        return null;
+    },
+
+    onKeyDown : function(e){
+        var s = this.selNode || this.lastSelNode;
+        // undesirable, but required
+        var sm = this;
+        if(!s){
+            return;
+        }
+        var k = e.getKey();
+        switch(k){
+             case e.DOWN:
+                 e.stopEvent();
+                 this.selectNext();
+             break;
+             case e.UP:
+                 e.stopEvent();
+                 this.selectPrevious();
+             break;
+             case e.RIGHT:
+                 e.preventDefault();
+                 if(s.hasChildNodes()){
+                     if(!s.isExpanded()){
+                         s.expand();
+                     }else if(s.firstChild){
+                         this.select(s.firstChild, e);
+                     }
+                 }
+             break;
+             case e.LEFT:
+                 e.preventDefault();
+                 if(s.hasChildNodes() && s.isExpanded()){
+                     s.collapse();
+                 }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
+                     this.select(s.parentNode, e);
+                 }
+             break;
+        };
+    }
+});
+
+/**
+ * @class Roo.tree.MultiSelectionModel
+ * @extends Roo.util.Observable
+ * Multi selection for a TreePanel.
+ * @param {Object} cfg Configuration
+ */
+Roo.tree.MultiSelectionModel = function(){
+   this.selNodes = [];
+   this.selMap = {};
+   this.addEvents({
+       /**
+        * @event selectionchange
+        * Fires when the selected nodes change
+        * @param {MultiSelectionModel} this
+        * @param {Array} nodes Array of the selected nodes
+        */
+       "selectionchange" : true
+   });
+   Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
+   
+};
+
+Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
+    init : function(tree){
+        this.tree = tree;
+        tree.getTreeEl().on("keydown", this.onKeyDown, this);
+        tree.on("click", this.onNodeClick, this);
+    },
+    
+    onNodeClick : function(node, e){
+        this.select(node, e, e.ctrlKey);
+    },
+    
+    /**
+     * Select a node.
+     * @param {TreeNode} node The node to select
+     * @param {EventObject} e (optional) An event associated with the selection
+     * @param {Boolean} keepExisting True to retain existing selections
+     * @return {TreeNode} The selected node
+     */
+    select : function(node, e, keepExisting){
+        if(keepExisting !== true){
+            this.clearSelections(true);
+        }
+        if(this.isSelected(node)){
+            this.lastSelNode = node;
+            return node;
+        }
+        this.selNodes.push(node);
+        this.selMap[node.id] = node;
+        this.lastSelNode = node;
+        node.ui.onSelectedChange(true);
+        this.fireEvent("selectionchange", this, this.selNodes);
+        return node;
+    },
+    
+    /**
+     * Deselect a node.
+     * @param {TreeNode} node The node to unselect
+     */
+    unselect : function(node){
+        if(this.selMap[node.id]){
+            node.ui.onSelectedChange(false);
+            var sn = this.selNodes;
+            var index = -1;
+            if(sn.indexOf){
+                index = sn.indexOf(node);
+            }else{
+                for(var i = 0, len = sn.length; i < len; i++){
+                    if(sn[i] == node){
+                        index = i;
+                        break;
+                    }
+                }
+            }
+            if(index != -1){
+                this.selNodes.splice(index, 1);
+            }
+            delete this.selMap[node.id];
+            this.fireEvent("selectionchange", this, this.selNodes);
+        }
+    },
+    
+    /**
+     * Clear all selections
+     */
+    clearSelections : function(suppressEvent){
+        var sn = this.selNodes;
+        if(sn.length > 0){
+            for(var i = 0, len = sn.length; i < len; i++){
+                sn[i].ui.onSelectedChange(false);
+            }
+            this.selNodes = [];
+            this.selMap = {};
+            if(suppressEvent !== true){
+                this.fireEvent("selectionchange", this, this.selNodes);
+            }
+        }
+    },
+    
+    /**
+     * Returns true if the node is selected
+     * @param {TreeNode} node The node to check
+     * @return {Boolean}
+     */
+    isSelected : function(node){
+        return this.selMap[node.id] ? true : false;  
+    },
+    
+    /**
+     * Returns an array of the selected nodes
+     * @return {Array}
+     */
+    getSelectedNodes : function(){
+        return this.selNodes;    
+    },
+
+    onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
+
+    selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
+
+    selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
+});/*
+ * 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.tree.TreeNode
+ * @extends Roo.data.Node
+ * @cfg {String} text The text for this node
+ * @cfg {Boolean} expanded true to start the node expanded
+ * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
+ * @cfg {Boolean} allowDrop false if this node cannot be drop on
+ * @cfg {Boolean} disabled true to start the node disabled
+ * @cfg {String} icon The path to an icon for the node. The preferred way to do this
+ *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
+ * @cfg {String} cls A css class to be added to the node
+ * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
+ * @cfg {String} href URL of the link used for the node (defaults to #)
+ * @cfg {String} hrefTarget target frame for the link
+ * @cfg {String} qtip An Ext QuickTip for the node
+ * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
+ * @cfg {Boolean} singleClickExpand True for single click expand on this node
+ * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
+ * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
+ * (defaults to undefined with no checkbox rendered)
+ * @constructor
+ * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
+ */
+Roo.tree.TreeNode = function(attributes){
+    attributes = attributes || {};
+    if(typeof attributes == "string"){
+        attributes = {text: attributes};
+    }
+    this.childrenRendered = false;
+    this.rendered = false;
+    Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
+    this.expanded = attributes.expanded === true;
+    this.isTarget = attributes.isTarget !== false;
+    this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
+    this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
+
+    /**
+     * Read-only. The text for this node. To change it use setText().
+     * @type String
+     */
+    this.text = attributes.text;
+    /**
+     * True if this node is disabled.
+     * @type Boolean
+     */
+    this.disabled = attributes.disabled === true;
+
+    this.addEvents({
+        /**
+        * @event textchange
+        * Fires when the text for this node is changed
+        * @param {Node} this This node
+        * @param {String} text The new text
+        * @param {String} oldText The old text
+        */
+        "textchange" : true,
+        /**
+        * @event beforeexpand
+        * Fires before this node is expanded, return false to cancel.
+        * @param {Node} this This node
+        * @param {Boolean} deep
+        * @param {Boolean} anim
+        */
+        "beforeexpand" : true,
+        /**
+        * @event beforecollapse
+        * Fires before this node is collapsed, return false to cancel.
+        * @param {Node} this This node
+        * @param {Boolean} deep
+        * @param {Boolean} anim
+        */
+        "beforecollapse" : true,
+        /**
+        * @event expand
+        * Fires when this node is expanded
+        * @param {Node} this This node
+        */
+        "expand" : true,
+        /**
+        * @event disabledchange
+        * Fires when the disabled status of this node changes
+        * @param {Node} this This node
+        * @param {Boolean} disabled
+        */
+        "disabledchange" : true,
+        /**
+        * @event collapse
+        * Fires when this node is collapsed
+        * @param {Node} this This node
+        */
+        "collapse" : true,
+        /**
+        * @event beforeclick
+        * Fires before click processing. Return false to cancel the default action.
+        * @param {Node} this This node
+        * @param {Roo.EventObject} e The event object
+        */
+        "beforeclick":true,
+        /**
+        * @event checkchange
+        * Fires when a node with a checkbox's checked property changes
+        * @param {Node} this This node
+        * @param {Boolean} checked
+        */
+        "checkchange":true,
+        /**
+        * @event click
+        * Fires when this node is clicked
+        * @param {Node} this This node
+        * @param {Roo.EventObject} e The event object
+        */
+        "click":true,
+        /**
+        * @event dblclick
+        * Fires when this node is double clicked
+        * @param {Node} this This node
+        * @param {Roo.EventObject} e The event object
+        */
+        "dblclick":true,
+        /**
+        * @event contextmenu
+        * Fires when this node is right clicked
+        * @param {Node} this This node
+        * @param {Roo.EventObject} e The event object
+        */
+        "contextmenu":true,
+        /**
+        * @event beforechildrenrendered
+        * Fires right before the child nodes for this node are rendered
+        * @param {Node} this This node
+        */
+        "beforechildrenrendered":true
+    });
+
+    var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
+
+    /**
+     * Read-only. The UI for this node
+     * @type TreeNodeUI
+     */
+    this.ui = new uiClass(this);
+    
+    // finally support items[]
+    if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
+        return;
+    }
+    
+    
+    Roo.each(this.attributes.items, function(c) {
+        this.appendChild(Roo.factory(c,Roo.Tree));
+    }, this);
+    delete this.attributes.items;
+    
+    
+    
+};
+Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
+    preventHScroll: true,
+    /**
+     * Returns true if this node is expanded
+     * @return {Boolean}
+     */
+    isExpanded : function(){
+        return this.expanded;
+    },
+
+    /**
+     * Returns the UI object for this node
+     * @return {TreeNodeUI}
+     */
+    getUI : function(){
+        return this.ui;
+    },
+
+    // private override
+    setFirstChild : function(node){
+        var of = this.firstChild;
+        Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
+        if(this.childrenRendered && of && node != of){
+            of.renderIndent(true, true);
+        }
+        if(this.rendered){
+            this.renderIndent(true, true);
+        }
+    },
+
+    // private override
+    setLastChild : function(node){
+        var ol = this.lastChild;
+        Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
+        if(this.childrenRendered && ol && node != ol){
+            ol.renderIndent(true, true);
+        }
+        if(this.rendered){
+            this.renderIndent(true, true);
+        }
+    },
+
+    // these methods are overridden to provide lazy rendering support
+    // private override
+    appendChild : function()
+    {
+        var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
+        if(node && this.childrenRendered){
+            node.render();
+        }
+        this.ui.updateExpandIcon();
+        return node;
+    },
+
+    // private override
+    removeChild : function(node){
+        this.ownerTree.getSelectionModel().unselect(node);
+        Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
+        // if it's been rendered remove dom node
+        if(this.childrenRendered){
+            node.ui.remove();
+        }
+        if(this.childNodes.length < 1){
+            this.collapse(false, false);
+        }else{
+            this.ui.updateExpandIcon();
+        }
+        if(!this.firstChild) {
+            this.childrenRendered = false;
+        }
+        return node;
+    },
+
+    // private override
+    insertBefore : function(node, refNode){
+        var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
+        if(newNode && refNode && this.childrenRendered){
+            node.render();
+        }
+        this.ui.updateExpandIcon();
+        return newNode;
+    },
+
+    /**
+     * Sets the text for this node
+     * @param {String} text
+     */
+    setText : function(text){
+        var oldText = this.text;
+        this.text = text;
+        this.attributes.text = text;
+        if(this.rendered){ // event without subscribing
+            this.ui.onTextChange(this, text, oldText);
+        }
+        this.fireEvent("textchange", this, text, oldText);
+    },
+
+    /**
+     * Triggers selection of this node
+     */
+    select : function(){
+        this.getOwnerTree().getSelectionModel().select(this);
+    },
+
+    /**
+     * Triggers deselection of this node
+     */
+    unselect : function(){
+        this.getOwnerTree().getSelectionModel().unselect(this);
+    },
+
+    /**
+     * Returns true if this node is selected
+     * @return {Boolean}
+     */
+    isSelected : function(){
+        return this.getOwnerTree().getSelectionModel().isSelected(this);
+    },
+
+    /**
+     * Expand this node.
+     * @param {Boolean} deep (optional) True to expand all children as well
+     * @param {Boolean} anim (optional) false to cancel the default animation
+     * @param {Function} callback (optional) A callback to be called when
+     * expanding this node completes (does not wait for deep expand to complete).
+     * Called with 1 parameter, this node.
+     */
+    expand : function(deep, anim, callback){
+        if(!this.expanded){
+            if(this.fireEvent("beforeexpand", this, deep, anim) === false){
+                return;
+            }
+            if(!this.childrenRendered){
+                this.renderChildren();
+            }
+            this.expanded = true;
+            
+            if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
+                this.ui.animExpand(function(){
+                    this.fireEvent("expand", this);
+                    if(typeof callback == "function"){
+                        callback(this);
+                    }
+                    if(deep === true){
+                        this.expandChildNodes(true);
+                    }
+                }.createDelegate(this));
+                return;
+            }else{
+                this.ui.expand();
+                this.fireEvent("expand", this);
+                if(typeof callback == "function"){
+                    callback(this);
+                }
+            }
+        }else{
+           if(typeof callback == "function"){
+               callback(this);
+           }
+        }
+        if(deep === true){
+            this.expandChildNodes(true);
+        }
+    },
+
+    isHiddenRoot : function(){
+        return this.isRoot && !this.getOwnerTree().rootVisible;
+    },
+
+    /**
+     * Collapse this node.
+     * @param {Boolean} deep (optional) True to collapse all children as well
+     * @param {Boolean} anim (optional) false to cancel the default animation
+     */
+    collapse : function(deep, anim){
+        if(this.expanded && !this.isHiddenRoot()){
+            if(this.fireEvent("beforecollapse", this, deep, anim) === false){
+                return;
+            }
+            this.expanded = false;
+            if((this.getOwnerTree().animate && anim !== false) || anim){
+                this.ui.animCollapse(function(){
+                    this.fireEvent("collapse", this);
+                    if(deep === true){
+                        this.collapseChildNodes(true);
+                    }
+                }.createDelegate(this));
+                return;
+            }else{
+                this.ui.collapse();
+                this.fireEvent("collapse", this);
+            }
+        }
+        if(deep === true){
+            var cs = this.childNodes;
+            for(var i = 0, len = cs.length; i < len; i++) {
+               cs[i].collapse(true, false);
+            }
+        }
+    },
+
+    // private
+    delayedExpand : function(delay){
+        if(!this.expandProcId){
+            this.expandProcId = this.expand.defer(delay, this);
+        }
+    },
+
+    // private
+    cancelExpand : function(){
+        if(this.expandProcId){
+            clearTimeout(this.expandProcId);
+        }
+        this.expandProcId = false;
+    },
+
+    /**
+     * Toggles expanded/collapsed state of the node
+     */
+    toggle : function(){
+        if(this.expanded){
+            this.collapse();
+        }else{
+            this.expand();
+        }
+    },
+
+    /**
+     * Ensures all parent nodes are expanded
+     */
+    ensureVisible : function(callback){
+        var tree = this.getOwnerTree();
+        tree.expandPath(this.parentNode.getPath(), false, function(){
+            tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
+            Roo.callback(callback);
+        }.createDelegate(this));
+    },
+
+    /**
+     * Expand all child nodes
+     * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
+     */
+    expandChildNodes : function(deep){
+        var cs = this.childNodes;
+        for(var i = 0, len = cs.length; i < len; i++) {
+               cs[i].expand(deep);
+        }
+    },
+
+    /**
+     * Collapse all child nodes
+     * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
+     */
+    collapseChildNodes : function(deep){
+        var cs = this.childNodes;
+        for(var i = 0, len = cs.length; i < len; i++) {
+               cs[i].collapse(deep);
+        }
+    },
+
+    /**
+     * Disables this node
+     */
+    disable : function(){
+        this.disabled = true;
+        this.unselect();
+        if(this.rendered && this.ui.onDisableChange){ // event without subscribing
+            this.ui.onDisableChange(this, true);
+        }
+        this.fireEvent("disabledchange", this, true);
+    },
+
+    /**
+     * Enables this node
+     */
+    enable : function(){
+        this.disabled = false;
+        if(this.rendered && this.ui.onDisableChange){ // event without subscribing
+            this.ui.onDisableChange(this, false);
+        }
+        this.fireEvent("disabledchange", this, false);
+    },
+
+    // private
+    renderChildren : function(suppressEvent){
+        if(suppressEvent !== false){
+            this.fireEvent("beforechildrenrendered", this);
+        }
+        var cs = this.childNodes;
+        for(var i = 0, len = cs.length; i < len; i++){
+            cs[i].render(true);
+        }
+        this.childrenRendered = true;
+    },
+
+    // private
+    sort : function(fn, scope){
+        Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
+        if(this.childrenRendered){
+            var cs = this.childNodes;
+            for(var i = 0, len = cs.length; i < len; i++){
+                cs[i].render(true);
+            }
+        }
+    },
+
+    // private
+    render : function(bulkRender){
+        this.ui.render(bulkRender);
+        if(!this.rendered){
+            this.rendered = true;
+            if(this.expanded){
+                this.expanded = false;
+                this.expand(false, false);
+            }
+        }
+    },
+
+    // private
+    renderIndent : function(deep, refresh){
+        if(refresh){
+            this.ui.childIndent = null;
+        }
+        this.ui.renderIndent();
+        if(deep === true && this.childrenRendered){
+            var cs = this.childNodes;
+            for(var i = 0, len = cs.length; i < len; i++){
+                cs[i].renderIndent(true, refresh);
+            }
+        }
+    }
+});/*
+ * 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.tree.AsyncTreeNode
+ * @extends Roo.tree.TreeNode
+ * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
+ * @constructor
+ * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
+ */
+ Roo.tree.AsyncTreeNode = function(config){
+    this.loaded = false;
+    this.loading = false;
+    Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
+    /**
+    * @event beforeload
+    * Fires before this node is loaded, return false to cancel
+    * @param {Node} this This node
+    */
+    this.addEvents({'beforeload':true, 'load': true});
+    /**
+    * @event load
+    * Fires when this node is loaded
+    * @param {Node} this This node
+    */
+    /**
+     * The loader used by this node (defaults to using the tree's defined loader)
+     * @type TreeLoader
+     * @property loader
+     */
+};
+Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
+    expand : function(deep, anim, callback){
+        if(this.loading){ // if an async load is already running, waiting til it's done
+            var timer;
+            var f = function(){
+                if(!this.loading){ // done loading
+                    clearInterval(timer);
+                    this.expand(deep, anim, callback);
+                }
+            }.createDelegate(this);
+            timer = setInterval(f, 200);
+            return;
+        }
+        if(!this.loaded){
+            if(this.fireEvent("beforeload", this) === false){
+                return;
+            }
+            this.loading = true;
+            this.ui.beforeLoad(this);
+            var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
+            if(loader){
+                loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
+                return;
+            }
+        }
+        Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
+    },
+    
+    /**
+     * Returns true if this node is currently loading
+     * @return {Boolean}
+     */
+    isLoading : function(){
+        return this.loading;  
+    },
+    
+    loadComplete : function(deep, anim, callback){
+        this.loading = false;
+        this.loaded = true;
+        this.ui.afterLoad(this);
+        this.fireEvent("load", this);
+        this.expand(deep, anim, callback);
+    },
+    
+    /**
+     * Returns true if this node has been loaded
+     * @return {Boolean}
+     */
+    isLoaded : function(){
+        return this.loaded;
+    },
+    
+    hasChildNodes : function(){
+        if(!this.isLeaf() && !this.loaded){
+            return true;
+        }else{
+            return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
+        }
+    },
+
+    /**
+     * Trigger a reload for this node
+     * @param {Function} callback
+     */
+    reload : function(callback){
+        this.collapse(false, false);
+        while(this.firstChild){
+            this.removeChild(this.firstChild);
+        }
+        this.childrenRendered = false;
+        this.loaded = false;
+        if(this.isHiddenRoot()){
+            this.expanded = false;
+        }
+        this.expand(false, false, callback);
+    }
+});/*
+ * 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.tree.TreeNodeUI
+ * @constructor
+ * @param {Object} node The node to render
+ * The TreeNode UI implementation is separate from the
+ * tree implementation. Unless you are customizing the tree UI,
+ * you should never have to use this directly.
+ */
+Roo.tree.TreeNodeUI = function(node){
+    this.node = node;
+    this.rendered = false;
+    this.animating = false;
+    this.emptyIcon = Roo.BLANK_IMAGE_URL;
+};
+
+Roo.tree.TreeNodeUI.prototype = {
+    removeChild : function(node){
+        if(this.rendered){
+            this.ctNode.removeChild(node.ui.getEl());
+        }
+    },
+
+    beforeLoad : function(){
+         this.addClass("x-tree-node-loading");
+    },
+
+    afterLoad : function(){
+         this.removeClass("x-tree-node-loading");
+    },
+
+    onTextChange : function(node, text, oldText){
+        if(this.rendered){
+            this.textNode.innerHTML = text;
+        }
+    },
+
+    onDisableChange : function(node, state){
+        this.disabled = state;
+        if(state){
+            this.addClass("x-tree-node-disabled");
+        }else{
+            this.removeClass("x-tree-node-disabled");
+        }
+    },
+
+    onSelectedChange : function(state){
+        if(state){
+            this.focus();
+            this.addClass("x-tree-selected");
+        }else{
+            //this.blur();
+            this.removeClass("x-tree-selected");
+        }
+    },
+
+    onMove : function(tree, node, oldParent, newParent, index, refNode){
+        this.childIndent = null;
+        if(this.rendered){
+            var targetNode = newParent.ui.getContainer();
+            if(!targetNode){//target not rendered
+                this.holder = document.createElement("div");
+                this.holder.appendChild(this.wrap);
+                return;
+            }
+            var insertBefore = refNode ? refNode.ui.getEl() : null;
+            if(insertBefore){
+                targetNode.insertBefore(this.wrap, insertBefore);
+            }else{
+                targetNode.appendChild(this.wrap);
+            }
+            this.node.renderIndent(true);
+        }
+    },
+
+    addClass : function(cls){
+        if(this.elNode){
+            Roo.fly(this.elNode).addClass(cls);
+        }
+    },
+
+    removeClass : function(cls){
+        if(this.elNode){
+            Roo.fly(this.elNode).removeClass(cls);
+        }
+    },
+
+    remove : function(){
+        if(this.rendered){
+            this.holder = document.createElement("div");
+            this.holder.appendChild(this.wrap);
+        }
+    },
+
+    fireEvent : function(){
+        return this.node.fireEvent.apply(this.node, arguments);
+    },
+
+    initEvents : function(){
+        this.node.on("move", this.onMove, this);
+        var E = Roo.EventManager;
+        var a = this.anchor;
+
+        var el = Roo.fly(a, '_treeui');
+
+        if(Roo.isOpera){ // opera render bug ignores the CSS
+            el.setStyle("text-decoration", "none");
+        }
+
+        el.on("click", this.onClick, this);
+        el.on("dblclick", this.onDblClick, this);
+
+        if(this.checkbox){
+            Roo.EventManager.on(this.checkbox,
+                    Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
+        }
+
+        el.on("contextmenu", this.onContextMenu, this);
+
+        var icon = Roo.fly(this.iconNode);
+        icon.on("click", this.onClick, this);
+        icon.on("dblclick", this.onDblClick, this);
+        icon.on("contextmenu", this.onContextMenu, this);
+        E.on(this.ecNode, "click", this.ecClick, this, true);
+
+        if(this.node.disabled){
+            this.addClass("x-tree-node-disabled");
+        }
+        if(this.node.hidden){
+            this.addClass("x-tree-node-disabled");
+        }
+        var ot = this.node.getOwnerTree();
+        var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
+        if(dd && (!this.node.isRoot || ot.rootVisible)){
+            Roo.dd.Registry.register(this.elNode, {
+                node: this.node,
+                handles: this.getDDHandles(),
+                isHandle: false
+            });
+        }
+    },
+
+    getDDHandles : function(){
+        return [this.iconNode, this.textNode];
+    },
+
+    hide : function(){
+        if(this.rendered){
+            this.wrap.style.display = "none";
+        }
+    },
+
+    show : function(){
+        if(this.rendered){
+            this.wrap.style.display = "";
+        }
+    },
+
+    onContextMenu : function(e){
+        if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
+            e.preventDefault();
+            this.focus();
+            this.fireEvent("contextmenu", this.node, e);
+        }
+    },
+
+    onClick : function(e){
+        if(this.dropping){
+            e.stopEvent();
+            return;
+        }
+        if(this.fireEvent("beforeclick", this.node, e) !== false){
+            if(!this.disabled && this.node.attributes.href){
+                this.fireEvent("click", this.node, e);
+                return;
+            }
+            e.preventDefault();
+            if(this.disabled){
+                return;
+            }
+
+            if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
+                this.node.toggle();
+            }
+
+            this.fireEvent("click", this.node, e);
+        }else{
+            e.stopEvent();
+        }
+    },
+
+    onDblClick : function(e){
+        e.preventDefault();
+        if(this.disabled){
+            return;
+        }
+        if(this.checkbox){
+            this.toggleCheck();
+        }
+        if(!this.animating && this.node.hasChildNodes()){
+            this.node.toggle();
+        }
+        this.fireEvent("dblclick", this.node, e);
+    },
+
+    onCheckChange : function(){
+        var checked = this.checkbox.checked;
+        this.node.attributes.checked = checked;
+        this.fireEvent('checkchange', this.node, checked);
+    },
+
+    ecClick : function(e){
+        if(!this.animating && this.node.hasChildNodes()){
+            this.node.toggle();
+        }
+    },
+
+    startDrop : function(){
+        this.dropping = true;
+    },
+
+    // delayed drop so the click event doesn't get fired on a drop
+    endDrop : function(){
+       setTimeout(function(){
+           this.dropping = false;
+       }.createDelegate(this), 50);
+    },
+
+    expand : function(){
+        this.updateExpandIcon();
+        this.ctNode.style.display = "";
+    },
+
+    focus : function(){
+        if(!this.node.preventHScroll){
+            try{this.anchor.focus();
+            }catch(e){}
+        }else if(!Roo.isIE){
+            try{
+                var noscroll = this.node.getOwnerTree().getTreeEl().dom;
+                var l = noscroll.scrollLeft;
+                this.anchor.focus();
+                noscroll.scrollLeft = l;
+            }catch(e){}
+        }
+    },
+
+    toggleCheck : function(value){
+        var cb = this.checkbox;
+        if(cb){
+            cb.checked = (value === undefined ? !cb.checked : value);
+        }
+    },
+
+    blur : function(){
+        try{
+            this.anchor.blur();
+        }catch(e){}
+    },
+
+    animExpand : function(callback){
+        var ct = Roo.get(this.ctNode);
+        ct.stopFx();
+        if(!this.node.hasChildNodes()){
+            this.updateExpandIcon();
+            this.ctNode.style.display = "";
+            Roo.callback(callback);
+            return;
+        }
+        this.animating = true;
+        this.updateExpandIcon();
+
+        ct.slideIn('t', {
+           callback : function(){
+               this.animating = false;
+               Roo.callback(callback);
+            },
+            scope: this,
+            duration: this.node.ownerTree.duration || .25
+        });
+    },
+
+    highlight : function(){
+        var tree = this.node.getOwnerTree();
+        Roo.fly(this.wrap).highlight(
+            tree.hlColor || "C3DAF9",
+            {endColor: tree.hlBaseColor}
+        );
+    },
+
+    collapse : function(){
+        this.updateExpandIcon();
+        this.ctNode.style.display = "none";
+    },
+
+    animCollapse : function(callback){
+        var ct = Roo.get(this.ctNode);
+        ct.enableDisplayMode('block');
+        ct.stopFx();
+
+        this.animating = true;
+        this.updateExpandIcon();
+
+        ct.slideOut('t', {
+            callback : function(){
+               this.animating = false;
+               Roo.callback(callback);
+            },
+            scope: this,
+            duration: this.node.ownerTree.duration || .25
+        });
+    },
+
+    getContainer : function(){
+        return this.ctNode;
+    },
+
+    getEl : function(){
+        return this.wrap;
+    },
+
+    appendDDGhost : function(ghostNode){
+        ghostNode.appendChild(this.elNode.cloneNode(true));
+    },
+
+    getDDRepairXY : function(){
+        return Roo.lib.Dom.getXY(this.iconNode);
+    },
+
+    onRender : function(){
+        this.render();
+    },
+
+    render : function(bulkRender){
+        var n = this.node, a = n.attributes;
+        var targetNode = n.parentNode ?
+              n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
+
+        if(!this.rendered){
+            this.rendered = true;
+
+            this.renderElements(n, a, targetNode, bulkRender);
+
+            if(a.qtip){
+               if(this.textNode.setAttributeNS){
+                   this.textNode.setAttributeNS("ext", "qtip", a.qtip);
+                   if(a.qtipTitle){
+                       this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
+                   }
+               }else{
+                   this.textNode.setAttribute("ext:qtip", a.qtip);
+                   if(a.qtipTitle){
+                       this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
+                   }
+               }
+            }else if(a.qtipCfg){
+                a.qtipCfg.target = Roo.id(this.textNode);
+                Roo.QuickTips.register(a.qtipCfg);
+            }
+            this.initEvents();
+            if(!this.node.expanded){
+                this.updateExpandIcon();
+            }
+        }else{
+            if(bulkRender === true) {
+                targetNode.appendChild(this.wrap);
+            }
+        }
+    },
+
+    renderElements : function(n, a, targetNode, bulkRender)
+    {
+        // add some indent caching, this helps performance when rendering a large tree
+        this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
+        var t = n.getOwnerTree();
+        var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
+        if (typeof(n.attributes.html) != 'undefined') {
+            txt = n.attributes.html;
+        }
+        var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
+        var cb = typeof a.checked == 'boolean';
+        var href = a.href ? a.href : Roo.isGecko ? "" : "#";
+        var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
+            '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
+            '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
+            '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
+            cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
+            '<a hidefocus="on" href="',href,'" tabIndex="1" ',
+             a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
+                '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
+            '<ul class="x-tree-node-ct" style="display:none;"></ul>',
+            "</li>"];
+
+        if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
+            this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
+                                n.nextSibling.ui.getEl(), buf.join(""));
+        }else{
+            this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
+        }
+
+        this.elNode = this.wrap.childNodes[0];
+        this.ctNode = this.wrap.childNodes[1];
+        var cs = this.elNode.childNodes;
+        this.indentNode = cs[0];
+        this.ecNode = cs[1];
+        this.iconNode = cs[2];
+        var index = 3;
+        if(cb){
+            this.checkbox = cs[3];
+            index++;
+        }
+        this.anchor = cs[index];
+        this.textNode = cs[index].firstChild;
+    },
+
+    getAnchor : function(){
+        return this.anchor;
+    },
+
+    getTextEl : function(){
+        return this.textNode;
+    },
+
+    getIconEl : function(){
+        return this.iconNode;
+    },
+
+    isChecked : function(){
+        return this.checkbox ? this.checkbox.checked : false;
+    },
+
+    updateExpandIcon : function(){
+        if(this.rendered){
+            var n = this.node, c1, c2;
+            var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
+            var hasChild = n.hasChildNodes();
+            if(hasChild){
+                if(n.expanded){
+                    cls += "-minus";
+                    c1 = "x-tree-node-collapsed";
+                    c2 = "x-tree-node-expanded";
+                }else{
+                    cls += "-plus";
+                    c1 = "x-tree-node-expanded";
+                    c2 = "x-tree-node-collapsed";
+                }
+                if(this.wasLeaf){
+                    this.removeClass("x-tree-node-leaf");
+                    this.wasLeaf = false;
+                }
+                if(this.c1 != c1 || this.c2 != c2){
+                    Roo.fly(this.elNode).replaceClass(c1, c2);
+                    this.c1 = c1; this.c2 = c2;
+                }
+            }else{
+                // this changes non-leafs into leafs if they have no children.
+                // it's not very rational behaviour..
+                
+                if(!this.wasLeaf && this.node.leaf){
+                    Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
+                    delete this.c1;
+                    delete this.c2;
+                    this.wasLeaf = true;
+                }
+            }
+            var ecc = "x-tree-ec-icon "+cls;
+            if(this.ecc != ecc){
+                this.ecNode.className = ecc;
+                this.ecc = ecc;
+            }
+        }
+    },
+
+    getChildIndent : function(){
+        if(!this.childIndent){
+            var buf = [];
+            var p = this.node;
+            while(p){
+                if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
+                    if(!p.isLast()) {
+                        buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
+                    } else {
+                        buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
+                    }
+                }
+                p = p.parentNode;
+            }
+            this.childIndent = buf.join("");
+        }
+        return this.childIndent;
+    },
+
+    renderIndent : function(){
+        if(this.rendered){
+            var indent = "";
+            var p = this.node.parentNode;
+            if(p){
+                indent = p.ui.getChildIndent();
+            }
+            if(this.indentMarkup != indent){ // don't rerender if not required
+                this.indentNode.innerHTML = indent;
+                this.indentMarkup = indent;
+            }
+            this.updateExpandIcon();
+        }
+    }
+};
+
+Roo.tree.RootTreeNodeUI = function(){
+    Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
+};
+Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
+    render : function(){
+        if(!this.rendered){
+            var targetNode = this.node.ownerTree.innerCt.dom;
+            this.node.expanded = true;
+            targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
+            this.wrap = this.ctNode = targetNode.firstChild;
+        }
+    },
+    collapse : function(){
+    },
+    expand : function(){
+    }
+});/*
+ * 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.tree.TreeLoader
+ * @extends Roo.util.Observable
+ * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
+ * nodes from a specified URL. The response must be a javascript Array definition
+ * who's elements are node definition objects. eg:
+ * <pre><code>
+{  success : true,
+   data :      [
+   
+    { 'id': 1, 'text': 'A folder Node', 'leaf': false },
+    { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
+    ]
+}
+
+
+</code></pre>
+ * <br><br>
+ * The old style respose with just an array is still supported, but not recommended.
+ * <br><br>
+ *
+ * A server request is sent, and child nodes are loaded only when a node is expanded.
+ * The loading node's id is passed to the server under the parameter name "node" to
+ * enable the server to produce the correct child nodes.
+ * <br><br>
+ * To pass extra parameters, an event handler may be attached to the "beforeload"
+ * event, and the parameters specified in the TreeLoader's baseParams property:
+ * <pre><code>
+    myTreeLoader.on("beforeload", function(treeLoader, node) {
+        this.baseParams.category = node.attributes.category;
+    }, this);
+    
+</code></pre>
+ *
+ * This would pass an HTTP parameter called "category" to the server containing
+ * the value of the Node's "category" attribute.
+ * @constructor
+ * Creates a new Treeloader.
+ * @param {Object} config A config object containing config properties.
+ */
+Roo.tree.TreeLoader = function(config){
+    this.baseParams = {};
+    this.requestMethod = "POST";
+    Roo.apply(this, config);
+
+    this.addEvents({
+    
+        /**
+         * @event beforeload
+         * Fires before a network request is made to retrieve the Json text which specifies a node's children.
+         * @param {Object} This TreeLoader object.
+         * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
+         * @param {Object} callback The callback function specified in the {@link #load} call.
+         */
+        beforeload : true,
+        /**
+         * @event load
+         * Fires when the node has been successfuly loaded.
+         * @param {Object} This TreeLoader object.
+         * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
+         * @param {Object} response The response object containing the data from the server.
+         */
+        load : true,
+        /**
+         * @event loadexception
+         * Fires if the network request failed.
+         * @param {Object} This TreeLoader object.
+         * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
+         * @param {Object} response The response object containing the data from the server.
+         */
+        loadexception : true,
+        /**
+         * @event create
+         * Fires before a node is created, enabling you to return custom Node types 
+         * @param {Object} This TreeLoader object.
+         * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
+         */
+        create : true
+    });
+
+    Roo.tree.TreeLoader.superclass.constructor.call(this);
+};
+
+Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
+    /**
+    * @cfg {String} dataUrl The URL from which to request a Json string which
+    * specifies an array of node definition object representing the child nodes
+    * to be loaded.
+    */
+    /**
+    * @cfg {String} requestMethod either GET or POST
+    * defaults to POST (due to BC)
+    * to be loaded.
+    */
+    /**
+    * @cfg {Object} baseParams (optional) An object containing properties which
+    * specify HTTP parameters to be passed to each request for child nodes.
+    */
+    /**
+    * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
+    * created by this loader. If the attributes sent by the server have an attribute in this object,
+    * they take priority.
+    */
+    /**
+    * @cfg {Object} uiProviders (optional) An object containing properties which
+    * 
+    * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
+    * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
+    * <i>uiProvider</i> attribute of a returned child node is a string rather
+    * than a reference to a TreeNodeUI implementation, this that string value
+    * is used as a property name in the uiProviders object. You can define the provider named
+    * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
+    */
+    uiProviders : {},
+
+    /**
+    * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
+    * child nodes before loading.
+    */
+    clearOnLoad : true,
+
+    /**
+    * @cfg {String} root (optional) Default to false. Use this to read data from an object 
+    * property on loading, rather than expecting an array. (eg. more compatible to a standard
+    * Grid query { data : [ .....] }
+    */
+    
+    root : false,
+     /**
+    * @cfg {String} queryParam (optional) 
+    * Name of the query as it will be passed on the querystring (defaults to 'node')
+    * eg. the request will be ?node=[id]
+    */
+    
+    
+    queryParam: false,
+    
+    /**
+     * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
+     * This is called automatically when a node is expanded, but may be used to reload
+     * a node (or append new children if the {@link #clearOnLoad} option is false.)
+     * @param {Roo.tree.TreeNode} node
+     * @param {Function} callback
+     */
+    load : function(node, callback){
+        if(this.clearOnLoad){
+            while(node.firstChild){
+                node.removeChild(node.firstChild);
+            }
+        }
+        if(node.attributes.children){ // preloaded json children
+            var cs = node.attributes.children;
+            for(var i = 0, len = cs.length; i < len; i++){
+                node.appendChild(this.createNode(cs[i]));
+            }
+            if(typeof callback == "function"){
+                callback();
+            }
+        }else if(this.dataUrl){
+            this.requestData(node, callback);
+        }
+    },
+
+    getParams: function(node){
+        var buf = [], bp = this.baseParams;
+        for(var key in bp){
+            if(typeof bp[key] != "function"){
+                buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
+            }
+        }
+        var n = this.queryParam === false ? 'node' : this.queryParam;
+        buf.push(n + "=", encodeURIComponent(node.id));
+        return buf.join("");
+    },
+
+    requestData : function(node, callback){
+        if(this.fireEvent("beforeload", this, node, callback) !== false){
+            this.transId = Roo.Ajax.request({
+                method:this.requestMethod,
+                url: this.dataUrl||this.url,
+                success: this.handleResponse,
+                failure: this.handleFailure,
+                scope: this,
+                argument: {callback: callback, node: node},
+                params: this.getParams(node)
+            });
+        }else{
+            // if the load is cancelled, make sure we notify
+            // the node that we are done
+            if(typeof callback == "function"){
+                callback();
+            }
+        }
+    },
+
+    isLoading : function(){
+        return this.transId ? true : false;
+    },
+
+    abort : function(){
+        if(this.isLoading()){
+            Roo.Ajax.abort(this.transId);
+        }
+    },
+
+    // private
+    createNode : function(attr)
+    {
+        // apply baseAttrs, nice idea Corey!
+        if(this.baseAttrs){
+            Roo.applyIf(attr, this.baseAttrs);
+        }
+        if(this.applyLoader !== false){
+            attr.loader = this;
+        }
+        // uiProvider = depreciated..
+        
+        if(typeof(attr.uiProvider) == 'string'){
+           attr.uiProvider = this.uiProviders[attr.uiProvider] || 
+                /**  eval:var:attr */ eval(attr.uiProvider);
+        }
+        if(typeof(this.uiProviders['default']) != 'undefined') {
+            attr.uiProvider = this.uiProviders['default'];
+        }
+        
+        this.fireEvent('create', this, attr);
+        
+        attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
+        return(attr.leaf ?
+                        new Roo.tree.TreeNode(attr) :
+                        new Roo.tree.AsyncTreeNode(attr));
+    },
+
+    processResponse : function(response, node, callback)
+    {
+        var json = response.responseText;
+        try {
+            
+            var o = Roo.decode(json);
+            
+            if (this.root === false && typeof(o.success) != undefined) {
+                this.root = 'data'; // the default behaviour for list like data..
+                }
+                
+            if (this.root !== false &&  !o.success) {
+                // it's a failure condition.
+                var a = response.argument;
+                this.fireEvent("loadexception", this, a.node, response);
+                Roo.log("Load failed - should have a handler really");
+                return;
+            }
+            
+            
+            
+            if (this.root !== false) {
+                 o = o[this.root];
+            }
+            
+            for(var i = 0, len = o.length; i < len; i++){
+                var n = this.createNode(o[i]);
+                if(n){
+                    node.appendChild(n);
+                }
+            }
+            if(typeof callback == "function"){
+                callback(this, node);
+            }
+        }catch(e){
+            this.handleFailure(response);
+        }
+    },
+
+    handleResponse : function(response){
+        this.transId = false;
+        var a = response.argument;
+        this.processResponse(response, a.node, a.callback);
+        this.fireEvent("load", this, a.node, response);
+    },
+
+    handleFailure : function(response)
+    {
+        // should handle failure better..
+        this.transId = false;
+        var a = response.argument;
+        this.fireEvent("loadexception", this, a.node, response);
+        if(typeof a.callback == "function"){
+            a.callback(this, a.node);
+        }
+    }
+});/*
+ * 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.tree.TreeFilter
+* Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
+* @param {TreePanel} tree
+* @param {Object} config (optional)
+ */
+Roo.tree.TreeFilter = function(tree, config){
+    this.tree = tree;
+    this.filtered = {};
+    Roo.apply(this, config);
+};
+
+Roo.tree.TreeFilter.prototype = {
+    clearBlank:false,
+    reverse:false,
+    autoClear:false,
+    remove:false,
+
+     /**
+     * Filter the data by a specific attribute.
+     * @param {String/RegExp} value Either string that the attribute value
+     * should start with or a RegExp to test against the attribute
+     * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
+     * @param {TreeNode} startNode (optional) The node to start the filter at.
+     */
+    filter : function(value, attr, startNode){
+        attr = attr || "text";
+        var f;
+        if(typeof value == "string"){
+            var vlen = value.length;
+            // auto clear empty filter
+            if(vlen == 0 && this.clearBlank){
+                this.clear();
+                return;
+            }
+            value = value.toLowerCase();
+            f = function(n){
+                return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
+            };
+        }else if(value.exec){ // regex?
+            f = function(n){
+                return value.test(n.attributes[attr]);
+            };
+        }else{
+            throw 'Illegal filter type, must be string or regex';
+        }
+        this.filterBy(f, null, startNode);
+       },
+
+    /**
+     * Filter by a function. The passed function will be called with each
+     * node in the tree (or from the startNode). If the function returns true, the node is kept
+     * otherwise it is filtered. If a node is filtered, its children are also filtered.
+     * @param {Function} fn The filter function
+     * @param {Object} scope (optional) The scope of the function (defaults to the current node)
+     */
+    filterBy : function(fn, scope, startNode){
+        startNode = startNode || this.tree.root;
+        if(this.autoClear){
+            this.clear();
+        }
+        var af = this.filtered, rv = this.reverse;
+        var f = function(n){
+            if(n == startNode){
+                return true;
+            }
+            if(af[n.id]){
+                return false;
+            }
+            var m = fn.call(scope || n, n);
+            if(!m || rv){
+                af[n.id] = n;
+                n.ui.hide();
+                return false;
+            }
+            return true;
+        };
+        startNode.cascade(f);
+        if(this.remove){
+           for(var id in af){
+               if(typeof id != "function"){
+                   var n = af[id];
+                   if(n && n.parentNode){
+                       n.parentNode.removeChild(n);
+                   }
+               }
+           }
+        }
+    },
+
+    /**
+     * Clears the current filter. Note: with the "remove" option
+     * set a filter cannot be cleared.
+     */
+    clear : function(){
+        var t = this.tree;
+        var af = this.filtered;
+        for(var id in af){
+            if(typeof id != "function"){
+                var n = af[id];
+                if(n){
+                    n.ui.show();
+                }
+            }
+        }
+        this.filtered = {};
+    }
+};
+/*
+ * 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.tree.TreeSorter
+ * Provides sorting of nodes in a TreePanel
+ * 
+ * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
+ * @cfg {String} property The named attribute on the node to sort by (defaults to text)
+ * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
+ * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
+ * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
+ * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
+ * @constructor
+ * @param {TreePanel} tree
+ * @param {Object} config
+ */
+Roo.tree.TreeSorter = function(tree, config){
+    Roo.apply(this, config);
+    tree.on("beforechildrenrendered", this.doSort, this);
+    tree.on("append", this.updateSort, this);
+    tree.on("insert", this.updateSort, this);
+    
+    var dsc = this.dir && this.dir.toLowerCase() == "desc";
+    var p = this.property || "text";
+    var sortType = this.sortType;
+    var fs = this.folderSort;
+    var cs = this.caseSensitive === true;
+    var leafAttr = this.leafAttr || 'leaf';
+
+    this.sortFn = function(n1, n2){
+        if(fs){
+            if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
+                return 1;
+            }
+            if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
+                return -1;
+            }
+        }
+       var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
+       var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
+       if(v1 < v2){
+                       return dsc ? +1 : -1;
+               }else if(v1 > v2){
+                       return dsc ? -1 : +1;
+        }else{
+               return 0;
+        }
+    };
+};
+
+Roo.tree.TreeSorter.prototype = {
+    doSort : function(node){
+        node.sort(this.sortFn);
+    },
+    
+    compareNodes : function(n1, n2){
+        return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
+    },
+    
+    updateSort : function(tree, node){
+        if(node.childrenRendered){
+            this.doSort.defer(1, this, [node]);
+        }
+    }
+};/*
+ * 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">
+ */
+
+if(Roo.dd.DropZone){
+    
+Roo.tree.TreeDropZone = function(tree, config){
+    this.allowParentInsert = false;
+    this.allowContainerDrop = false;
+    this.appendOnly = false;
+    Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
+    this.tree = tree;
+    this.lastInsertClass = "x-tree-no-status";
+    this.dragOverData = {};
+};
+
+Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
+    ddGroup : "TreeDD",
+    scroll:  true,
+    
+    expandDelay : 1000,
+    
+    expandNode : function(node){
+        if(node.hasChildNodes() && !node.isExpanded()){
+            node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
+        }
+    },
+    
+    queueExpand : function(node){
+        this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
+    },
+    
+    cancelExpand : function(){
+        if(this.expandProcId){
+            clearTimeout(this.expandProcId);
+            this.expandProcId = false;
+        }
+    },
+    
+    isValidDropPoint : function(n, pt, dd, e, data){
+        if(!n || !data){ return false; }
+        var targetNode = n.node;
+        var dropNode = data.node;
+        // default drop rules
+        if(!(targetNode && targetNode.isTarget && pt)){
+            return false;
+        }
+        if(pt == "append" && targetNode.allowChildren === false){
+            return false;
+        }
+        if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
+            return false;
+        }
+        if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
+            return false;
+        }
+        // reuse the object
+        var overEvent = this.dragOverData;
+        overEvent.tree = this.tree;
+        overEvent.target = targetNode;
+        overEvent.data = data;
+        overEvent.point = pt;
+        overEvent.source = dd;
+        overEvent.rawEvent = e;
+        overEvent.dropNode = dropNode;
+        overEvent.cancel = false;  
+        var result = this.tree.fireEvent("nodedragover", overEvent);
+        return overEvent.cancel === false && result !== false;
+    },
+    
+    getDropPoint : function(e, n, dd)
+    {
+        var tn = n.node;
+        if(tn.isRoot){
+            return tn.allowChildren !== false ? "append" : false; // always append for root
+        }
+        var dragEl = n.ddel;
+        var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
+        var y = Roo.lib.Event.getPageY(e);
+        //var noAppend = tn.allowChildren === false || tn.isLeaf();
+        
+        // we may drop nodes anywhere, as long as allowChildren has not been set to false..
+        var noAppend = tn.allowChildren === false;
+        if(this.appendOnly || tn.parentNode.allowChildren === false){
+            return noAppend ? false : "append";
+        }
+        var noBelow = false;
+        if(!this.allowParentInsert){
+            noBelow = tn.hasChildNodes() && tn.isExpanded();
+        }
+        var q = (b - t) / (noAppend ? 2 : 3);
+        if(y >= t && y < (t + q)){
+            return "above";
+        }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
+            return "below";
+        }else{
+            return "append";
+        }
+    },
+    
+    onNodeEnter : function(n, dd, e, data)
+    {
+        this.cancelExpand();
+    },
+    
+    onNodeOver : function(n, dd, e, data)
+    {
+       
+        var pt = this.getDropPoint(e, n, dd);
+        var node = n.node;
+        
+        // auto node expand check
+        if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
+            this.queueExpand(node);
+        }else if(pt != "append"){
+            this.cancelExpand();
+        }
+        
+        // set the insert point style on the target node
+        var returnCls = this.dropNotAllowed;
+        if(this.isValidDropPoint(n, pt, dd, e, data)){
+           if(pt){
+               var el = n.ddel;
+               var cls;
+               if(pt == "above"){
+                   returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
+                   cls = "x-tree-drag-insert-above";
+               }else if(pt == "below"){
+                   returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
+                   cls = "x-tree-drag-insert-below";
+               }else{
+                   returnCls = "x-tree-drop-ok-append";
+                   cls = "x-tree-drag-append";
+               }
+               if(this.lastInsertClass != cls){
+                   Roo.fly(el).replaceClass(this.lastInsertClass, cls);
+                   this.lastInsertClass = cls;
+               }
+           }
+       }
+       return returnCls;
+    },
+    
+    onNodeOut : function(n, dd, e, data){
+        
+        this.cancelExpand();
+        this.removeDropIndicators(n);
+    },
+    
+    onNodeDrop : function(n, dd, e, data){
+        var point = this.getDropPoint(e, n, dd);
+        var targetNode = n.node;
+        targetNode.ui.startDrop();
+        if(!this.isValidDropPoint(n, point, dd, e, data)){
+            targetNode.ui.endDrop();
+            return false;
+        }
+        // first try to find the drop node
+        var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
+        var dropEvent = {
+            tree : this.tree,
+            target: targetNode,
+            data: data,
+            point: point,
+            source: dd,
+            rawEvent: e,
+            dropNode: dropNode,
+            cancel: !dropNode   
+        };
+        var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
+        if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
+            targetNode.ui.endDrop();
+            return false;
+        }
+        // allow target changing
+        targetNode = dropEvent.target;
+        if(point == "append" && !targetNode.isExpanded()){
+            targetNode.expand(false, null, function(){
+                this.completeDrop(dropEvent);
+            }.createDelegate(this));
+        }else{
+            this.completeDrop(dropEvent);
+        }
+        return true;
+    },
+    
+    completeDrop : function(de){
+        var ns = de.dropNode, p = de.point, t = de.target;
+        if(!(ns instanceof Array)){
+            ns = [ns];
+        }
+        var n;
+        for(var i = 0, len = ns.length; i < len; i++){
+            n = ns[i];
+            if(p == "above"){
+                t.parentNode.insertBefore(n, t);
+            }else if(p == "below"){
+                t.parentNode.insertBefore(n, t.nextSibling);
+            }else{
+                t.appendChild(n);
+            }
+        }
+        n.ui.focus();
+        if(this.tree.hlDrop){
+            n.ui.highlight();
+        }
+        t.ui.endDrop();
+        this.tree.fireEvent("nodedrop", de);
+    },
+    
+    afterNodeMoved : function(dd, data, e, targetNode, dropNode){
+        if(this.tree.hlDrop){
+            dropNode.ui.focus();
+            dropNode.ui.highlight();
+        }
+        this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
+    },
+    
+    getTree : function(){
+        return this.tree;
+    },
+    
+    removeDropIndicators : function(n){
+        if(n && n.ddel){
+            var el = n.ddel;
+            Roo.fly(el).removeClass([
+                    "x-tree-drag-insert-above",
+                    "x-tree-drag-insert-below",
+                    "x-tree-drag-append"]);
+            this.lastInsertClass = "_noclass";
+        }
+    },
+    
+    beforeDragDrop : function(target, e, id){
+        this.cancelExpand();
+        return true;
+    },
+    
+    afterRepair : function(data){
+        if(data && Roo.enableFx){
+            data.node.ui.highlight();
+        }
+        this.hideProxy();
+    } 
+    
+});
+
+}
+/*
+ * 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">
+ */
+
+if(Roo.dd.DragZone){
+Roo.tree.TreeDragZone = function(tree, config){
+    Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
+    this.tree = tree;
+};
+
+Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
+    ddGroup : "TreeDD",
+   
+    onBeforeDrag : function(data, e){
+        var n = data.node;
+        return n && n.draggable && !n.disabled;
+    },
+     
+    
+    onInitDrag : function(e){
+        var data = this.dragData;
+        this.tree.getSelectionModel().select(data.node);
+        this.proxy.update("");
+        data.node.ui.appendDDGhost(this.proxy.ghost.dom);
+        this.tree.fireEvent("startdrag", this.tree, data.node, e);
+    },
+    
+    getRepairXY : function(e, data){
+        return data.node.ui.getDDRepairXY();
+    },
+    
+    onEndDrag : function(data, e){
+        this.tree.fireEvent("enddrag", this.tree, data.node, e);
+        
+        
+    },
+    
+    onValidDrop : function(dd, e, id){
+        this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
+        this.hideProxy();
+    },
+    
+    beforeInvalidDrop : function(e, id){
+        // this scrolls the original position back into view
+        var sm = this.tree.getSelectionModel();
+        sm.clearSelections();
+        sm.select(this.dragData.node);
+    }
+});
+}/*
+ * 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.tree.TreeEditor
+ * @extends Roo.Editor
+ * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
+ * as the editor field.
+ * @constructor
+ * @param {Object} config (used to be the tree panel.)
+ * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
+ * 
+ * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
+ * @cfg {Roo.form.TextField} field [required] The field configuration
+ *
+ * 
+ */
+Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
+    var tree = config;
+    var field;
+    if (oldconfig) { // old style..
+        field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
+    } else {
+        // new style..
+        tree = config.tree;
+        config.field = config.field  || {};
+        config.field.xtype = 'TextField';
+        field = Roo.factory(config.field, Roo.form);
+    }
+    config = config || {};
+    
     
-    rendererTip: false,
-    // private
-    restrictExpand : function(node){
-        var p = node.parentNode;
-        if(p){
-            if(p.expandedChild && p.expandedChild.parentNode == p){
-                p.expandedChild.collapse();
-            }
-            p.expandedChild = node;
-        }
-    },
+    this.addEvents({
+        /**
+         * @event beforenodeedit
+         * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
+         * false from the handler of this event.
+         * @param {Editor} this
+         * @param {Roo.tree.Node} node 
+         */
+        "beforenodeedit" : true
+    });
+    
+    //Roo.log(config);
+    Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
 
-    // private override
-    setRootNode : function(node){
-        Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
-        if(!this.rootVisible){
-            node.ui = new Roo.tree.RootTreeNodeUI(node);
-        }
-        return node;
-    },
+    this.tree = tree;
+
+    tree.on('beforeclick', this.beforeNodeClick, this);
+    tree.getTreeEl().on('mousedown', this.hide, this);
+    this.on('complete', this.updateNode, this);
+    this.on('beforestartedit', this.fitToTree, this);
+    this.on('startedit', this.bindScroll, this, {delay:10});
+    this.on('specialkey', this.onSpecialKey, this);
+};
 
+Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
     /**
-     * Returns the container element for this TreePanel
+     * @cfg {String} alignment
+     * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
      */
-    getEl : function(){
-        return this.el;
-    },
-
+    alignment: "l-l",
+    // inherit
+    autoSize: false,
     /**
-     * Returns the default TreeLoader for this TreePanel
+     * @cfg {Boolean} hideEl
+     * True to hide the bound element while the editor is displayed (defaults to false)
      */
-    getLoader : function(){
-        return this.loader;
-    },
-
+    hideEl : false,
     /**
-     * Expand all nodes
+     * @cfg {String} cls
+     * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
      */
-    expandAll : function(){
-        this.root.expand(true);
-    },
-
+    cls: "x-small-editor x-tree-editor",
     /**
-     * Collapse all nodes
+     * @cfg {Boolean} shim
+     * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
      */
-    collapseAll : function(){
-        this.root.collapse(true);
-    },
-
+    shim:false,
+    // inherit
+    shadow:"frame",
     /**
-     * Returns the selection model used by this TreePanel
+     * @cfg {Number} maxWidth
+     * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
+     * the containing tree element's size, it will be automatically limited for you to the container width, taking
+     * scroll and client offsets into account prior to each edit.
      */
-    getSelectionModel : function(){
-        if(!this.selModel){
-            this.selModel = new Roo.tree.DefaultSelectionModel();
+    maxWidth: 250,
+
+    editDelay : 350,
+
+    // private
+    fitToTree : function(ed, el){
+        var td = this.tree.getTreeEl().dom, nd = el.dom;
+        if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
+            td.scrollLeft = nd.offsetLeft;
         }
-        return this.selModel;
+        var w = Math.min(
+                this.maxWidth,
+                (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
+        this.setSize(w, '');
+        
+        return this.fireEvent('beforenodeedit', this, this.editNode);
+        
     },
 
-    /**
-     * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
-     * @param {String} attribute (optional) Defaults to null (return the actual nodes)
-     * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
-     * @return {Array}
-     */
-    getChecked : function(a, startNode){
-        startNode = startNode || this.root;
-        var r = [];
-        var f = function(){
-            if(this.attributes.checked){
-                r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
-            }
-        }
-        startNode.cascade(f);
-        return r;
+    // private
+    triggerEdit : function(node){
+        this.completeEdit();
+        this.editNode = node;
+        this.startEdit(node.ui.textNode, node.text);
     },
 
-    /**
-     * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
-     * @param {String} path
-     * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
-     * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
-     * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
-     */
-    expandPath : function(path, attr, callback){
-        attr = attr || "id";
-        var keys = path.split(this.pathSeparator);
-        var curNode = this.root;
-        if(curNode.attributes[attr] != keys[1]){ // invalid root
-            if(callback){
-                callback(false, null);
-            }
-            return;
-        }
-        var index = 1;
-        var f = function(){
-            if(++index == keys.length){
-                if(callback){
-                    callback(true, curNode);
-                }
-                return;
-            }
-            var c = curNode.findChild(attr, keys[index]);
-            if(!c){
-                if(callback){
-                    callback(false, curNode);
-                }
-                return;
-            }
-            curNode = c;
-            c.expand(false, false, f);
-        };
-        curNode.expand(false, false, f);
+    // private
+    bindScroll : function(){
+        this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
     },
 
-    /**
-     * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
-     * @param {String} path
-     * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
-     * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
-     * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
-     */
-    selectPath : function(path, attr, callback){
-        attr = attr || "id";
-        var keys = path.split(this.pathSeparator);
-        var v = keys.pop();
-        if(keys.length > 0){
-            var f = function(success, node){
-                if(success && node){
-                    var n = node.findChild(attr, v);
-                    if(n){
-                        n.select();
-                        if(callback){
-                            callback(true, n);
-                        }
-                    }else if(callback){
-                        callback(false, n);
-                    }
-                }else{
-                    if(callback){
-                        callback(false, n);
-                    }
-                }
-            };
-            this.expandPath(keys.join(this.pathSeparator), attr, f);
-        }else{
-            this.root.select();
-            if(callback){
-                callback(true, this.root);
-            }
+    // private
+    beforeNodeClick : function(node, e){
+        var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
+        this.lastClick = new Date();
+        if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
+            e.stopEvent();
+            this.triggerEdit(node);
+            return false;
         }
+        return true;
     },
 
-    getTreeEl : function(){
-        return this.el;
+    // private
+    updateNode : function(ed, value){
+        this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
+        this.editNode.setText(value);
     },
 
-    /**
-     * Trigger rendering of this TreePanel
-     */
-    render : function(){
-        if (this.innerCt) {
-            return this; // stop it rendering more than once!!
+    // private
+    onHide : function(){
+        Roo.tree.TreeEditor.superclass.onHide.call(this);
+        if(this.editNode){
+            this.editNode.ui.focus();
         }
-        
-        this.innerCt = this.el.createChild({tag:"ul",
-               cls:"x-tree-root-ct " +
-               (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
+    },
 
-        if(this.containerScroll){
-            Roo.dd.ScrollManager.register(this.el);
-        }
-        if((this.enableDD || this.enableDrop) && !this.dropZone){
-           /**
-            * The dropZone used by this tree if drop is enabled
-            * @type Roo.tree.TreeDropZone
-            */
-             this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
-               ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
-           });
-        }
-        if((this.enableDD || this.enableDrag) && !this.dragZone){
-           /**
-            * The dragZone used by this tree if drag is enabled
-            * @type Roo.tree.TreeDragZone
-            */
-            this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
-               ddGroup: this.ddGroup || "TreeDD",
-               scroll: this.ddScroll
-           });
-        }
-        this.getSelectionModel().init(this);
-        if (!this.root) {
-            Roo.log("ROOT not set in tree");
-            return this;
-        }
-        this.root.render();
-        if(!this.rootVisible){
-            this.root.renderChildren();
+    // private
+    onSpecialKey : function(field, e){
+        var k = e.getKey();
+        if(k == e.ESC){
+            e.stopEvent();
+            this.cancelEdit();
+        }else if(k == e.ENTER && !e.hasModifier()){
+            e.stopEvent();
+            this.completeEdit();
         }
-        return this;
     }
-});/*
+});//<Script type="text/javascript">
+/*
  * Based on:
  * Ext JS Library 1.1.1
  * Copyright(c) 2006-2007, Ext JS, LLC.
@@ -34598,327 +38694,256 @@ Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
  * <script type="text/javascript">
  */
  
-
 /**
- * @class Roo.tree.DefaultSelectionModel
- * @extends Roo.util.Observable
- * The default single selection for a TreePanel.
- * @param {Object} cfg Configuration
+ * Not documented??? - probably should be...
  */
-Roo.tree.DefaultSelectionModel = function(cfg){
-   this.selNode = null;
-   
-   
-   
-   this.addEvents({
-       /**
-        * @event selectionchange
-        * Fires when the selected node changes
-        * @param {DefaultSelectionModel} this
-        * @param {TreeNode} node the new selection
-        */
-       "selectionchange" : true,
-
-       /**
-        * @event beforeselect
-        * Fires before the selected node changes, return false to cancel the change
-        * @param {DefaultSelectionModel} this
-        * @param {TreeNode} node the new selection
-        * @param {TreeNode} node the old selection
-        */
-       "beforeselect" : true
-   });
-   
-    Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
-};
 
-Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
-    init : function(tree){
-        this.tree = tree;
-        tree.getTreeEl().on("keydown", this.onKeyDown, this);
-        tree.on("click", this.onNodeClick, this);
-    },
-    
-    onNodeClick : function(node, e){
-        if (e.ctrlKey && this.selNode == node)  {
-            this.unselect(node);
-            return;
-        }
-        this.select(node);
-    },
+Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
+    //focus: Roo.emptyFn, // prevent odd scrolling behavior
     
-    /**
-     * Select a node.
-     * @param {TreeNode} node The node to select
-     * @return {TreeNode} The selected node
-     */
-    select : function(node){
-        var last = this.selNode;
-        if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
-            if(last){
-                last.ui.onSelectedChange(false);
-            }
-            this.selNode = node;
-            node.ui.onSelectedChange(true);
-            this.fireEvent("selectionchange", this, node, last);
+    renderElements : function(n, a, targetNode, bulkRender){
+        //consel.log("renderElements?");
+        this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
+
+        var t = n.getOwnerTree();
+        var tid = Pman.Tab.Document_TypesTree.tree.el.id;
+        
+        var cols = t.columns;
+        var bw = t.borderWidth;
+        var c = cols[0];
+        var href = a.href ? a.href : Roo.isGecko ? "" : "#";
+         var cb = typeof a.checked == "boolean";
+        var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
+        var colcls = 'x-t-' + tid + '-c0';
+        var buf = [
+            '<li class="x-tree-node">',
+            
+                
+                '<div class="x-tree-node-el ', a.cls,'">',
+                    // extran...
+                    '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
+                
+                
+                        '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
+                        '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
+                        '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
+                           (a.icon ? ' x-tree-node-inline-icon' : ''),
+                           (a.iconCls ? ' '+a.iconCls : ''),
+                           '" unselectable="on" />',
+                        (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
+                             (a.checked ? 'checked="checked" />' : ' />')) : ''),
+                             
+                        '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
+                            (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
+                            '<span unselectable="on" qtip="' + tx + '">',
+                             tx,
+                             '</span></a>' ,
+                    '</div>',
+                     '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
+                            (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
+                 ];
+        for(var i = 1, len = cols.length; i < len; i++){
+            c = cols[i];
+            colcls = 'x-t-' + tid + '-c' +i;
+            tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
+            buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
+                        '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
+                      "</div>");
+         }
+         
+         buf.push(
+            '</a>',
+            '<div class="x-clear"></div></div>',
+            '<ul class="x-tree-node-ct" style="display:none;"></ul>',
+            "</li>");
+        
+        if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
+            this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
+                                n.nextSibling.ui.getEl(), buf.join(""));
+        }else{
+            this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
         }
-        return node;
-    },
-    
-    /**
-     * Deselect a node.
-     * @param {TreeNode} node The node to unselect
-     */
-    unselect : function(node){
-        if(this.selNode == node){
-            this.clearSelections();
-        }    
-    },
-    
-    /**
-     * Clear all selections
-     */
-    clearSelections : function(){
-        var n = this.selNode;
-        if(n){
-            n.ui.onSelectedChange(false);
-            this.selNode = null;
-            this.fireEvent("selectionchange", this, null);
+        var el = this.wrap.firstChild;
+        this.elRow = el;
+        this.elNode = el.firstChild;
+        this.ranchor = el.childNodes[1];
+        this.ctNode = this.wrap.childNodes[1];
+        var cs = el.firstChild.childNodes;
+        this.indentNode = cs[0];
+        this.ecNode = cs[1];
+        this.iconNode = cs[2];
+        var index = 3;
+        if(cb){
+            this.checkbox = cs[3];
+            index++;
         }
-        return n;
-    },
-    
-    /**
-     * Get the selected node
-     * @return {TreeNode} The selected node
-     */
-    getSelectedNode : function(){
-        return this.selNode;    
-    },
-    
-    /**
-     * Returns true if the node is selected
-     * @param {TreeNode} node The node to check
-     * @return {Boolean}
-     */
-    isSelected : function(node){
-        return this.selNode == node;  
+        this.anchor = cs[index];
+        
+        this.textNode = cs[index].firstChild;
+        
+        //el.on("click", this.onClick, this);
+        //el.on("dblclick", this.onDblClick, this);
+        
+        
+       // console.log(this);
     },
+    initEvents : function(){
+        Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
+        
+            
+        var a = this.ranchor;
 
-    /**
-     * Selects the node above the selected node in the tree, intelligently walking the nodes
-     * @return TreeNode The new selection
-     */
-    selectPrevious : function(){
-        var s = this.selNode || this.lastSelNode;
-        if(!s){
-            return null;
-        }
-        var ps = s.previousSibling;
-        if(ps){
-            if(!ps.isExpanded() || ps.childNodes.length < 1){
-                return this.select(ps);
-            } else{
-                var lc = ps.lastChild;
-                while(lc && lc.isExpanded() && lc.childNodes.length > 0){
-                    lc = lc.lastChild;
-                }
-                return this.select(lc);
-            }
-        } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
-            return this.select(s.parentNode);
+        var el = Roo.get(a);
+
+        if(Roo.isOpera){ // opera render bug ignores the CSS
+            el.setStyle("text-decoration", "none");
         }
-        return null;
-    },
 
-    /**
-     * Selects the node above the selected node in the tree, intelligently walking the nodes
-     * @return TreeNode The new selection
-     */
-    selectNext : function(){
-        var s = this.selNode || this.lastSelNode;
-        if(!s){
-            return null;
+        el.on("click", this.onClick, this);
+        el.on("dblclick", this.onDblClick, this);
+        el.on("contextmenu", this.onContextMenu, this);
+        
+    },
+    
+    /*onSelectedChange : function(state){
+        if(state){
+            this.focus();
+            this.addClass("x-tree-selected");
+        }else{
+            //this.blur();
+            this.removeClass("x-tree-selected");
         }
-        if(s.firstChild && s.isExpanded()){
-             return this.select(s.firstChild);
-         }else if(s.nextSibling){
-             return this.select(s.nextSibling);
-         }else if(s.parentNode){
-            var newS = null;
-            s.parentNode.bubble(function(){
-                if(this.nextSibling){
-                    newS = this.getOwnerTree().selModel.select(this.nextSibling);
-                    return false;
-                }
-            });
-            return newS;
-         }
-        return null;
+    },*/
+    addClass : function(cls){
+        if(this.elRow){
+            Roo.fly(this.elRow).addClass(cls);
+        }
+        
     },
-
-    onKeyDown : function(e){
-        var s = this.selNode || this.lastSelNode;
-        // undesirable, but required
-        var sm = this;
-        if(!s){
-            return;
+    
+    
+    removeClass : function(cls){
+        if(this.elRow){
+            Roo.fly(this.elRow).removeClass(cls);
         }
-        var k = e.getKey();
-        switch(k){
-             case e.DOWN:
-                 e.stopEvent();
-                 this.selectNext();
-             break;
-             case e.UP:
-                 e.stopEvent();
-                 this.selectPrevious();
-             break;
-             case e.RIGHT:
-                 e.preventDefault();
-                 if(s.hasChildNodes()){
-                     if(!s.isExpanded()){
-                         s.expand();
-                     }else if(s.firstChild){
-                         this.select(s.firstChild, e);
-                     }
-                 }
-             break;
-             case e.LEFT:
-                 e.preventDefault();
-                 if(s.hasChildNodes() && s.isExpanded()){
-                     s.collapse();
-                 }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
-                     this.select(s.parentNode, e);
-                 }
-             break;
-        };
     }
-});
+
+    
+    
+});//<Script type="text/javascript">
+
+/*
+ * 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.tree.MultiSelectionModel
- * @extends Roo.util.Observable
- * Multi selection for a TreePanel.
- * @param {Object} cfg Configuration
+ * @class Roo.tree.ColumnTree
+ * @extends Roo.tree.TreePanel
+ * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
+ * @cfg {int} borderWidth  compined right/left border allowance
+ * @constructor
+ * @param {String/HTMLElement/Element} el The container element
+ * @param {Object} config
  */
-Roo.tree.MultiSelectionModel = function(){
-   this.selNodes = [];
-   this.selMap = {};
+Roo.tree.ColumnTree =  function(el, config)
+{
+   Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
    this.addEvents({
-       /**
-        * @event selectionchange
-        * Fires when the selected nodes change
-        * @param {MultiSelectionModel} this
-        * @param {Array} nodes Array of the selected nodes
+        /**
+        * @event resize
+        * Fire this event on a container when it resizes
+        * @param {int} w Width
+        * @param {int} h Height
         */
-       "selectionchange" : true
-   });
-   Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
-   
+       "resize" : true
+    });
+    this.on('resize', this.onResize, this);
 };
 
-Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
-    init : function(tree){
-        this.tree = tree;
-        tree.getTreeEl().on("keydown", this.onKeyDown, this);
-        tree.on("click", this.onNodeClick, this);
-    },
+Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
+    //lines:false,
     
-    onNodeClick : function(node, e){
-        this.select(node, e, e.ctrlKey);
-    },
     
-    /**
-     * Select a node.
-     * @param {TreeNode} node The node to select
-     * @param {EventObject} e (optional) An event associated with the selection
-     * @param {Boolean} keepExisting True to retain existing selections
-     * @return {TreeNode} The selected node
-     */
-    select : function(node, e, keepExisting){
-        if(keepExisting !== true){
-            this.clearSelections(true);
-        }
-        if(this.isSelected(node)){
-            this.lastSelNode = node;
-            return node;
-        }
-        this.selNodes.push(node);
-        this.selMap[node.id] = node;
-        this.lastSelNode = node;
-        node.ui.onSelectedChange(true);
-        this.fireEvent("selectionchange", this, this.selNodes);
-        return node;
-    },
+    borderWidth: Roo.isBorderBox ? 0 : 2, 
+    headEls : false,
     
-    /**
-     * Deselect a node.
-     * @param {TreeNode} node The node to unselect
-     */
-    unselect : function(node){
-        if(this.selMap[node.id]){
-            node.ui.onSelectedChange(false);
-            var sn = this.selNodes;
-            var index = -1;
-            if(sn.indexOf){
-                index = sn.indexOf(node);
-            }else{
-                for(var i = 0, len = sn.length; i < len; i++){
-                    if(sn[i] == node){
-                        index = i;
-                        break;
-                    }
-                }
-            }
-            if(index != -1){
-                this.selNodes.splice(index, 1);
-            }
-            delete this.selMap[node.id];
-            this.fireEvent("selectionchange", this, this.selNodes);
+    render : function(){
+        // add the header.....
+       
+        Roo.tree.ColumnTree.superclass.render.apply(this);
+        
+        this.el.addClass('x-column-tree');
+        
+        this.headers = this.el.createChild(
+            {cls:'x-tree-headers'},this.innerCt.dom);
+   
+        var cols = this.columns, c;
+        var totalWidth = 0;
+        this.headEls = [];
+        var  len = cols.length;
+        for(var i = 0; i < len; i++){
+             c = cols[i];
+             totalWidth += c.width;
+            this.headEls.push(this.headers.createChild({
+                 cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
+                 cn: {
+                     cls:'x-tree-hd-text',
+                     html: c.header
+                 },
+                 style:'width:'+(c.width-this.borderWidth)+'px;'
+             }));
         }
+        this.headers.createChild({cls:'x-clear'});
+        // prevent floats from wrapping when clipped
+        this.headers.setWidth(totalWidth);
+        //this.innerCt.setWidth(totalWidth);
+        this.innerCt.setStyle({ overflow: 'auto' });
+        this.onResize(this.width, this.height);
+             
+        
     },
-    
-    /**
-     * Clear all selections
-     */
-    clearSelections : function(suppressEvent){
-        var sn = this.selNodes;
-        if(sn.length > 0){
-            for(var i = 0, len = sn.length; i < len; i++){
-                sn[i].ui.onSelectedChange(false);
-            }
-            this.selNodes = [];
-            this.selMap = {};
-            if(suppressEvent !== true){
-                this.fireEvent("selectionchange", this, this.selNodes);
+    onResize : function(w,h)
+    {
+        this.height = h;
+        this.width = w;
+        // resize cols..
+        this.innerCt.setWidth(this.width);
+        this.innerCt.setHeight(this.height-20);
+        
+        // headers...
+        var cols = this.columns, c;
+        var totalWidth = 0;
+        var expEl = false;
+        var len = cols.length;
+        for(var i = 0; i < len; i++){
+            c = cols[i];
+            if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
+                // it's the expander..
+                expEl  = this.headEls[i];
+                continue;
             }
+            totalWidth += c.width;
+            
         }
-    },
-    
-    /**
-     * Returns true if the node is selected
-     * @param {TreeNode} node The node to check
-     * @return {Boolean}
-     */
-    isSelected : function(node){
-        return this.selMap[node.id] ? true : false;  
-    },
-    
-    /**
-     * Returns an array of the selected nodes
-     * @return {Array}
-     */
-    getSelectedNodes : function(){
-        return this.selNodes;    
-    },
-
-    onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
-
-    selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
+        if (expEl) {
+            expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
+        }
+        this.headers.setWidth(w-20);
 
-    selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
-});/*
+        
+        
+        
+    }
+});
+/*
  * Based on:
  * Ext JS Library 1.1.1
  * Copyright(c) 2006-2007, Ext JS, LLC.
@@ -34930,606 +38955,563 @@ Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
  */
  
 /**
- * @class Roo.tree.TreeNode
- * @extends Roo.data.Node
- * @cfg {String} text The text for this node
- * @cfg {Boolean} expanded true to start the node expanded
- * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
- * @cfg {Boolean} allowDrop false if this node cannot be drop on
- * @cfg {Boolean} disabled true to start the node disabled
- * @cfg {String} icon The path to an icon for the node. The preferred way to do this
- *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
- * @cfg {String} cls A css class to be added to the node
- * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
- * @cfg {String} href URL of the link used for the node (defaults to #)
- * @cfg {String} hrefTarget target frame for the link
- * @cfg {String} qtip An Ext QuickTip for the node
- * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
- * @cfg {Boolean} singleClickExpand True for single click expand on this node
- * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
- * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
- * (defaults to undefined with no checkbox rendered)
+ * @class Roo.menu.Menu
+ * @extends Roo.util.Observable
+ * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
+ * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
+ * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
  * @constructor
- * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
+ * Creates a new Menu
+ * @param {Object} config Configuration options
  */
-Roo.tree.TreeNode = function(attributes){
-    attributes = attributes || {};
-    if(typeof attributes == "string"){
-        attributes = {text: attributes};
-    }
-    this.childrenRendered = false;
-    this.rendered = false;
-    Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
-    this.expanded = attributes.expanded === true;
-    this.isTarget = attributes.isTarget !== false;
-    this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
-    this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
-
-    /**
-     * Read-only. The text for this node. To change it use setText().
-     * @type String
-     */
-    this.text = attributes.text;
-    /**
-     * True if this node is disabled.
-     * @type Boolean
-     */
-    this.disabled = attributes.disabled === true;
-
+Roo.menu.Menu = function(config){
+    
+    Roo.menu.Menu.superclass.constructor.call(this, config);
+    
+    this.id = this.id || Roo.id();
     this.addEvents({
         /**
-        * @event textchange
-        * Fires when the text for this node is changed
-        * @param {Node} this This node
-        * @param {String} text The new text
-        * @param {String} oldText The old text
-        */
-        "textchange" : true,
-        /**
-        * @event beforeexpand
-        * Fires before this node is expanded, return false to cancel.
-        * @param {Node} this This node
-        * @param {Boolean} deep
-        * @param {Boolean} anim
-        */
-        "beforeexpand" : true,
-        /**
-        * @event beforecollapse
-        * Fires before this node is collapsed, return false to cancel.
-        * @param {Node} this This node
-        * @param {Boolean} deep
-        * @param {Boolean} anim
-        */
-        "beforecollapse" : true,
-        /**
-        * @event expand
-        * Fires when this node is expanded
-        * @param {Node} this This node
-        */
-        "expand" : true,
-        /**
-        * @event disabledchange
-        * Fires when the disabled status of this node changes
-        * @param {Node} this This node
-        * @param {Boolean} disabled
-        */
-        "disabledchange" : true,
+         * @event beforeshow
+         * Fires before this menu is displayed
+         * @param {Roo.menu.Menu} this
+         */
+        beforeshow : true,
         /**
-        * @event collapse
-        * Fires when this node is collapsed
-        * @param {Node} this This node
-        */
-        "collapse" : true,
+         * @event beforehide
+         * Fires before this menu is hidden
+         * @param {Roo.menu.Menu} this
+         */
+        beforehide : true,
         /**
-        * @event beforeclick
-        * Fires before click processing. Return false to cancel the default action.
-        * @param {Node} this This node
-        * @param {Roo.EventObject} e The event object
-        */
-        "beforeclick":true,
+         * @event show
+         * Fires after this menu is displayed
+         * @param {Roo.menu.Menu} this
+         */
+        show : true,
         /**
-        * @event checkchange
-        * Fires when a node with a checkbox's checked property changes
-        * @param {Node} this This node
-        * @param {Boolean} checked
-        */
-        "checkchange":true,
+         * @event hide
+         * Fires after this menu is hidden
+         * @param {Roo.menu.Menu} this
+         */
+        hide : true,
         /**
-        * @event click
-        * Fires when this node is clicked
-        * @param {Node} this This node
-        * @param {Roo.EventObject} e The event object
-        */
-        "click":true,
+         * @event click
+         * Fires when this menu is clicked (or when the enter key is pressed while it is active)
+         * @param {Roo.menu.Menu} this
+         * @param {Roo.menu.Item} menuItem The menu item that was clicked
+         * @param {Roo.EventObject} e
+         */
+        click : true,
         /**
-        * @event dblclick
-        * Fires when this node is double clicked
-        * @param {Node} this This node
-        * @param {Roo.EventObject} e The event object
-        */
-        "dblclick":true,
+         * @event mouseover
+         * Fires when the mouse is hovering over this menu
+         * @param {Roo.menu.Menu} this
+         * @param {Roo.EventObject} e
+         * @param {Roo.menu.Item} menuItem The menu item that was clicked
+         */
+        mouseover : true,
         /**
-        * @event contextmenu
-        * Fires when this node is right clicked
-        * @param {Node} this This node
-        * @param {Roo.EventObject} e The event object
-        */
-        "contextmenu":true,
+         * @event mouseout
+         * Fires when the mouse exits this menu
+         * @param {Roo.menu.Menu} this
+         * @param {Roo.EventObject} e
+         * @param {Roo.menu.Item} menuItem The menu item that was clicked
+         */
+        mouseout : true,
         /**
-        * @event beforechildrenrendered
-        * Fires right before the child nodes for this node are rendered
-        * @param {Node} this This node
-        */
-        "beforechildrenrendered":true
+         * @event itemclick
+         * Fires when a menu item contained in this menu is clicked
+         * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
+         * @param {Roo.EventObject} e
+         */
+        itemclick: true
     });
-
-    var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
-
-    /**
-     * Read-only. The UI for this node
-     * @type TreeNodeUI
-     */
-    this.ui = new uiClass(this);
-    
-    // finally support items[]
-    if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
-        return;
+    if (this.registerMenu) {
+        Roo.menu.MenuMgr.register(this);
     }
     
-    
-    Roo.each(this.attributes.items, function(c) {
-        this.appendChild(Roo.factory(c,Roo.Tree));
-    }, this);
-    delete this.attributes.items;
-    
-    
-    
+    var mis = this.items;
+    this.items = new Roo.util.MixedCollection();
+    if(mis){
+        this.add.apply(this, mis);
+    }
 };
-Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
-    preventHScroll: true,
+
+Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
+    /**
+     * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
+     */
+    minWidth : 120,
+    /**
+     * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
+     * for bottom-right shadow (defaults to "sides")
+     */
+    shadow : "sides",
     /**
-     * Returns true if this node is expanded
-     * @return {Boolean}
+     * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
+     * this menu (defaults to "tl-tr?")
      */
-    isExpanded : function(){
-        return this.expanded;
-    },
-
+    subMenuAlign : "tl-tr?",
     /**
-     * Returns the UI object for this node
-     * @return {TreeNodeUI}
+     * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
+     * relative to its element of origin (defaults to "tl-bl?")
      */
-    getUI : function(){
-        return this.ui;
-    },
+    defaultAlign : "tl-bl?",
+    /**
+     * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
+     */
+    allowOtherMenus : false,
+    /**
+     * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
+     */
+    registerMenu : true,
 
-    // private override
-    setFirstChild : function(node){
-        var of = this.firstChild;
-        Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
-        if(this.childrenRendered && of && node != of){
-            of.renderIndent(true, true);
+    hidden:true,
+
+    // private
+    render : function(){
+        if(this.el){
+            return;
         }
-        if(this.rendered){
-            this.renderIndent(true, true);
+        var el = this.el = new Roo.Layer({
+            cls: "x-menu",
+            shadow:this.shadow,
+            constrain: false,
+            parentEl: this.parentEl || document.body,
+            zindex:15000
+        });
+
+        this.keyNav = new Roo.menu.MenuNav(this);
+
+        if(this.plain){
+            el.addClass("x-menu-plain");
+        }
+        if(this.cls){
+            el.addClass(this.cls);
         }
+        // generic focus element
+        this.focusEl = el.createChild({
+            tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
+        });
+        var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
+        //disabling touch- as it's causing issues ..
+        //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
+        ul.on('click'   , this.onClick, this);
+        
+        
+        ul.on("mouseover", this.onMouseOver, this);
+        ul.on("mouseout", this.onMouseOut, this);
+        this.items.each(function(item){
+            if (item.hidden) {
+                return;
+            }
+            
+            var li = document.createElement("li");
+            li.className = "x-menu-list-item";
+            ul.dom.appendChild(li);
+            item.render(li, this);
+        }, this);
+        this.ul = ul;
+        this.autoWidth();
     },
 
-    // private override
-    setLastChild : function(node){
-        var ol = this.lastChild;
-        Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
-        if(this.childrenRendered && ol && node != ol){
-            ol.renderIndent(true, true);
+    // private
+    autoWidth : function(){
+        var el = this.el, ul = this.ul;
+        if(!el){
+            return;
+        }
+        var w = this.width;
+        if(w){
+            el.setWidth(w);
+        }else if(Roo.isIE){
+            el.setWidth(this.minWidth);
+            var t = el.dom.offsetWidth; // force recalc
+            el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
         }
+    },
+
+    // private
+    delayAutoWidth : function(){
         if(this.rendered){
-            this.renderIndent(true, true);
+            if(!this.awTask){
+                this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
+            }
+            this.awTask.delay(20);
         }
     },
 
-    // these methods are overridden to provide lazy rendering support
-    // private override
-    appendChild : function()
-    {
-        var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
-        if(node && this.childrenRendered){
-            node.render();
+    // private
+    findTargetItem : function(e){
+        var t = e.getTarget(".x-menu-list-item", this.ul,  true);
+        if(t && t.menuItemId){
+            return this.items.get(t.menuItemId);
         }
-        this.ui.updateExpandIcon();
-        return node;
     },
 
-    // private override
-    removeChild : function(node){
-        this.ownerTree.getSelectionModel().unselect(node);
-        Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
-        // if it's been rendered remove dom node
-        if(this.childrenRendered){
-            node.ui.remove();
+    // private
+    onClick : function(e){
+        Roo.log("menu.onClick");
+        var t = this.findTargetItem(e);
+        if(!t){
+            return;
         }
-        if(this.childNodes.length < 1){
-            this.collapse(false, false);
-        }else{
-            this.ui.updateExpandIcon();
+        Roo.log(e);
+        if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
+            if(t == this.activeItem && t.shouldDeactivate(e)){
+                this.activeItem.deactivate();
+                delete this.activeItem;
+                return;
+            }
+            if(t.canActivate){
+                this.setActiveItem(t, true);
+            }
+            return;
+            
+            
         }
-        if(!this.firstChild) {
-            this.childrenRendered = false;
+        
+        t.onClick(e);
+        this.fireEvent("click", this, t, e);
+    },
+
+    // private
+    setActiveItem : function(item, autoExpand){
+        if(item != this.activeItem){
+            if(this.activeItem){
+                this.activeItem.deactivate();
+            }
+            this.activeItem = item;
+            item.activate(autoExpand);
+        }else if(autoExpand){
+            item.expandMenu();
         }
-        return node;
     },
 
-    // private override
-    insertBefore : function(node, refNode){
-        var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
-        if(newNode && refNode && this.childrenRendered){
-            node.render();
+    // private
+    tryActivate : function(start, step){
+        var items = this.items;
+        for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
+            var item = items.get(i);
+            if(!item.disabled && item.canActivate){
+                this.setActiveItem(item, false);
+                return item;
+            }
         }
-        this.ui.updateExpandIcon();
-        return newNode;
+        return false;
     },
 
-    /**
-     * Sets the text for this node
-     * @param {String} text
-     */
-    setText : function(text){
-        var oldText = this.text;
-        this.text = text;
-        this.attributes.text = text;
-        if(this.rendered){ // event without subscribing
-            this.ui.onTextChange(this, text, oldText);
+    // private
+    onMouseOver : function(e){
+        var t;
+        if(t = this.findTargetItem(e)){
+            if(t.canActivate && !t.disabled){
+                this.setActiveItem(t, true);
+            }
         }
-        this.fireEvent("textchange", this, text, oldText);
+        this.fireEvent("mouseover", this, e, t);
     },
 
-    /**
-     * Triggers selection of this node
-     */
-    select : function(){
-        this.getOwnerTree().getSelectionModel().select(this);
+    // private
+    onMouseOut : function(e){
+        var t;
+        if(t = this.findTargetItem(e)){
+            if(t == this.activeItem && t.shouldDeactivate(e)){
+                this.activeItem.deactivate();
+                delete this.activeItem;
+            }
+        }
+        this.fireEvent("mouseout", this, e, t);
     },
 
     /**
-     * Triggers deselection of this node
+     * Read-only.  Returns true if the menu is currently displayed, else false.
+     * @type Boolean
      */
-    unselect : function(){
-        this.getOwnerTree().getSelectionModel().unselect(this);
+    isVisible : function(){
+        return this.el && !this.hidden;
     },
 
     /**
-     * Returns true if this node is selected
-     * @return {Boolean}
+     * Displays this menu relative to another element
+     * @param {String/HTMLElement/Roo.Element} element The element to align to
+     * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
+     * the element (defaults to this.defaultAlign)
+     * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
      */
-    isSelected : function(){
-        return this.getOwnerTree().getSelectionModel().isSelected(this);
+    show : function(el, pos, parentMenu){
+        this.parentMenu = parentMenu;
+        if(!this.el){
+            this.render();
+        }
+        this.fireEvent("beforeshow", this);
+        this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
     },
 
     /**
-     * Expand this node.
-     * @param {Boolean} deep (optional) True to expand all children as well
-     * @param {Boolean} anim (optional) false to cancel the default animation
-     * @param {Function} callback (optional) A callback to be called when
-     * expanding this node completes (does not wait for deep expand to complete).
-     * Called with 1 parameter, this node.
+     * Displays this menu at a specific xy position
+     * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
+     * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
      */
-    expand : function(deep, anim, callback){
-        if(!this.expanded){
-            if(this.fireEvent("beforeexpand", this, deep, anim) === false){
-                return;
-            }
-            if(!this.childrenRendered){
-                this.renderChildren();
-            }
-            this.expanded = true;
-            
-            if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
-                this.ui.animExpand(function(){
-                    this.fireEvent("expand", this);
-                    if(typeof callback == "function"){
-                        callback(this);
-                    }
-                    if(deep === true){
-                        this.expandChildNodes(true);
-                    }
-                }.createDelegate(this));
-                return;
-            }else{
-                this.ui.expand();
-                this.fireEvent("expand", this);
-                if(typeof callback == "function"){
-                    callback(this);
-                }
-            }
-        }else{
-           if(typeof callback == "function"){
-               callback(this);
-           }
+    showAt : function(xy, parentMenu, /* private: */_e){
+        this.parentMenu = parentMenu;
+        if(!this.el){
+            this.render();
         }
-        if(deep === true){
-            this.expandChildNodes(true);
+        if(_e !== false){
+            this.fireEvent("beforeshow", this);
+            xy = this.el.adjustForConstraints(xy);
         }
+        this.el.setXY(xy);
+        this.el.show();
+        this.hidden = false;
+        this.focus();
+        this.fireEvent("show", this);
     },
 
-    isHiddenRoot : function(){
-        return this.isRoot && !this.getOwnerTree().rootVisible;
+    focus : function(){
+        if(!this.hidden){
+            this.doFocus.defer(50, this);
+        }
+    },
+
+    doFocus : function(){
+        if(!this.hidden){
+            this.focusEl.focus();
+        }
     },
 
     /**
-     * Collapse this node.
-     * @param {Boolean} deep (optional) True to collapse all children as well
-     * @param {Boolean} anim (optional) false to cancel the default animation
+     * Hides this menu and optionally all parent menus
+     * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
      */
-    collapse : function(deep, anim){
-        if(this.expanded && !this.isHiddenRoot()){
-            if(this.fireEvent("beforecollapse", this, deep, anim) === false){
-                return;
-            }
-            this.expanded = false;
-            if((this.getOwnerTree().animate && anim !== false) || anim){
-                this.ui.animCollapse(function(){
-                    this.fireEvent("collapse", this);
-                    if(deep === true){
-                        this.collapseChildNodes(true);
-                    }
-                }.createDelegate(this));
-                return;
-            }else{
-                this.ui.collapse();
-                this.fireEvent("collapse", this);
+    hide : function(deep){
+        if(this.el && this.isVisible()){
+            this.fireEvent("beforehide", this);
+            if(this.activeItem){
+                this.activeItem.deactivate();
+                this.activeItem = null;
             }
+            this.el.hide();
+            this.hidden = true;
+            this.fireEvent("hide", this);
         }
-        if(deep === true){
-            var cs = this.childNodes;
-            for(var i = 0, len = cs.length; i < len; i++) {
-               cs[i].collapse(true, false);
-            }
+        if(deep === true && this.parentMenu){
+            this.parentMenu.hide(true);
         }
     },
 
-    // private
-    delayedExpand : function(delay){
-        if(!this.expandProcId){
-            this.expandProcId = this.expand.defer(delay, this);
+    /**
+     * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
+     * Any of the following are valid:
+     * <ul>
+     * <li>Any menu item object based on {@link Roo.menu.Item}</li>
+     * <li>An HTMLElement object which will be converted to a menu item</li>
+     * <li>A menu item config object that will be created as a new menu item</li>
+     * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
+     * it will be converted into a {@link Roo.menu.TextItem} and added</li>
+     * </ul>
+     * Usage:
+     * <pre><code>
+// Create the menu
+var menu = new Roo.menu.Menu();
+
+// Create a menu item to add by reference
+var menuItem = new Roo.menu.Item({ text: 'New Item!' });
+
+// Add a bunch of items at once using different methods.
+// Only the last item added will be returned.
+var item = menu.add(
+    menuItem,                // add existing item by ref
+    'Dynamic Item',          // new TextItem
+    '-',                     // new separator
+    { text: 'Config Item' }  // new item by config
+);
+</code></pre>
+     * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
+     * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
+     */
+    add : function(){
+        var a = arguments, l = a.length, item;
+        for(var i = 0; i < l; i++){
+            var el = a[i];
+            if ((typeof(el) == "object") && el.xtype && el.xns) {
+                el = Roo.factory(el, Roo.menu);
+            }
+            
+            if(el.render){ // some kind of Item
+                item = this.addItem(el);
+            }else if(typeof el == "string"){ // string
+                if(el == "separator" || el == "-"){
+                    item = this.addSeparator();
+                }else{
+                    item = this.addText(el);
+                }
+            }else if(el.tagName || el.el){ // element
+                item = this.addElement(el);
+            }else if(typeof el == "object"){ // must be menu item config?
+                item = this.addMenuItem(el);
+            }
         }
+        return item;
     },
 
-    // private
-    cancelExpand : function(){
-        if(this.expandProcId){
-            clearTimeout(this.expandProcId);
+    /**
+     * Returns this menu's underlying {@link Roo.Element} object
+     * @return {Roo.Element} The element
+     */
+    getEl : function(){
+        if(!this.el){
+            this.render();
         }
-        this.expandProcId = false;
+        return this.el;
     },
 
     /**
-     * Toggles expanded/collapsed state of the node
+     * Adds a separator bar to the menu
+     * @return {Roo.menu.Item} The menu item that was added
      */
-    toggle : function(){
-        if(this.expanded){
-            this.collapse();
-        }else{
-            this.expand();
-        }
+    addSeparator : function(){
+        return this.addItem(new Roo.menu.Separator());
     },
 
     /**
-     * Ensures all parent nodes are expanded
+     * Adds an {@link Roo.Element} object to the menu
+     * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
+     * @return {Roo.menu.Item} The menu item that was added
      */
-    ensureVisible : function(callback){
-        var tree = this.getOwnerTree();
-        tree.expandPath(this.parentNode.getPath(), false, function(){
-            tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
-            Roo.callback(callback);
-        }.createDelegate(this));
+    addElement : function(el){
+        return this.addItem(new Roo.menu.BaseItem(el));
     },
 
     /**
-     * Expand all child nodes
-     * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
+     * Adds an existing object based on {@link Roo.menu.Item} to the menu
+     * @param {Roo.menu.Item} item The menu item to add
+     * @return {Roo.menu.Item} The menu item that was added
      */
-    expandChildNodes : function(deep){
-        var cs = this.childNodes;
-        for(var i = 0, len = cs.length; i < len; i++) {
-               cs[i].expand(deep);
+    addItem : function(item){
+        this.items.add(item);
+        if(this.ul){
+            var li = document.createElement("li");
+            li.className = "x-menu-list-item";
+            this.ul.dom.appendChild(li);
+            item.render(li, this);
+            this.delayAutoWidth();
         }
+        return item;
     },
 
     /**
-     * Collapse all child nodes
-     * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
+     * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
+     * @param {Object} config A MenuItem config object
+     * @return {Roo.menu.Item} The menu item that was added
      */
-    collapseChildNodes : function(deep){
-        var cs = this.childNodes;
-        for(var i = 0, len = cs.length; i < len; i++) {
-               cs[i].collapse(deep);
+    addMenuItem : function(config){
+        if(!(config instanceof Roo.menu.Item)){
+            if(typeof config.checked == "boolean"){ // must be check menu item config?
+                config = new Roo.menu.CheckItem(config);
+            }else{
+                config = new Roo.menu.Item(config);
+            }
         }
+        return this.addItem(config);
     },
 
     /**
-     * Disables this node
+     * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
+     * @param {String} text The text to display in the menu item
+     * @return {Roo.menu.Item} The menu item that was added
      */
-    disable : function(){
-        this.disabled = true;
-        this.unselect();
-        if(this.rendered && this.ui.onDisableChange){ // event without subscribing
-            this.ui.onDisableChange(this, true);
+    addText : function(text){
+        return this.addItem(new Roo.menu.TextItem({ text : text }));
+    },
+
+    /**
+     * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
+     * @param {Number} index The index in the menu's list of current items where the new item should be inserted
+     * @param {Roo.menu.Item} item The menu item to add
+     * @return {Roo.menu.Item} The menu item that was added
+     */
+    insert : function(index, item){
+        this.items.insert(index, item);
+        if(this.ul){
+            var li = document.createElement("li");
+            li.className = "x-menu-list-item";
+            this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
+            item.render(li, this);
+            this.delayAutoWidth();
         }
-        this.fireEvent("disabledchange", this, true);
+        return item;
+    },
+
+    /**
+     * Removes an {@link Roo.menu.Item} from the menu and destroys the object
+     * @param {Roo.menu.Item} item The menu item to remove
+     */
+    remove : function(item){
+        this.items.removeKey(item.id);
+        item.destroy();
     },
 
     /**
-     * Enables this node
+     * Removes and destroys all items in the menu
      */
-    enable : function(){
-        this.disabled = false;
-        if(this.rendered && this.ui.onDisableChange){ // event without subscribing
-            this.ui.onDisableChange(this, false);
+    removeAll : function(){
+        var f;
+        while(f = this.items.first()){
+            this.remove(f);
         }
-        this.fireEvent("disabledchange", this, false);
-    },
+    }
+});
 
-    // private
-    renderChildren : function(suppressEvent){
-        if(suppressEvent !== false){
-            this.fireEvent("beforechildrenrendered", this);
-        }
-        var cs = this.childNodes;
-        for(var i = 0, len = cs.length; i < len; i++){
-            cs[i].render(true);
+// MenuNav is a private utility class used internally by the Menu
+Roo.menu.MenuNav = function(menu){
+    Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
+    this.scope = this.menu = menu;
+};
+
+Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
+    doRelay : function(e, h){
+        var k = e.getKey();
+        if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
+            this.menu.tryActivate(0, 1);
+            return false;
         }
-        this.childrenRendered = true;
+        return h.call(this.scope || this, e, this.menu);
     },
 
-    // private
-    sort : function(fn, scope){
-        Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
-        if(this.childrenRendered){
-            var cs = this.childNodes;
-            for(var i = 0, len = cs.length; i < len; i++){
-                cs[i].render(true);
-            }
+    up : function(e, m){
+        if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
+            m.tryActivate(m.items.length-1, -1);
         }
     },
 
-    // private
-    render : function(bulkRender){
-        this.ui.render(bulkRender);
-        if(!this.rendered){
-            this.rendered = true;
-            if(this.expanded){
-                this.expanded = false;
-                this.expand(false, false);
-            }
+    down : function(e, m){
+        if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
+            m.tryActivate(0, 1);
         }
     },
 
-    // private
-    renderIndent : function(deep, refresh){
-        if(refresh){
-            this.ui.childIndent = null;
-        }
-        this.ui.renderIndent();
-        if(deep === true && this.childrenRendered){
-            var cs = this.childNodes;
-            for(var i = 0, len = cs.length; i < len; i++){
-                cs[i].renderIndent(true, refresh);
-            }
-        }
-    }
-});/*
- * 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.tree.AsyncTreeNode
- * @extends Roo.tree.TreeNode
- * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
- * @constructor
- * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
- */
- Roo.tree.AsyncTreeNode = function(config){
-    this.loaded = false;
-    this.loading = false;
-    Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
-    /**
-    * @event beforeload
-    * Fires before this node is loaded, return false to cancel
-    * @param {Node} this This node
-    */
-    this.addEvents({'beforeload':true, 'load': true});
-    /**
-    * @event load
-    * Fires when this node is loaded
-    * @param {Node} this This node
-    */
-    /**
-     * The loader used by this node (defaults to using the tree's defined loader)
-     * @type TreeLoader
-     * @property loader
-     */
-};
-Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
-    expand : function(deep, anim, callback){
-        if(this.loading){ // if an async load is already running, waiting til it's done
-            var timer;
-            var f = function(){
-                if(!this.loading){ // done loading
-                    clearInterval(timer);
-                    this.expand(deep, anim, callback);
-                }
-            }.createDelegate(this);
-            timer = setInterval(f, 200);
-            return;
-        }
-        if(!this.loaded){
-            if(this.fireEvent("beforeload", this) === false){
-                return;
-            }
-            this.loading = true;
-            this.ui.beforeLoad(this);
-            var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
-            if(loader){
-                loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
-                return;
-            }
+    right : function(e, m){
+        if(m.activeItem){
+            m.activeItem.expandMenu(true);
         }
-        Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
-    },
-    
-    /**
-     * Returns true if this node is currently loading
-     * @return {Boolean}
-     */
-    isLoading : function(){
-        return this.loading;  
-    },
-    
-    loadComplete : function(deep, anim, callback){
-        this.loading = false;
-        this.loaded = true;
-        this.ui.afterLoad(this);
-        this.fireEvent("load", this);
-        this.expand(deep, anim, callback);
-    },
-    
-    /**
-     * Returns true if this node has been loaded
-     * @return {Boolean}
-     */
-    isLoaded : function(){
-        return this.loaded;
     },
-    
-    hasChildNodes : function(){
-        if(!this.isLeaf() && !this.loaded){
-            return true;
-        }else{
-            return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
+
+    left : function(e, m){
+        m.hide();
+        if(m.parentMenu && m.parentMenu.activeItem){
+            m.parentMenu.activeItem.activate();
         }
     },
 
-    /**
-     * Trigger a reload for this node
-     * @param {Function} callback
-     */
-    reload : function(callback){
-        this.collapse(false, false);
-        while(this.firstChild){
-            this.removeChild(this.firstChild);
-        }
-        this.childrenRendered = false;
-        this.loaded = false;
-        if(this.isHiddenRoot()){
-            this.expanded = false;
+    enter : function(e, m){
+        if(m.activeItem){
+            e.stopPropagation();
+            m.activeItem.onClick(e);
+            m.fireEvent("click", this, m.activeItem);
+            return true;
         }
-        this.expand(false, false, callback);
     }
 });/*
  * Based on:
@@ -35543,527 +39525,663 @@ Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
  */
  
 /**
- * @class Roo.tree.TreeNodeUI
- * @constructor
- * @param {Object} node The node to render
- * The TreeNode UI implementation is separate from the
- * tree implementation. Unless you are customizing the tree UI,
- * you should never have to use this directly.
+ * @class Roo.menu.MenuMgr
+ * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
+ * @static
  */
-Roo.tree.TreeNodeUI = function(node){
-    this.node = node;
-    this.rendered = false;
-    this.animating = false;
-    this.emptyIcon = Roo.BLANK_IMAGE_URL;
-};
-
-Roo.tree.TreeNodeUI.prototype = {
-    removeChild : function(node){
-        if(this.rendered){
-            this.ctNode.removeChild(node.ui.getEl());
-        }
-    },
-
-    beforeLoad : function(){
-         this.addClass("x-tree-node-loading");
-    },
+Roo.menu.MenuMgr = function(){
+   var menus, active, groups = {}, attached = false, lastShow = new Date();
 
-    afterLoad : function(){
-         this.removeClass("x-tree-node-loading");
-    },
+   // private - called when first menu is created
+   function init(){
+       menus = {};
+       active = new Roo.util.MixedCollection();
+       Roo.get(document).addKeyListener(27, function(){
+           if(active.length > 0){
+               hideAll();
+           }
+       });
+   }
 
-    onTextChange : function(node, text, oldText){
-        if(this.rendered){
-            this.textNode.innerHTML = text;
-        }
-    },
+   // private
+   function hideAll(){
+       if(active && active.length > 0){
+           var c = active.clone();
+           c.each(function(m){
+               m.hide();
+           });
+       }
+   }
 
-    onDisableChange : function(node, state){
-        this.disabled = state;
-        if(state){
-            this.addClass("x-tree-node-disabled");
-        }else{
-            this.removeClass("x-tree-node-disabled");
-        }
-    },
+   // private
+   function onHide(m){
+       active.remove(m);
+       if(active.length < 1){
+           Roo.get(document).un("mousedown", onMouseDown);
+           attached = false;
+       }
+   }
 
-    onSelectedChange : function(state){
-        if(state){
-            this.focus();
-            this.addClass("x-tree-selected");
-        }else{
-            //this.blur();
-            this.removeClass("x-tree-selected");
-        }
-    },
+   // private
+   function onShow(m){
+       var last = active.last();
+       lastShow = new Date();
+       active.add(m);
+       if(!attached){
+           Roo.get(document).on("mousedown", onMouseDown);
+           attached = true;
+       }
+       if(m.parentMenu){
+          m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
+          m.parentMenu.activeChild = m;
+       }else if(last && last.isVisible()){
+          m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
+       }
+   }
 
-    onMove : function(tree, node, oldParent, newParent, index, refNode){
-        this.childIndent = null;
-        if(this.rendered){
-            var targetNode = newParent.ui.getContainer();
-            if(!targetNode){//target not rendered
-                this.holder = document.createElement("div");
-                this.holder.appendChild(this.wrap);
-                return;
-            }
-            var insertBefore = refNode ? refNode.ui.getEl() : null;
-            if(insertBefore){
-                targetNode.insertBefore(this.wrap, insertBefore);
-            }else{
-                targetNode.appendChild(this.wrap);
-            }
-            this.node.renderIndent(true);
-        }
-    },
+   // private
+   function onBeforeHide(m){
+       if(m.activeChild){
+           m.activeChild.hide();
+       }
+       if(m.autoHideTimer){
+           clearTimeout(m.autoHideTimer);
+           delete m.autoHideTimer;
+       }
+   }
 
-    addClass : function(cls){
-        if(this.elNode){
-            Roo.fly(this.elNode).addClass(cls);
-        }
-    },
+   // private
+   function onBeforeShow(m){
+       var pm = m.parentMenu;
+       if(!pm && !m.allowOtherMenus){
+           hideAll();
+       }else if(pm && pm.activeChild && active != m){
+           pm.activeChild.hide();
+       }
+   }
 
-    removeClass : function(cls){
-        if(this.elNode){
-            Roo.fly(this.elNode).removeClass(cls);
-        }
-    },
+   // private
+   function onMouseDown(e){
+       if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
+           hideAll();
+       }
+   }
 
-    remove : function(){
-        if(this.rendered){
-            this.holder = document.createElement("div");
-            this.holder.appendChild(this.wrap);
-        }
-    },
+   // private
+   function onBeforeCheck(mi, state){
+       if(state){
+           var g = groups[mi.group];
+           for(var i = 0, l = g.length; i < l; i++){
+               if(g[i] != mi){
+                   g[i].setChecked(false);
+               }
+           }
+       }
+   }
 
-    fireEvent : function(){
-        return this.node.fireEvent.apply(this.node, arguments);
-    },
+   return {
 
-    initEvents : function(){
-        this.node.on("move", this.onMove, this);
-        var E = Roo.EventManager;
-        var a = this.anchor;
+       /**
+        * Hides all menus that are currently visible
+        */
+       hideAll : function(){
+            hideAll();  
+       },
 
-        var el = Roo.fly(a, '_treeui');
+       // private
+       register : function(menu){
+           if(!menus){
+               init();
+           }
+           menus[menu.id] = menu;
+           menu.on("beforehide", onBeforeHide);
+           menu.on("hide", onHide);
+           menu.on("beforeshow", onBeforeShow);
+           menu.on("show", onShow);
+           var g = menu.group;
+           if(g && menu.events["checkchange"]){
+               if(!groups[g]){
+                   groups[g] = [];
+               }
+               groups[g].push(menu);
+               menu.on("checkchange", onCheck);
+           }
+       },
 
-        if(Roo.isOpera){ // opera render bug ignores the CSS
-            el.setStyle("text-decoration", "none");
-        }
+        /**
+         * Returns a {@link Roo.menu.Menu} object
+         * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
+         * be used to generate and return a new Menu instance.
+         */
+       get : function(menu){
+           if(typeof menu == "string"){ // menu id
+               return menus[menu];
+           }else if(menu.events){  // menu instance
+               return menu;
+           }else if(typeof menu.length == 'number'){ // array of menu items?
+               return new Roo.menu.Menu({items:menu});
+           }else{ // otherwise, must be a config
+               return new Roo.menu.Menu(menu);
+           }
+       },
 
-        el.on("click", this.onClick, this);
-        el.on("dblclick", this.onDblClick, this);
+       // private
+       unregister : function(menu){
+           delete menus[menu.id];
+           menu.un("beforehide", onBeforeHide);
+           menu.un("hide", onHide);
+           menu.un("beforeshow", onBeforeShow);
+           menu.un("show", onShow);
+           var g = menu.group;
+           if(g && menu.events["checkchange"]){
+               groups[g].remove(menu);
+               menu.un("checkchange", onCheck);
+           }
+       },
 
-        if(this.checkbox){
-            Roo.EventManager.on(this.checkbox,
-                    Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
-        }
+       // private
+       registerCheckable : function(menuItem){
+           var g = menuItem.group;
+           if(g){
+               if(!groups[g]){
+                   groups[g] = [];
+               }
+               groups[g].push(menuItem);
+               menuItem.on("beforecheckchange", onBeforeCheck);
+           }
+       },
 
-        el.on("contextmenu", this.onContextMenu, this);
+       // private
+       unregisterCheckable : function(menuItem){
+           var g = menuItem.group;
+           if(g){
+               groups[g].remove(menuItem);
+               menuItem.un("beforecheckchange", onBeforeCheck);
+           }
+       }
+   };
+}();/*
+ * 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">
+ */
 
-        var icon = Roo.fly(this.iconNode);
-        icon.on("click", this.onClick, this);
-        icon.on("dblclick", this.onDblClick, this);
-        icon.on("contextmenu", this.onContextMenu, this);
-        E.on(this.ecNode, "click", this.ecClick, this, true);
+/**
+ * @class Roo.menu.BaseItem
+ * @extends Roo.Component
+ * @abstract
+ * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
+ * management and base configuration options shared by all menu components.
+ * @constructor
+ * Creates a new BaseItem
+ * @param {Object} config Configuration options
+ */
+Roo.menu.BaseItem = function(config){
+    Roo.menu.BaseItem.superclass.constructor.call(this, config);
 
-        if(this.node.disabled){
-            this.addClass("x-tree-node-disabled");
-        }
-        if(this.node.hidden){
-            this.addClass("x-tree-node-disabled");
-        }
-        var ot = this.node.getOwnerTree();
-        var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
-        if(dd && (!this.node.isRoot || ot.rootVisible)){
-            Roo.dd.Registry.register(this.elNode, {
-                node: this.node,
-                handles: this.getDDHandles(),
-                isHandle: false
-            });
-        }
-    },
+    this.addEvents({
+        /**
+         * @event click
+         * Fires when this item is clicked
+         * @param {Roo.menu.BaseItem} this
+         * @param {Roo.EventObject} e
+         */
+        click: true,
+        /**
+         * @event activate
+         * Fires when this item is activated
+         * @param {Roo.menu.BaseItem} this
+         */
+        activate : true,
+        /**
+         * @event deactivate
+         * Fires when this item is deactivated
+         * @param {Roo.menu.BaseItem} this
+         */
+        deactivate : true
+    });
 
-    getDDHandles : function(){
-        return [this.iconNode, this.textNode];
-    },
+    if(this.handler){
+        this.on("click", this.handler, this.scope, true);
+    }
+};
 
-    hide : function(){
-        if(this.rendered){
-            this.wrap.style.display = "none";
-        }
-    },
+Roo.extend(Roo.menu.BaseItem, Roo.Component, {
+    /**
+     * @cfg {Function} handler
+     * A function that will handle the click event of this menu item (defaults to undefined)
+     */
+    /**
+     * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
+     */
+    canActivate : false,
+    
+     /**
+     * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
+     */
+    hidden: false,
+    
+    /**
+     * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
+     */
+    activeClass : "x-menu-item-active",
+    /**
+     * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
+     */
+    hideOnClick : true,
+    /**
+     * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
+     */
+    hideDelay : 100,
 
-    show : function(){
-        if(this.rendered){
-            this.wrap.style.display = "";
-        }
+    // private
+    ctype: "Roo.menu.BaseItem",
+
+    // private
+    actionMode : "container",
+
+    // private
+    render : function(container, parentMenu){
+        this.parentMenu = parentMenu;
+        Roo.menu.BaseItem.superclass.render.call(this, container);
+        this.container.menuItemId = this.id;
     },
 
-    onContextMenu : function(e){
-        if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
-            e.preventDefault();
-            this.focus();
-            this.fireEvent("contextmenu", this.node, e);
-        }
+    // private
+    onRender : function(container, position){
+        this.el = Roo.get(this.el);
+        container.dom.appendChild(this.el.dom);
     },
 
+    // private
     onClick : function(e){
-        if(this.dropping){
-            e.stopEvent();
-            return;
-        }
-        if(this.fireEvent("beforeclick", this.node, e) !== false){
-            if(!this.disabled && this.node.attributes.href){
-                this.fireEvent("click", this.node, e);
-                return;
-            }
-            e.preventDefault();
-            if(this.disabled){
-                return;
-            }
-
-            if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
-                this.node.toggle();
-            }
-
-            this.fireEvent("click", this.node, e);
+        if(!this.disabled && this.fireEvent("click", this, e) !== false
+                && this.parentMenu.fireEvent("itemclick", this, e) !== false){
+            this.handleClick(e);
         }else{
             e.stopEvent();
         }
     },
 
-    onDblClick : function(e){
-        e.preventDefault();
+    // private
+    activate : function(){
         if(this.disabled){
-            return;
-        }
-        if(this.checkbox){
-            this.toggleCheck();
-        }
-        if(!this.animating && this.node.hasChildNodes()){
-            this.node.toggle();
-        }
-        this.fireEvent("dblclick", this.node, e);
-    },
-
-    onCheckChange : function(){
-        var checked = this.checkbox.checked;
-        this.node.attributes.checked = checked;
-        this.fireEvent('checkchange', this.node, checked);
-    },
-
-    ecClick : function(e){
-        if(!this.animating && this.node.hasChildNodes()){
-            this.node.toggle();
+            return false;
         }
+        var li = this.container;
+        li.addClass(this.activeClass);
+        this.region = li.getRegion().adjust(2, 2, -2, -2);
+        this.fireEvent("activate", this);
+        return true;
     },
 
-    startDrop : function(){
-        this.dropping = true;
-    },
-
-    // delayed drop so the click event doesn't get fired on a drop
-    endDrop : function(){
-       setTimeout(function(){
-           this.dropping = false;
-       }.createDelegate(this), 50);
-    },
-
-    expand : function(){
-        this.updateExpandIcon();
-        this.ctNode.style.display = "";
+    // private
+    deactivate : function(){
+        this.container.removeClass(this.activeClass);
+        this.fireEvent("deactivate", this);
     },
 
-    focus : function(){
-        if(!this.node.preventHScroll){
-            try{this.anchor.focus();
-            }catch(e){}
-        }else if(!Roo.isIE){
-            try{
-                var noscroll = this.node.getOwnerTree().getTreeEl().dom;
-                var l = noscroll.scrollLeft;
-                this.anchor.focus();
-                noscroll.scrollLeft = l;
-            }catch(e){}
-        }
+    // private
+    shouldDeactivate : function(e){
+        return !this.region || !this.region.contains(e.getPoint());
     },
 
-    toggleCheck : function(value){
-        var cb = this.checkbox;
-        if(cb){
-            cb.checked = (value === undefined ? !cb.checked : value);
+    // private
+    handleClick : function(e){
+        if(this.hideOnClick){
+            this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
         }
     },
 
-    blur : function(){
-        try{
-            this.anchor.blur();
-        }catch(e){}
+    // private
+    expandMenu : function(autoActivate){
+        // do nothing
     },
 
-    animExpand : function(callback){
-        var ct = Roo.get(this.ctNode);
-        ct.stopFx();
-        if(!this.node.hasChildNodes()){
-            this.updateExpandIcon();
-            this.ctNode.style.display = "";
-            Roo.callback(callback);
-            return;
-        }
-        this.animating = true;
-        this.updateExpandIcon();
-
-        ct.slideIn('t', {
-           callback : function(){
-               this.animating = false;
-               Roo.callback(callback);
-            },
-            scope: this,
-            duration: this.node.ownerTree.duration || .25
-        });
-    },
+    // private
+    hideMenu : function(){
+        // do nothing
+    }
+});/*
+ * 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.menu.Adapter
+ * @extends Roo.menu.BaseItem
+ * @abstract
+ * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
+ * It provides basic rendering, activation management and enable/disable logic required to work in menus.
+ * @constructor
+ * Creates a new Adapter
+ * @param {Object} config Configuration options
+ */
+Roo.menu.Adapter = function(component, config){
+    Roo.menu.Adapter.superclass.constructor.call(this, config);
+    this.component = component;
+};
+Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
+    // private
+    canActivate : true,
 
-    highlight : function(){
-        var tree = this.node.getOwnerTree();
-        Roo.fly(this.wrap).highlight(
-            tree.hlColor || "C3DAF9",
-            {endColor: tree.hlBaseColor}
-        );
+    // private
+    onRender : function(container, position){
+        this.component.render(container);
+        this.el = this.component.getEl();
     },
 
-    collapse : function(){
-        this.updateExpandIcon();
-        this.ctNode.style.display = "none";
+    // private
+    activate : function(){
+        if(this.disabled){
+            return false;
+        }
+        this.component.focus();
+        this.fireEvent("activate", this);
+        return true;
     },
 
-    animCollapse : function(callback){
-        var ct = Roo.get(this.ctNode);
-        ct.enableDisplayMode('block');
-        ct.stopFx();
-
-        this.animating = true;
-        this.updateExpandIcon();
-
-        ct.slideOut('t', {
-            callback : function(){
-               this.animating = false;
-               Roo.callback(callback);
-            },
-            scope: this,
-            duration: this.node.ownerTree.duration || .25
-        });
+    // private
+    deactivate : function(){
+        this.fireEvent("deactivate", this);
     },
 
-    getContainer : function(){
-        return this.ctNode;
+    // private
+    disable : function(){
+        this.component.disable();
+        Roo.menu.Adapter.superclass.disable.call(this);
     },
 
-    getEl : function(){
-        return this.wrap;
-    },
+    // private
+    enable : function(){
+        this.component.enable();
+        Roo.menu.Adapter.superclass.enable.call(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">
+ */
 
-    appendDDGhost : function(ghostNode){
-        ghostNode.appendChild(this.elNode.cloneNode(true));
-    },
+/**
+ * @class Roo.menu.TextItem
+ * @extends Roo.menu.BaseItem
+ * Adds a static text string to a menu, usually used as either a heading or group separator.
+ * Note: old style constructor with text is still supported.
+ * 
+ * @constructor
+ * Creates a new TextItem
+ * @param {Object} cfg Configuration
+ */
+Roo.menu.TextItem = function(cfg){
+    if (typeof(cfg) == 'string') {
+        this.text = cfg;
+    } else {
+        Roo.apply(this,cfg);
+    }
+    
+    Roo.menu.TextItem.superclass.constructor.call(this);
+};
 
-    getDDRepairXY : function(){
-        return Roo.lib.Dom.getXY(this.iconNode);
-    },
+Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
+    /**
+     * @cfg {String} text Text to show on item.
+     */
+    text : '',
+    
+    /**
+     * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
+     */
+    hideOnClick : false,
+    /**
+     * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
+     */
+    itemCls : "x-menu-text",
 
+    // private
     onRender : function(){
-        this.render();
-    },
-
-    render : function(bulkRender){
-        var n = this.node, a = n.attributes;
-        var targetNode = n.parentNode ?
-              n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
-
-        if(!this.rendered){
-            this.rendered = true;
-
-            this.renderElements(n, a, targetNode, bulkRender);
-
-            if(a.qtip){
-               if(this.textNode.setAttributeNS){
-                   this.textNode.setAttributeNS("ext", "qtip", a.qtip);
-                   if(a.qtipTitle){
-                       this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
-                   }
-               }else{
-                   this.textNode.setAttribute("ext:qtip", a.qtip);
-                   if(a.qtipTitle){
-                       this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
-                   }
-               }
-            }else if(a.qtipCfg){
-                a.qtipCfg.target = Roo.id(this.textNode);
-                Roo.QuickTips.register(a.qtipCfg);
-            }
-            this.initEvents();
-            if(!this.node.expanded){
-                this.updateExpandIcon();
-            }
-        }else{
-            if(bulkRender === true) {
-                targetNode.appendChild(this.wrap);
-            }
-        }
-    },
-
-    renderElements : function(n, a, targetNode, bulkRender)
-    {
-        // add some indent caching, this helps performance when rendering a large tree
-        this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
-        var t = n.getOwnerTree();
-        var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
-        if (typeof(n.attributes.html) != 'undefined') {
-            txt = n.attributes.html;
-        }
-        var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
-        var cb = typeof a.checked == 'boolean';
-        var href = a.href ? a.href : Roo.isGecko ? "" : "#";
-        var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
-            '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
-            '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
-            '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
-            cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
-            '<a hidefocus="on" href="',href,'" tabIndex="1" ',
-             a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
-                '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
-            '<ul class="x-tree-node-ct" style="display:none;"></ul>',
-            "</li>"];
+        var s = document.createElement("span");
+        s.className = this.itemCls;
+        s.innerHTML = this.text;
+        this.el = s;
+        Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
+    }
+});/*
+ * 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">
+ */
 
-        if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
-            this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
-                                n.nextSibling.ui.getEl(), buf.join(""));
-        }else{
-            this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
-        }
+/**
+ * @class Roo.menu.Separator
+ * @extends Roo.menu.BaseItem
+ * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
+ * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
+ * @constructor
+ * @param {Object} config Configuration options
+ */
+Roo.menu.Separator = function(config){
+    Roo.menu.Separator.superclass.constructor.call(this, config);
+};
 
-        this.elNode = this.wrap.childNodes[0];
-        this.ctNode = this.wrap.childNodes[1];
-        var cs = this.elNode.childNodes;
-        this.indentNode = cs[0];
-        this.ecNode = cs[1];
-        this.iconNode = cs[2];
-        var index = 3;
-        if(cb){
-            this.checkbox = cs[3];
-            index++;
-        }
-        this.anchor = cs[index];
-        this.textNode = cs[index].firstChild;
-    },
+Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
+    /**
+     * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
+     */
+    itemCls : "x-menu-sep",
+    /**
+     * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
+     */
+    hideOnClick : false,
 
-    getAnchor : function(){
-        return this.anchor;
-    },
+    // private
+    onRender : function(li){
+        var s = document.createElement("span");
+        s.className = this.itemCls;
+        s.innerHTML = "&#160;";
+        this.el = s;
+        li.addClass("x-menu-sep-li");
+        Roo.menu.Separator.superclass.onRender.apply(this, arguments);
+    }
+});/*
+ * 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.menu.Item
+ * @extends Roo.menu.BaseItem
+ * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
+ * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
+ * activation and click handling.
+ * @constructor
+ * Creates a new Item
+ * @param {Object} config Configuration options
+ */
+Roo.menu.Item = function(config){
+    Roo.menu.Item.superclass.constructor.call(this, config);
+    if(this.menu){
+        this.menu = Roo.menu.MenuMgr.get(this.menu);
+    }
+};
+Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
+    /**
+     * @cfg {Roo.menu.Menu} menu
+     * A Sub menu
+     */
+    /**
+     * @cfg {String} text
+     * The text to show on the menu item.
+     */
+    text: '',
+     /**
+     * @cfg {String} html to render in menu
+     * The text to show on the menu item (HTML version).
+     */
+    html: '',
+    /**
+     * @cfg {String} icon
+     * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
+     */
+    icon: undefined,
+    /**
+     * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
+     */
+    itemCls : "x-menu-item",
+    /**
+     * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
+     */
+    canActivate : true,
+    /**
+     * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
+     */
+    showDelay: 200,
+    // doc'd in BaseItem
+    hideDelay: 200,
 
-    getTextEl : function(){
-        return this.textNode;
+    // private
+    ctype: "Roo.menu.Item",
+    
+    // private
+    onRender : function(container, position){
+        var el = document.createElement("a");
+        el.hideFocus = true;
+        el.unselectable = "on";
+        el.href = this.href || "#";
+        if(this.hrefTarget){
+            el.target = this.hrefTarget;
+        }
+        el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
+        
+        var html = this.html.length ? this.html  : String.format('{0}',this.text);
+        
+        el.innerHTML = String.format(
+                '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
+                this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
+        this.el = el;
+        Roo.menu.Item.superclass.onRender.call(this, container, position);
     },
 
-    getIconEl : function(){
-        return this.iconNode;
+    /**
+     * Sets the text to display in this menu item
+     * @param {String} text The text to display
+     * @param {Boolean} isHTML true to indicate text is pure html.
+     */
+    setText : function(text, isHTML){
+        if (isHTML) {
+            this.html = text;
+        } else {
+            this.text = text;
+            this.html = '';
+        }
+        if(this.rendered){
+            var html = this.html.length ? this.html  : String.format('{0}',this.text);
+     
+            this.el.update(String.format(
+                '<img src="{0}" class="x-menu-item-icon {2}">' + html,
+                this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
+            this.parentMenu.autoWidth();
+        }
     },
 
-    isChecked : function(){
-        return this.checkbox ? this.checkbox.checked : false;
+    // private
+    handleClick : function(e){
+        if(!this.href){ // if no link defined, stop the event automatically
+            e.stopEvent();
+        }
+        Roo.menu.Item.superclass.handleClick.apply(this, arguments);
     },
 
-    updateExpandIcon : function(){
-        if(this.rendered){
-            var n = this.node, c1, c2;
-            var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
-            var hasChild = n.hasChildNodes();
-            if(hasChild){
-                if(n.expanded){
-                    cls += "-minus";
-                    c1 = "x-tree-node-collapsed";
-                    c2 = "x-tree-node-expanded";
-                }else{
-                    cls += "-plus";
-                    c1 = "x-tree-node-expanded";
-                    c2 = "x-tree-node-collapsed";
-                }
-                if(this.wasLeaf){
-                    this.removeClass("x-tree-node-leaf");
-                    this.wasLeaf = false;
-                }
-                if(this.c1 != c1 || this.c2 != c2){
-                    Roo.fly(this.elNode).replaceClass(c1, c2);
-                    this.c1 = c1; this.c2 = c2;
-                }
-            }else{
-                // this changes non-leafs into leafs if they have no children.
-                // it's not very rational behaviour..
-                
-                if(!this.wasLeaf && this.node.leaf){
-                    Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
-                    delete this.c1;
-                    delete this.c2;
-                    this.wasLeaf = true;
-                }
-            }
-            var ecc = "x-tree-ec-icon "+cls;
-            if(this.ecc != ecc){
-                this.ecNode.className = ecc;
-                this.ecc = ecc;
+    // private
+    activate : function(autoExpand){
+        if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
+            this.focus();
+            if(autoExpand){
+                this.expandMenu();
             }
         }
+        return true;
     },
 
-    getChildIndent : function(){
-        if(!this.childIndent){
-            var buf = [];
-            var p = this.node;
-            while(p){
-                if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
-                    if(!p.isLast()) {
-                        buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
-                    } else {
-                        buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
-                    }
-                }
-                p = p.parentNode;
+    // private
+    shouldDeactivate : function(e){
+        if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
+            if(this.menu && this.menu.isVisible()){
+                return !this.menu.getEl().getRegion().contains(e.getPoint());
             }
-            this.childIndent = buf.join("");
+            return true;
         }
-        return this.childIndent;
+        return false;
     },
 
-    renderIndent : function(){
-        if(this.rendered){
-            var indent = "";
-            var p = this.node.parentNode;
-            if(p){
-                indent = p.ui.getChildIndent();
-            }
-            if(this.indentMarkup != indent){ // don't rerender if not required
-                this.indentNode.innerHTML = indent;
-                this.indentMarkup = indent;
+    // private
+    deactivate : function(){
+        Roo.menu.Item.superclass.deactivate.apply(this, arguments);
+        this.hideMenu();
+    },
+
+    // private
+    expandMenu : function(autoActivate){
+        if(!this.disabled && this.menu){
+            clearTimeout(this.hideTimer);
+            delete this.hideTimer;
+            if(!this.menu.isVisible() && !this.showTimer){
+                this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
+            }else if (this.menu.isVisible() && autoActivate){
+                this.menu.tryActivate(0, 1);
             }
-            this.updateExpandIcon();
         }
-    }
-};
+    },
 
-Roo.tree.RootTreeNodeUI = function(){
-    Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
-};
-Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
-    render : function(){
-        if(!this.rendered){
-            var targetNode = this.node.ownerTree.innerCt.dom;
-            this.node.expanded = true;
-            targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
-            this.wrap = this.ctNode = targetNode.firstChild;
+    // private
+    deferExpand : function(autoActivate){
+        delete this.showTimer;
+        this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
+        if(autoActivate){
+            this.menu.tryActivate(0, 1);
         }
     },
-    collapse : function(){
+
+    // private
+    hideMenu : function(){
+        clearTimeout(this.showTimer);
+        delete this.showTimer;
+        if(!this.hideTimer && this.menu && this.menu.isVisible()){
+            this.hideTimer = this.deferHide.defer(this.hideDelay, this);
+        }
     },
-    expand : function(){
+
+    // private
+    deferHide : function(){
+        delete this.hideTimer;
+        this.menu.hide();
     }
 });/*
  * Based on:
@@ -36075,294 +40193,145 @@ Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
  * Fork - LGPL
  * <script type="text/javascript">
  */
 /**
- * @class Roo.tree.TreeLoader
- * @extends Roo.util.Observable
- * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
- * nodes from a specified URL. The response must be a javascript Array definition
- * who's elements are node definition objects. eg:
- * <pre><code>
-{  success : true,
-   data :      [
-   
-    { 'id': 1, 'text': 'A folder Node', 'leaf': false },
-    { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
-    ]
-}
-
-
-</code></pre>
- * <br><br>
- * The old style respose with just an array is still supported, but not recommended.
- * <br><br>
- *
- * A server request is sent, and child nodes are loaded only when a node is expanded.
- * The loading node's id is passed to the server under the parameter name "node" to
- * enable the server to produce the correct child nodes.
- * <br><br>
- * To pass extra parameters, an event handler may be attached to the "beforeload"
- * event, and the parameters specified in the TreeLoader's baseParams property:
- * <pre><code>
-    myTreeLoader.on("beforeload", function(treeLoader, node) {
-        this.baseParams.category = node.attributes.category;
-    }, this);
-    
-</code></pre>
- *
- * This would pass an HTTP parameter called "category" to the server containing
- * the value of the Node's "category" attribute.
+ * @class Roo.menu.CheckItem
+ * @extends Roo.menu.Item
+ * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
  * @constructor
- * Creates a new Treeloader.
- * @param {Object} config A config object containing config properties.
+ * Creates a new CheckItem
+ * @param {Object} config Configuration options
  */
-Roo.tree.TreeLoader = function(config){
-    this.baseParams = {};
-    this.requestMethod = "POST";
-    Roo.apply(this, config);
-
+Roo.menu.CheckItem = function(config){
+    Roo.menu.CheckItem.superclass.constructor.call(this, config);
     this.addEvents({
-    
-        /**
-         * @event beforeload
-         * Fires before a network request is made to retrieve the Json text which specifies a node's children.
-         * @param {Object} This TreeLoader object.
-         * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
-         * @param {Object} callback The callback function specified in the {@link #load} call.
-         */
-        beforeload : true,
-        /**
-         * @event load
-         * Fires when the node has been successfuly loaded.
-         * @param {Object} This TreeLoader object.
-         * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
-         * @param {Object} response The response object containing the data from the server.
-         */
-        load : true,
         /**
-         * @event loadexception
-         * Fires if the network request failed.
-         * @param {Object} This TreeLoader object.
-         * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
-         * @param {Object} response The response object containing the data from the server.
+         * @event beforecheckchange
+         * Fires before the checked value is set, providing an opportunity to cancel if needed
+         * @param {Roo.menu.CheckItem} this
+         * @param {Boolean} checked The new checked value that will be set
          */
-        loadexception : true,
+        "beforecheckchange" : true,
         /**
-         * @event create
-         * Fires before a node is created, enabling you to return custom Node types 
-         * @param {Object} This TreeLoader object.
-         * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
+         * @event checkchange
+         * Fires after the checked value has been set
+         * @param {Roo.menu.CheckItem} this
+         * @param {Boolean} checked The checked value that was set
          */
-        create : true
+        "checkchange" : true
     });
-
-    Roo.tree.TreeLoader.superclass.constructor.call(this);
+    if(this.checkHandler){
+        this.on('checkchange', this.checkHandler, this.scope);
+    }
 };
-
-Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
-    /**
-    * @cfg {String} dataUrl The URL from which to request a Json string which
-    * specifies an array of node definition object representing the child nodes
-    * to be loaded.
-    */
-    /**
-    * @cfg {String} requestMethod either GET or POST
-    * defaults to POST (due to BC)
-    * to be loaded.
-    */
-    /**
-    * @cfg {Object} baseParams (optional) An object containing properties which
-    * specify HTTP parameters to be passed to each request for child nodes.
-    */
+Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
     /**
-    * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
-    * created by this loader. If the attributes sent by the server have an attribute in this object,
-    * they take priority.
-    */
+     * @cfg {String} group
+     * All check items with the same group name will automatically be grouped into a single-select
+     * radio button group (defaults to '')
+     */
     /**
-    * @cfg {Object} uiProviders (optional) An object containing properties which
-    * 
-    * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
-    * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
-    * <i>uiProvider</i> attribute of a returned child node is a string rather
-    * than a reference to a TreeNodeUI implementation, this that string value
-    * is used as a property name in the uiProviders object. You can define the provider named
-    * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
-    */
-    uiProviders : {},
-
+     * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
+     */
+    itemCls : "x-menu-item x-menu-check-item",
     /**
-    * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
-    * child nodes before loading.
-    */
-    clearOnLoad : true,
+     * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
+     */
+    groupClass : "x-menu-group-item",
 
     /**
-    * @cfg {String} root (optional) Default to false. Use this to read data from an object 
-    * property on loading, rather than expecting an array. (eg. more compatible to a standard
-    * Grid query { data : [ .....] }
-    */
-    
-    root : false,
-     /**
-    * @cfg {String} queryParam (optional) 
-    * Name of the query as it will be passed on the querystring (defaults to 'node')
-    * eg. the request will be ?node=[id]
-    */
-    
-    
-    queryParam: false,
-    
-    /**
-     * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
-     * This is called automatically when a node is expanded, but may be used to reload
-     * a node (or append new children if the {@link #clearOnLoad} option is false.)
-     * @param {Roo.tree.TreeNode} node
-     * @param {Function} callback
+     * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
+     * if this checkbox is part of a radio group (group = true) only the last item in the group that is
+     * initialized with checked = true will be rendered as checked.
      */
-    load : function(node, callback){
-        if(this.clearOnLoad){
-            while(node.firstChild){
-                node.removeChild(node.firstChild);
-            }
-        }
-        if(node.attributes.children){ // preloaded json children
-            var cs = node.attributes.children;
-            for(var i = 0, len = cs.length; i < len; i++){
-                node.appendChild(this.createNode(cs[i]));
-            }
-            if(typeof callback == "function"){
-                callback();
-            }
-        }else if(this.dataUrl){
-            this.requestData(node, callback);
-        }
-    },
+    checked: false,
 
-    getParams: function(node){
-        var buf = [], bp = this.baseParams;
-        for(var key in bp){
-            if(typeof bp[key] != "function"){
-                buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
-            }
-        }
-        var n = this.queryParam === false ? 'node' : this.queryParam;
-        buf.push(n + "=", encodeURIComponent(node.id));
-        return buf.join("");
-    },
+    // private
+    ctype: "Roo.menu.CheckItem",
 
-    requestData : function(node, callback){
-        if(this.fireEvent("beforeload", this, node, callback) !== false){
-            this.transId = Roo.Ajax.request({
-                method:this.requestMethod,
-                url: this.dataUrl||this.url,
-                success: this.handleResponse,
-                failure: this.handleFailure,
-                scope: this,
-                argument: {callback: callback, node: node},
-                params: this.getParams(node)
-            });
-        }else{
-            // if the load is cancelled, make sure we notify
-            // the node that we are done
-            if(typeof callback == "function"){
-                callback();
-            }
+    // private
+    onRender : function(c){
+        Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
+        if(this.group){
+            this.el.addClass(this.groupClass);
         }
-    },
-
-    isLoading : function(){
-        return this.transId ? true : false;
-    },
-
-    abort : function(){
-        if(this.isLoading()){
-            Roo.Ajax.abort(this.transId);
+        Roo.menu.MenuMgr.registerCheckable(this);
+        if(this.checked){
+            this.checked = false;
+            this.setChecked(true, true);
         }
     },
 
     // private
-    createNode : function(attr)
-    {
-        // apply baseAttrs, nice idea Corey!
-        if(this.baseAttrs){
-            Roo.applyIf(attr, this.baseAttrs);
-        }
-        if(this.applyLoader !== false){
-            attr.loader = this;
-        }
-        // uiProvider = depreciated..
-        
-        if(typeof(attr.uiProvider) == 'string'){
-           attr.uiProvider = this.uiProviders[attr.uiProvider] || 
-                /**  eval:var:attr */ eval(attr.uiProvider);
-        }
-        if(typeof(this.uiProviders['default']) != 'undefined') {
-            attr.uiProvider = this.uiProviders['default'];
+    destroy : function(){
+        if(this.rendered){
+            Roo.menu.MenuMgr.unregisterCheckable(this);
         }
-        
-        this.fireEvent('create', this, attr);
-        
-        attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
-        return(attr.leaf ?
-                        new Roo.tree.TreeNode(attr) :
-                        new Roo.tree.AsyncTreeNode(attr));
+        Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
     },
 
-    processResponse : function(response, node, callback)
-    {
-        var json = response.responseText;
-        try {
-            
-            var o = Roo.decode(json);
-            
-            if (this.root === false && typeof(o.success) != undefined) {
-                this.root = 'data'; // the default behaviour for list like data..
-                }
-                
-            if (this.root !== false &&  !o.success) {
-                // it's a failure condition.
-                var a = response.argument;
-                this.fireEvent("loadexception", this, a.node, response);
-                Roo.log("Load failed - should have a handler really");
-                return;
-            }
-            
-            
-            
-            if (this.root !== false) {
-                 o = o[this.root];
-            }
-            
-            for(var i = 0, len = o.length; i < len; i++){
-                var n = this.createNode(o[i]);
-                if(n){
-                    node.appendChild(n);
-                }
+    /**
+     * Set the checked state of this item
+     * @param {Boolean} checked The new checked value
+     * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
+     */
+    setChecked : function(state, suppressEvent){
+        if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
+            if(this.container){
+                this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
             }
-            if(typeof callback == "function"){
-                callback(this, node);
+            this.checked = state;
+            if(suppressEvent !== true){
+                this.fireEvent("checkchange", this, state);
             }
-        }catch(e){
-            this.handleFailure(response);
         }
     },
 
-    handleResponse : function(response){
-        this.transId = false;
-        var a = response.argument;
-        this.processResponse(response, a.node, a.callback);
-        this.fireEvent("load", this, a.node, response);
-    },
+    // private
+    handleClick : function(e){
+       if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
+           this.setChecked(!this.checked);
+       }
+       Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
+    }
+});/*
+ * 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.menu.DateItem
+ * @extends Roo.menu.Adapter
+ * A menu item that wraps the {@link Roo.DatPicker} component.
+ * @constructor
+ * Creates a new DateItem
+ * @param {Object} config Configuration options
+ */
+Roo.menu.DateItem = function(config){
+    Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
+    /** The Roo.DatePicker object @type Roo.DatePicker */
+    this.picker = this.component;
+    this.addEvents({select: true});
+    
+    this.picker.on("render", function(picker){
+        picker.getEl().swallowEvent("click");
+        picker.container.addClass("x-menu-date-item");
+    });
+
+    this.picker.on("select", this.onSelect, this);
+};
 
-    handleFailure : function(response)
-    {
-        // should handle failure better..
-        this.transId = false;
-        var a = response.argument;
-        this.fireEvent("loadexception", this, a.node, response);
-        if(typeof a.callback == "function"){
-            a.callback(this, a.node);
-        }
+Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
+    // private
+    onSelect : function(picker, date){
+        this.fireEvent("select", this, date, picker);
+        Roo.menu.DateItem.superclass.handleClick.call(this);
     }
 });/*
  * Based on:
@@ -36374,116 +40343,106 @@ Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
  * Fork - LGPL
  * <script type="text/javascript">
  */
-
 /**
-* @class Roo.tree.TreeFilter
-* Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
-* @param {TreePanel} tree
-* @param {Object} config (optional)
+ * @class Roo.menu.ColorItem
+ * @extends Roo.menu.Adapter
+ * A menu item that wraps the {@link Roo.ColorPalette} component.
+ * @constructor
+ * Creates a new ColorItem
+ * @param {Object} config Configuration options
  */
-Roo.tree.TreeFilter = function(tree, config){
-    this.tree = tree;
-    this.filtered = {};
-    Roo.apply(this, config);
+Roo.menu.ColorItem = function(config){
+    Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
+    /** The Roo.ColorPalette object @type Roo.ColorPalette */
+    this.palette = this.component;
+    this.relayEvents(this.palette, ["select"]);
+    if(this.selectHandler){
+        this.on('select', this.selectHandler, this.scope);
+    }
 };
+Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
+ * 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.tree.TreeFilter.prototype = {
-    clearBlank:false,
-    reverse:false,
-    autoClear:false,
-    remove:false,
-
-     /**
-     * Filter the data by a specific attribute.
-     * @param {String/RegExp} value Either string that the attribute value
-     * should start with or a RegExp to test against the attribute
-     * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
-     * @param {TreeNode} startNode (optional) The node to start the filter at.
+/**
+ * @class Roo.menu.DateMenu
+ * @extends Roo.menu.Menu
+ * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
+ * @constructor
+ * Creates a new DateMenu
+ * @param {Object} config Configuration options
+ */
+Roo.menu.DateMenu = function(config){
+    Roo.menu.DateMenu.superclass.constructor.call(this, config);
+    this.plain = true;
+    var di = new Roo.menu.DateItem(config);
+    this.add(di);
+    /**
+     * The {@link Roo.DatePicker} instance for this DateMenu
+     * @type DatePicker
      */
-    filter : function(value, attr, startNode){
-        attr = attr || "text";
-        var f;
-        if(typeof value == "string"){
-            var vlen = value.length;
-            // auto clear empty filter
-            if(vlen == 0 && this.clearBlank){
-                this.clear();
-                return;
-            }
-            value = value.toLowerCase();
-            f = function(n){
-                return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
-            };
-        }else if(value.exec){ // regex?
-            f = function(n){
-                return value.test(n.attributes[attr]);
-            };
-        }else{
-            throw 'Illegal filter type, must be string or regex';
-        }
-        this.filterBy(f, null, startNode);
-       },
-
+    this.picker = di.picker;
     /**
-     * Filter by a function. The passed function will be called with each
-     * node in the tree (or from the startNode). If the function returns true, the node is kept
-     * otherwise it is filtered. If a node is filtered, its children are also filtered.
-     * @param {Function} fn The filter function
-     * @param {Object} scope (optional) The scope of the function (defaults to the current node)
+     * @event select
+     * @param {DatePicker} picker
+     * @param {Date} date
      */
-    filterBy : function(fn, scope, startNode){
-        startNode = startNode || this.tree.root;
-        if(this.autoClear){
-            this.clear();
-        }
-        var af = this.filtered, rv = this.reverse;
-        var f = function(n){
-            if(n == startNode){
-                return true;
-            }
-            if(af[n.id]){
-                return false;
-            }
-            var m = fn.call(scope || n, n);
-            if(!m || rv){
-                af[n.id] = n;
-                n.ui.hide();
-                return false;
-            }
-            return true;
-        };
-        startNode.cascade(f);
-        if(this.remove){
-           for(var id in af){
-               if(typeof id != "function"){
-                   var n = af[id];
-                   if(n && n.parentNode){
-                       n.parentNode.removeChild(n);
-                   }
-               }
-           }
+    this.relayEvents(di, ["select"]);
+    this.on('beforeshow', function(){
+        if(this.picker){
+            this.picker.hideMonthPicker(false);
         }
-    },
+    }, this);
+};
+Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
+    cls:'x-date-menu'
+});/*
+ * 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.menu.ColorMenu
+ * @extends Roo.menu.Menu
+ * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
+ * @constructor
+ * Creates a new ColorMenu
+ * @param {Object} config Configuration options
+ */
+Roo.menu.ColorMenu = function(config){
+    Roo.menu.ColorMenu.superclass.constructor.call(this, config);
+    this.plain = true;
+    var ci = new Roo.menu.ColorItem(config);
+    this.add(ci);
     /**
-     * Clears the current filter. Note: with the "remove" option
-     * set a filter cannot be cleared.
+     * The {@link Roo.ColorPalette} instance for this ColorMenu
+     * @type ColorPalette
      */
-    clear : function(){
-        var t = this.tree;
-        var af = this.filtered;
-        for(var id in af){
-            if(typeof id != "function"){
-                var n = af[id];
-                if(n){
-                    n.ui.show();
-                }
-            }
-        }
-        this.filtered = {};
-    }
+    this.palette = ci.palette;
+    /**
+     * @event select
+     * @param {ColorPalette} palette
+     * @param {String} color
+     */
+    this.relayEvents(ci, ["select"]);
 };
-/*
+Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
  * Based on:
  * Ext JS Library 1.1.1
  * Copyright(c) 2006-2007, Ext JS, LLC.
@@ -36494,70 +40453,67 @@ Roo.tree.TreeFilter.prototype = {
  * <script type="text/javascript">
  */
  
-
 /**
- * @class Roo.tree.TreeSorter
- * Provides sorting of nodes in a TreePanel
- * 
- * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
- * @cfg {String} property The named attribute on the node to sort by (defaults to text)
- * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
- * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
- * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
- * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
+ * @class Roo.form.TextItem
+ * @extends Roo.BoxComponent
+ * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
  * @constructor
- * @param {TreePanel} tree
- * @param {Object} config
+ * Creates a new TextItem
+ * @param {Object} config Configuration options
  */
-Roo.tree.TreeSorter = function(tree, config){
-    Roo.apply(this, config);
-    tree.on("beforechildrenrendered", this.doSort, this);
-    tree.on("append", this.updateSort, this);
-    tree.on("insert", this.updateSort, this);
-    
-    var dsc = this.dir && this.dir.toLowerCase() == "desc";
-    var p = this.property || "text";
-    var sortType = this.sortType;
-    var fs = this.folderSort;
-    var cs = this.caseSensitive === true;
-    var leafAttr = this.leafAttr || 'leaf';
-
-    this.sortFn = function(n1, n2){
-        if(fs){
-            if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
-                return 1;
-            }
-            if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
-                return -1;
-            }
-        }
-       var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
-       var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
-       if(v1 < v2){
-                       return dsc ? +1 : -1;
-               }else if(v1 > v2){
-                       return dsc ? -1 : +1;
-        }else{
-               return 0;
-        }
-    };
+Roo.form.TextItem = function(config){
+    Roo.form.TextItem.superclass.constructor.call(this, config);
 };
 
-Roo.tree.TreeSorter.prototype = {
-    doSort : function(node){
-        node.sort(this.sortFn);
-    },
+Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
     
-    compareNodes : function(n1, n2){
-        return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
+    /**
+     * @cfg {String} tag the tag for this item (default div)
+     */
+    tag : 'div',
+    /**
+     * @cfg {String} html the content for this item
+     */
+    html : '',
+    
+    getAutoCreate : function()
+    {
+        var cfg = {
+            id: this.id,
+            tag: this.tag,
+            html: this.html,
+            cls: 'x-form-item'
+        };
+        
+        return cfg;
+        
     },
     
-    updateSort : function(tree, node){
-        if(node.childrenRendered){
-            this.doSort.defer(1, this, [node]);
+    onRender : function(ct, position)
+    {
+        Roo.form.TextItem.superclass.onRender.call(this, ct, position);
+        
+        if(!this.el){
+            var cfg = this.getAutoCreate();
+            if(!cfg.name){
+                cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
+            }
+            if (!cfg.name.length) {
+                delete cfg.name;
+            }
+            this.el = ct.createChild(cfg, position);
         }
+    },
+    /*
+     * setHTML
+     * @param {String} html update the Contents of the element.
+     */
+    setHTML : function(html)
+    {
+        this.fieldEl.dom.innerHTML = html;
     }
-};/*
+    
+});/*
  * Based on:
  * Ext JS Library 1.1.1
  * Copyright(c) 2006-2007, Ext JS, LLC.
@@ -36567,309 +40523,578 @@ Roo.tree.TreeSorter.prototype = {
  * Fork - LGPL
  * <script type="text/javascript">
  */
-
-if(Roo.dd.DropZone){
-    
-Roo.tree.TreeDropZone = function(tree, config){
-    this.allowParentInsert = false;
-    this.allowContainerDrop = false;
-    this.appendOnly = false;
-    Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
-    this.tree = tree;
-    this.lastInsertClass = "x-tree-no-status";
-    this.dragOverData = {};
+/**
+ * @class Roo.form.Field
+ * @extends Roo.BoxComponent
+ * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
+ * @constructor
+ * Creates a new Field
+ * @param {Object} config Configuration options
+ */
+Roo.form.Field = function(config){
+    Roo.form.Field.superclass.constructor.call(this, config);
 };
 
-Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
-    ddGroup : "TreeDD",
-    scroll:  true,
-    
-    expandDelay : 1000,
-    
-    expandNode : function(node){
-        if(node.hasChildNodes() && !node.isExpanded()){
-            node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
-        }
-    },
-    
-    queueExpand : function(node){
-        this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
-    },
+Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
+    /**
+     * @cfg {String} fieldLabel Label to use when rendering a form.
+     */
+       /**
+     * @cfg {String} qtip Mouse over tip
+     */
+     
+    /**
+     * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
+     */
+    invalidClass : "x-form-invalid",
+    /**
+     * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
+     */
+    invalidText : "The value in this field is invalid",
+    /**
+     * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
+     */
+    focusClass : "x-form-focus",
+    /**
+     * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
+      automatic validation (defaults to "keyup").
+     */
+    validationEvent : "keyup",
+    /**
+     * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
+     */
+    validateOnBlur : true,
+    /**
+     * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
+     */
+    validationDelay : 250,
+    /**
+     * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
+     * {tag: "input", type: "text", size: "20", autocomplete: "off"})
+     */
+    defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
+    /**
+     * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
+     */
+    fieldClass : "x-form-field",
+    /**
+     * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
+     *<pre>
+Value         Description
+-----------   ----------------------------------------------------------------------
+qtip          Display a quick tip when the user hovers over the field
+title         Display a default browser title attribute popup
+under         Add a block div beneath the field containing the error text
+side          Add an error icon to the right of the field with a popup on hover
+[element id]  Add the error text directly to the innerHTML of the specified element
+</pre>
+     */
+    msgTarget : 'qtip',
+    /**
+     * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
+     */
+    msgFx : 'normal',
+
+    /**
+     * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
+     */
+    readOnly : false,
+
+    /**
+     * @cfg {Boolean} disabled True to disable the field (defaults to false).
+     */
+    disabled : false,
+
+    /**
+     * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
+     */
+    inputType : undefined,
     
-    cancelExpand : function(){
-        if(this.expandProcId){
-            clearTimeout(this.expandProcId);
-            this.expandProcId = false;
-        }
+    /**
+     * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
+        */
+       tabIndex : undefined,
+       
+    // private
+    isFormField : true,
+
+    // private
+    hasFocus : false,
+    /**
+     * @property {Roo.Element} fieldEl
+     * Element Containing the rendered Field (with label etc.)
+     */
+    /**
+     * @cfg {Mixed} value A value to initialize this field with.
+     */
+    value : undefined,
+
+    /**
+     * @cfg {String} name The field's HTML name attribute.
+     */
+    /**
+     * @cfg {String} cls A CSS class to apply to the field's underlying element.
+     */
+    // private
+    loadedValue : false,
+     
+     
+       // private ??
+       initComponent : function(){
+        Roo.form.Field.superclass.initComponent.call(this);
+        this.addEvents({
+            /**
+             * @event focus
+             * Fires when this field receives input focus.
+             * @param {Roo.form.Field} this
+             */
+            focus : true,
+            /**
+             * @event blur
+             * Fires when this field loses input focus.
+             * @param {Roo.form.Field} this
+             */
+            blur : true,
+            /**
+             * @event specialkey
+             * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
+             * {@link Roo.EventObject#getKey} to determine which key was pressed.
+             * @param {Roo.form.Field} this
+             * @param {Roo.EventObject} e The event object
+             */
+            specialkey : true,
+            /**
+             * @event change
+             * Fires just before the field blurs if the field value has changed.
+             * @param {Roo.form.Field} this
+             * @param {Mixed} newValue The new value
+             * @param {Mixed} oldValue The original value
+             */
+            change : true,
+            /**
+             * @event invalid
+             * Fires after the field has been marked as invalid.
+             * @param {Roo.form.Field} this
+             * @param {String} msg The validation message
+             */
+            invalid : true,
+            /**
+             * @event valid
+             * Fires after the field has been validated with no errors.
+             * @param {Roo.form.Field} this
+             */
+            valid : true,
+             /**
+             * @event keyup
+             * Fires after the key up
+             * @param {Roo.form.Field} this
+             * @param {Roo.EventObject}  e The event Object
+             */
+            keyup : true
+        });
     },
-    
-    isValidDropPoint : function(n, pt, dd, e, data){
-        if(!n || !data){ return false; }
-        var targetNode = n.node;
-        var dropNode = data.node;
-        // default drop rules
-        if(!(targetNode && targetNode.isTarget && pt)){
-            return false;
+
+    /**
+     * Returns the name attribute of the field if available
+     * @return {String} name The field name
+     */
+    getName: function(){
+         return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
+    },
+
+    // private
+    onRender : function(ct, position){
+        Roo.form.Field.superclass.onRender.call(this, ct, position);
+        if(!this.el){
+            var cfg = this.getAutoCreate();
+            if(!cfg.name){
+                cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
+            }
+            if (!cfg.name.length) {
+                delete cfg.name;
+            }
+            if(this.inputType){
+                cfg.type = this.inputType;
+            }
+            this.el = ct.createChild(cfg, position);
         }
-        if(pt == "append" && targetNode.allowChildren === false){
-            return false;
+        var type = this.el.dom.type;
+        if(type){
+            if(type == 'password'){
+                type = 'text';
+            }
+            this.el.addClass('x-form-'+type);
         }
-        if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
-            return false;
+        if(this.readOnly){
+            this.el.dom.readOnly = true;
         }
-        if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
-            return false;
+        if(this.tabIndex !== undefined){
+            this.el.dom.setAttribute('tabIndex', this.tabIndex);
         }
-        // reuse the object
-        var overEvent = this.dragOverData;
-        overEvent.tree = this.tree;
-        overEvent.target = targetNode;
-        overEvent.data = data;
-        overEvent.point = pt;
-        overEvent.source = dd;
-        overEvent.rawEvent = e;
-        overEvent.dropNode = dropNode;
-        overEvent.cancel = false;  
-        var result = this.tree.fireEvent("nodedragover", overEvent);
-        return overEvent.cancel === false && result !== false;
+
+        this.el.addClass([this.fieldClass, this.cls]);
+        this.initValue();
     },
-    
-    getDropPoint : function(e, n, dd)
-    {
-        var tn = n.node;
-        if(tn.isRoot){
-            return tn.allowChildren !== false ? "append" : false; // always append for root
-        }
-        var dragEl = n.ddel;
-        var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
-        var y = Roo.lib.Event.getPageY(e);
-        //var noAppend = tn.allowChildren === false || tn.isLeaf();
-        
-        // we may drop nodes anywhere, as long as allowChildren has not been set to false..
-        var noAppend = tn.allowChildren === false;
-        if(this.appendOnly || tn.parentNode.allowChildren === false){
-            return noAppend ? false : "append";
-        }
-        var noBelow = false;
-        if(!this.allowParentInsert){
-            noBelow = tn.hasChildNodes() && tn.isExpanded();
+
+    /**
+     * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
+     * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
+     * @return {Roo.form.Field} this
+     */
+    applyTo : function(target){
+        this.allowDomMove = false;
+        this.el = Roo.get(target);
+        this.render(this.el.dom.parentNode);
+        return this;
+    },
+
+    // private
+    initValue : function(){
+        if(this.value !== undefined){
+            this.setValue(this.value);
+        }else if(this.el.dom.value.length > 0){
+            this.setValue(this.el.dom.value);
         }
-        var q = (b - t) / (noAppend ? 2 : 3);
-        if(y >= t && y < (t + q)){
-            return "above";
-        }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
-            return "below";
-        }else{
-            return "append";
+    },
+
+    /**
+     * Returns true if this field has been changed since it was originally loaded and is not disabled.
+     * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
+     */
+    isDirty : function() {
+        if(this.disabled) {
+            return false;
         }
+        return String(this.getValue()) !== String(this.originalValue);
     },
-    
-    onNodeEnter : function(n, dd, e, data)
+
+    /**
+     * stores the current value in loadedValue
+     */
+    resetHasChanged : function()
     {
-        this.cancelExpand();
+        this.loadedValue = String(this.getValue());
     },
-    
-    onNodeOver : function(n, dd, e, data)
+    /**
+     * checks the current value against the 'loaded' value.
+     * Note - will return false if 'resetHasChanged' has not been called first.
+     */
+    hasChanged : function()
     {
-       
-        var pt = this.getDropPoint(e, n, dd);
-        var node = n.node;
-        
-        // auto node expand check
-        if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
-            this.queueExpand(node);
-        }else if(pt != "append"){
-            this.cancelExpand();
+        if(this.disabled || this.readOnly) {
+            return false;
         }
-        
-        // set the insert point style on the target node
-        var returnCls = this.dropNotAllowed;
-        if(this.isValidDropPoint(n, pt, dd, e, data)){
-           if(pt){
-               var el = n.ddel;
-               var cls;
-               if(pt == "above"){
-                   returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
-                   cls = "x-tree-drag-insert-above";
-               }else if(pt == "below"){
-                   returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
-                   cls = "x-tree-drag-insert-below";
-               }else{
-                   returnCls = "x-tree-drop-ok-append";
-                   cls = "x-tree-drag-append";
-               }
-               if(this.lastInsertClass != cls){
-                   Roo.fly(el).replaceClass(this.lastInsertClass, cls);
-                   this.lastInsertClass = cls;
-               }
-           }
-       }
-       return returnCls;
+        return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
     },
     
-    onNodeOut : function(n, dd, e, data){
-        
-        this.cancelExpand();
-        this.removeDropIndicators(n);
-    },
     
-    onNodeDrop : function(n, dd, e, data){
-        var point = this.getDropPoint(e, n, dd);
-        var targetNode = n.node;
-        targetNode.ui.startDrop();
-        if(!this.isValidDropPoint(n, point, dd, e, data)){
-            targetNode.ui.endDrop();
-            return false;
+    
+    // private
+    afterRender : function(){
+        Roo.form.Field.superclass.afterRender.call(this);
+        this.initEvents();
+    },
+
+    // private
+    fireKey : function(e){
+        //Roo.log('field ' + e.getKey());
+        if(e.isNavKeyPress()){
+            this.fireEvent("specialkey", this, e);
         }
-        // first try to find the drop node
-        var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
-        var dropEvent = {
-            tree : this.tree,
-            target: targetNode,
-            data: data,
-            point: point,
-            source: dd,
-            rawEvent: e,
-            dropNode: dropNode,
-            cancel: !dropNode   
-        };
-        var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
-        if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
-            targetNode.ui.endDrop();
-            return false;
+    },
+
+    /**
+     * Resets the current field value to the originally loaded value and clears any validation messages
+     */
+    reset : function(){
+        this.setValue(this.resetValue);
+        this.originalValue = this.getValue();
+        this.clearInvalid();
+    },
+
+    // private
+    initEvents : function(){
+        // safari killled keypress - so keydown is now used..
+        this.el.on("keydown" , this.fireKey,  this);
+        this.el.on("focus", this.onFocus,  this);
+        this.el.on("blur", this.onBlur,  this);
+        this.el.relayEvent('keyup', this);
+
+        // reference to original value for reset
+        this.originalValue = this.getValue();
+        this.resetValue =  this.getValue();
+    },
+
+    // private
+    onFocus : function(){
+        if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
+            this.el.addClass(this.focusClass);
         }
-        // allow target changing
-        targetNode = dropEvent.target;
-        if(point == "append" && !targetNode.isExpanded()){
-            targetNode.expand(false, null, function(){
-                this.completeDrop(dropEvent);
-            }.createDelegate(this));
-        }else{
-            this.completeDrop(dropEvent);
+        if(!this.hasFocus){
+            this.hasFocus = true;
+            this.startValue = this.getValue();
+            this.fireEvent("focus", this);
         }
-        return true;
     },
-    
-    completeDrop : function(de){
-        var ns = de.dropNode, p = de.point, t = de.target;
-        if(!(ns instanceof Array)){
-            ns = [ns];
+
+    beforeBlur : Roo.emptyFn,
+
+    // private
+    onBlur : function(){
+        this.beforeBlur();
+        if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
+            this.el.removeClass(this.focusClass);
         }
-        var n;
-        for(var i = 0, len = ns.length; i < len; i++){
-            n = ns[i];
-            if(p == "above"){
-                t.parentNode.insertBefore(n, t);
-            }else if(p == "below"){
-                t.parentNode.insertBefore(n, t.nextSibling);
-            }else{
-                t.appendChild(n);
-            }
+        this.hasFocus = false;
+        if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
+            this.validate();
         }
-        n.ui.focus();
-        if(this.tree.hlDrop){
-            n.ui.highlight();
+        var v = this.getValue();
+        if(String(v) !== String(this.startValue)){
+            this.fireEvent('change', this, v, this.startValue);
         }
-        t.ui.endDrop();
-        this.tree.fireEvent("nodedrop", de);
+        this.fireEvent("blur", this);
     },
-    
-    afterNodeMoved : function(dd, data, e, targetNode, dropNode){
-        if(this.tree.hlDrop){
-            dropNode.ui.focus();
-            dropNode.ui.highlight();
+
+    /**
+     * Returns whether or not the field value is currently valid
+     * @param {Boolean} preventMark True to disable marking the field invalid
+     * @return {Boolean} True if the value is valid, else false
+     */
+    isValid : function(preventMark){
+        if(this.disabled){
+            return true;
         }
-        this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
-    },
-    
-    getTree : function(){
-        return this.tree;
+        var restore = this.preventMark;
+        this.preventMark = preventMark === true;
+        var v = this.validateValue(this.processValue(this.getRawValue()));
+        this.preventMark = restore;
+        return v;
     },
-    
-    removeDropIndicators : function(n){
-        if(n && n.ddel){
-            var el = n.ddel;
-            Roo.fly(el).removeClass([
-                    "x-tree-drag-insert-above",
-                    "x-tree-drag-insert-below",
-                    "x-tree-drag-append"]);
-            this.lastInsertClass = "_noclass";
+
+    /**
+     * Validates the field value
+     * @return {Boolean} True if the value is valid, else false
+     */
+    validate : function(){
+        if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
+            this.clearInvalid();
+            return true;
         }
+        return false;
     },
-    
-    beforeDragDrop : function(target, e, id){
-        this.cancelExpand();
+
+    processValue : function(value){
+        return value;
+    },
+
+    // private
+    // Subclasses should provide the validation implementation by overriding this
+    validateValue : function(value){
         return true;
     },
-    
-    afterRepair : function(data){
-        if(data && Roo.enableFx){
-            data.node.ui.highlight();
+
+    /**
+     * Mark this field as invalid
+     * @param {String} msg The validation message
+     */
+    markInvalid : function(msg){
+        if(!this.rendered || this.preventMark){ // not rendered
+            return;
         }
-        this.hideProxy();
-    } 
-    
-});
+        
+        var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
+        
+        obj.el.addClass(this.invalidClass);
+        msg = msg || this.invalidText;
+        switch(this.msgTarget){
+            case 'qtip':
+                obj.el.dom.qtip = msg;
+                obj.el.dom.qclass = 'x-form-invalid-tip';
+                if(Roo.QuickTips){ // fix for floating editors interacting with DND
+                    Roo.QuickTips.enable();
+                }
+                break;
+            case 'title':
+                this.el.dom.title = msg;
+                break;
+            case 'under':
+                if(!this.errorEl){
+                    var elp = this.el.findParent('.x-form-element', 5, true);
+                    this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
+                    this.errorEl.setWidth(elp.getWidth(true)-20);
+                }
+                this.errorEl.update(msg);
+                Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
+                break;
+            case 'side':
+                if(!this.errorIcon){
+                    var elp = this.el.findParent('.x-form-element', 5, true);
+                    this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
+                }
+                this.alignErrorIcon();
+                this.errorIcon.dom.qtip = msg;
+                this.errorIcon.dom.qclass = 'x-form-invalid-tip';
+                this.errorIcon.show();
+                this.on('resize', this.alignErrorIcon, this);
+                break;
+            default:
+                var t = Roo.getDom(this.msgTarget);
+                t.innerHTML = msg;
+                t.style.display = this.msgDisplay;
+                break;
+        }
+        this.fireEvent('invalid', this, msg);
+    },
 
-}
-/*
- * 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">
- */
+    // private
+    alignErrorIcon : function(){
+        this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
+    },
 
-if(Roo.dd.DragZone){
-Roo.tree.TreeDragZone = function(tree, config){
-    Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
-    this.tree = tree;
-};
+    /**
+     * Clear any invalid styles/messages for this field
+     */
+    clearInvalid : function(){
+        if(!this.rendered || this.preventMark){ // not rendered
+            return;
+        }
+        var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
+        
+        obj.el.removeClass(this.invalidClass);
+        switch(this.msgTarget){
+            case 'qtip':
+                obj.el.dom.qtip = '';
+                break;
+            case 'title':
+                this.el.dom.title = '';
+                break;
+            case 'under':
+                if(this.errorEl){
+                    Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
+                }
+                break;
+            case 'side':
+                if(this.errorIcon){
+                    this.errorIcon.dom.qtip = '';
+                    this.errorIcon.hide();
+                    this.un('resize', this.alignErrorIcon, this);
+                }
+                break;
+            default:
+                var t = Roo.getDom(this.msgTarget);
+                t.innerHTML = '';
+                t.style.display = 'none';
+                break;
+        }
+        this.fireEvent('valid', this);
+    },
 
-Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
-    ddGroup : "TreeDD",
-   
-    onBeforeDrag : function(data, e){
-        var n = data.node;
-        return n && n.draggable && !n.disabled;
+    /**
+     * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
+     * @return {Mixed} value The field value
+     */
+    getRawValue : function(){
+        var v = this.el.getValue();
+        
+        return v;
     },
-     
-    
-    onInitDrag : function(e){
-        var data = this.dragData;
-        this.tree.getSelectionModel().select(data.node);
-        this.proxy.update("");
-        data.node.ui.appendDDGhost(this.proxy.ghost.dom);
-        this.tree.fireEvent("startdrag", this.tree, data.node, e);
+
+    /**
+     * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
+     * @return {Mixed} value The field value
+     */
+    getValue : function(){
+        var v = this.el.getValue();
+         
+        return v;
+    },
+
+    /**
+     * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
+     * @param {Mixed} value The value to set
+     */
+    setRawValue : function(v){
+        return this.el.dom.value = (v === null || v === undefined ? '' : v);
+    },
+
+    /**
+     * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
+     * @param {Mixed} value The value to set
+     */
+    setValue : function(v){
+        this.value = v;
+        if(this.rendered){
+            this.el.dom.value = (v === null || v === undefined ? '' : v);
+             this.validate();
+        }
     },
-    
-    getRepairXY : function(e, data){
-        return data.node.ui.getDDRepairXY();
+
+    adjustSize : function(w, h){
+        var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
+        s.width = this.adjustWidth(this.el.dom.tagName, s.width);
+        return s;
     },
-    
-    onEndDrag : function(data, e){
-        this.tree.fireEvent("enddrag", this.tree, data.node, e);
-        
-        
+
+    adjustWidth : function(tag, w){
+        tag = tag.toLowerCase();
+        if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
+            if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
+                if(tag == 'input'){
+                    return w + 2;
+                }
+                if(tag == 'textarea'){
+                    return w-2;
+                }
+            }else if(Roo.isOpera){
+                if(tag == 'input'){
+                    return w + 2;
+                }
+                if(tag == 'textarea'){
+                    return w-2;
+                }
+            }
+        }
+        return w;
+    }
+});
+
+
+// anything other than normal should be considered experimental
+Roo.form.Field.msgFx = {
+    normal : {
+        show: function(msgEl, f){
+            msgEl.setDisplayed('block');
+        },
+
+        hide : function(msgEl, f){
+            msgEl.setDisplayed(false).update('');
+        }
     },
-    
-    onValidDrop : function(dd, e, id){
-        this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
-        this.hideProxy();
+
+    slide : {
+        show: function(msgEl, f){
+            msgEl.slideIn('t', {stopFx:true});
+        },
+
+        hide : function(msgEl, f){
+            msgEl.slideOut('t', {stopFx:true,useDisplay:true});
+        }
     },
-    
-    beforeInvalidDrop : function(e, id){
-        // this scrolls the original position back into view
-        var sm = this.tree.getSelectionModel();
-        sm.clearSelections();
-        sm.select(this.dragData.node);
+
+    slideRight : {
+        show: function(msgEl, f){
+            msgEl.fixDisplay();
+            msgEl.alignTo(f.el, 'tl-tr');
+            msgEl.slideIn('l', {stopFx:true});
+        },
+
+        hide : function(msgEl, f){
+            msgEl.slideOut('l', {stopFx:true,useDisplay:true});
+        }
     }
-});
-}/*
+};/*
  * Based on:
  * Ext JS Library 1.1.1
  * Copyright(c) 2006-2007, Ext JS, LLC.
@@ -36879,309 +41104,338 @@ Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
  * Fork - LGPL
  * <script type="text/javascript">
  */
+
 /**
- * @class Roo.tree.TreeEditor
- * @extends Roo.Editor
- * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
- * as the editor field.
+ * @class Roo.form.TextField
+ * @extends Roo.form.Field
+ * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
+ * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
  * @constructor
- * @param {Object} config (used to be the tree panel.)
- * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
- * 
- * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
- * @cfg {Roo.form.TextField|Object} field The field configuration
- *
- * 
+ * Creates a new TextField
+ * @param {Object} config Configuration options
  */
-Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
-    var tree = config;
-    var field;
-    if (oldconfig) { // old style..
-        field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
-    } else {
-        // new style..
-        tree = config.tree;
-        config.field = config.field  || {};
-        config.field.xtype = 'TextField';
-        field = Roo.factory(config.field, Roo.form);
-    }
-    config = config || {};
-    
-    
+Roo.form.TextField = function(config){
+    Roo.form.TextField.superclass.constructor.call(this, config);
     this.addEvents({
         /**
-         * @event beforenodeedit
-         * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
-         * false from the handler of this event.
-         * @param {Editor} this
-         * @param {Roo.tree.Node} node 
-         */
-        "beforenodeedit" : true
+         * @event autosize
+         * Fires when the autosize function is triggered.  The field may or may not have actually changed size
+         * according to the default logic, but this event provides a hook for the developer to apply additional
+         * logic at runtime to resize the field if needed.
+            * @param {Roo.form.Field} this This text field
+            * @param {Number} width The new field width
+            */
+        autosize : true
     });
-    
-    //Roo.log(config);
-    Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
-
-    this.tree = tree;
-
-    tree.on('beforeclick', this.beforeNodeClick, this);
-    tree.getTreeEl().on('mousedown', this.hide, this);
-    this.on('complete', this.updateNode, this);
-    this.on('beforestartedit', this.fitToTree, this);
-    this.on('startedit', this.bindScroll, this, {delay:10});
-    this.on('specialkey', this.onSpecialKey, this);
 };
 
-Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
+Roo.extend(Roo.form.TextField, Roo.form.Field,  {
     /**
-     * @cfg {String} alignment
-     * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
+     * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
      */
-    alignment: "l-l",
-    // inherit
-    autoSize: false,
+    grow : false,
     /**
-     * @cfg {Boolean} hideEl
-     * True to hide the bound element while the editor is displayed (defaults to false)
+     * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
      */
-    hideEl : false,
+    growMin : 30,
     /**
-     * @cfg {String} cls
-     * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
+     * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
      */
-    cls: "x-small-editor x-tree-editor",
+    growMax : 800,
     /**
-     * @cfg {Boolean} shim
-     * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
+     * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
      */
-    shim:false,
-    // inherit
-    shadow:"frame",
+    vtype : null,
     /**
-     * @cfg {Number} maxWidth
-     * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
-     * the containing tree element's size, it will be automatically limited for you to the container width, taking
-     * scroll and client offsets into account prior to each edit.
+     * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
      */
-    maxWidth: 250,
-
-    editDelay : 350,
+    maskRe : null,
+    /**
+     * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
+     */
+    disableKeyFilter : false,
+    /**
+     * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
+     */
+    allowBlank : true,
+    /**
+     * @cfg {Number} minLength Minimum input field length required (defaults to 0)
+     */
+    minLength : 0,
+    /**
+     * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
+     */
+    maxLength : Number.MAX_VALUE,
+    /**
+     * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
+     */
+    minLengthText : "The minimum length for this field is {0}",
+    /**
+     * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
+     */
+    maxLengthText : "The maximum length for this field is {0}",
+    /**
+     * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
+     */
+    selectOnFocus : false,
+    /**
+     * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
+     */    
+    allowLeadingSpace : false,
+    /**
+     * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
+     */
+    blankText : "This field is required",
+    /**
+     * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
+     * If available, this function will be called only after the basic validators all return true, and will be passed the
+     * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
+     */
+    validator : null,
+    /**
+     * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
+     * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
+     * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
+     */
+    regex : null,
+    /**
+     * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
+     */
+    regexText : "",
+    /**
+     * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
+     */
+    emptyText : null,
+   
 
     // private
-    fitToTree : function(ed, el){
-        var td = this.tree.getTreeEl().dom, nd = el.dom;
-        if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
-            td.scrollLeft = nd.offsetLeft;
+    initEvents : function()
+    {
+        if (this.emptyText) {
+            this.el.attr('placeholder', this.emptyText);
         }
-        var w = Math.min(
-                this.maxWidth,
-                (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
-        this.setSize(w, '');
         
-        return this.fireEvent('beforenodeedit', this, this.editNode);
+        Roo.form.TextField.superclass.initEvents.call(this);
+        if(this.validationEvent == 'keyup'){
+            this.validationTask = new Roo.util.DelayedTask(this.validate, this);
+            this.el.on('keyup', this.filterValidation, this);
+        }
+        else if(this.validationEvent !== false){
+            this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
+        }
         
+        if(this.selectOnFocus){
+            this.on("focus", this.preFocus, this);
+        }
+       if (!this.allowLeadingSpace) {
+           this.on('blur', this.cleanLeadingSpace, this);
+       }
+       
+        if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
+            this.el.on("keypress", this.filterKeys, this);
+        }
+        if(this.grow){
+            this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
+            this.el.on("click", this.autoSize,  this);
+        }
+        if(this.el.is('input[type=password]') && Roo.isSafari){
+            this.el.on('keydown', this.SafariOnKeyDown, this);
+        }
     },
 
-    // private
-    triggerEdit : function(node){
-        this.completeEdit();
-        this.editNode = node;
-        this.startEdit(node.ui.textNode, node.text);
+    processValue : function(value){
+        if(this.stripCharsRe){
+            var newValue = value.replace(this.stripCharsRe, '');
+            if(newValue !== value){
+                this.setRawValue(newValue);
+                return newValue;
+            }
+        }
+        return value;
     },
 
-    // private
-    bindScroll : function(){
-        this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
+    filterValidation : function(e){
+        if(!e.isNavKeyPress()){
+            this.validationTask.delay(this.validationDelay);
+        }
     },
 
     // private
-    beforeNodeClick : function(node, e){
-        var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
-        this.lastClick = new Date();
-        if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
-            e.stopEvent();
-            this.triggerEdit(node);
-            return false;
+    onKeyUp : function(e){
+        if(!e.isNavKeyPress()){
+            this.autoSize();
         }
-        return true;
     },
-
-    // private
-    updateNode : function(ed, value){
-        this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
-        this.editNode.setText(value);
+    // private - clean the leading white space
+    cleanLeadingSpace : function(e)
+    {
+        if ( this.inputType == 'file') {
+            return;
+        }
+        
+        this.setValue((this.getValue() + '').replace(/^\s+/,''));
     },
-
+    /**
+     * Resets the current field value to the originally-loaded value and clears any validation messages.
+     *  
+     */
+    reset : function(){
+        Roo.form.TextField.superclass.reset.call(this);
+       
+    }, 
     // private
-    onHide : function(){
-        Roo.tree.TreeEditor.superclass.onHide.call(this);
-        if(this.editNode){
-            this.editNode.ui.focus();
+    preFocus : function(){
+        
+        if(this.selectOnFocus){
+            this.el.dom.select();
         }
     },
 
+    
     // private
-    onSpecialKey : function(field, e){
+    filterKeys : function(e){
         var k = e.getKey();
-        if(k == e.ESC){
-            e.stopEvent();
-            this.cancelEdit();
-        }else if(k == e.ENTER && !e.hasModifier()){
+        if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
+            return;
+        }
+        var c = e.getCharCode(), cc = String.fromCharCode(c);
+        if(Roo.isIE && (e.isSpecialKey() || !cc)){
+            return;
+        }
+        if(!this.maskRe.test(cc)){
             e.stopEvent();
-            this.completeEdit();
         }
-    }
-});//<Script type="text/javascript">
-/*
- * 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">
- */
-/**
- * Not documented??? - probably should be...
- */
-
-Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
-    //focus: Roo.emptyFn, // prevent odd scrolling behavior
-    
-    renderElements : function(n, a, targetNode, bulkRender){
-        //consel.log("renderElements?");
-        this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
+    },
 
-        var t = n.getOwnerTree();
-        var tid = Pman.Tab.Document_TypesTree.tree.el.id;
+    setValue : function(v){
         
-        var cols = t.columns;
-        var bw = t.borderWidth;
-        var c = cols[0];
-        var href = a.href ? a.href : Roo.isGecko ? "" : "#";
-         var cb = typeof a.checked == "boolean";
-        var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
-        var colcls = 'x-t-' + tid + '-c0';
-        var buf = [
-            '<li class="x-tree-node">',
-            
-                
-                '<div class="x-tree-node-el ', a.cls,'">',
-                    // extran...
-                    '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
-                
-                
-                        '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
-                        '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
-                        '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
-                           (a.icon ? ' x-tree-node-inline-icon' : ''),
-                           (a.iconCls ? ' '+a.iconCls : ''),
-                           '" unselectable="on" />',
-                        (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
-                             (a.checked ? 'checked="checked" />' : ' />')) : ''),
-                             
-                        '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
-                            (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
-                            '<span unselectable="on" qtip="' + tx + '">',
-                             tx,
-                             '</span></a>' ,
-                    '</div>',
-                     '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
-                            (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
-                 ];
-        for(var i = 1, len = cols.length; i < len; i++){
-            c = cols[i];
-            colcls = 'x-t-' + tid + '-c' +i;
-            tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
-            buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
-                        '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
-                      "</div>");
-         }
-         
-         buf.push(
-            '</a>',
-            '<div class="x-clear"></div></div>',
-            '<ul class="x-tree-node-ct" style="display:none;"></ul>',
-            "</li>");
+        Roo.form.TextField.superclass.setValue.apply(this, arguments);
         
-        if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
-            this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
-                                n.nextSibling.ui.getEl(), buf.join(""));
-        }else{
-            this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
+        this.autoSize();
+    },
+
+    /**
+     * Validates a value according to the field's validation rules and marks the field as invalid
+     * if the validation fails
+     * @param {Mixed} value The value to validate
+     * @return {Boolean} True if the value is valid, else false
+     */
+    validateValue : function(value){
+        if(value.length < 1)  { // if it's blank
+             if(this.allowBlank){
+                this.clearInvalid();
+                return true;
+             }else{
+                this.markInvalid(this.blankText);
+                return false;
+             }
         }
-        var el = this.wrap.firstChild;
-        this.elRow = el;
-        this.elNode = el.firstChild;
-        this.ranchor = el.childNodes[1];
-        this.ctNode = this.wrap.childNodes[1];
-        var cs = el.firstChild.childNodes;
-        this.indentNode = cs[0];
-        this.ecNode = cs[1];
-        this.iconNode = cs[2];
-        var index = 3;
-        if(cb){
-            this.checkbox = cs[3];
-            index++;
+        if(value.length < this.minLength){
+            this.markInvalid(String.format(this.minLengthText, this.minLength));
+            return false;
+        }
+        if(value.length > this.maxLength){
+            this.markInvalid(String.format(this.maxLengthText, this.maxLength));
+            return false;
+        }
+        if(this.vtype){
+            var vt = Roo.form.VTypes;
+            if(!vt[this.vtype](value, this)){
+                this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
+                return false;
+            }
+        }
+        if(typeof this.validator == "function"){
+            var msg = this.validator(value);
+            if(msg !== true){
+                this.markInvalid(msg);
+                return false;
+            }
+        }
+        if(this.regex && !this.regex.test(value)){
+            this.markInvalid(this.regexText);
+            return false;
+        }
+        return true;
+    },
+
+    /**
+     * Selects text in this field
+     * @param {Number} start (optional) The index where the selection should start (defaults to 0)
+     * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
+     */
+    selectText : function(start, end){
+        var v = this.getRawValue();
+        if(v.length > 0){
+            start = start === undefined ? 0 : start;
+            end = end === undefined ? v.length : end;
+            var d = this.el.dom;
+            if(d.setSelectionRange){
+                d.setSelectionRange(start, end);
+            }else if(d.createTextRange){
+                var range = d.createTextRange();
+                range.moveStart("character", start);
+                range.moveEnd("character", v.length-end);
+                range.select();
+            }
         }
-        this.anchor = cs[index];
-        
-        this.textNode = cs[index].firstChild;
-        
-        //el.on("click", this.onClick, this);
-        //el.on("dblclick", this.onDblClick, this);
-        
-        
-       // console.log(this);
     },
-    initEvents : function(){
-        Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
-        
-            
-        var a = this.ranchor;
-
-        var el = Roo.get(a);
 
-        if(Roo.isOpera){ // opera render bug ignores the CSS
-            el.setStyle("text-decoration", "none");
+    /**
+     * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
+     * This only takes effect if grow = true, and fires the autosize event.
+     */
+    autoSize : function(){
+        if(!this.grow || !this.rendered){
+            return;
         }
-
-        el.on("click", this.onClick, this);
-        el.on("dblclick", this.onDblClick, this);
-        el.on("contextmenu", this.onContextMenu, this);
-        
+        if(!this.metrics){
+            this.metrics = Roo.util.TextMetrics.createInstance(this.el);
+        }
+        var el = this.el;
+        var v = el.dom.value;
+        var d = document.createElement('div');
+        d.appendChild(document.createTextNode(v));
+        v = d.innerHTML;
+        d = null;
+        v += "&#160;";
+        var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
+        this.el.setWidth(w);
+        this.fireEvent("autosize", this, w);
     },
     
-    /*onSelectedChange : function(state){
-        if(state){
-            this.focus();
-            this.addClass("x-tree-selected");
-        }else{
-            //this.blur();
-            this.removeClass("x-tree-selected");
+    // private
+    SafariOnKeyDown : function(event)
+    {
+        // this is a workaround for a password hang bug on chrome/ webkit.
+        
+        var isSelectAll = false;
+        
+        if(this.el.dom.selectionEnd > 0){
+            isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
         }
-    },*/
-    addClass : function(cls){
-        if(this.elRow){
-            Roo.fly(this.elRow).addClass(cls);
+        if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
+            event.preventDefault();
+            this.setValue('');
+            return;
         }
         
-    },
-    
-    
-    removeClass : function(cls){
-        if(this.elRow){
-            Roo.fly(this.elRow).removeClass(cls);
+        if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
+            
+            event.preventDefault();
+            // this is very hacky as keydown always get's upper case.
+            
+            var cc = String.fromCharCode(event.getCharCode());
+            
+            
+            this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
+            
         }
+        
+        
     }
-
-    
-    
-});//<Script type="text/javascript">
-
-/*
+});/*
  * Based on:
  * Ext JS Library 1.1.1
  * Copyright(c) 2006-2007, Ext JS, LLC.
@@ -37192,106 +41446,38 @@ Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
  * <script type="text/javascript">
  */
  
-
 /**
- * @class Roo.tree.ColumnTree
- * @extends Roo.data.TreePanel
- * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
- * @cfg {int} borderWidth  compined right/left border allowance
+ * @class Roo.form.Hidden
+ * @extends Roo.form.TextField
+ * Simple Hidden element used on forms 
+ * 
+ * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
+ * 
  * @constructor
- * @param {String/HTMLElement/Element} el The container element
- * @param {Object} config
+ * Creates a new Hidden form element.
+ * @param {Object} config Configuration options
  */
-Roo.tree.ColumnTree =  function(el, config)
-{
-   Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
-   this.addEvents({
-        /**
-        * @event resize
-        * Fire this event on a container when it resizes
-        * @param {int} w Width
-        * @param {int} h Height
-        */
-       "resize" : true
-    });
-    this.on('resize', this.onResize, this);
+
+
+
+// easy hidden field...
+Roo.form.Hidden = function(config){
+    Roo.form.Hidden.superclass.constructor.call(this, config);
 };
+  
+Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
+    fieldLabel:      '',
+    inputType:      'hidden',
+    width:          50,
+    allowBlank:     true,
+    labelSeparator: '',
+    hidden:         true,
+    itemCls :       'x-form-item-display-none'
 
-Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
-    //lines:false,
-    
-    
-    borderWidth: Roo.isBorderBox ? 0 : 2, 
-    headEls : false,
-    
-    render : function(){
-        // add the header.....
-       
-        Roo.tree.ColumnTree.superclass.render.apply(this);
-        
-        this.el.addClass('x-column-tree');
-        
-        this.headers = this.el.createChild(
-            {cls:'x-tree-headers'},this.innerCt.dom);
-   
-        var cols = this.columns, c;
-        var totalWidth = 0;
-        this.headEls = [];
-        var  len = cols.length;
-        for(var i = 0; i < len; i++){
-             c = cols[i];
-             totalWidth += c.width;
-            this.headEls.push(this.headers.createChild({
-                 cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
-                 cn: {
-                     cls:'x-tree-hd-text',
-                     html: c.header
-                 },
-                 style:'width:'+(c.width-this.borderWidth)+'px;'
-             }));
-        }
-        this.headers.createChild({cls:'x-clear'});
-        // prevent floats from wrapping when clipped
-        this.headers.setWidth(totalWidth);
-        //this.innerCt.setWidth(totalWidth);
-        this.innerCt.setStyle({ overflow: 'auto' });
-        this.onResize(this.width, this.height);
-             
-        
-    },
-    onResize : function(w,h)
-    {
-        this.height = h;
-        this.width = w;
-        // resize cols..
-        this.innerCt.setWidth(this.width);
-        this.innerCt.setHeight(this.height-20);
-        
-        // headers...
-        var cols = this.columns, c;
-        var totalWidth = 0;
-        var expEl = false;
-        var len = cols.length;
-        for(var i = 0; i < len; i++){
-            c = cols[i];
-            if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
-                // it's the expander..
-                expEl  = this.headEls[i];
-                continue;
-            }
-            totalWidth += c.width;
-            
-        }
-        if (expEl) {
-            expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
-        }
-        this.headers.setWidth(w-20);
 
-        
-        
-        
-    }
 });
+
+
 /*
  * Based on:
  * Ext JS Library 1.1.1
@@ -37304,561 +41490,530 @@ Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
  */
  
 /**
- * @class Roo.menu.Menu
- * @extends Roo.util.Observable
- * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
- * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
+ * @class Roo.form.TriggerField
+ * @extends Roo.form.TextField
+ * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
+ * The trigger has no default action, so you must assign a function to implement the trigger click handler by
+ * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
+ * for which you can provide a custom implementation.  For example:
+ * <pre><code>
+var trigger = new Roo.form.TriggerField();
+trigger.onTriggerClick = myTriggerFn;
+trigger.applyTo('my-field');
+</code></pre>
+ *
+ * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
+ * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
+ * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
+ * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
  * @constructor
- * Creates a new Menu
- * @param {Object} config Configuration options
+ * Create a new TriggerField.
+ * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
+ * to the base TextField)
  */
-Roo.menu.Menu = function(config){
-    
-    Roo.menu.Menu.superclass.constructor.call(this, config);
-    
-    this.id = this.id || Roo.id();
-    this.addEvents({
-        /**
-         * @event beforeshow
-         * Fires before this menu is displayed
-         * @param {Roo.menu.Menu} this
-         */
-        beforeshow : true,
-        /**
-         * @event beforehide
-         * Fires before this menu is hidden
-         * @param {Roo.menu.Menu} this
-         */
-        beforehide : true,
-        /**
-         * @event show
-         * Fires after this menu is displayed
-         * @param {Roo.menu.Menu} this
-         */
-        show : true,
-        /**
-         * @event hide
-         * Fires after this menu is hidden
-         * @param {Roo.menu.Menu} this
-         */
-        hide : true,
-        /**
-         * @event click
-         * Fires when this menu is clicked (or when the enter key is pressed while it is active)
-         * @param {Roo.menu.Menu} this
-         * @param {Roo.menu.Item} menuItem The menu item that was clicked
-         * @param {Roo.EventObject} e
-         */
-        click : true,
-        /**
-         * @event mouseover
-         * Fires when the mouse is hovering over this menu
-         * @param {Roo.menu.Menu} this
-         * @param {Roo.EventObject} e
-         * @param {Roo.menu.Item} menuItem The menu item that was clicked
-         */
-        mouseover : true,
-        /**
-         * @event mouseout
-         * Fires when the mouse exits this menu
-         * @param {Roo.menu.Menu} this
-         * @param {Roo.EventObject} e
-         * @param {Roo.menu.Item} menuItem The menu item that was clicked
-         */
-        mouseout : true,
-        /**
-         * @event itemclick
-         * Fires when a menu item contained in this menu is clicked
-         * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
-         * @param {Roo.EventObject} e
-         */
-        itemclick: true
-    });
-    if (this.registerMenu) {
-        Roo.menu.MenuMgr.register(this);
-    }
-    
-    var mis = this.items;
-    this.items = new Roo.util.MixedCollection();
-    if(mis){
-        this.add.apply(this, mis);
-    }
+Roo.form.TriggerField = function(config){
+    this.mimicing = false;
+    Roo.form.TriggerField.superclass.constructor.call(this, config);
 };
 
-Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
-    /**
-     * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
-     */
-    minWidth : 120,
-    /**
-     * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
-     * for bottom-right shadow (defaults to "sides")
-     */
-    shadow : "sides",
+Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
     /**
-     * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
-     * this menu (defaults to "tl-tr?")
+     * @cfg {String} triggerClass A CSS class to apply to the trigger
      */
-    subMenuAlign : "tl-tr?",
     /**
-     * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
-     * relative to its element of origin (defaults to "tl-bl?")
+     * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
+     * {tag: "input", type: "text", size: "16", autocomplete: "off"})
      */
-    defaultAlign : "tl-bl?",
+    defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
     /**
-     * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
+     * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
      */
-    allowOtherMenus : false,
+    hideTrigger:false,
+
+    /** @cfg {Boolean} grow @hide */
+    /** @cfg {Number} growMin @hide */
+    /** @cfg {Number} growMax @hide */
+
     /**
-     * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
+     * @hide 
+     * @method
      */
-    registerMenu : true,
-
-    hidden:true,
+    autoSize: Roo.emptyFn,
+    // private
+    monitorTab : true,
+    // private
+    deferHeight : true,
 
+    
+    actionMode : 'wrap',
     // private
-    render : function(){
-        if(this.el){
-            return;
+    onResize : function(w, h){
+        Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
+        if(typeof w == 'number'){
+            var x = w - this.trigger.getWidth();
+            this.el.setWidth(this.adjustWidth('input', x));
+            this.trigger.setStyle('left', x+'px');
         }
-        var el = this.el = new Roo.Layer({
-            cls: "x-menu",
-            shadow:this.shadow,
-            constrain: false,
-            parentEl: this.parentEl || document.body,
-            zindex:15000
-        });
+    },
 
-        this.keyNav = new Roo.menu.MenuNav(this);
+    // private
+    adjustSize : Roo.BoxComponent.prototype.adjustSize,
 
-        if(this.plain){
-            el.addClass("x-menu-plain");
+    // private
+    getResizeEl : function(){
+        return this.wrap;
+    },
+
+    // private
+    getPositionEl : function(){
+        return this.wrap;
+    },
+
+    // private
+    alignErrorIcon : function(){
+        this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
+    },
+
+    // private
+    onRender : function(ct, position){
+        Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
+        this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
+        this.trigger = this.wrap.createChild(this.triggerConfig ||
+                {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
+        if(this.hideTrigger){
+            this.trigger.setDisplayed(false);
         }
-        if(this.cls){
-            el.addClass(this.cls);
+        this.initTrigger();
+        if(!this.width){
+            this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
         }
-        // generic focus element
-        this.focusEl = el.createChild({
-            tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
-        });
-        var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
-        //disabling touch- as it's causing issues ..
-        //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
-        ul.on('click'   , this.onClick, this);
-        
-        
-        ul.on("mouseover", this.onMouseOver, this);
-        ul.on("mouseout", this.onMouseOut, this);
-        this.items.each(function(item){
-            if (item.hidden) {
-                return;
-            }
-            
-            var li = document.createElement("li");
-            li.className = "x-menu-list-item";
-            ul.dom.appendChild(li);
-            item.render(li, this);
-        }, this);
-        this.ul = ul;
-        this.autoWidth();
     },
 
     // private
-    autoWidth : function(){
-        var el = this.el, ul = this.ul;
-        if(!el){
-            return;
+    initTrigger : function(){
+        this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
+        this.trigger.addClassOnOver('x-form-trigger-over');
+        this.trigger.addClassOnClick('x-form-trigger-click');
+    },
+
+    // private
+    onDestroy : function(){
+        if(this.trigger){
+            this.trigger.removeAllListeners();
+            this.trigger.remove();
         }
-        var w = this.width;
-        if(w){
-            el.setWidth(w);
-        }else if(Roo.isIE){
-            el.setWidth(this.minWidth);
-            var t = el.dom.offsetWidth; // force recalc
-            el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
+        if(this.wrap){
+            this.wrap.remove();
         }
+        Roo.form.TriggerField.superclass.onDestroy.call(this);
     },
 
     // private
-    delayAutoWidth : function(){
-        if(this.rendered){
-            if(!this.awTask){
-                this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
+    onFocus : function(){
+        Roo.form.TriggerField.superclass.onFocus.call(this);
+        if(!this.mimicing){
+            this.wrap.addClass('x-trigger-wrap-focus');
+            this.mimicing = true;
+            Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
+            if(this.monitorTab){
+                this.el.on("keydown", this.checkTab, this);
             }
-            this.awTask.delay(20);
         }
     },
 
     // private
-    findTargetItem : function(e){
-        var t = e.getTarget(".x-menu-list-item", this.ul,  true);
-        if(t && t.menuItemId){
-            return this.items.get(t.menuItemId);
+    checkTab : function(e){
+        if(e.getKey() == e.TAB){
+            this.triggerBlur();
         }
     },
 
     // private
-    onClick : function(e){
-        Roo.log("menu.onClick");
-        var t = this.findTargetItem(e);
-        if(!t){
-            return;
+    onBlur : function(){
+        // do nothing
+    },
+
+    // private
+    mimicBlur : function(e, t){
+        if(!this.wrap.contains(t) && this.validateBlur()){
+            this.triggerBlur();
         }
-        Roo.log(e);
-        if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
-            if(t == this.activeItem && t.shouldDeactivate(e)){
-                this.activeItem.deactivate();
-                delete this.activeItem;
-                return;
-            }
-            if(t.canActivate){
-                this.setActiveItem(t, true);
-            }
-            return;
-            
-            
+    },
+
+    // private
+    triggerBlur : function(){
+        this.mimicing = false;
+        Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
+        if(this.monitorTab){
+            this.el.un("keydown", this.checkTab, this);
         }
-        
-        t.onClick(e);
-        this.fireEvent("click", this, t, e);
+        this.wrap.removeClass('x-trigger-wrap-focus');
+        Roo.form.TriggerField.superclass.onBlur.call(this);
+    },
+
+    // private
+    // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
+    validateBlur : function(e, t){
+        return true;
     },
 
     // private
-    setActiveItem : function(item, autoExpand){
-        if(item != this.activeItem){
-            if(this.activeItem){
-                this.activeItem.deactivate();
-            }
-            this.activeItem = item;
-            item.activate(autoExpand);
-        }else if(autoExpand){
-            item.expandMenu();
+    onDisable : function(){
+        Roo.form.TriggerField.superclass.onDisable.call(this);
+        if(this.wrap){
+            this.wrap.addClass('x-item-disabled');
         }
     },
 
     // private
-    tryActivate : function(start, step){
-        var items = this.items;
-        for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
-            var item = items.get(i);
-            if(!item.disabled && item.canActivate){
-                this.setActiveItem(item, false);
-                return item;
-            }
+    onEnable : function(){
+        Roo.form.TriggerField.superclass.onEnable.call(this);
+        if(this.wrap){
+            this.wrap.removeClass('x-item-disabled');
         }
-        return false;
     },
 
     // private
-    onMouseOver : function(e){
-        var t;
-        if(t = this.findTargetItem(e)){
-            if(t.canActivate && !t.disabled){
-                this.setActiveItem(t, true);
-            }
+    onShow : function(){
+        var ae = this.getActionEl();
+        
+        if(ae){
+            ae.dom.style.display = '';
+            ae.dom.style.visibility = 'visible';
         }
-        this.fireEvent("mouseover", this, e, t);
     },
 
     // private
-    onMouseOut : function(e){
-        var t;
-        if(t = this.findTargetItem(e)){
-            if(t == this.activeItem && t.shouldDeactivate(e)){
-                this.activeItem.deactivate();
-                delete this.activeItem;
-            }
-        }
-        this.fireEvent("mouseout", this, e, t);
+    
+    onHide : function(){
+        var ae = this.getActionEl();
+        ae.dom.style.display = 'none';
     },
 
     /**
-     * Read-only.  Returns true if the menu is currently displayed, else false.
-     * @type Boolean
+     * The function that should handle the trigger's click event.  This method does nothing by default until overridden
+     * by an implementing function.
+     * @method
+     * @param {EventObject} e
      */
-    isVisible : function(){
-        return this.el && !this.hidden;
+    onTriggerClick : Roo.emptyFn
+});
+
+// TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
+// to be extended by an implementing class.  For an example of implementing this class, see the custom
+// SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
+Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
+    initComponent : function(){
+        Roo.form.TwinTriggerField.superclass.initComponent.call(this);
+
+        this.triggerConfig = {
+            tag:'span', cls:'x-form-twin-triggers', cn:[
+            {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
+            {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
+        ]};
     },
 
-    /**
-     * Displays this menu relative to another element
-     * @param {String/HTMLElement/Roo.Element} element The element to align to
-     * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
-     * the element (defaults to this.defaultAlign)
-     * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
-     */
-    show : function(el, pos, parentMenu){
-        this.parentMenu = parentMenu;
-        if(!this.el){
-            this.render();
-        }
-        this.fireEvent("beforeshow", this);
-        this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
+    getTrigger : function(index){
+        return this.triggers[index];
+    },
+
+    initTrigger : function(){
+        var ts = this.trigger.select('.x-form-trigger', true);
+        this.wrap.setStyle('overflow', 'hidden');
+        var triggerField = this;
+        ts.each(function(t, all, index){
+            t.hide = function(){
+                var w = triggerField.wrap.getWidth();
+                this.dom.style.display = 'none';
+                triggerField.el.setWidth(w-triggerField.trigger.getWidth());
+            };
+            t.show = function(){
+                var w = triggerField.wrap.getWidth();
+                this.dom.style.display = '';
+                triggerField.el.setWidth(w-triggerField.trigger.getWidth());
+            };
+            var triggerIndex = 'Trigger'+(index+1);
+
+            if(this['hide'+triggerIndex]){
+                t.dom.style.display = 'none';
+            }
+            t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
+            t.addClassOnOver('x-form-trigger-over');
+            t.addClassOnClick('x-form-trigger-click');
+        }, this);
+        this.triggers = ts.elements;
     },
 
+    onTrigger1Click : Roo.emptyFn,
+    onTrigger2Click : Roo.emptyFn
+});/*
+ * 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.form.TextArea
+ * @extends Roo.form.TextField
+ * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
+ * support for auto-sizing.
+ * @constructor
+ * Creates a new TextArea
+ * @param {Object} config Configuration options
+ */
+Roo.form.TextArea = function(config){
+    Roo.form.TextArea.superclass.constructor.call(this, config);
+    // these are provided exchanges for backwards compat
+    // minHeight/maxHeight were replaced by growMin/growMax to be
+    // compatible with TextField growing config values
+    if(this.minHeight !== undefined){
+        this.growMin = this.minHeight;
+    }
+    if(this.maxHeight !== undefined){
+        this.growMax = this.maxHeight;
+    }
+};
+
+Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
     /**
-     * Displays this menu at a specific xy position
-     * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
-     * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
+     * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
      */
-    showAt : function(xy, parentMenu, /* private: */_e){
-        this.parentMenu = parentMenu;
+    growMin : 60,
+    /**
+     * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
+     */
+    growMax: 1000,
+    /**
+     * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
+     * in the field (equivalent to setting overflow: hidden, defaults to false)
+     */
+    preventScrollbars: false,
+    /**
+     * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
+     * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
+     */
+
+    // private
+    onRender : function(ct, position){
         if(!this.el){
-            this.render();
+            this.defaultAutoCreate = {
+                tag: "textarea",
+                style:"width:300px;height:60px;",
+                autocomplete: "new-password"
+            };
         }
-        if(_e !== false){
-            this.fireEvent("beforeshow", this);
-            xy = this.el.adjustForConstraints(xy);
+        Roo.form.TextArea.superclass.onRender.call(this, ct, position);
+        if(this.grow){
+            this.textSizeEl = Roo.DomHelper.append(document.body, {
+                tag: "pre", cls: "x-form-grow-sizer"
+            });
+            if(this.preventScrollbars){
+                this.el.setStyle("overflow", "hidden");
+            }
+            this.el.setHeight(this.growMin);
         }
-        this.el.setXY(xy);
-        this.el.show();
-        this.hidden = false;
-        this.focus();
-        this.fireEvent("show", this);
     },
 
-    focus : function(){
-        if(!this.hidden){
-            this.doFocus.defer(50, this);
+    onDestroy : function(){
+        if(this.textSizeEl){
+            this.textSizeEl.parentNode.removeChild(this.textSizeEl);
         }
+        Roo.form.TextArea.superclass.onDestroy.call(this);
     },
 
-    doFocus : function(){
-        if(!this.hidden){
-            this.focusEl.focus();
+    // private
+    onKeyUp : function(e){
+        if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
+            this.autoSize();
         }
     },
 
     /**
-     * Hides this menu and optionally all parent menus
-     * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
+     * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
+     * This only takes effect if grow = true, and fires the autosize event if the height changes.
      */
-    hide : function(deep){
-        if(this.el && this.isVisible()){
-            this.fireEvent("beforehide", this);
-            if(this.activeItem){
-                this.activeItem.deactivate();
-                this.activeItem = null;
-            }
-            this.el.hide();
-            this.hidden = true;
-            this.fireEvent("hide", this);
-        }
-        if(deep === true && this.parentMenu){
-            this.parentMenu.hide(true);
+    autoSize : function(){
+        if(!this.grow || !this.textSizeEl){
+            return;
         }
-    },
-
-    /**
-     * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
-     * Any of the following are valid:
-     * <ul>
-     * <li>Any menu item object based on {@link Roo.menu.Item}</li>
-     * <li>An HTMLElement object which will be converted to a menu item</li>
-     * <li>A menu item config object that will be created as a new menu item</li>
-     * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
-     * it will be converted into a {@link Roo.menu.TextItem} and added</li>
-     * </ul>
-     * Usage:
-     * <pre><code>
-// Create the menu
-var menu = new Roo.menu.Menu();
+        var el = this.el;
+        var v = el.dom.value;
+        var ts = this.textSizeEl;
 
-// Create a menu item to add by reference
-var menuItem = new Roo.menu.Item({ text: 'New Item!' });
+        ts.innerHTML = '';
+        ts.appendChild(document.createTextNode(v));
+        v = ts.innerHTML;
 
-// Add a bunch of items at once using different methods.
-// Only the last item added will be returned.
-var item = menu.add(
-    menuItem,                // add existing item by ref
-    'Dynamic Item',          // new TextItem
-    '-',                     // new separator
-    { text: 'Config Item' }  // new item by config
-);
-</code></pre>
-     * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
-     * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
-     */
-    add : function(){
-        var a = arguments, l = a.length, item;
-        for(var i = 0; i < l; i++){
-            var el = a[i];
-            if ((typeof(el) == "object") && el.xtype && el.xns) {
-                el = Roo.factory(el, Roo.menu);
-            }
-            
-            if(el.render){ // some kind of Item
-                item = this.addItem(el);
-            }else if(typeof el == "string"){ // string
-                if(el == "separator" || el == "-"){
-                    item = this.addSeparator();
-                }else{
-                    item = this.addText(el);
-                }
-            }else if(el.tagName || el.el){ // element
-                item = this.addElement(el);
-            }else if(typeof el == "object"){ // must be menu item config?
-                item = this.addMenuItem(el);
+        Roo.fly(ts).setWidth(this.el.getWidth());
+        if(v.length < 1){
+            v = "&#160;&#160;";
+        }else{
+            if(Roo.isIE){
+                v = v.replace(/\n/g, '<p>&#160;</p>');
             }
+            v += "&#160;\n&#160;";
         }
-        return item;
-    },
+        ts.innerHTML = v;
+        var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
+        if(h != this.lastHeight){
+            this.lastHeight = h;
+            this.el.setHeight(h);
+            this.fireEvent("autosize", this, h);
+        }
+    }
+});/*
+ * 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.form.NumberField
+ * @extends Roo.form.TextField
+ * Numeric text field that provides automatic keystroke filtering and numeric validation.
+ * @constructor
+ * Creates a new NumberField
+ * @param {Object} config Configuration options
+ */
+Roo.form.NumberField = function(config){
+    Roo.form.NumberField.superclass.constructor.call(this, config);
+};
 
+Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
     /**
-     * Returns this menu's underlying {@link Roo.Element} object
-     * @return {Roo.Element} The element
+     * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
      */
-    getEl : function(){
-        if(!this.el){
-            this.render();
-        }
-        return this.el;
-    },
-
+    fieldClass: "x-form-field x-form-num-field",
     /**
-     * Adds a separator bar to the menu
-     * @return {Roo.menu.Item} The menu item that was added
+     * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
      */
-    addSeparator : function(){
-        return this.addItem(new Roo.menu.Separator());
-    },
-
+    allowDecimals : true,
     /**
-     * Adds an {@link Roo.Element} object to the menu
-     * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
-     * @return {Roo.menu.Item} The menu item that was added
+     * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
      */
-    addElement : function(el){
-        return this.addItem(new Roo.menu.BaseItem(el));
-    },
-
+    decimalSeparator : ".",
     /**
-     * Adds an existing object based on {@link Roo.menu.Item} to the menu
-     * @param {Roo.menu.Item} item The menu item to add
-     * @return {Roo.menu.Item} The menu item that was added
+     * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
      */
-    addItem : function(item){
-        this.items.add(item);
-        if(this.ul){
-            var li = document.createElement("li");
-            li.className = "x-menu-list-item";
-            this.ul.dom.appendChild(li);
-            item.render(li, this);
-            this.delayAutoWidth();
-        }
-        return item;
-    },
-
+    decimalPrecision : 2,
     /**
-     * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
-     * @param {Object} config A MenuItem config object
-     * @return {Roo.menu.Item} The menu item that was added
+     * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
      */
-    addMenuItem : function(config){
-        if(!(config instanceof Roo.menu.Item)){
-            if(typeof config.checked == "boolean"){ // must be check menu item config?
-                config = new Roo.menu.CheckItem(config);
-            }else{
-                config = new Roo.menu.Item(config);
-            }
-        }
-        return this.addItem(config);
-    },
-
+    allowNegative : true,
     /**
-     * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
-     * @param {String} text The text to display in the menu item
-     * @return {Roo.menu.Item} The menu item that was added
+     * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
      */
-    addText : function(text){
-        return this.addItem(new Roo.menu.TextItem({ text : text }));
-    },
-
+    minValue : Number.NEGATIVE_INFINITY,
     /**
-     * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
-     * @param {Number} index The index in the menu's list of current items where the new item should be inserted
-     * @param {Roo.menu.Item} item The menu item to add
-     * @return {Roo.menu.Item} The menu item that was added
+     * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
      */
-    insert : function(index, item){
-        this.items.insert(index, item);
-        if(this.ul){
-            var li = document.createElement("li");
-            li.className = "x-menu-list-item";
-            this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
-            item.render(li, this);
-            this.delayAutoWidth();
-        }
-        return item;
-    },
-
+    maxValue : Number.MAX_VALUE,
     /**
-     * Removes an {@link Roo.menu.Item} from the menu and destroys the object
-     * @param {Roo.menu.Item} item The menu item to remove
+     * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
      */
-    remove : function(item){
-        this.items.removeKey(item.id);
-        item.destroy();
-    },
-
+    minText : "The minimum value for this field is {0}",
     /**
-     * Removes and destroys all items in the menu
+     * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
      */
-    removeAll : function(){
-        var f;
-        while(f = this.items.first()){
-            this.remove(f);
-        }
-    }
-});
+    maxText : "The maximum value for this field is {0}",
+    /**
+     * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
+     * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
+     */
+    nanText : "{0} is not a valid number",
 
-// MenuNav is a private utility class used internally by the Menu
-Roo.menu.MenuNav = function(menu){
-    Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
-    this.scope = this.menu = menu;
-};
+    // private
+    initEvents : function(){
+        Roo.form.NumberField.superclass.initEvents.call(this);
+        var allowed = "0123456789";
+        if(this.allowDecimals){
+            allowed += this.decimalSeparator;
+        }
+        if(this.allowNegative){
+            allowed += "-";
+        }
+        this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
+        var keyPress = function(e){
+            var k = e.getKey();
+            if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
+                return;
+            }
+            var c = e.getCharCode();
+            if(allowed.indexOf(String.fromCharCode(c)) === -1){
+                e.stopEvent();
+            }
+        };
+        this.el.on("keypress", keyPress, this);
+    },
 
-Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
-    doRelay : function(e, h){
-        var k = e.getKey();
-        if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
-            this.menu.tryActivate(0, 1);
+    // private
+    validateValue : function(value){
+        if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
             return false;
         }
-        return h.call(this.scope || this, e, this.menu);
+        if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
+             return true;
+        }
+        var num = this.parseValue(value);
+        if(isNaN(num)){
+            this.markInvalid(String.format(this.nanText, value));
+            return false;
+        }
+        if(num < this.minValue){
+            this.markInvalid(String.format(this.minText, this.minValue));
+            return false;
+        }
+        if(num > this.maxValue){
+            this.markInvalid(String.format(this.maxText, this.maxValue));
+            return false;
+        }
+        return true;
     },
 
-    up : function(e, m){
-        if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
-            m.tryActivate(m.items.length-1, -1);
-        }
+    getValue : function(){
+        return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
+    },
+
+    // private
+    parseValue : function(value){
+        value = parseFloat(String(value).replace(this.decimalSeparator, "."));
+        return isNaN(value) ? '' : value;
     },
 
-    down : function(e, m){
-        if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
-            m.tryActivate(0, 1);
+    // private
+    fixPrecision : function(value){
+        var nan = isNaN(value);
+        if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
+            return nan ? '' : value;
         }
+        return parseFloat(value).toFixed(this.decimalPrecision);
     },
 
-    right : function(e, m){
-        if(m.activeItem){
-            m.activeItem.expandMenu(true);
-        }
+    setValue : function(v){
+        v = this.fixPrecision(v);
+        Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
     },
 
-    left : function(e, m){
-        m.hide();
-        if(m.parentMenu && m.parentMenu.activeItem){
-            m.parentMenu.activeItem.activate();
-        }
+    // private
+    decimalPrecisionFcn : function(v){
+        return Math.floor(v);
     },
 
-    enter : function(e, m){
-        if(m.activeItem){
-            e.stopPropagation();
-            m.activeItem.onClick(e);
-            m.fireEvent("click", this, m.activeItem);
-            return true;
+    beforeBlur : function(){
+        var v = this.parseValue(this.getRawValue());
+        if(v){
+            this.setValue(v);
         }
     }
 });/*
@@ -37873,388 +42028,391 @@ Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
  */
  
 /**
- * @class Roo.menu.MenuMgr
- * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
- * @singleton
- */
-Roo.menu.MenuMgr = function(){
-   var menus, active, groups = {}, attached = false, lastShow = new Date();
-
-   // private - called when first menu is created
-   function init(){
-       menus = {};
-       active = new Roo.util.MixedCollection();
-       Roo.get(document).addKeyListener(27, function(){
-           if(active.length > 0){
-               hideAll();
-           }
-       });
-   }
-
-   // private
-   function hideAll(){
-       if(active && active.length > 0){
-           var c = active.clone();
-           c.each(function(m){
-               m.hide();
-           });
-       }
-   }
-
-   // private
-   function onHide(m){
-       active.remove(m);
-       if(active.length < 1){
-           Roo.get(document).un("mousedown", onMouseDown);
-           attached = false;
-       }
-   }
-
-   // private
-   function onShow(m){
-       var last = active.last();
-       lastShow = new Date();
-       active.add(m);
-       if(!attached){
-           Roo.get(document).on("mousedown", onMouseDown);
-           attached = true;
-       }
-       if(m.parentMenu){
-          m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
-          m.parentMenu.activeChild = m;
-       }else if(last && last.isVisible()){
-          m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
-       }
-   }
-
-   // private
-   function onBeforeHide(m){
-       if(m.activeChild){
-           m.activeChild.hide();
-       }
-       if(m.autoHideTimer){
-           clearTimeout(m.autoHideTimer);
-           delete m.autoHideTimer;
-       }
-   }
-
-   // private
-   function onBeforeShow(m){
-       var pm = m.parentMenu;
-       if(!pm && !m.allowOtherMenus){
-           hideAll();
-       }else if(pm && pm.activeChild && active != m){
-           pm.activeChild.hide();
-       }
-   }
-
-   // private
-   function onMouseDown(e){
-       if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
-           hideAll();
-       }
-   }
-
-   // private
-   function onBeforeCheck(mi, state){
-       if(state){
-           var g = groups[mi.group];
-           for(var i = 0, l = g.length; i < l; i++){
-               if(g[i] != mi){
-                   g[i].setChecked(false);
-               }
-           }
-       }
-   }
-
-   return {
-
-       /**
-        * Hides all menus that are currently visible
-        */
-       hideAll : function(){
-            hideAll();  
-       },
-
-       // private
-       register : function(menu){
-           if(!menus){
-               init();
-           }
-           menus[menu.id] = menu;
-           menu.on("beforehide", onBeforeHide);
-           menu.on("hide", onHide);
-           menu.on("beforeshow", onBeforeShow);
-           menu.on("show", onShow);
-           var g = menu.group;
-           if(g && menu.events["checkchange"]){
-               if(!groups[g]){
-                   groups[g] = [];
-               }
-               groups[g].push(menu);
-               menu.on("checkchange", onCheck);
-           }
-       },
-
-        /**
-         * Returns a {@link Roo.menu.Menu} object
-         * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
-         * be used to generate and return a new Menu instance.
-         */
-       get : function(menu){
-           if(typeof menu == "string"){ // menu id
-               return menus[menu];
-           }else if(menu.events){  // menu instance
-               return menu;
-           }else if(typeof menu.length == 'number'){ // array of menu items?
-               return new Roo.menu.Menu({items:menu});
-           }else{ // otherwise, must be a config
-               return new Roo.menu.Menu(menu);
-           }
-       },
-
-       // private
-       unregister : function(menu){
-           delete menus[menu.id];
-           menu.un("beforehide", onBeforeHide);
-           menu.un("hide", onHide);
-           menu.un("beforeshow", onBeforeShow);
-           menu.un("show", onShow);
-           var g = menu.group;
-           if(g && menu.events["checkchange"]){
-               groups[g].remove(menu);
-               menu.un("checkchange", onCheck);
-           }
-       },
-
-       // private
-       registerCheckable : function(menuItem){
-           var g = menuItem.group;
-           if(g){
-               if(!groups[g]){
-                   groups[g] = [];
-               }
-               groups[g].push(menuItem);
-               menuItem.on("beforecheckchange", onBeforeCheck);
-           }
-       },
-
-       // private
-       unregisterCheckable : function(menuItem){
-           var g = menuItem.group;
-           if(g){
-               groups[g].remove(menuItem);
-               menuItem.un("beforecheckchange", onBeforeCheck);
-           }
-       }
-   };
-}();/*
- * 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.menu.BaseItem
- * @extends Roo.Component
- * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
- * management and base configuration options shared by all menu components.
- * @constructor
- * Creates a new BaseItem
- * @param {Object} config Configuration options
+ * @class Roo.form.DateField
+ * @extends Roo.form.TriggerField
+ * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
+* @constructor
+* Create a new DateField
+* @param {Object} config
  */
-Roo.menu.BaseItem = function(config){
-    Roo.menu.BaseItem.superclass.constructor.call(this, config);
-
-    this.addEvents({
-        /**
-         * @event click
-         * Fires when this item is clicked
-         * @param {Roo.menu.BaseItem} this
-         * @param {Roo.EventObject} e
-         */
-        click: true,
-        /**
-         * @event activate
-         * Fires when this item is activated
-         * @param {Roo.menu.BaseItem} this
-         */
-        activate : true,
+Roo.form.DateField = function(config)
+{
+    Roo.form.DateField.superclass.constructor.call(this, config);
+    
+      this.addEvents({
+         
         /**
-         * @event deactivate
-         * Fires when this item is deactivated
-         * @param {Roo.menu.BaseItem} this
-         */
-        deactivate : true
+         * @event select
+         * Fires when a date is selected
+            * @param {Roo.form.DateField} combo This combo box
+            * @param {Date} date The date selected
+            */
+        'select' : true
+         
     });
-
-    if(this.handler){
-        this.on("click", this.handler, this.scope, true);
+    
+    
+    if(typeof this.minValue == "string") {
+        this.minValue = this.parseDate(this.minValue);
+    }
+    if(typeof this.maxValue == "string") {
+        this.maxValue = this.parseDate(this.maxValue);
+    }
+    this.ddMatch = null;
+    if(this.disabledDates){
+        var dd = this.disabledDates;
+        var re = "(?:";
+        for(var i = 0; i < dd.length; i++){
+            re += dd[i];
+            if(i != dd.length-1) {
+                re += "|";
+            }
+        }
+        this.ddMatch = new RegExp(re + ")");
     }
 };
 
-Roo.extend(Roo.menu.BaseItem, Roo.Component, {
+Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
     /**
-     * @cfg {Function} handler
-     * A function that will handle the click event of this menu item (defaults to undefined)
+     * @cfg {String} format
+     * The default date format string which can be overriden for localization support.  The format must be
+     * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
      */
+    format : "m/d/y",
     /**
-     * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
+     * @cfg {String} altFormats
+     * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
+     * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
      */
-    canActivate : false,
-    
-     /**
-     * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
+    altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
+    /**
+     * @cfg {Array} disabledDays
+     * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
      */
-    hidden: false,
-    
+    disabledDays : null,
     /**
-     * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
+     * @cfg {String} disabledDaysText
+     * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
      */
-    activeClass : "x-menu-item-active",
+    disabledDaysText : "Disabled",
     /**
-     * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
+     * @cfg {Array} disabledDates
+     * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
+     * expression so they are very powerful. Some examples:
+     * <ul>
+     * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
+     * <li>["03/08", "09/16"] would disable those days for every year</li>
+     * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
+     * <li>["03/../2006"] would disable every day in March 2006</li>
+     * <li>["^03"] would disable every day in every March</li>
+     * </ul>
+     * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
+     * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
      */
-    hideOnClick : true,
+    disabledDates : null,
     /**
-     * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
+     * @cfg {String} disabledDatesText
+     * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
      */
-    hideDelay : 100,
-
-    // private
-    ctype: "Roo.menu.BaseItem",
+    disabledDatesText : "Disabled",
+       
+       
+       /**
+     * @cfg {Date/String} zeroValue
+     * if the date is less that this number, then the field is rendered as empty
+     * default is 1800
+     */
+       zeroValue : '1800-01-01',
+       
+       
+    /**
+     * @cfg {Date/String} minValue
+     * The minimum allowed date. Can be either a Javascript date object or a string date in a
+     * valid format (defaults to null).
+     */
+    minValue : null,
+    /**
+     * @cfg {Date/String} maxValue
+     * The maximum allowed date. Can be either a Javascript date object or a string date in a
+     * valid format (defaults to null).
+     */
+    maxValue : null,
+    /**
+     * @cfg {String} minText
+     * The error text to display when the date in the cell is before minValue (defaults to
+     * 'The date in this field must be after {minValue}').
+     */
+    minText : "The date in this field must be equal to or after {0}",
+    /**
+     * @cfg {String} maxText
+     * The error text to display when the date in the cell is after maxValue (defaults to
+     * 'The date in this field must be before {maxValue}').
+     */
+    maxText : "The date in this field must be equal to or before {0}",
+    /**
+     * @cfg {String} invalidText
+     * The error text to display when the date in the field is invalid (defaults to
+     * '{value} is not a valid date - it must be in the format {format}').
+     */
+    invalidText : "{0} is not a valid date - it must be in the format {1}",
+    /**
+     * @cfg {String} triggerClass
+     * An additional CSS class used to style the trigger button.  The trigger will always get the
+     * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
+     * which displays a calendar icon).
+     */
+    triggerClass : 'x-form-date-trigger',
+    
 
+    /**
+     * @cfg {Boolean} useIso
+     * if enabled, then the date field will use a hidden field to store the 
+     * real value as iso formated date. default (false)
+     */ 
+    useIso : false,
+    /**
+     * @cfg {String/Object} autoCreate
+     * A DomHelper element spec, or true for a default element spec (defaults to
+     * {tag: "input", type: "text", size: "10", autocomplete: "off"})
+     */ 
     // private
-    actionMode : "container",
-
+    defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
+    
     // private
-    render : function(container, parentMenu){
-        this.parentMenu = parentMenu;
-        Roo.menu.BaseItem.superclass.render.call(this, container);
-        this.container.menuItemId = this.id;
+    hiddenField: false,
+    
+    onRender : function(ct, position)
+    {
+        Roo.form.DateField.superclass.onRender.call(this, ct, position);
+        if (this.useIso) {
+            //this.el.dom.removeAttribute('name'); 
+            Roo.log("Changing name?");
+            this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
+            this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
+                    'before', true);
+            this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
+            // prevent input submission
+            this.hiddenName = this.name;
+        }
+            
+            
     },
-
+    
     // private
-    onRender : function(container, position){
-        this.el = Roo.get(this.el);
-        container.dom.appendChild(this.el.dom);
+    validateValue : function(value)
+    {
+        value = this.formatDate(value);
+        if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
+            Roo.log('super failed');
+            return false;
+        }
+        if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
+             return true;
+        }
+        var svalue = value;
+        value = this.parseDate(value);
+        if(!value){
+            Roo.log('parse date failed' + svalue);
+            this.markInvalid(String.format(this.invalidText, svalue, this.format));
+            return false;
+        }
+        var time = value.getTime();
+        if(this.minValue && time < this.minValue.getTime()){
+            this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
+            return false;
+        }
+        if(this.maxValue && time > this.maxValue.getTime()){
+            this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
+            return false;
+        }
+        if(this.disabledDays){
+            var day = value.getDay();
+            for(var i = 0; i < this.disabledDays.length; i++) {
+               if(day === this.disabledDays[i]){
+                   this.markInvalid(this.disabledDaysText);
+                    return false;
+               }
+            }
+        }
+        var fvalue = this.formatDate(value);
+        if(this.ddMatch && this.ddMatch.test(fvalue)){
+            this.markInvalid(String.format(this.disabledDatesText, fvalue));
+            return false;
+        }
+        return true;
     },
 
     // private
-    onClick : function(e){
-        if(!this.disabled && this.fireEvent("click", this, e) !== false
-                && this.parentMenu.fireEvent("itemclick", this, e) !== false){
-            this.handleClick(e);
-        }else{
-            e.stopEvent();
-        }
+    // Provides logic to override the default TriggerField.validateBlur which just returns true
+    validateBlur : function(){
+        return !this.menu || !this.menu.isVisible();
+    },
+    
+    getName: function()
+    {
+        // returns hidden if it's set..
+        if (!this.rendered) {return ''};
+        return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
+        
     },
 
-    // private
-    activate : function(){
-        if(this.disabled){
-            return false;
-        }
-        var li = this.container;
-        li.addClass(this.activeClass);
-        this.region = li.getRegion().adjust(2, 2, -2, -2);
-        this.fireEvent("activate", this);
-        return true;
+    /**
+     * Returns the current date value of the date field.
+     * @return {Date} The date value
+     */
+    getValue : function(){
+        
+        return  this.hiddenField ?
+                this.hiddenField.value :
+                this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
     },
 
-    // private
-    deactivate : function(){
-        this.container.removeClass(this.activeClass);
-        this.fireEvent("deactivate", this);
-    },
+    /**
+     * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
+     * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
+     * (the default format used is "m/d/y").
+     * <br />Usage:
+     * <pre><code>
+//All of these calls set the same date value (May 4, 2006)
+
+//Pass a date object:
+var dt = new Date('5/4/06');
+dateField.setValue(dt);
 
-    // private
-    shouldDeactivate : function(e){
-        return !this.region || !this.region.contains(e.getPoint());
-    },
+//Pass a date string (default format):
+dateField.setValue('5/4/06');
 
-    // private
-    handleClick : function(e){
-        if(this.hideOnClick){
-            this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
+//Pass a date string (custom format):
+dateField.format = 'Y-m-d';
+dateField.setValue('2006-5-4');
+</code></pre>
+     * @param {String/Date} date The date or valid date string
+     */
+    setValue : function(date){
+        if (this.hiddenField) {
+            this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
         }
+        Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
+        // make sure the value field is always stored as a date..
+        this.value = this.parseDate(date);
+        
+        
     },
 
     // private
-    expandMenu : function(autoActivate){
-        // do nothing
+    parseDate : function(value){
+               
+               if (value instanceof Date) {
+                       if (value < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
+                               return  '';
+                       }
+                       return value;
+               }
+               
+               
+        if(!value || value instanceof Date){
+            return value;
+        }
+        var v = Date.parseDate(value, this.format);
+         if (!v && this.useIso) {
+            v = Date.parseDate(value, 'Y-m-d');
+        }
+        if(!v && this.altFormats){
+            if(!this.altFormatsArray){
+                this.altFormatsArray = this.altFormats.split("|");
+            }
+            for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
+                v = Date.parseDate(value, this.altFormatsArray[i]);
+            }
+        }
+               if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
+                       v = '';
+               }
+        return v;
     },
 
     // private
-    hideMenu : function(){
-        // do nothing
-    }
-});/*
- * 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.menu.Adapter
- * @extends Roo.menu.BaseItem
- * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
- * It provides basic rendering, activation management and enable/disable logic required to work in menus.
- * @constructor
- * Creates a new Adapter
- * @param {Object} config Configuration options
- */
-Roo.menu.Adapter = function(component, config){
-    Roo.menu.Adapter.superclass.constructor.call(this, config);
-    this.component = component;
-};
-Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
-    // private
-    canActivate : true,
+    formatDate : function(date, fmt){
+        return (!date || !(date instanceof Date)) ?
+               date : date.dateFormat(fmt || this.format);
+    },
 
     // private
-    onRender : function(container, position){
-        this.component.render(container);
-        this.el = this.component.getEl();
+    menuListeners : {
+        select: function(m, d){
+            
+            this.setValue(d);
+            this.fireEvent('select', this, d);
+        },
+        show : function(){ // retain focus styling
+            this.onFocus();
+        },
+        hide : function(){
+            this.focus.defer(10, this);
+            var ml = this.menuListeners;
+            this.menu.un("select", ml.select,  this);
+            this.menu.un("show", ml.show,  this);
+            this.menu.un("hide", ml.hide,  this);
+        }
     },
 
     // private
-    activate : function(){
+    // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
+    onTriggerClick : function(){
         if(this.disabled){
-            return false;
+            return;
         }
-        this.component.focus();
-        this.fireEvent("activate", this);
-        return true;
+        if(this.menu == null){
+            this.menu = new Roo.menu.DateMenu();
+        }
+        Roo.apply(this.menu.picker,  {
+            showClear: this.allowBlank,
+            minDate : this.minValue,
+            maxDate : this.maxValue,
+            disabledDatesRE : this.ddMatch,
+            disabledDatesText : this.disabledDatesText,
+            disabledDays : this.disabledDays,
+            disabledDaysText : this.disabledDaysText,
+            format : this.useIso ? 'Y-m-d' : this.format,
+            minText : String.format(this.minText, this.formatDate(this.minValue)),
+            maxText : String.format(this.maxText, this.formatDate(this.maxValue))
+        });
+        this.menu.on(Roo.apply({}, this.menuListeners, {
+            scope:this
+        }));
+        this.menu.picker.setValue(this.getValue() || new Date());
+        this.menu.show(this.el, "tl-bl?");
     },
 
-    // private
-    deactivate : function(){
-        this.fireEvent("deactivate", this);
+    beforeBlur : function(){
+        var v = this.parseDate(this.getRawValue());
+        if(v){
+            this.setValue(v);
+        }
     },
 
-    // private
-    disable : function(){
-        this.component.disable();
-        Roo.menu.Adapter.superclass.disable.call(this);
+    /*@
+     * overide
+     * 
+     */
+    isDirty : function() {
+        if(this.disabled) {
+            return false;
+        }
+        
+        if(typeof(this.startValue) === 'undefined'){
+            return false;
+        }
+        
+        return String(this.getValue()) !== String(this.startValue);
+        
     },
-
-    // private
-    enable : function(){
-        this.component.enable();
-        Roo.menu.Adapter.superclass.enable.call(this);
+    // @overide
+    cleanLeadingSpace : function(e)
+    {
+       return;
     }
+    
 });/*
  * Based on:
  * Ext JS Library 1.1.1
@@ -38265,267 +42423,402 @@ Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
  * Fork - LGPL
  * <script type="text/javascript">
  */
-
 /**
- * @class Roo.menu.TextItem
- * @extends Roo.menu.BaseItem
- * Adds a static text string to a menu, usually used as either a heading or group separator.
- * Note: old style constructor with text is still supported.
- * 
- * @constructor
- * Creates a new TextItem
- * @param {Object} cfg Configuration
+ * @class Roo.form.MonthField
+ * @extends Roo.form.TriggerField
+ * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
+* @constructor
+* Create a new MonthField
+* @param {Object} config
  */
-Roo.menu.TextItem = function(cfg){
-    if (typeof(cfg) == 'string') {
-        this.text = cfg;
-    } else {
-        Roo.apply(this,cfg);
-    }
+Roo.form.MonthField = function(config){
     
-    Roo.menu.TextItem.superclass.constructor.call(this);
+    Roo.form.MonthField.superclass.constructor.call(this, config);
+    
+      this.addEvents({
+         
+        /**
+         * @event select
+         * Fires when a date is selected
+            * @param {Roo.form.MonthFieeld} combo This combo box
+            * @param {Date} date The date selected
+            */
+        'select' : true
+         
+    });
+    
+    
+    if(typeof this.minValue == "string") {
+        this.minValue = this.parseDate(this.minValue);
+    }
+    if(typeof this.maxValue == "string") {
+        this.maxValue = this.parseDate(this.maxValue);
+    }
+    this.ddMatch = null;
+    if(this.disabledDates){
+        var dd = this.disabledDates;
+        var re = "(?:";
+        for(var i = 0; i < dd.length; i++){
+            re += dd[i];
+            if(i != dd.length-1) {
+                re += "|";
+            }
+        }
+        this.ddMatch = new RegExp(re + ")");
+    }
 };
 
-Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
+Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
     /**
-     * @cfg {Boolean} text Text to show on item.
+     * @cfg {String} format
+     * The default date format string which can be overriden for localization support.  The format must be
+     * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
      */
-    text : '',
-    
+    format : "M Y",
     /**
-     * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
+     * @cfg {String} altFormats
+     * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
+     * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
      */
-    hideOnClick : false,
+    altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
     /**
-     * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
+     * @cfg {Array} disabledDays
+     * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
      */
-    itemCls : "x-menu-text",
-
-    // private
-    onRender : function(){
-        var s = document.createElement("span");
-        s.className = this.itemCls;
-        s.innerHTML = this.text;
-        this.el = s;
-        Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
-    }
-});/*
- * 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.menu.Separator
- * @extends Roo.menu.BaseItem
- * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
- * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
- * @constructor
- * @param {Object} config Configuration options
- */
-Roo.menu.Separator = function(config){
-    Roo.menu.Separator.superclass.constructor.call(this, config);
-};
-
-Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
+    disabledDays : [0,1,2,3,4,5,6],
     /**
-     * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
+     * @cfg {String} disabledDaysText
+     * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
      */
-    itemCls : "x-menu-sep",
+    disabledDaysText : "Disabled",
     /**
-     * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
+     * @cfg {Array} disabledDates
+     * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
+     * expression so they are very powerful. Some examples:
+     * <ul>
+     * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
+     * <li>["03/08", "09/16"] would disable those days for every year</li>
+     * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
+     * <li>["03/../2006"] would disable every day in March 2006</li>
+     * <li>["^03"] would disable every day in every March</li>
+     * </ul>
+     * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
+     * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
      */
-    hideOnClick : false,
-
-    // private
-    onRender : function(li){
-        var s = document.createElement("span");
-        s.className = this.itemCls;
-        s.innerHTML = "&#160;";
-        this.el = s;
-        li.addClass("x-menu-sep-li");
-        Roo.menu.Separator.superclass.onRender.apply(this, arguments);
-    }
-});/*
- * 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.menu.Item
- * @extends Roo.menu.BaseItem
- * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
- * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
- * activation and click handling.
- * @constructor
- * Creates a new Item
- * @param {Object} config Configuration options
- */
-Roo.menu.Item = function(config){
-    Roo.menu.Item.superclass.constructor.call(this, config);
-    if(this.menu){
-        this.menu = Roo.menu.MenuMgr.get(this.menu);
-    }
-};
-Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
-    
+    disabledDates : null,
     /**
-     * @cfg {String} text
-     * The text to show on the menu item.
+     * @cfg {String} disabledDatesText
+     * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
      */
-    text: '',
-     /**
-     * @cfg {String} HTML to render in menu
-     * The text to show on the menu item (HTML version).
+    disabledDatesText : "Disabled",
+    /**
+     * @cfg {Date/String} minValue
+     * The minimum allowed date. Can be either a Javascript date object or a string date in a
+     * valid format (defaults to null).
      */
-    html: '',
+    minValue : null,
     /**
-     * @cfg {String} icon
-     * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
+     * @cfg {Date/String} maxValue
+     * The maximum allowed date. Can be either a Javascript date object or a string date in a
+     * valid format (defaults to null).
      */
-    icon: undefined,
+    maxValue : null,
     /**
-     * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
+     * @cfg {String} minText
+     * The error text to display when the date in the cell is before minValue (defaults to
+     * 'The date in this field must be after {minValue}').
      */
-    itemCls : "x-menu-item",
+    minText : "The date in this field must be equal to or after {0}",
     /**
-     * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
+     * @cfg {String} maxTextf
+     * The error text to display when the date in the cell is after maxValue (defaults to
+     * 'The date in this field must be before {maxValue}').
      */
-    canActivate : true,
+    maxText : "The date in this field must be equal to or before {0}",
     /**
-     * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
+     * @cfg {String} invalidText
+     * The error text to display when the date in the field is invalid (defaults to
+     * '{value} is not a valid date - it must be in the format {format}').
      */
-    showDelay: 200,
-    // doc'd in BaseItem
-    hideDelay: 200,
+    invalidText : "{0} is not a valid date - it must be in the format {1}",
+    /**
+     * @cfg {String} triggerClass
+     * An additional CSS class used to style the trigger button.  The trigger will always get the
+     * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
+     * which displays a calendar icon).
+     */
+    triggerClass : 'x-form-date-trigger',
+    
 
+    /**
+     * @cfg {Boolean} useIso
+     * if enabled, then the date field will use a hidden field to store the 
+     * real value as iso formated date. default (true)
+     */ 
+    useIso : true,
+    /**
+     * @cfg {String/Object} autoCreate
+     * A DomHelper element spec, or true for a default element spec (defaults to
+     * {tag: "input", type: "text", size: "10", autocomplete: "off"})
+     */ 
     // private
-    ctype: "Roo.menu.Item",
+    defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
     
     // private
-    onRender : function(container, position){
-        var el = document.createElement("a");
-        el.hideFocus = true;
-        el.unselectable = "on";
-        el.href = this.href || "#";
-        if(this.hrefTarget){
-            el.target = this.hrefTarget;
+    hiddenField: false,
+    
+    hideMonthPicker : false,
+    
+    onRender : function(ct, position)
+    {
+        Roo.form.MonthField.superclass.onRender.call(this, ct, position);
+        if (this.useIso) {
+            this.el.dom.removeAttribute('name'); 
+            this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
+                    'before', true);
+            this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
+            // prevent input submission
+            this.hiddenName = this.name;
         }
-        el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
+            
+            
+    },
+    
+    // private
+    validateValue : function(value)
+    {
+        value = this.formatDate(value);
+        if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
+            return false;
+        }
+        if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
+             return true;
+        }
+        var svalue = value;
+        value = this.parseDate(value);
+        if(!value){
+            this.markInvalid(String.format(this.invalidText, svalue, this.format));
+            return false;
+        }
+        var time = value.getTime();
+        if(this.minValue && time < this.minValue.getTime()){
+            this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
+            return false;
+        }
+        if(this.maxValue && time > this.maxValue.getTime()){
+            this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
+            return false;
+        }
+        /*if(this.disabledDays){
+            var day = value.getDay();
+            for(var i = 0; i < this.disabledDays.length; i++) {
+               if(day === this.disabledDays[i]){
+                   this.markInvalid(this.disabledDaysText);
+                    return false;
+               }
+            }
+        }
+        */
+        var fvalue = this.formatDate(value);
+        /*if(this.ddMatch && this.ddMatch.test(fvalue)){
+            this.markInvalid(String.format(this.disabledDatesText, fvalue));
+            return false;
+        }
+        */
+        return true;
+    },
+
+    // private
+    // Provides logic to override the default TriggerField.validateBlur which just returns true
+    validateBlur : function(){
+        return !this.menu || !this.menu.isVisible();
+    },
+
+    /**
+     * Returns the current date value of the date field.
+     * @return {Date} The date value
+     */
+    getValue : function(){
         
-        var html = this.html.length ? this.html  : String.format('{0}',this.text);
         
-        el.innerHTML = String.format(
-                '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
-                this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
-        this.el = el;
-        Roo.menu.Item.superclass.onRender.call(this, container, position);
+        
+        return  this.hiddenField ?
+                this.hiddenField.value :
+                this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
     },
 
-    /**
-     * Sets the text to display in this menu item
-     * @param {String} text The text to display
-     * @param {Boolean} isHTML true to indicate text is pure html.
+    /**
+     * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
+     * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
+     * (the default format used is "m/d/y").
+     * <br />Usage:
+     * <pre><code>
+//All of these calls set the same date value (May 4, 2006)
+
+//Pass a date object:
+var dt = new Date('5/4/06');
+monthField.setValue(dt);
+
+//Pass a date string (default format):
+monthField.setValue('5/4/06');
+
+//Pass a date string (custom format):
+monthField.format = 'Y-m-d';
+monthField.setValue('2006-5-4');
+</code></pre>
+     * @param {String/Date} date The date or valid date string
      */
-    setText : function(text, isHTML){
-        if (isHTML) {
-            this.html = text;
-        } else {
-            this.text = text;
-            this.html = '';
-        }
-        if(this.rendered){
-            var html = this.html.length ? this.html  : String.format('{0}',this.text);
-     
-            this.el.update(String.format(
-                '<img src="{0}" class="x-menu-item-icon {2}">' + html,
-                this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
-            this.parentMenu.autoWidth();
+    setValue : function(date){
+        Roo.log('month setValue' + date);
+        // can only be first of month..
+        
+        var val = this.parseDate(date);
+        
+        if (this.hiddenField) {
+            this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
         }
+        Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
+        this.value = this.parseDate(date);
     },
 
     // private
-    handleClick : function(e){
-        if(!this.href){ // if no link defined, stop the event automatically
-            e.stopEvent();
+    parseDate : function(value){
+        if(!value || value instanceof Date){
+            value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
+            return value;
         }
-        Roo.menu.Item.superclass.handleClick.apply(this, arguments);
-    },
-
-    // private
-    activate : function(autoExpand){
-        if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
-            this.focus();
-            if(autoExpand){
-                this.expandMenu();
-            }
+        var v = Date.parseDate(value, this.format);
+        if (!v && this.useIso) {
+            v = Date.parseDate(value, 'Y-m-d');
         }
-        return true;
-    },
-
-    // private
-    shouldDeactivate : function(e){
-        if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
-            if(this.menu && this.menu.isVisible()){
-                return !this.menu.getEl().getRegion().contains(e.getPoint());
+        if (v) {
+            // 
+            v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
+        }
+        
+        
+        if(!v && this.altFormats){
+            if(!this.altFormatsArray){
+                this.altFormatsArray = this.altFormats.split("|");
+            }
+            for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
+                v = Date.parseDate(value, this.altFormatsArray[i]);
             }
-            return true;
         }
-        return false;
+        return v;
     },
 
     // private
-    deactivate : function(){
-        Roo.menu.Item.superclass.deactivate.apply(this, arguments);
-        this.hideMenu();
+    formatDate : function(date, fmt){
+        return (!date || !(date instanceof Date)) ?
+               date : date.dateFormat(fmt || this.format);
     },
 
     // private
-    expandMenu : function(autoActivate){
-        if(!this.disabled && this.menu){
-            clearTimeout(this.hideTimer);
-            delete this.hideTimer;
-            if(!this.menu.isVisible() && !this.showTimer){
-                this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
-            }else if (this.menu.isVisible() && autoActivate){
-                this.menu.tryActivate(0, 1);
-            }
+    menuListeners : {
+        select: function(m, d){
+            this.setValue(d);
+            this.fireEvent('select', this, d);
+        },
+        show : function(){ // retain focus styling
+            this.onFocus();
+        },
+        hide : function(){
+            this.focus.defer(10, this);
+            var ml = this.menuListeners;
+            this.menu.un("select", ml.select,  this);
+            this.menu.un("show", ml.show,  this);
+            this.menu.un("hide", ml.hide,  this);
         }
     },
-
     // private
-    deferExpand : function(autoActivate){
-        delete this.showTimer;
-        this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
-        if(autoActivate){
-            this.menu.tryActivate(0, 1);
+    // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
+    onTriggerClick : function(){
+        if(this.disabled){
+            return;
         }
-    },
-
-    // private
-    hideMenu : function(){
-        clearTimeout(this.showTimer);
-        delete this.showTimer;
-        if(!this.hideTimer && this.menu && this.menu.isVisible()){
-            this.hideTimer = this.deferHide.defer(this.hideDelay, this);
+        if(this.menu == null){
+            this.menu = new Roo.menu.DateMenu();
+           
+        }
+        
+        Roo.apply(this.menu.picker,  {
+            
+            showClear: this.allowBlank,
+            minDate : this.minValue,
+            maxDate : this.maxValue,
+            disabledDatesRE : this.ddMatch,
+            disabledDatesText : this.disabledDatesText,
+            
+            format : this.useIso ? 'Y-m-d' : this.format,
+            minText : String.format(this.minText, this.formatDate(this.minValue)),
+            maxText : String.format(this.maxText, this.formatDate(this.maxValue))
+            
+        });
+         this.menu.on(Roo.apply({}, this.menuListeners, {
+            scope:this
+        }));
+       
+        
+        var m = this.menu;
+        var p = m.picker;
+        
+        // hide month picker get's called when we called by 'before hide';
+        
+        var ignorehide = true;
+        p.hideMonthPicker  = function(disableAnim){
+            if (ignorehide) {
+                return;
+            }
+             if(this.monthPicker){
+                Roo.log("hideMonthPicker called");
+                if(disableAnim === true){
+                    this.monthPicker.hide();
+                }else{
+                    this.monthPicker.slideOut('t', {duration:.2});
+                    p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
+                    p.fireEvent("select", this, this.value);
+                    m.hide();
+                }
+            }
         }
+        
+        Roo.log('picker set value');
+        Roo.log(this.getValue());
+        p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
+        m.show(this.el, 'tl-bl?');
+        ignorehide  = false;
+        // this will trigger hideMonthPicker..
+        
+        
+        // hidden the day picker
+        Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
+        
+        
+        
+      
+        
+        p.showMonthPicker.defer(100, p);
+    
+        
+       
     },
 
-    // private
-    deferHide : function(){
-        delete this.hideTimer;
-        this.menu.hide();
+    beforeBlur : function(){
+        var v = this.parseDate(this.getRawValue());
+        if(v){
+            this.setValue(v);
+        }
     }
+
+    /** @cfg {Boolean} grow @hide */
+    /** @cfg {Number} growMin @hide */
+    /** @cfg {Number} growMax @hide */
+    /**
+     * @hide
+     * @method autoSize
+     */
 });/*
  * Based on:
  * Ext JS Library 1.1.1
@@ -38537,1550 +42830,2002 @@ Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
  * <script type="text/javascript">
  */
  
+
 /**
- * @class Roo.menu.CheckItem
- * @extends Roo.menu.Item
- * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
+ * @class Roo.form.ComboBox
+ * @extends Roo.form.TriggerField
+ * A combobox control with support for autocomplete, remote-loading, paging and many other features.
  * @constructor
- * Creates a new CheckItem
+ * Create a new ComboBox.
  * @param {Object} config Configuration options
  */
-Roo.menu.CheckItem = function(config){
-    Roo.menu.CheckItem.superclass.constructor.call(this, config);
+Roo.form.ComboBox = function(config){
+    Roo.form.ComboBox.superclass.constructor.call(this, config);
     this.addEvents({
         /**
-         * @event beforecheckchange
-         * Fires before the checked value is set, providing an opportunity to cancel if needed
-         * @param {Roo.menu.CheckItem} this
-         * @param {Boolean} checked The new checked value that will be set
-         */
-        "beforecheckchange" : true,
+         * @event expand
+         * Fires when the dropdown list is expanded
+            * @param {Roo.form.ComboBox} combo This combo box
+            */
+        'expand' : true,
         /**
-         * @event checkchange
-         * Fires after the checked value has been set
-         * @param {Roo.menu.CheckItem} this
-         * @param {Boolean} checked The checked value that was set
-         */
-        "checkchange" : true
+         * @event collapse
+         * Fires when the dropdown list is collapsed
+            * @param {Roo.form.ComboBox} combo This combo box
+            */
+        'collapse' : true,
+        /**
+         * @event beforeselect
+         * Fires before a list item is selected. Return false to cancel the selection.
+            * @param {Roo.form.ComboBox} combo This combo box
+            * @param {Roo.data.Record} record The data record returned from the underlying store
+            * @param {Number} index The index of the selected item in the dropdown list
+            */
+        'beforeselect' : true,
+        /**
+         * @event select
+         * Fires when a list item is selected
+            * @param {Roo.form.ComboBox} combo This combo box
+            * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
+            * @param {Number} index The index of the selected item in the dropdown list
+            */
+        'select' : true,
+        /**
+         * @event beforequery
+         * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
+         * The event object passed has these properties:
+            * @param {Roo.form.ComboBox} combo This combo box
+            * @param {String} query The query
+            * @param {Boolean} forceAll true to force "all" query
+            * @param {Boolean} cancel true to cancel the query
+            * @param {Object} e The query event object
+            */
+        'beforequery': true,
+         /**
+         * @event add
+         * Fires when the 'add' icon is pressed (add a listener to enable add button)
+            * @param {Roo.form.ComboBox} combo This combo box
+            */
+        'add' : true,
+        /**
+         * @event edit
+         * Fires when the 'edit' icon is pressed (add a listener to enable add button)
+            * @param {Roo.form.ComboBox} combo This combo box
+            * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
+            */
+        'edit' : true
+        
+        
     });
-    if(this.checkHandler){
-        this.on('checkchange', this.checkHandler, this.scope);
+    if(this.transform){
+        this.allowDomMove = false;
+        var s = Roo.getDom(this.transform);
+        if(!this.hiddenName){
+            this.hiddenName = s.name;
+        }
+        if(!this.store){
+            this.mode = 'local';
+            var d = [], opts = s.options;
+            for(var i = 0, len = opts.length;i < len; i++){
+                var o = opts[i];
+                var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
+                if(o.selected) {
+                    this.value = value;
+                }
+                d.push([value, o.text]);
+            }
+            this.store = new Roo.data.SimpleStore({
+                'id': 0,
+                fields: ['value', 'text'],
+                data : d
+            });
+            this.valueField = 'value';
+            this.displayField = 'text';
+        }
+        s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
+        if(!this.lazyRender){
+            this.target = true;
+            this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
+            s.parentNode.removeChild(s); // remove it
+            this.render(this.el.parentNode);
+        }else{
+            s.parentNode.removeChild(s); // remove it
+        }
+
+    }
+    if (this.store) {
+        this.store = Roo.factory(this.store, Roo.data);
+    }
+    
+    this.selectedIndex = -1;
+    if(this.mode == 'local'){
+        if(config.queryDelay === undefined){
+            this.queryDelay = 10;
+        }
+        if(config.minChars === undefined){
+            this.minChars = 0;
+        }
     }
 };
-Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
+
+Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
     /**
-     * @cfg {String} group
-     * All check items with the same group name will automatically be grouped into a single-select
-     * radio button group (defaults to '')
+     * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
      */
     /**
-     * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
+     * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
+     * rendering into an Roo.Editor, defaults to false)
      */
-    itemCls : "x-menu-item x-menu-check-item",
     /**
-     * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
+     * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
+     * {tag: "input", type: "text", size: "24", autocomplete: "off"})
      */
-    groupClass : "x-menu-group-item",
-
     /**
-     * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
-     * if this checkbox is part of a radio group (group = true) only the last item in the group that is
-     * initialized with checked = true will be rendered as checked.
+     * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
      */
-    checked: false,
-
-    // private
-    ctype: "Roo.menu.CheckItem",
-
-    // private
-    onRender : function(c){
-        Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
-        if(this.group){
-            this.el.addClass(this.groupClass);
-        }
-        Roo.menu.MenuMgr.registerCheckable(this);
-        if(this.checked){
-            this.checked = false;
-            this.setChecked(true, true);
-        }
-    },
-
-    // private
-    destroy : function(){
-        if(this.rendered){
-            Roo.menu.MenuMgr.unregisterCheckable(this);
-        }
-        Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
-    },
-
     /**
-     * Set the checked state of this item
-     * @param {Boolean} checked The new checked value
-     * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
+     * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
+     * the dropdown list (defaults to undefined, with no header element)
      */
-    setChecked : function(state, suppressEvent){
-        if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
-            if(this.container){
-                this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
-            }
-            this.checked = state;
-            if(suppressEvent !== true){
-                this.fireEvent("checkchange", this, state);
-            }
-        }
-    },
 
+     /**
+     * @cfg {String/Roo.Template} tpl The template to use to render the output
+     */
+     
     // private
-    handleClick : function(e){
-       if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
-           this.setChecked(!this.checked);
-       }
-       Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
-    }
-});/*
- * 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.menu.DateItem
- * @extends Roo.menu.Adapter
- * A menu item that wraps the {@link Roo.DatPicker} component.
- * @constructor
- * Creates a new DateItem
- * @param {Object} config Configuration options
- */
-Roo.menu.DateItem = function(config){
-    Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
-    /** The Roo.DatePicker object @type Roo.DatePicker */
-    this.picker = this.component;
-    this.addEvents({select: true});
+    defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
+    /**
+     * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
+     */
+    listWidth: undefined,
+    /**
+     * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
+     * mode = 'remote' or 'text' if mode = 'local')
+     */
+    displayField: undefined,
+    /**
+     * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
+     * mode = 'remote' or 'value' if mode = 'local'). 
+     * Note: use of a valueField requires the user make a selection
+     * in order for a value to be mapped.
+     */
+    valueField: undefined,
+    
     
-    this.picker.on("render", function(picker){
-        picker.getEl().swallowEvent("click");
-        picker.container.addClass("x-menu-date-item");
-    });
-
-    this.picker.on("select", this.onSelect, this);
-};
-
-Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
-    // private
-    onSelect : function(picker, date){
-        this.fireEvent("select", this, date, picker);
-        Roo.menu.DateItem.superclass.handleClick.call(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.menu.ColorItem
- * @extends Roo.menu.Adapter
- * A menu item that wraps the {@link Roo.ColorPalette} component.
- * @constructor
- * Creates a new ColorItem
- * @param {Object} config Configuration options
- */
-Roo.menu.ColorItem = function(config){
-    Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
-    /** The Roo.ColorPalette object @type Roo.ColorPalette */
-    this.palette = this.component;
-    this.relayEvents(this.palette, ["select"]);
-    if(this.selectHandler){
-        this.on('select', this.selectHandler, this.scope);
-    }
-};
-Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
- * 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.menu.DateMenu
- * @extends Roo.menu.Menu
- * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
- * @constructor
- * Creates a new DateMenu
- * @param {Object} config Configuration options
- */
-Roo.menu.DateMenu = function(config){
-    Roo.menu.DateMenu.superclass.constructor.call(this, config);
-    this.plain = true;
-    var di = new Roo.menu.DateItem(config);
-    this.add(di);
     /**
-     * The {@link Roo.DatePicker} instance for this DateMenu
-     * @type DatePicker
+     * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
+     * field's data value (defaults to the underlying DOM element's name)
      */
-    this.picker = di.picker;
+    hiddenName: undefined,
     /**
-     * @event select
-     * @param {DatePicker} picker
-     * @param {Date} date
+     * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
      */
-    this.relayEvents(di, ["select"]);
-    this.on('beforeshow', function(){
-        if(this.picker){
-            this.picker.hideMonthPicker(false);
-        }
-    }, this);
-};
-Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
-    cls:'x-date-menu'
-});/*
- * 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.menu.ColorMenu
- * @extends Roo.menu.Menu
- * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
- * @constructor
- * Creates a new ColorMenu
- * @param {Object} config Configuration options
- */
-Roo.menu.ColorMenu = function(config){
-    Roo.menu.ColorMenu.superclass.constructor.call(this, config);
-    this.plain = true;
-    var ci = new Roo.menu.ColorItem(config);
-    this.add(ci);
+    listClass: '',
     /**
-     * The {@link Roo.ColorPalette} instance for this ColorMenu
-     * @type ColorPalette
+     * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
      */
-    this.palette = ci.palette;
+    selectedClass: 'x-combo-selected',
     /**
-     * @event select
-     * @param {ColorPalette} palette
-     * @param {String} color
+     * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
+     * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
+     * which displays a downward arrow icon).
      */
-    this.relayEvents(ci, ["select"]);
-};
-Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
- * 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.form.TextItem
- * @extends Roo.BoxComponent
- * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
- * @constructor
- * Creates a new TextItem
- * @param {Object} config Configuration options
- */
-Roo.form.TextItem = function(config){
-    Roo.form.TextItem.superclass.constructor.call(this, config);
-};
-
-Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
-    
+    triggerClass : 'x-form-arrow-trigger',
     /**
-     * @cfg {String} tag the tag for this item (default div)
+     * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
      */
-    tag : 'div',
+    shadow:'sides',
     /**
-     * @cfg {String} html the content for this item
+     * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
+     * anchor positions (defaults to 'tl-bl')
      */
-    html : '',
-    
-    getAutoCreate : function()
-    {
-        var cfg = {
-            id: this.id,
-            tag: this.tag,
-            html: this.html,
-            cls: 'x-form-item'
-        };
-        
-        return cfg;
-        
-    },
-    
-    onRender : function(ct, position)
-    {
-        Roo.form.TextItem.superclass.onRender.call(this, ct, position);
-        
-        if(!this.el){
-            var cfg = this.getAutoCreate();
-            if(!cfg.name){
-                cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
-            }
-            if (!cfg.name.length) {
-                delete cfg.name;
-            }
-            this.el = ct.createChild(cfg, position);
-        }
-    }
-    
-});/*
- * 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.form.Field
- * @extends Roo.BoxComponent
- * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
- * @constructor
- * Creates a new Field
- * @param {Object} config Configuration options
- */
-Roo.form.Field = function(config){
-    Roo.form.Field.superclass.constructor.call(this, config);
-};
-
-Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
+    listAlign: 'tl-bl?',
     /**
-     * @cfg {String} fieldLabel Label to use when rendering a form.
+     * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
      */
-       /**
-     * @cfg {String} qtip Mouse over tip
+    maxHeight: 300,
+    /**
+     * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
+     * query specified by the allQuery config option (defaults to 'query')
      */
-     
+    triggerAction: 'query',
     /**
-     * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
+     * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
+     * (defaults to 4, does not apply if editable = false)
      */
-    invalidClass : "x-form-invalid",
+    minChars : 4,
     /**
-     * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
+     * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
+     * delay (typeAheadDelay) if it matches a known value (defaults to false)
      */
-    invalidText : "The value in this field is invalid",
+    typeAhead: false,
     /**
-     * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
+     * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
+     * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
      */
-    focusClass : "x-form-focus",
+    queryDelay: 500,
     /**
-     * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
-      automatic validation (defaults to "keyup").
+     * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
+     * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
      */
-    validationEvent : "keyup",
+    pageSize: 0,
     /**
-     * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
+     * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
+     * when editable = true (defaults to false)
      */
-    validateOnBlur : true,
+    selectOnFocus:false,
     /**
-     * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
+     * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
      */
-    validationDelay : 250,
+    queryParam: 'query',
     /**
-     * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
-     * {tag: "input", type: "text", size: "20", autocomplete: "off"})
+     * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
+     * when mode = 'remote' (defaults to 'Loading...')
      */
-    defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
+    loadingText: 'Loading...',
     /**
-     * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
+     * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
      */
-    fieldClass : "x-form-field",
+    resizable: false,
     /**
-     * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
-     *<pre>
-Value         Description
------------   ----------------------------------------------------------------------
-qtip          Display a quick tip when the user hovers over the field
-title         Display a default browser title attribute popup
-under         Add a block div beneath the field containing the error text
-side          Add an error icon to the right of the field with a popup on hover
-[element id]  Add the error text directly to the innerHTML of the specified element
-</pre>
+     * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
      */
-    msgTarget : 'qtip',
+    handleHeight : 8,
     /**
-     * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
+     * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
+     * traditional select (defaults to true)
      */
-    msgFx : 'normal',
-
+    editable: true,
     /**
-     * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
+     * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
      */
-    readOnly : false,
-
+    allQuery: '',
     /**
-     * @cfg {Boolean} disabled True to disable the field (defaults to false).
+     * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
      */
-    disabled : false,
-
+    mode: 'remote',
     /**
-     * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
+     * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
+     * listWidth has a higher value)
      */
-    inputType : undefined,
-    
+    minListWidth : 70,
     /**
-     * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
-        */
-       tabIndex : undefined,
-       
-    // private
-    isFormField : true,
-
-    // private
-    hasFocus : false,
+     * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
+     * allow the user to set arbitrary text into the field (defaults to false)
+     */
+    forceSelection:false,
     /**
-     * @property {Roo.Element} fieldEl
-     * Element Containing the rendered Field (with label etc.)
+     * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
+     * if typeAhead = true (defaults to 250)
      */
+    typeAheadDelay : 250,
     /**
-     * @cfg {Mixed} value A value to initialize this field with.
+     * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
+     * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
      */
-    value : undefined,
-
+    valueNotFoundText : undefined,
     /**
-     * @cfg {String} name The field's HTML name attribute.
+     * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
      */
+    blockFocus : false,
+    
     /**
-     * @cfg {String} cls A CSS class to apply to the field's underlying element.
+     * @cfg {Boolean} disableClear Disable showing of clear button.
      */
-    // private
-    loadedValue : false,
-     
+    disableClear : false,
+    /**
+     * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
+     */
+    alwaysQuery : false,
+    
+    //private
+    addicon : false,
+    editicon: false,
+    
+    // element that contains real text value.. (when hidden is used..)
      
-       // private ??
-       initComponent : function(){
-        Roo.form.Field.superclass.initComponent.call(this);
-        this.addEvents({
-            /**
-             * @event focus
-             * Fires when this field receives input focus.
-             * @param {Roo.form.Field} this
-             */
-            focus : true,
-            /**
-             * @event blur
-             * Fires when this field loses input focus.
-             * @param {Roo.form.Field} this
-             */
-            blur : true,
-            /**
-             * @event specialkey
-             * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
-             * {@link Roo.EventObject#getKey} to determine which key was pressed.
-             * @param {Roo.form.Field} this
-             * @param {Roo.EventObject} e The event object
-             */
-            specialkey : true,
-            /**
-             * @event change
-             * Fires just before the field blurs if the field value has changed.
-             * @param {Roo.form.Field} this
-             * @param {Mixed} newValue The new value
-             * @param {Mixed} oldValue The original value
-             */
-            change : true,
-            /**
-             * @event invalid
-             * Fires after the field has been marked as invalid.
-             * @param {Roo.form.Field} this
-             * @param {String} msg The validation message
-             */
-            invalid : true,
-            /**
-             * @event valid
-             * Fires after the field has been validated with no errors.
-             * @param {Roo.form.Field} this
-             */
-            valid : true,
-             /**
-             * @event keyup
-             * Fires after the key up
-             * @param {Roo.form.Field} this
-             * @param {Roo.EventObject}  e The event Object
-             */
-            keyup : true
+    // private
+    onRender : function(ct, position)
+    {
+        Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
+        
+       if(this.hiddenName){
+            this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
+                    'before', true);
+            this.hiddenField.value =
+                this.hiddenValue !== undefined ? this.hiddenValue :
+                this.value !== undefined ? this.value : '';
+
+            // prevent input submission
+            this.el.dom.removeAttribute('name');
+             
+             
+        }
+       
+        if(Roo.isGecko){
+            this.el.dom.setAttribute('autocomplete', 'off');
+        }
+
+        var cls = 'x-combo-list';
+
+        this.list = new Roo.Layer({
+            shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
+        });
+
+        var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
+        this.list.setWidth(lw);
+        this.list.swallowEvent('mousewheel');
+        this.assetHeight = 0;
+
+        if(this.title){
+            this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
+            this.assetHeight += this.header.getHeight();
+        }
+
+        this.innerList = this.list.createChild({cls:cls+'-inner'});
+        this.innerList.on('mouseover', this.onViewOver, this);
+        this.innerList.on('mousemove', this.onViewMove, this);
+        this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
+        
+        if(this.allowBlank && !this.pageSize && !this.disableClear){
+            this.footer = this.list.createChild({cls:cls+'-ft'});
+            this.pageTb = new Roo.Toolbar(this.footer);
+           
+        }
+        if(this.pageSize){
+            this.footer = this.list.createChild({cls:cls+'-ft'});
+            this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
+                    {pageSize: this.pageSize});
+            
+        }
+        
+        if (this.pageTb && this.allowBlank && !this.disableClear) {
+            var _this = this;
+            this.pageTb.add(new Roo.Toolbar.Fill(), {
+                cls: 'x-btn-icon x-btn-clear',
+                text: '&#160;',
+                handler: function()
+                {
+                    _this.collapse();
+                    _this.clearValue();
+                    _this.onSelect(false, -1);
+                }
+            });
+        }
+        if (this.footer) {
+            this.assetHeight += this.footer.getHeight();
+        }
+        
+
+        if(!this.tpl){
+            this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
+        }
+
+        this.view = new Roo.View(this.innerList, this.tpl, {
+            singleSelect:true,
+           store: this.store,
+           selectedClass: this.selectedClass
+        });
+
+        this.view.on('click', this.onViewClick, this);
+
+        this.store.on('beforeload', this.onBeforeLoad, this);
+        this.store.on('load', this.onLoad, this);
+        this.store.on('loadexception', this.onLoadException, this);
+
+        if(this.resizable){
+            this.resizer = new Roo.Resizable(this.list,  {
+               pinned:true, handles:'se'
+            });
+            this.resizer.on('resize', function(r, w, h){
+                this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
+                this.listWidth = w;
+                this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
+                this.restrictHeight();
+            }, this);
+            this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
+        }
+        if(!this.editable){
+            this.editable = true;
+            this.setEditable(false);
+        }  
+        
+        
+        if (typeof(this.events.add.listeners) != 'undefined') {
+            
+            this.addicon = this.wrap.createChild(
+                {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
+       
+            this.addicon.on('click', function(e) {
+                this.fireEvent('add', this);
+            }, this);
+        }
+        if (typeof(this.events.edit.listeners) != 'undefined') {
+            
+            this.editicon = this.wrap.createChild(
+                {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
+            if (this.addicon) {
+                this.editicon.setStyle('margin-left', '40px');
+            }
+            this.editicon.on('click', function(e) {
+                
+                // we fire even  if inothing is selected..
+                this.fireEvent('edit', this, this.lastData );
+                
+            }, this);
+        }
+        
+        
+        
+    },
+
+    // private
+    initEvents : function(){
+        Roo.form.ComboBox.superclass.initEvents.call(this);
+
+        this.keyNav = new Roo.KeyNav(this.el, {
+            "up" : function(e){
+                this.inKeyMode = true;
+                this.selectPrev();
+            },
+
+            "down" : function(e){
+                if(!this.isExpanded()){
+                    this.onTriggerClick();
+                }else{
+                    this.inKeyMode = true;
+                    this.selectNext();
+                }
+            },
+
+            "enter" : function(e){
+                this.onViewClick();
+                //return true;
+            },
+
+            "esc" : function(e){
+                this.collapse();
+            },
+
+            "tab" : function(e){
+                this.onViewClick(false);
+                this.fireEvent("specialkey", this, e);
+                return true;
+            },
+
+            scope : this,
+
+            doRelay : function(foo, bar, hname){
+                if(hname == 'down' || this.scope.isExpanded()){
+                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
+                }
+                return true;
+            },
+
+            forceKeyDown: true
         });
+        this.queryDelay = Math.max(this.queryDelay || 10,
+                this.mode == 'local' ? 10 : 250);
+        this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
+        if(this.typeAhead){
+            this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
+        }
+        if(this.editable !== false){
+            this.el.on("keyup", this.onKeyUp, this);
+        }
+        if(this.forceSelection){
+            this.on('blur', this.doForce, this);
+        }
     },
 
-    /**
-     * Returns the name attribute of the field if available
-     * @return {String} name The field name
-     */
-    getName: function(){
-         return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
+    onDestroy : function(){
+        if(this.view){
+            this.view.setStore(null);
+            this.view.el.removeAllListeners();
+            this.view.el.remove();
+            this.view.purgeListeners();
+        }
+        if(this.list){
+            this.list.destroy();
+        }
+        if(this.store){
+            this.store.un('beforeload', this.onBeforeLoad, this);
+            this.store.un('load', this.onLoad, this);
+            this.store.un('loadexception', this.onLoadException, this);
+        }
+        Roo.form.ComboBox.superclass.onDestroy.call(this);
     },
 
     // private
-    onRender : function(ct, position){
-        Roo.form.Field.superclass.onRender.call(this, ct, position);
-        if(!this.el){
-            var cfg = this.getAutoCreate();
-            if(!cfg.name){
-                cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
-            }
-            if (!cfg.name.length) {
-                delete cfg.name;
-            }
-            if(this.inputType){
-                cfg.type = this.inputType;
-            }
-            this.el = ct.createChild(cfg, position);
-        }
-        var type = this.el.dom.type;
-        if(type){
-            if(type == 'password'){
-                type = 'text';
-            }
-            this.el.addClass('x-form-'+type);
+    fireKey : function(e){
+        if(e.isNavKeyPress() && !this.list.isVisible()){
+            this.fireEvent("specialkey", this, e);
         }
-        if(this.readOnly){
-            this.el.dom.readOnly = true;
+    },
+
+    // private
+    onResize: function(w, h){
+        Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
+        
+        if(typeof w != 'number'){
+            // we do not handle it!?!?
+            return;
         }
-        if(this.tabIndex !== undefined){
-            this.el.dom.setAttribute('tabIndex', this.tabIndex);
+        var tw = this.trigger.getWidth();
+        tw += this.addicon ? this.addicon.getWidth() : 0;
+        tw += this.editicon ? this.editicon.getWidth() : 0;
+        var x = w - tw;
+        this.el.setWidth( this.adjustWidth('input', x));
+            
+        this.trigger.setStyle('left', x+'px');
+        
+        if(this.list && this.listWidth === undefined){
+            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
+            this.list.setWidth(lw);
+            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
         }
-
-        this.el.addClass([this.fieldClass, this.cls]);
-        this.initValue();
+        
+    
+        
     },
 
     /**
-     * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
-     * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
-     * @return {Roo.form.Field} this
+     * Allow or prevent the user from directly editing the field text.  If false is passed,
+     * the user will only be able to select from the items defined in the dropdown list.  This method
+     * is the runtime equivalent of setting the 'editable' config option at config time.
+     * @param {Boolean} value True to allow the user to directly edit the field text
      */
-    applyTo : function(target){
-        this.allowDomMove = false;
-        this.el = Roo.get(target);
-        this.render(this.el.dom.parentNode);
-        return this;
+    setEditable : function(value){
+        if(value == this.editable){
+            return;
+        }
+        this.editable = value;
+        if(!value){
+            this.el.dom.setAttribute('readOnly', true);
+            this.el.on('mousedown', this.onTriggerClick,  this);
+            this.el.addClass('x-combo-noedit');
+        }else{
+            this.el.dom.setAttribute('readOnly', false);
+            this.el.un('mousedown', this.onTriggerClick,  this);
+            this.el.removeClass('x-combo-noedit');
+        }
     },
 
     // private
-    initValue : function(){
-        if(this.value !== undefined){
-            this.setValue(this.value);
-        }else if(this.el.dom.value.length > 0){
-            this.setValue(this.el.dom.value);
+    onBeforeLoad : function(){
+        if(!this.hasFocus){
+            return;
         }
+        this.innerList.update(this.loadingText ?
+               '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
+        this.restrictHeight();
+        this.selectedIndex = -1;
     },
 
-    /**
-     * Returns true if this field has been changed since it was originally loaded and is not disabled.
-     * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
-     */
-    isDirty : function() {
-        if(this.disabled) {
-            return false;
+    // private
+    onLoad : function(){
+        if(!this.hasFocus){
+            return;
+        }
+        if(this.store.getCount() > 0){
+            this.expand();
+            this.restrictHeight();
+            if(this.lastQuery == this.allQuery){
+                if(this.editable){
+                    this.el.dom.select();
+                }
+                if(!this.selectByValue(this.value, true)){
+                    this.select(0, true);
+                }
+            }else{
+                this.selectNext();
+                if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
+                    this.taTask.delay(this.typeAheadDelay);
+                }
+            }
+        }else{
+            this.onEmptyResults();
+        }
+        //this.el.focus();
+    },
+    // private
+    onLoadException : function()
+    {
+        this.collapse();
+        Roo.log(this.store.reader.jsonData);
+        if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
+            Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
+        }
+        
+        
+    },
+    // private
+    onTypeAhead : function(){
+        if(this.store.getCount() > 0){
+            var r = this.store.getAt(0);
+            var newValue = r.data[this.displayField];
+            var len = newValue.length;
+            var selStart = this.getRawValue().length;
+            if(selStart != len){
+                this.setRawValue(newValue);
+                this.selectText(selStart, newValue.length);
+            }
+        }
+    },
+
+    // private
+    onSelect : function(record, index){
+        if(this.fireEvent('beforeselect', this, record, index) !== false){
+            this.setFromData(index > -1 ? record.data : false);
+            this.collapse();
+            this.fireEvent('select', this, record, index);
         }
-        return String(this.getValue()) !== String(this.originalValue);
     },
 
     /**
-     * stores the current value in loadedValue
+     * Returns the currently selected field value or empty string if no value is set.
+     * @return {String} value The selected value
      */
-    resetHasChanged : function()
-    {
-        this.loadedValue = String(this.getValue());
+    getValue : function(){
+        if(this.valueField){
+            return typeof this.value != 'undefined' ? this.value : '';
+        }
+        return Roo.form.ComboBox.superclass.getValue.call(this);
     },
+
     /**
-     * checks the current value against the 'loaded' value.
-     * Note - will return false if 'resetHasChanged' has not been called first.
+     * Clears any text/value currently set in the field
      */
-    hasChanged : function()
-    {
-        if(this.disabled || this.readOnly) {
-            return false;
+    clearValue : function(){
+        if(this.hiddenField){
+            this.hiddenField.value = '';
         }
-        return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
-    },
-    
-    
-    
-    // private
-    afterRender : function(){
-        Roo.form.Field.superclass.afterRender.call(this);
-        this.initEvents();
+        this.value = '';
+        this.setRawValue('');
+        this.lastSelectionText = '';
+        
     },
 
-    // private
-    fireKey : function(e){
-        //Roo.log('field ' + e.getKey());
-        if(e.isNavKeyPress()){
-            this.fireEvent("specialkey", this, e);
+    /**
+     * Sets the specified value into the field.  If the value finds a match, the corresponding record text
+     * will be displayed in the field.  If the value does not match the data value of an existing item,
+     * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
+     * Otherwise the field will be blank (although the value will still be set).
+     * @param {String} value The value to match
+     */
+    setValue : function(v){
+        var text = v;
+        if(this.valueField){
+            var r = this.findRecord(this.valueField, v);
+            if(r){
+                text = r.data[this.displayField];
+            }else if(this.valueNotFoundText !== undefined){
+                text = this.valueNotFoundText;
+            }
+        }
+        this.lastSelectionText = text;
+        if(this.hiddenField){
+            this.hiddenField.value = v;
         }
+        Roo.form.ComboBox.superclass.setValue.call(this, text);
+        this.value = v;
     },
-
     /**
-     * Resets the current field value to the originally loaded value and clears any validation messages
+     * @property {Object} the last set data for the element
+     */
+    
+    lastData : false,
+    /**
+     * Sets the value of the field based on a object which is related to the record format for the store.
+     * @param {Object} value the value to set as. or false on reset?
      */
+    setFromData : function(o){
+        var dv = ''; // display value
+        var vv = ''; // value value..
+        this.lastData = o;
+        if (this.displayField) {
+            dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
+        } else {
+            // this is an error condition!!!
+            Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
+        }
+        
+        if(this.valueField){
+            vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
+        }
+        if(this.hiddenField){
+            this.hiddenField.value = vv;
+            
+            this.lastSelectionText = dv;
+            Roo.form.ComboBox.superclass.setValue.call(this, dv);
+            this.value = vv;
+            return;
+        }
+        // no hidden field.. - we store the value in 'value', but still display
+        // display field!!!!
+        this.lastSelectionText = dv;
+        Roo.form.ComboBox.superclass.setValue.call(this, dv);
+        this.value = vv;
+        
+        
+    },
+    // private
     reset : function(){
+        // overridden so that last data is reset..
         this.setValue(this.resetValue);
         this.originalValue = this.getValue();
         this.clearInvalid();
+        this.lastData = false;
+        if (this.view) {
+            this.view.clearSelections();
+        }
     },
-
     // private
-    initEvents : function(){
-        // safari killled keypress - so keydown is now used..
-        this.el.on("keydown" , this.fireKey,  this);
-        this.el.on("focus", this.onFocus,  this);
-        this.el.on("blur", this.onBlur,  this);
-        this.el.relayEvent('keyup', this);
-
-        // reference to original value for reset
-        this.originalValue = this.getValue();
-        this.resetValue =  this.getValue();
+    findRecord : function(prop, value){
+        var record;
+        if(this.store.getCount() > 0){
+            this.store.each(function(r){
+                if(r.data[prop] == value){
+                    record = r;
+                    return false;
+                }
+                return true;
+            });
+        }
+        return record;
+    },
+    
+    getName: function()
+    {
+        // returns hidden if it's set..
+        if (!this.rendered) {return ''};
+        return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
+        
+    },
+    // private
+    onViewMove : function(e, t){
+        this.inKeyMode = false;
     },
 
     // private
-    onFocus : function(){
-        if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
-            this.el.addClass(this.focusClass);
+    onViewOver : function(e, t){
+        if(this.inKeyMode){ // prevent key nav and mouse over conflicts
+            return;
         }
-        if(!this.hasFocus){
-            this.hasFocus = true;
-            this.startValue = this.getValue();
-            this.fireEvent("focus", this);
+        var item = this.view.findItemFromChild(t);
+        if(item){
+            var index = this.view.indexOf(item);
+            this.select(index, false);
         }
     },
 
-    beforeBlur : Roo.emptyFn,
-
     // private
-    onBlur : function(){
-        this.beforeBlur();
-        if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
-            this.el.removeClass(this.focusClass);
-        }
-        this.hasFocus = false;
-        if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
-            this.validate();
+    onViewClick : function(doFocus)
+    {
+        var index = this.view.getSelectedIndexes()[0];
+        var r = this.store.getAt(index);
+        if(r){
+            this.onSelect(r, index);
         }
-        var v = this.getValue();
-        if(String(v) !== String(this.startValue)){
-            this.fireEvent('change', this, v, this.startValue);
+        if(doFocus !== false && !this.blockFocus){
+            this.el.focus();
         }
-        this.fireEvent("blur", this);
+    },
+
+    // private
+    restrictHeight : function(){
+        this.innerList.dom.style.height = '';
+        var inner = this.innerList.dom;
+        var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
+        this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
+        this.list.beginUpdate();
+        this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
+        this.list.alignTo(this.el, this.listAlign);
+        this.list.endUpdate();
+    },
+
+    // private
+    onEmptyResults : function(){
+        this.collapse();
     },
 
     /**
-     * Returns whether or not the field value is currently valid
-     * @param {Boolean} preventMark True to disable marking the field invalid
-     * @return {Boolean} True if the value is valid, else false
+     * Returns true if the dropdown list is expanded, else false.
      */
-    isValid : function(preventMark){
-        if(this.disabled){
-            return true;
-        }
-        var restore = this.preventMark;
-        this.preventMark = preventMark === true;
-        var v = this.validateValue(this.processValue(this.getRawValue()));
-        this.preventMark = restore;
-        return v;
+    isExpanded : function(){
+        return this.list.isVisible();
     },
 
     /**
-     * Validates the field value
-     * @return {Boolean} True if the value is valid, else false
+     * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
+     * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
+     * @param {String} value The data value of the item to select
+     * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
+     * selected item if it is not currently in view (defaults to true)
+     * @return {Boolean} True if the value matched an item in the list, else false
      */
-    validate : function(){
-        if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
-            this.clearInvalid();
-            return true;
+    selectByValue : function(v, scrollIntoView){
+        if(v !== undefined && v !== null){
+            var r = this.findRecord(this.valueField || this.displayField, v);
+            if(r){
+                this.select(this.store.indexOf(r), scrollIntoView);
+                return true;
+            }
         }
         return false;
     },
 
-    processValue : function(value){
-        return value;
+    /**
+     * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
+     * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
+     * @param {Number} index The zero-based index of the list item to select
+     * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
+     * selected item if it is not currently in view (defaults to true)
+     */
+    select : function(index, scrollIntoView){
+        this.selectedIndex = index;
+        this.view.select(index);
+        if(scrollIntoView !== false){
+            var el = this.view.getNode(index);
+            if(el){
+                this.innerList.scrollChildIntoView(el, false);
+            }
+        }
     },
 
     // private
-    // Subclasses should provide the validation implementation by overriding this
-    validateValue : function(value){
-        return true;
+    selectNext : function(){
+        var ct = this.store.getCount();
+        if(ct > 0){
+            if(this.selectedIndex == -1){
+                this.select(0);
+            }else if(this.selectedIndex < ct-1){
+                this.select(this.selectedIndex+1);
+            }
+        }
     },
 
-    /**
-     * Mark this field as invalid
-     * @param {String} msg The validation message
-     */
-    markInvalid : function(msg){
-        if(!this.rendered || this.preventMark){ // not rendered
-            return;
+    // private
+    selectPrev : function(){
+        var ct = this.store.getCount();
+        if(ct > 0){
+            if(this.selectedIndex == -1){
+                this.select(0);
+            }else if(this.selectedIndex != 0){
+                this.select(this.selectedIndex-1);
+            }
         }
-        
-        var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
-        
-        obj.el.addClass(this.invalidClass);
-        msg = msg || this.invalidText;
-        switch(this.msgTarget){
-            case 'qtip':
-                obj.el.dom.qtip = msg;
-                obj.el.dom.qclass = 'x-form-invalid-tip';
-                if(Roo.QuickTips){ // fix for floating editors interacting with DND
-                    Roo.QuickTips.enable();
-                }
-                break;
-            case 'title':
-                this.el.dom.title = msg;
-                break;
-            case 'under':
-                if(!this.errorEl){
-                    var elp = this.el.findParent('.x-form-element', 5, true);
-                    this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
-                    this.errorEl.setWidth(elp.getWidth(true)-20);
-                }
-                this.errorEl.update(msg);
-                Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
-                break;
-            case 'side':
-                if(!this.errorIcon){
-                    var elp = this.el.findParent('.x-form-element', 5, true);
-                    this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
-                }
-                this.alignErrorIcon();
-                this.errorIcon.dom.qtip = msg;
-                this.errorIcon.dom.qclass = 'x-form-invalid-tip';
-                this.errorIcon.show();
-                this.on('resize', this.alignErrorIcon, this);
-                break;
-            default:
-                var t = Roo.getDom(this.msgTarget);
-                t.innerHTML = msg;
-                t.style.display = this.msgDisplay;
-                break;
+    },
+
+    // private
+    onKeyUp : function(e){
+        if(this.editable !== false && !e.isSpecialKey()){
+            this.lastKey = e.getKey();
+            this.dqTask.delay(this.queryDelay);
         }
-        this.fireEvent('invalid', this, msg);
     },
 
     // private
-    alignErrorIcon : function(){
-        this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
+    validateBlur : function(){
+        return !this.list || !this.list.isVisible();   
+    },
+
+    // private
+    initQuery : function(){
+        this.doQuery(this.getRawValue());
+    },
+
+    // private
+    doForce : function(){
+        if(this.el.dom.value.length > 0){
+            this.el.dom.value =
+                this.lastSelectionText === undefined ? '' : this.lastSelectionText;
+             
+        }
     },
 
     /**
-     * Clear any invalid styles/messages for this field
+     * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
+     * query allowing the query action to be canceled if needed.
+     * @param {String} query The SQL query to execute
+     * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
+     * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
+     * saved in the current store (defaults to false)
      */
-    clearInvalid : function(){
-        if(!this.rendered || this.preventMark){ // not rendered
-            return;
+    doQuery : function(q, forceAll){
+        if(q === undefined || q === null){
+            q = '';
         }
-        var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
-        
-        obj.el.removeClass(this.invalidClass);
-        switch(this.msgTarget){
-            case 'qtip':
-                obj.el.dom.qtip = '';
-                break;
-            case 'title':
-                this.el.dom.title = '';
-                break;
-            case 'under':
-                if(this.errorEl){
-                    Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
-                }
-                break;
-            case 'side':
-                if(this.errorIcon){
-                    this.errorIcon.dom.qtip = '';
-                    this.errorIcon.hide();
-                    this.un('resize', this.alignErrorIcon, this);
+        var qe = {
+            query: q,
+            forceAll: forceAll,
+            combo: this,
+            cancel:false
+        };
+        if(this.fireEvent('beforequery', qe)===false || qe.cancel){
+            return false;
+        }
+        q = qe.query;
+        forceAll = qe.forceAll;
+        if(forceAll === true || (q.length >= this.minChars)){
+            if(this.lastQuery != q || this.alwaysQuery){
+                this.lastQuery = q;
+                if(this.mode == 'local'){
+                    this.selectedIndex = -1;
+                    if(forceAll){
+                        this.store.clearFilter();
+                    }else{
+                        this.store.filter(this.displayField, q);
+                    }
+                    this.onLoad();
+                }else{
+                    this.store.baseParams[this.queryParam] = q;
+                    this.store.load({
+                        params: this.getParams(q)
+                    });
+                    this.expand();
                 }
-                break;
-            default:
-                var t = Roo.getDom(this.msgTarget);
-                t.innerHTML = '';
-                t.style.display = 'none';
-                break;
+            }else{
+                this.selectedIndex = -1;
+                this.onLoad();   
+            }
         }
-        this.fireEvent('valid', this);
     },
 
-    /**
-     * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
-     * @return {Mixed} value The field value
-     */
-    getRawValue : function(){
-        var v = this.el.getValue();
-        
-        return v;
+    // private
+    getParams : function(q){
+        var p = {};
+        //p[this.queryParam] = q;
+        if(this.pageSize){
+            p.start = 0;
+            p.limit = this.pageSize;
+        }
+        return p;
     },
 
     /**
-     * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
-     * @return {Mixed} value The field value
+     * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
      */
-    getValue : function(){
-        var v = this.el.getValue();
-         
-        return v;
+    collapse : function(){
+        if(!this.isExpanded()){
+            return;
+        }
+        this.list.hide();
+        Roo.get(document).un('mousedown', this.collapseIf, this);
+        Roo.get(document).un('mousewheel', this.collapseIf, this);
+        if (!this.editable) {
+            Roo.get(document).un('keydown', this.listKeyPress, this);
+        }
+        this.fireEvent('collapse', this);
     },
 
-    /**
-     * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
-     * @param {Mixed} value The value to set
-     */
-    setRawValue : function(v){
-        return this.el.dom.value = (v === null || v === undefined ? '' : v);
+    // private
+    collapseIf : function(e){
+        if(!e.within(this.wrap) && !e.within(this.list)){
+            this.collapse();
+        }
     },
 
     /**
-     * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
-     * @param {Mixed} value The value to set
+     * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
      */
-    setValue : function(v){
-        this.value = v;
-        if(this.rendered){
-            this.el.dom.value = (v === null || v === undefined ? '' : v);
-             this.validate();
+    expand : function(){
+        if(this.isExpanded() || !this.hasFocus){
+            return;
         }
+        this.list.alignTo(this.el, this.listAlign);
+        this.list.show();
+        Roo.get(document).on('mousedown', this.collapseIf, this);
+        Roo.get(document).on('mousewheel', this.collapseIf, this);
+        if (!this.editable) {
+            Roo.get(document).on('keydown', this.listKeyPress, this);
+        }
+        
+        this.fireEvent('expand', this);
     },
 
-    adjustSize : function(w, h){
-        var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
-        s.width = this.adjustWidth(this.el.dom.tagName, s.width);
-        return s;
-    },
-
-    adjustWidth : function(tag, w){
-        tag = tag.toLowerCase();
-        if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
-            if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
-                if(tag == 'input'){
-                    return w + 2;
-                }
-                if(tag == 'textarea'){
-                    return w-2;
-                }
-            }else if(Roo.isOpera){
-                if(tag == 'input'){
-                    return w + 2;
-                }
-                if(tag == 'textarea'){
-                    return w-2;
-                }
-            }
+    // private
+    // Implements the default empty TriggerField.onTriggerClick function
+    onTriggerClick : function(){
+        if(this.disabled){
+            return;
         }
-        return w;
-    }
-});
-
-
-// anything other than normal should be considered experimental
-Roo.form.Field.msgFx = {
-    normal : {
-        show: function(msgEl, f){
-            msgEl.setDisplayed('block');
-        },
-
-        hide : function(msgEl, f){
-            msgEl.setDisplayed(false).update('');
+        if(this.isExpanded()){
+            this.collapse();
+            if (!this.blockFocus) {
+                this.el.focus();
+            }
+            
+        }else {
+            this.hasFocus = true;
+            if(this.triggerAction == 'all') {
+                this.doQuery(this.allQuery, true);
+            } else {
+                this.doQuery(this.getRawValue());
+            }
+            if (!this.blockFocus) {
+                this.el.focus();
+            }
         }
     },
-
-    slide : {
-        show: function(msgEl, f){
-            msgEl.slideIn('t', {stopFx:true});
-        },
-
-        hide : function(msgEl, f){
-            msgEl.slideOut('t', {stopFx:true,useDisplay:true});
+    listKeyPress : function(e)
+    {
+        //Roo.log('listkeypress');
+        // scroll to first matching element based on key pres..
+        if (e.isSpecialKey()) {
+            return false;
         }
-    },
-
-    slideRight : {
-        show: function(msgEl, f){
-            msgEl.fixDisplay();
-            msgEl.alignTo(f.el, 'tl-tr');
-            msgEl.slideIn('l', {stopFx:true});
-        },
-
-        hide : function(msgEl, f){
-            msgEl.slideOut('l', {stopFx:true,useDisplay:true});
+        var k = String.fromCharCode(e.getKey()).toUpperCase();
+        //Roo.log(k);
+        var match  = false;
+        var csel = this.view.getSelectedNodes();
+        var cselitem = false;
+        if (csel.length) {
+            var ix = this.view.indexOf(csel[0]);
+            cselitem  = this.store.getAt(ix);
+            if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
+                cselitem = false;
+            }
+            
         }
-    }
-};/*
- * Based on:
- * Ext JS Library 1.1.1
- * Copyright(c) 2006-2007, Ext JS, LLC.
+        
+        this.store.each(function(v) { 
+            if (cselitem) {
+                // start at existing selection.
+                if (cselitem.id == v.id) {
+                    cselitem = false;
+                }
+                return;
+            }
+                
+            if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
+                match = this.store.indexOf(v);
+                return false;
+            }
+        }, this);
+        
+        if (match === false) {
+            return true; // no more action?
+        }
+        // scroll to?
+        this.view.select(match);
+        var sn = Roo.get(this.view.getSelectedNodes()[0]);
+        sn.scrollIntoView(sn.dom.parentNode, false);
+    } 
+
+    /** 
+    * @cfg {Boolean} grow 
+    * @hide 
+    */
+    /** 
+    * @cfg {Number} growMin 
+    * @hide 
+    */
+    /** 
+    * @cfg {Number} growMax 
+    * @hide 
+    */
+    /**
+     * @hide
+     * @method autoSize
+     */
+});/*
+ * Copyright(c) 2010-2012, Roo J Solutions Limited
  *
- * Originally Released Under LGPL - original licence link has changed is not relivant.
+ * Licence LGPL
  *
- * Fork - LGPL
- * <script type="text/javascript">
  */
 
 /**
- * @class Roo.form.TextField
- * @extends Roo.form.Field
- * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
- * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
+ * @class Roo.form.ComboBoxArray
+ * @extends Roo.form.TextField
+ * A facebook style adder... for lists of email / people / countries  etc...
+ * pick multiple items from a combo box, and shows each one.
+ *
+ *  Fred [x]  Brian [x]  [Pick another |v]
+ *
+ *
+ *  For this to work: it needs various extra information
+ *    - normal combo problay has
+ *      name, hiddenName
+ *    + displayField, valueField
+ *
+ *    For our purpose...
+ *
+ *
+ *   If we change from 'extends' to wrapping...
+ *   
+ *  
+ *
  * @constructor
- * Creates a new TextField
+ * Create a new ComboBoxArray.
  * @param {Object} config Configuration options
  */
-Roo.form.TextField = function(config){
-    Roo.form.TextField.superclass.constructor.call(this, config);
+
+Roo.form.ComboBoxArray = function(config)
+{
     this.addEvents({
         /**
-         * @event autosize
-         * Fires when the autosize function is triggered.  The field may or may not have actually changed size
-         * according to the default logic, but this event provides a hook for the developer to apply additional
-         * logic at runtime to resize the field if needed.
-            * @param {Roo.form.Field} this This text field
-            * @param {Number} width The new field width
+         * @event beforeremove
+         * Fires before remove the value from the list
+            * @param {Roo.form.ComboBoxArray} _self This combo box array
+             * @param {Roo.form.ComboBoxArray.Item} item removed item
             */
-        autosize : true
+        'beforeremove' : true,
+        /**
+         * @event remove
+         * Fires when remove the value from the list
+            * @param {Roo.form.ComboBoxArray} _self This combo box array
+             * @param {Roo.form.ComboBoxArray.Item} item removed item
+            */
+        'remove' : true
+        
+        
     });
-};
+    
+    Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
+    
+    this.items = new Roo.util.MixedCollection(false);
+    
+    // construct the child combo...
+    
+    
+    
+    
+   
+    
+}
 
-Roo.extend(Roo.form.TextField, Roo.form.Field,  {
-    /**
-     * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
-     */
-    grow : false,
-    /**
-     * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
-     */
-    growMin : 30,
-    /**
-     * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
-     */
-    growMax : 800,
-    /**
-     * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
-     */
-    vtype : null,
-    /**
-     * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
-     */
-    maskRe : null,
-    /**
-     * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
-     */
-    disableKeyFilter : false,
-    /**
-     * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
-     */
-    allowBlank : true,
-    /**
-     * @cfg {Number} minLength Minimum input field length required (defaults to 0)
-     */
-    minLength : 0,
-    /**
-     * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
-     */
-    maxLength : Number.MAX_VALUE,
-    /**
-     * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
-     */
-    minLengthText : "The minimum length for this field is {0}",
-    /**
-     * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
-     */
-    maxLengthText : "The maximum length for this field is {0}",
-    /**
-     * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
-     */
-    selectOnFocus : false,
-    /**
-     * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
-     */    
-    allowLeadingSpace : false,
+Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
+{ 
     /**
-     * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
+     * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
      */
-    blankText : "This field is required",
+    
+    lastData : false,
+    
+    // behavies liek a hiddne field
+    inputType:      'hidden',
     /**
-     * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
-     * If available, this function will be called only after the basic validators all return true, and will be passed the
-     * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
-     */
-    validator : null,
+     * @cfg {Number} width The width of the box that displays the selected element
+     */ 
+    width:          300,
+
+    
+    
     /**
-     * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
-     * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
-     * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
+     * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
      */
-    regex : null,
+    name : false,
     /**
-     * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
+     * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
      */
-    regexText : "",
-    /**
-     * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
+    hiddenName : false,
+      /**
+     * @cfg {String} seperator    The value seperator normally ',' 
      */
-    emptyText : null,
-   
-
-    // private
-    initEvents : function()
+    seperator : ',',
+    
+    // private the array of items that are displayed..
+    items  : false,
+    // private - the hidden field el.
+    hiddenEl : false,
+    // private - the filed el..
+    el : false,
+    
+    //validateValue : function() { return true; }, // all values are ok!
+    //onAddClick: function() { },
+    
+    onRender : function(ct, position) 
     {
-        if (this.emptyText) {
-            this.el.attr('placeholder', this.emptyText);
+        
+        // create the standard hidden element
+        //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
+        
+        
+        // give fake names to child combo;
+        this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
+        this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
+        
+        this.combo = Roo.factory(this.combo, Roo.form);
+        this.combo.onRender(ct, position);
+        if (typeof(this.combo.width) != 'undefined') {
+            this.combo.onResize(this.combo.width,0);
         }
         
-        Roo.form.TextField.superclass.initEvents.call(this);
-        if(this.validationEvent == 'keyup'){
-            this.validationTask = new Roo.util.DelayedTask(this.validate, this);
-            this.el.on('keyup', this.filterValidation, this);
-        }
-        else if(this.validationEvent !== false){
-            this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
-        }
+        this.combo.initEvents();
+        
+        // assigned so form know we need to do this..
+        this.store          = this.combo.store;
+        this.valueField     = this.combo.valueField;
+        this.displayField   = this.combo.displayField ;
+        
+        
+        this.combo.wrap.addClass('x-cbarray-grp');
+        
+        var cbwrap = this.combo.wrap.createChild(
+            {tag: 'div', cls: 'x-cbarray-cb'},
+            this.combo.el.dom
+        );
+        
+             
+        this.hiddenEl = this.combo.wrap.createChild({
+            tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
+        });
+        this.el = this.combo.wrap.createChild({
+            tag: 'input',  type:'hidden' , name: this.name, value : ''
+        });
+         //   this.el.dom.removeAttribute("name");
+        
         
-        if(this.selectOnFocus){
-            this.on("focus", this.preFocus, this);
-        }
-       if (!this.allowLeadingSpace) {
-           this.on('blur', this.cleanLeadingSpace, this);
-       }
-       
-        if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
-            this.el.on("keypress", this.filterKeys, this);
-        }
-        if(this.grow){
-            this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
-            this.el.on("click", this.autoSize,  this);
-        }
-        if(this.el.is('input[type=password]') && Roo.isSafari){
-            this.el.on('keydown', this.SafariOnKeyDown, this);
+        this.outerWrap = this.combo.wrap;
+        this.wrap = cbwrap;
+        
+        this.outerWrap.setWidth(this.width);
+        this.outerWrap.dom.removeChild(this.el.dom);
+        
+        this.wrap.dom.appendChild(this.el.dom);
+        this.outerWrap.dom.removeChild(this.combo.trigger.dom);
+        this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
+        
+        this.combo.trigger.setStyle('position','relative');
+        this.combo.trigger.setStyle('left', '0px');
+        this.combo.trigger.setStyle('top', '2px');
+        
+        this.combo.el.setStyle('vertical-align', 'text-bottom');
+        
+        //this.trigger.setStyle('vertical-align', 'top');
+        
+        // this should use the code from combo really... on('add' ....)
+        if (this.adder) {
+            
+        
+            this.adder = this.outerWrap.createChild(
+                {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
+            var _t = this;
+            this.adder.on('click', function(e) {
+                _t.fireEvent('adderclick', this, e);
+            }, _t);
         }
+        //var _t = this;
+        //this.adder.on('click', this.onAddClick, _t);
+        
+        
+        this.combo.on('select', function(cb, rec, ix) {
+            this.addItem(rec.data);
+            
+            cb.setValue('');
+            cb.el.dom.value = '';
+            //cb.lastData = rec.data;
+            // add to list
+            
+        }, this);
+        
+        
     },
-
-    processValue : function(value){
-        if(this.stripCharsRe){
-            var newValue = value.replace(this.stripCharsRe, '');
-            if(newValue !== value){
-                this.setRawValue(newValue);
-                return newValue;
-            }
-        }
-        return value;
+    
+    
+    getName: function()
+    {
+        // returns hidden if it's set..
+        if (!this.rendered) {return ''};
+        return  this.hiddenName ? this.hiddenName : this.name;
+        
     },
-
-    filterValidation : function(e){
-        if(!e.isNavKeyPress()){
-            this.validationTask.delay(this.validationDelay);
+    
+    
+    onResize: function(w, h){
+        
+        return;
+        // not sure if this is needed..
+        //this.combo.onResize(w,h);
+        
+        if(typeof w != 'number'){
+            // we do not handle it!?!?
+            return;
         }
-    },
-
-    // private
-    onKeyUp : function(e){
-        if(!e.isNavKeyPress()){
-            this.autoSize();
+        var tw = this.combo.trigger.getWidth();
+        tw += this.addicon ? this.addicon.getWidth() : 0;
+        tw += this.editicon ? this.editicon.getWidth() : 0;
+        var x = w - tw;
+        this.combo.el.setWidth( this.combo.adjustWidth('input', x));
+            
+        this.combo.trigger.setStyle('left', '0px');
+        
+        if(this.list && this.listWidth === undefined){
+            var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
+            this.list.setWidth(lw);
+            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
         }
+        
+    
+        
     },
-    // private - clean the leading white space
-    cleanLeadingSpace : function(e)
+    
+    addItem: function(rec)
     {
-        if ( this.inputType == 'file') {
+        var valueField = this.combo.valueField;
+        var displayField = this.combo.displayField;
+       
+        if (this.items.indexOfKey(rec[valueField]) > -1) {
+            //console.log("GOT " + rec.data.id);
             return;
         }
         
-        this.setValue((this.getValue() + '').replace(/^\s+/,''));
-    },
-    /**
-     * Resets the current field value to the originally-loaded value and clears any validation messages.
-     *  
-     */
-    reset : function(){
-        Roo.form.TextField.superclass.reset.call(this);
-       
-    }, 
-    // private
-    preFocus : function(){
-        
-        if(this.selectOnFocus){
-            this.el.dom.select();
-        }
+        var x = new Roo.form.ComboBoxArray.Item({
+            //id : rec[this.idField],
+            data : rec,
+            displayField : displayField ,
+            tipField : displayField ,
+            cb : this
+        });
+        // use the 
+        this.items.add(rec[valueField],x);
+        // add it before the element..
+        this.updateHiddenEl();
+        x.render(this.outerWrap, this.wrap.dom);
+        // add the image handler..
     },
-
     
-    // private
-    filterKeys : function(e){
-        var k = e.getKey();
-        if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
-            return;
-        }
-        var c = e.getCharCode(), cc = String.fromCharCode(c);
-        if(Roo.isIE && (e.isSpecialKey() || !cc)){
+    updateHiddenEl : function()
+    {
+        this.validate();
+        if (!this.hiddenEl) {
             return;
         }
-        if(!this.maskRe.test(cc)){
-            e.stopEvent();
-        }
+        var ar = [];
+        var idField = this.combo.valueField;
+        
+        this.items.each(function(f) {
+            ar.push(f.data[idField]);
+        });
+        this.hiddenEl.dom.value = ar.join(this.seperator);
+        this.validate();
     },
-
-    setValue : function(v){
+    
+    reset : function()
+    {
+        this.items.clear();
         
-        Roo.form.TextField.superclass.setValue.apply(this, arguments);
+        Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
+           el.remove();
+        });
         
-        this.autoSize();
-    },
-
-    /**
-     * Validates a value according to the field's validation rules and marks the field as invalid
-     * if the validation fails
-     * @param {Mixed} value The value to validate
-     * @return {Boolean} True if the value is valid, else false
-     */
-    validateValue : function(value){
-        if(value.length < 1)  { // if it's blank
-             if(this.allowBlank){
-                this.clearInvalid();
-                return true;
-             }else{
-                this.markInvalid(this.blankText);
-                return false;
-             }
-        }
-        if(value.length < this.minLength){
-            this.markInvalid(String.format(this.minLengthText, this.minLength));
-            return false;
-        }
-        if(value.length > this.maxLength){
-            this.markInvalid(String.format(this.maxLengthText, this.maxLength));
-            return false;
-        }
-        if(this.vtype){
-            var vt = Roo.form.VTypes;
-            if(!vt[this.vtype](value, this)){
-                this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
-                return false;
-            }
+        this.el.dom.value = '';
+        if (this.hiddenEl) {
+            this.hiddenEl.dom.value = '';
         }
-        if(typeof this.validator == "function"){
-            var msg = this.validator(value);
-            if(msg !== true){
-                this.markInvalid(msg);
-                return false;
-            }
+        
+    },
+    getValue: function()
+    {
+        return this.hiddenEl ? this.hiddenEl.dom.value : '';
+    },
+    setValue: function(v) // not a valid action - must use addItems..
+    {
+        
+        this.reset();
+         
+        if (this.store.isLocal && (typeof(v) == 'string')) {
+            // then we can use the store to find the values..
+            // comma seperated at present.. this needs to allow JSON based encoding..
+            this.hiddenEl.value  = v;
+            var v_ar = [];
+            Roo.each(v.split(this.seperator), function(k) {
+                Roo.log("CHECK " + this.valueField + ',' + k);
+                var li = this.store.query(this.valueField, k);
+                if (!li.length) {
+                    return;
+                }
+                var add = {};
+                add[this.valueField] = k;
+                add[this.displayField] = li.item(0).data[this.displayField];
+                
+                this.addItem(add);
+            }, this) 
+             
         }
-        if(this.regex && !this.regex.test(value)){
-            this.markInvalid(this.regexText);
-            return false;
+        if (typeof(v) == 'object' ) {
+            // then let's assume it's an array of objects..
+            Roo.each(v, function(l) {
+                var add = l;
+                if (typeof(l) == 'string') {
+                    add = {};
+                    add[this.valueField] = l;
+                    add[this.displayField] = l
+                }
+                this.addItem(add);
+            }, this);
+             
         }
-        return true;
+        
+        
     },
-
-    /**
-     * Selects text in this field
-     * @param {Number} start (optional) The index where the selection should start (defaults to 0)
-     * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
-     */
-    selectText : function(start, end){
-        var v = this.getRawValue();
-        if(v.length > 0){
-            start = start === undefined ? 0 : start;
-            end = end === undefined ? v.length : end;
-            var d = this.el.dom;
-            if(d.setSelectionRange){
-                d.setSelectionRange(start, end);
-            }else if(d.createTextRange){
-                var range = d.createTextRange();
-                range.moveStart("character", start);
-                range.moveEnd("character", v.length-end);
-                range.select();
-            }
+    setFromData: function(v)
+    {
+        // this recieves an object, if setValues is called.
+        this.reset();
+        this.el.dom.value = v[this.displayField];
+        this.hiddenEl.dom.value = v[this.valueField];
+        if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
+            return;
+        }
+        var kv = v[this.valueField];
+        var dv = v[this.displayField];
+        kv = typeof(kv) != 'string' ? '' : kv;
+        dv = typeof(dv) != 'string' ? '' : dv;
+        
+        
+        var keys = kv.split(this.seperator);
+        var display = dv.split(this.seperator);
+        for (var i = 0 ; i < keys.length; i++) {
+            add = {};
+            add[this.valueField] = keys[i];
+            add[this.displayField] = display[i];
+            this.addItem(add);
         }
+      
+        
     },
-
+    
     /**
-     * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
-     * This only takes effect if grow = true, and fires the autosize event.
+     * Validates the combox array value
+     * @return {Boolean} True if the value is valid, else false
      */
-    autoSize : function(){
-        if(!this.grow || !this.rendered){
-            return;
-        }
-        if(!this.metrics){
-            this.metrics = Roo.util.TextMetrics.createInstance(this.el);
+    validate : function(){
+        if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
+            this.clearInvalid();
+            return true;
         }
-        var el = this.el;
-        var v = el.dom.value;
-        var d = document.createElement('div');
-        d.appendChild(document.createTextNode(v));
-        v = d.innerHTML;
-        d = null;
-        v += "&#160;";
-        var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
-        this.el.setWidth(w);
-        this.fireEvent("autosize", this, w);
+        return false;
     },
     
-    // private
-    SafariOnKeyDown : function(event)
-    {
-        // this is a workaround for a password hang bug on chrome/ webkit.
-        
-        var isSelectAll = false;
+    validateValue : function(value){
+        return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
         
-        if(this.el.dom.selectionEnd > 0){
-            isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
+    },
+    
+    /*@
+     * overide
+     * 
+     */
+    isDirty : function() {
+        if(this.disabled) {
+            return false;
         }
-        if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
-            event.preventDefault();
-            this.setValue('');
-            return;
+        
+        try {
+            var d = Roo.decode(String(this.originalValue));
+        } catch (e) {
+            return String(this.getValue()) !== String(this.originalValue);
         }
         
-        if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
-            
-            event.preventDefault();
-            // this is very hacky as keydown always get's upper case.
-            
-            var cc = String.fromCharCode(event.getCharCode());
-            
-            
-            this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
-            
+        var originalValue = [];
+        
+        for (var i = 0; i < d.length; i++){
+            originalValue.push(d[i][this.valueField]);
         }
         
+        return String(this.getValue()) !== String(originalValue.join(this.seperator));
         
     }
-});/*
- * 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.form.Hidden
- * @extends Roo.form.TextField
- * Simple Hidden element used on forms 
- * 
- * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
+ * @class Roo.form.ComboBoxArray.Item
+ * @extends Roo.BoxComponent
+ * A selected item in the list
+ *  Fred [x]  Brian [x]  [Pick another |v]
  * 
  * @constructor
- * Creates a new Hidden form element.
+ * Create a new item.
  * @param {Object} config Configuration options
  */
+Roo.form.ComboBoxArray.Item = function(config) {
+    config.id = Roo.id();
+    Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
+}
 
+Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
+    data : {},
+    cb: false,
+    displayField : false,
+    tipField : false,
+    
+    
+    defaultAutoCreate : {
+        tag: 'div',
+        cls: 'x-cbarray-item',
+        cn : [ 
+            { tag: 'div' },
+            {
+                tag: 'img',
+                width:16,
+                height : 16,
+                src : Roo.BLANK_IMAGE_URL ,
+                align: 'center'
+            }
+        ]
+        
+    },
+    
+    onRender : function(ct, position)
+    {
+        Roo.form.Field.superclass.onRender.call(this, ct, position);
+        
+        if(!this.el){
+            var cfg = this.getAutoCreate();
+            this.el = ct.createChild(cfg, position);
+        }
+        
+        this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
+        
+        this.el.child('div').dom.innerHTML = this.cb.renderer ? 
+            this.cb.renderer(this.data) :
+            String.format('{0}',this.data[this.displayField]);
+        
+            
+        this.el.child('div').dom.setAttribute('qtip',
+                        String.format('{0}',this.data[this.tipField])
+        );
+        
+        this.el.child('img').on('click', this.remove, this);
+        
+    },
+   
+    remove : function()
+    {
+        if(this.cb.disabled){
+            return;
+        }
+        
+        if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
+            this.cb.items.remove(this);
+            this.el.child('img').un('click', this.remove, this);
+            this.el.remove();
+            this.cb.updateHiddenEl();
 
-
-// easy hidden field...
-Roo.form.Hidden = function(config){
-    Roo.form.Hidden.superclass.constructor.call(this, config);
-};
-  
-Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
-    fieldLabel:      '',
-    inputType:      'hidden',
-    width:          50,
-    allowBlank:     true,
-    labelSeparator: '',
-    hidden:         true,
-    itemCls :       'x-form-item-display-none'
-
-
-});
-
-
-/*
- * 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.
+            this.cb.fireEvent('remove', this.cb, this);
+        }
+        
+    }
+});/*
+ * RooJS Library 1.1.1
+ * Copyright(c) 2008-2011  Alan Knowles
  *
- * Fork - LGPL
- * <script type="text/javascript">
+ * License - LGPL
  */
  
+
 /**
- * @class Roo.form.TriggerField
- * @extends Roo.form.TextField
- * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
- * The trigger has no default action, so you must assign a function to implement the trigger click handler by
- * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
- * for which you can provide a custom implementation.  For example:
- * <pre><code>
-var trigger = new Roo.form.TriggerField();
-trigger.onTriggerClick = myTriggerFn;
-trigger.applyTo('my-field');
-</code></pre>
+ * @class Roo.form.ComboNested
+ * @extends Roo.form.ComboBox
+ * A combobox for that allows selection of nested items in a list,
+ * eg.
  *
- * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
- * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
- * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
- * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
+ *  Book
+ *    -> red
+ *    -> green
+ *  Table
+ *    -> square
+ *      ->red
+ *      ->green
+ *    -> rectangle
+ *      ->green
+ *      
+ * 
  * @constructor
- * Create a new TriggerField.
- * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
- * to the base TextField)
+ * Create a new ComboNested
+ * @param {Object} config Configuration options
  */
-Roo.form.TriggerField = function(config){
-    this.mimicing = false;
-    Roo.form.TriggerField.superclass.constructor.call(this, config);
+Roo.form.ComboNested = function(config){
+    Roo.form.ComboCheck.superclass.constructor.call(this, config);
+    // should verify some data...
+    // like
+    // hiddenName = required..
+    // displayField = required
+    // valudField == required
+    var req= [ 'hiddenName', 'displayField', 'valueField' ];
+    var _t = this;
+    Roo.each(req, function(e) {
+        if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
+            throw "Roo.form.ComboNested : missing value for: " + e;
+        }
+    });
+     
+    
 };
 
-Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
-    /**
-     * @cfg {String} triggerClass A CSS class to apply to the trigger
-     */
-    /**
-     * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
-     * {tag: "input", type: "text", size: "16", autocomplete: "off"})
-     */
-    defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
-    /**
-     * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
-     */
-    hideTrigger:false,
-
-    /** @cfg {Boolean} grow @hide */
-    /** @cfg {Number} growMin @hide */
-    /** @cfg {Number} growMax @hide */
-
-    /**
-     * @hide 
-     * @method
+Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
+   
+    /*
+     * @config {Number} max Number of columns to show
      */
-    autoSize: Roo.emptyFn,
-    // private
-    monitorTab : true,
-    // private
-    deferHeight : true,
-
     
-    actionMode : 'wrap',
+    maxColumns : 3,
+   
+    list : null, // the outermost div..
+    innerLists : null, // the
+    views : null,
+    stores : null,
     // private
-    onResize : function(w, h){
-        Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
-        if(typeof w == 'number'){
-            var x = w - this.trigger.getWidth();
-            this.el.setWidth(this.adjustWidth('input', x));
-            this.trigger.setStyle('left', x+'px');
-        }
-    },
+    loadingChildren : false,
+    
+    onRender : function(ct, position)
+    {
+        Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
+        
+        if(this.hiddenName){
+            this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
+                    'before', true);
+            this.hiddenField.value =
+                this.hiddenValue !== undefined ? this.hiddenValue :
+                this.value !== undefined ? this.value : '';
 
-    // private
-    adjustSize : Roo.BoxComponent.prototype.adjustSize,
+            // prevent input submission
+            this.el.dom.removeAttribute('name');
+             
+             
+        }
+       
+        if(Roo.isGecko){
+            this.el.dom.setAttribute('autocomplete', 'off');
+        }
 
-    // private
-    getResizeEl : function(){
-        return this.wrap;
-    },
+        var cls = 'x-combo-list';
 
-    // private
-    getPositionEl : function(){
-        return this.wrap;
-    },
+        this.list = new Roo.Layer({
+            shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
+        });
 
-    // private
-    alignErrorIcon : function(){
-        this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
-    },
+        var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
+        this.list.setWidth(lw);
+        this.list.swallowEvent('mousewheel');
+        this.assetHeight = 0;
 
-    // private
-    onRender : function(ct, position){
-        Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
-        this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
-        this.trigger = this.wrap.createChild(this.triggerConfig ||
-                {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
-        if(this.hideTrigger){
-            this.trigger.setDisplayed(false);
+        if(this.title){
+            this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
+            this.assetHeight += this.header.getHeight();
         }
-        this.initTrigger();
-        if(!this.width){
-            this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
+        this.innerLists = [];
+        this.views = [];
+        this.stores = [];
+        for (var i =0 ; i < this.maxColumns; i++) {
+            this.onRenderList( cls, i);
+        }
+        
+        // always needs footer, as we are going to have an 'OK' button.
+        this.footer = this.list.createChild({cls:cls+'-ft'});
+        this.pageTb = new Roo.Toolbar(this.footer);  
+        var _this = this;
+        this.pageTb.add(  {
+            
+            text: 'Done',
+            handler: function()
+            {
+                _this.collapse();
+            }
+        });
+        
+        if ( this.allowBlank && !this.disableClear) {
+            
+            this.pageTb.add(new Roo.Toolbar.Fill(), {
+                cls: 'x-btn-icon x-btn-clear',
+                text: '&#160;',
+                handler: function()
+                {
+                    _this.collapse();
+                    _this.clearValue();
+                    _this.onSelect(false, -1);
+                }
+            });
+        }
+        if (this.footer) {
+            this.assetHeight += this.footer.getHeight();
         }
+        
     },
+    onRenderList : function (  cls, i)
+    {
+        
+        var lw = Math.floor(
+                ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
+        );
+        
+        this.list.setWidth(lw); // default to '1'
 
-    // private
-    initTrigger : function(){
-        this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
-        this.trigger.addClassOnOver('x-form-trigger-over');
-        this.trigger.addClassOnClick('x-form-trigger-click');
-    },
+        var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
+        //il.on('mouseover', this.onViewOver, this, { list:  i });
+        //il.on('mousemove', this.onViewMove, this, { list:  i });
+        il.setWidth(lw);
+        il.setStyle({ 'overflow-x' : 'hidden'});
 
-    // private
-    onDestroy : function(){
-        if(this.trigger){
-            this.trigger.removeAllListeners();
-            this.trigger.remove();
+        if(!this.tpl){
+            this.tpl = new Roo.Template({
+                html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
+                isEmpty: function (value, allValues) {
+                    //Roo.log(value);
+                    var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
+                    return dl ? 'has-children' : 'no-children'
+                }
+            });
         }
-        if(this.wrap){
-            this.wrap.remove();
+        
+        var store  = this.store;
+        if (i > 0) {
+            store  = new Roo.data.SimpleStore({
+                //fields : this.store.reader.meta.fields,
+                reader : this.store.reader,
+                data : [ ]
+            });
         }
-        Roo.form.TriggerField.superclass.onDestroy.call(this);
-    },
+        this.stores[i]  = store;
+                  
+        var view = this.views[i] = new Roo.View(
+            il,
+            this.tpl,
+            {
+                singleSelect:true,
+                store: store,
+                selectedClass: this.selectedClass
+            }
+        );
+        view.getEl().setWidth(lw);
+        view.getEl().setStyle({
+            position: i < 1 ? 'relative' : 'absolute',
+            top: 0,
+            left: (i * lw ) + 'px',
+            display : i > 0 ? 'none' : 'block'
+        });
+        view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
+        view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
+        //view.on('click', this.onViewClick, this, { list : i });
 
-    // private
-    onFocus : function(){
-        Roo.form.TriggerField.superclass.onFocus.call(this);
-        if(!this.mimicing){
-            this.wrap.addClass('x-trigger-wrap-focus');
-            this.mimicing = true;
-            Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
-            if(this.monitorTab){
-                this.el.on("keydown", this.checkTab, this);
+        store.on('beforeload', this.onBeforeLoad, this);
+        store.on('load',  this.onLoad, this, { list  : i});
+        store.on('loadexception', this.onLoadException, this);
+
+        // hide the other vies..
+        
+        
+        
+    },
+      
+    restrictHeight : function()
+    {
+        var mh = 0;
+        Roo.each(this.innerLists, function(il,i) {
+            var el = this.views[i].getEl();
+            el.dom.style.height = '';
+            var inner = el.dom;
+            var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
+            // only adjust heights on other ones..
+            mh = Math.max(h, mh);
+            if (i < 1) {
+                
+                el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
+                il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
+               
             }
-        }
+            
+            
+        }, this);
+        
+        this.list.beginUpdate();
+        this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
+        this.list.alignTo(this.el, this.listAlign);
+        this.list.endUpdate();
+        
     },
-
+     
+    
+    // -- store handlers..
     // private
-    checkTab : function(e){
-        if(e.getKey() == e.TAB){
-            this.triggerBlur();
+    onBeforeLoad : function()
+    {
+        if(!this.hasFocus){
+            return;
         }
+        this.innerLists[0].update(this.loadingText ?
+               '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
+        this.restrictHeight();
+        this.selectedIndex = -1;
     },
-
     // private
-    onBlur : function(){
-        // do nothing
-    },
+    onLoad : function(a,b,c,d)
+    {
+        if (!this.loadingChildren) {
+            // then we are loading the top level. - hide the children
+            for (var i = 1;i < this.views.length; i++) {
+                this.views[i].getEl().setStyle({ display : 'none' });
+            }
+            var lw = Math.floor(
+                ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
+            );
+        
+             this.list.setWidth(lw); // default to '1'
 
-    // private
-    mimicBlur : function(e, t){
-        if(!this.wrap.contains(t) && this.validateBlur()){
-            this.triggerBlur();
+            
+        }
+        if(!this.hasFocus){
+            return;
+        }
+        
+        if(this.store.getCount() > 0) {
+            this.expand();
+            this.restrictHeight();   
+        } else {
+            this.onEmptyResults();
+        }
+        
+        if (!this.loadingChildren) {
+            this.selectActive();
         }
+        /*
+        this.stores[1].loadData([]);
+        this.stores[2].loadData([]);
+        this.views
+        */    
+    
+        //this.el.focus();
     },
-
+    
+    
     // private
-    triggerBlur : function(){
-        this.mimicing = false;
-        Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
-        if(this.monitorTab){
-            this.el.un("keydown", this.checkTab, this);
+    onLoadException : function()
+    {
+        this.collapse();
+        Roo.log(this.store.reader.jsonData);
+        if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
+            Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
         }
-        this.wrap.removeClass('x-trigger-wrap-focus');
-        Roo.form.TriggerField.superclass.onBlur.call(this);
+        
+        
     },
+    // no cleaning of leading spaces on blur here.
+    cleanLeadingSpace : function(e) { },
+    
 
-    // private
-    // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
-    validateBlur : function(e, t){
-        return true;
-    },
+    onSelectChange : function (view, sels, opts )
+    {
+        var ix = view.getSelectedIndexes();
+         
+        if (opts.list > this.maxColumns - 2) {
+            if (view.store.getCount()<  1) {
+                this.views[opts.list ].getEl().setStyle({ display :   'none' });
 
-    // private
-    onDisable : function(){
-        Roo.form.TriggerField.superclass.onDisable.call(this);
-        if(this.wrap){
-            this.wrap.addClass('x-item-disabled');
+            } else  {
+                if (ix.length) {
+                    // used to clear ?? but if we are loading unselected 
+                    this.setFromData(view.store.getAt(ix[0]).data);
+                }
+                
+            }
+            
+            return;
         }
-    },
-
-    // private
-    onEnable : function(){
-        Roo.form.TriggerField.superclass.onEnable.call(this);
-        if(this.wrap){
-            this.wrap.removeClass('x-item-disabled');
+        
+        if (!ix.length) {
+            // this get's fired when trigger opens..
+           // this.setFromData({});
+            var str = this.stores[opts.list+1];
+            str.data.clear(); // removeall wihtout the fire events..
+            return;
+        }
+        
+        var rec = view.store.getAt(ix[0]);
+         
+        this.setFromData(rec.data);
+        this.fireEvent('select', this, rec, ix[0]);
+        
+        var lw = Math.floor(
+             (
+                (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
+             ) / this.maxColumns
+        );
+        this.loadingChildren = true;
+        this.stores[opts.list+1].loadDataFromChildren( rec );
+        this.loadingChildren = false;
+        var dl = this.stores[opts.list+1]. getTotalCount();
+        
+        this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
+        
+        this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
+        for (var i = opts.list+2; i < this.views.length;i++) {
+            this.views[i].getEl().setStyle({ display : 'none' });
+        }
+        
+        this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
+        this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
+        
+        if (this.isLoading) {
+           // this.selectActive(opts.list);
         }
+         
     },
-
+    
+    
+    
+    
+    onDoubleClick : function()
+    {
+        this.collapse(); //??
+    },
+    
+     
+    
+    
+    
     // private
-    onShow : function(){
-        var ae = this.getActionEl();
-        
-        if(ae){
-            ae.dom.style.display = '';
-            ae.dom.style.visibility = 'visible';
+    recordToStack : function(store, prop, value, stack)
+    {
+        var cstore = new Roo.data.SimpleStore({
+            //fields : this.store.reader.meta.fields, // we need array reader.. for
+            reader : this.store.reader,
+            data : [ ]
+        });
+        var _this = this;
+        var record  = false;
+        var srec = false;
+        if(store.getCount() < 1){
+            return false;
+        }
+        store.each(function(r){
+            if(r.data[prop] == value){
+                record = r;
+            srec = r;
+                return false;
+            }
+            if (r.data.cn && r.data.cn.length) {
+                cstore.loadDataFromChildren( r);
+                var cret = _this.recordToStack(cstore, prop, value, stack);
+                if (cret !== false) {
+                    record = cret;
+                    srec = r;
+                    return false;
+                }
+            }
+             
+            return true;
+        });
+        if (record == false) {
+            return false
         }
+        stack.unshift(srec);
+        return record;
     },
-
-    // private
     
-    onHide : function(){
-        var ae = this.getActionEl();
-        ae.dom.style.display = 'none';
-    },
-
-    /**
-     * The function that should handle the trigger's click event.  This method does nothing by default until overridden
-     * by an implementing function.
-     * @method
-     * @param {EventObject} e
+    /*
+     * find the stack of stores that match our value.
+     *
+     * 
      */
-    onTriggerClick : Roo.emptyFn
-});
-
-// TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
-// to be extended by an implementing class.  For an example of implementing this class, see the custom
-// SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
-Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
-    initComponent : function(){
-        Roo.form.TwinTriggerField.superclass.initComponent.call(this);
-
-        this.triggerConfig = {
-            tag:'span', cls:'x-form-twin-triggers', cn:[
-            {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
-            {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
-        ]};
-    },
-
-    getTrigger : function(index){
-        return this.triggers[index];
-    },
-
-    initTrigger : function(){
-        var ts = this.trigger.select('.x-form-trigger', true);
-        this.wrap.setStyle('overflow', 'hidden');
-        var triggerField = this;
-        ts.each(function(t, all, index){
-            t.hide = function(){
-                var w = triggerField.wrap.getWidth();
-                this.dom.style.display = 'none';
-                triggerField.el.setWidth(w-triggerField.trigger.getWidth());
-            };
-            t.show = function(){
-                var w = triggerField.wrap.getWidth();
-                this.dom.style.display = '';
-                triggerField.el.setWidth(w-triggerField.trigger.getWidth());
-            };
-            var triggerIndex = 'Trigger'+(index+1);
-
-            if(this['hide'+triggerIndex]){
-                t.dom.style.display = 'none';
-            }
-            t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
-            t.addClassOnOver('x-form-trigger-over');
-            t.addClassOnClick('x-form-trigger-click');
-        }, this);
-        this.triggers = ts.elements;
-    },
-
-    onTrigger1Click : Roo.emptyFn,
-    onTrigger2Click : Roo.emptyFn
+    
+    selectActive : function ()
+    {
+       // if store is not loaded, then we will need to wait for that to happen first.
+        var stack = [];
+        this.recordToStack(this.store, this.valueField, this.getValue(), stack);
+        for (var i = 0; i < stack.length; i++ ) {
+            this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
+        }
+       
+    }
+       
+        
+    
+    
+    
+    
 });/*
  * Based on:
  * Ext JS Library 1.1.1
@@ -40091,116 +44836,217 @@ Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
  * Fork - LGPL
  * <script type="text/javascript">
  */
 /**
- * @class Roo.form.TextArea
- * @extends Roo.form.TextField
- * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
- * support for auto-sizing.
+ * @class Roo.form.Checkbox
+ * @extends Roo.form.Field
+ * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
  * @constructor
- * Creates a new TextArea
+ * Creates a new Checkbox
  * @param {Object} config Configuration options
  */
-Roo.form.TextArea = function(config){
-    Roo.form.TextArea.superclass.constructor.call(this, config);
-    // these are provided exchanges for backwards compat
-    // minHeight/maxHeight were replaced by growMin/growMax to be
-    // compatible with TextField growing config values
-    if(this.minHeight !== undefined){
-        this.growMin = this.minHeight;
-    }
-    if(this.maxHeight !== undefined){
-        this.growMax = this.maxHeight;
-    }
+Roo.form.Checkbox = function(config){
+    Roo.form.Checkbox.superclass.constructor.call(this, config);
+    this.addEvents({
+        /**
+         * @event check
+         * Fires when the checkbox is checked or unchecked.
+            * @param {Roo.form.Checkbox} this This checkbox
+            * @param {Boolean} checked The new checked value
+            */
+        check : true
+    });
 };
 
-Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
+Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
     /**
-     * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
+     * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
      */
-    growMin : 60,
+    focusClass : undefined,
     /**
-     * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
+     * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
      */
-    growMax: 1000,
+    fieldClass: "x-form-field",
     /**
-     * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
-     * in the field (equivalent to setting overflow: hidden, defaults to false)
+     * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
      */
-    preventScrollbars: false,
+    checked: false,
     /**
      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
-     * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
+     * {tag: "input", type: "checkbox", autocomplete: "off"})
+     */
+    defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
+    /**
+     * @cfg {String} boxLabel The text that appears beside the checkbox
+     */
+    boxLabel : "",
+    /**
+     * @cfg {String} inputValue The value that should go into the generated input element's value attribute
+     */  
+    inputValue : '1',
+    /**
+     * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
      */
+     valueOff: '0', // value when not checked..
 
+    actionMode : 'viewEl', 
+    //
     // private
-    onRender : function(ct, position){
-        if(!this.el){
-            this.defaultAutoCreate = {
-                tag: "textarea",
-                style:"width:300px;height:60px;",
-                autocomplete: "new-password"
-            };
-        }
-        Roo.form.TextArea.superclass.onRender.call(this, ct, position);
-        if(this.grow){
-            this.textSizeEl = Roo.DomHelper.append(document.body, {
-                tag: "pre", cls: "x-form-grow-sizer"
-            });
-            if(this.preventScrollbars){
-                this.el.setStyle("overflow", "hidden");
-            }
-            this.el.setHeight(this.growMin);
+    itemCls : 'x-menu-check-item x-form-item',
+    groupClass : 'x-menu-group-item',
+    inputType : 'hidden',
+    
+    
+    inSetChecked: false, // check that we are not calling self...
+    
+    inputElement: false, // real input element?
+    basedOn: false, // ????
+    
+    isFormField: true, // not sure where this is needed!!!!
+
+    onResize : function(){
+        Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
+        if(!this.boxLabel){
+            this.el.alignTo(this.wrap, 'c-c');
         }
     },
 
-    onDestroy : function(){
-        if(this.textSizeEl){
-            this.textSizeEl.parentNode.removeChild(this.textSizeEl);
-        }
-        Roo.form.TextArea.superclass.onDestroy.call(this);
+    initEvents : function(){
+        Roo.form.Checkbox.superclass.initEvents.call(this);
+        this.el.on("click", this.onClick,  this);
+        this.el.on("change", this.onClick,  this);
+    },
+
+
+    getResizeEl : function(){
+        return this.wrap;
+    },
+
+    getPositionEl : function(){
+        return this.wrap;
     },
 
     // private
-    onKeyUp : function(e){
-        if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
-            this.autoSize();
+    onRender : function(ct, position){
+        Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
+        /*
+        if(this.inputValue !== undefined){
+            this.el.dom.value = this.inputValue;
+        }
+        */
+        //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
+        this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
+        var viewEl = this.wrap.createChild({ 
+            tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
+        this.viewEl = viewEl;   
+        this.wrap.on('click', this.onClick,  this); 
+        
+        this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
+        this.el.on('propertychange', this.setFromHidden,  this);  //ie
+        
+        
+        
+        if(this.boxLabel){
+            this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
+        //    viewEl.on('click', this.onClick,  this); 
         }
+        //if(this.checked){
+            this.setChecked(this.checked);
+        //}else{
+            //this.checked = this.el.dom;
+        //}
+
     },
 
+    // private
+    initValue : Roo.emptyFn,
+
     /**
-     * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
-     * This only takes effect if grow = true, and fires the autosize event if the height changes.
+     * Returns the checked state of the checkbox.
+     * @return {Boolean} True if checked, else false
      */
-    autoSize : function(){
-        if(!this.grow || !this.textSizeEl){
+    getValue : function(){
+        if(this.el){
+            return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
+        }
+        return this.valueOff;
+        
+    },
+
+       // private
+    onClick : function(){ 
+        if (this.disabled) {
             return;
         }
-        var el = this.el;
-        var v = el.dom.value;
-        var ts = this.textSizeEl;
+        this.setChecked(!this.checked);
 
-        ts.innerHTML = '';
-        ts.appendChild(document.createTextNode(v));
-        v = ts.innerHTML;
+        //if(this.el.dom.checked != this.checked){
+        //    this.setValue(this.el.dom.checked);
+       // }
+    },
 
-        Roo.fly(ts).setWidth(this.el.getWidth());
-        if(v.length < 1){
-            v = "&#160;&#160;";
-        }else{
-            if(Roo.isIE){
-                v = v.replace(/\n/g, '<p>&#160;</p>');
-            }
-            v += "&#160;\n&#160;";
+    /**
+     * 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.fireEvent("check", this, this.checked);
+    },
+    // private..
+    setChecked : function(state,suppressEvent)
+    {
+        if (this.inSetChecked) {
+            this.checked = state;
+            return;
         }
-        ts.innerHTML = v;
-        var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
-        if(h != this.lastHeight){
-            this.lastHeight = h;
-            this.el.setHeight(h);
-            this.fireEvent("autosize", this, h);
+        
+    
+        if(this.wrap){
+            this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
+        }
+        this.checked = state;
+        if(suppressEvent !== true){
+            this.fireEvent('check', this, state);
+        }
+        this.inSetChecked = true;
+        this.el.dom.value = state ? this.inputValue : this.valueOff;
+        this.inSetChecked = false;
+        
+    },
+    // handle setting of hidden value by some other method!!?!?
+    setFromHidden: function()
+    {
+        if(!this.el){
+            return;
+        }
+        //console.log("SET FROM HIDDEN");
+        //alert('setFrom hidden');
+        this.setValue(this.el.dom.value);
+    },
+    
+    onDestroy : function()
+    {
+        if(this.viewEl){
+            Roo.get(this.viewEl).remove();
         }
+         
+        Roo.form.Checkbox.superclass.onDestroy.call(this);
+    },
+    
+    setBoxLabel : function(str)
+    {
+        this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
     }
+
 });/*
  * Based on:
  * Ext JS Library 1.1.1
@@ -40212,3212 +45058,4678 @@ Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
  * <script type="text/javascript">
  */
  
-
 /**
- * @class Roo.form.NumberField
- * @extends Roo.form.TextField
- * Numeric text field that provides automatic keystroke filtering and numeric validation.
+ * @class Roo.form.Radio
+ * @extends Roo.form.Checkbox
+ * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
+ * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
  * @constructor
- * Creates a new NumberField
+ * Creates a new Radio
  * @param {Object} config Configuration options
  */
-Roo.form.NumberField = function(config){
-    Roo.form.NumberField.superclass.constructor.call(this, config);
+Roo.form.Radio = function(){
+    Roo.form.Radio.superclass.constructor.apply(this, arguments);
 };
+Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
+    inputType: 'radio',
 
-Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
-    /**
-     * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
-     */
-    fieldClass: "x-form-field x-form-num-field",
-    /**
-     * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
-     */
-    allowDecimals : true,
-    /**
-     * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
-     */
-    decimalSeparator : ".",
-    /**
-     * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
-     */
-    decimalPrecision : 2,
-    /**
-     * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
-     */
-    allowNegative : true,
-    /**
-     * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
-     */
-    minValue : Number.NEGATIVE_INFINITY,
-    /**
-     * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
-     */
-    maxValue : Number.MAX_VALUE,
-    /**
-     * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
-     */
-    minText : "The minimum value for this field is {0}",
-    /**
-     * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
-     */
-    maxText : "The maximum value for this field is {0}",
     /**
-     * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
-     * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
+     * If this radio is part of a group, it will return the selected value
+     * @return {String}
      */
-    nanText : "{0} is not a valid number",
-
-    // private
-    initEvents : function(){
-        Roo.form.NumberField.superclass.initEvents.call(this);
-        var allowed = "0123456789";
-        if(this.allowDecimals){
-            allowed += this.decimalSeparator;
-        }
-        if(this.allowNegative){
-            allowed += "-";
-        }
-        this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
-        var keyPress = function(e){
-            var k = e.getKey();
-            if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
-                return;
-            }
-            var c = e.getCharCode();
-            if(allowed.indexOf(String.fromCharCode(c)) === -1){
-                e.stopEvent();
-            }
-        };
-        this.el.on("keypress", keyPress, this);
+    getGroupValue : function(){
+        return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
     },
-
-    // private
-    validateValue : function(value){
-        if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
-            return false;
-        }
-        if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
-             return true;
-        }
-        var num = this.parseValue(value);
-        if(isNaN(num)){
-            this.markInvalid(String.format(this.nanText, value));
-            return false;
+    
+    
+    onRender : function(ct, position){
+        Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
+        
+        if(this.inputValue !== undefined){
+            this.el.dom.value = this.inputValue;
         }
-        if(num < this.minValue){
-            this.markInvalid(String.format(this.minText, this.minValue));
-            return false;
+         
+        this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
+        //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
+        //var viewEl = this.wrap.createChild({ 
+        //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
+        //this.viewEl = viewEl;   
+        //this.wrap.on('click', this.onClick,  this); 
+        
+        //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
+        //this.el.on('propertychange', this.setFromHidden,  this);  //ie
+        
+        
+        
+        if(this.boxLabel){
+            this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
+        //    viewEl.on('click', this.onClick,  this); 
         }
-        if(num > this.maxValue){
-            this.markInvalid(String.format(this.maxText, this.maxValue));
-            return false;
+         if(this.checked){
+            this.el.dom.checked =   'checked' ;
         }
-        return true;
-    },
+         
+    } 
+    
+    
+});Roo.rtf = {}; // namespace
+Roo.rtf.Hex = function(hex)
+{
+    this.hexstr = hex;
+};
+Roo.rtf.Paragraph = function(opts)
+{
+    this.content = []; ///??? is that used?
+};Roo.rtf.Span = function(opts)
+{
+    this.value = opts.value;
+};
 
-    getValue : function(){
-        return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
-    },
+Roo.rtf.Group = function(parent)
+{
+    // we dont want to acutally store parent - it will make debug a nightmare..
+    this.content = [];
+    this.cn  = [];
+     
+       
+    
+};
 
-    // private
-    parseValue : function(value){
-        value = parseFloat(String(value).replace(this.decimalSeparator, "."));
-        return isNaN(value) ? '' : value;
+Roo.rtf.Group.prototype = {
+    ignorable : false,
+    content: false,
+    cn: false,
+    addContent : function(node) {
+        // could set styles...
+        this.content.push(node);
     },
-
-    // private
-    fixPrecision : function(value){
-        var nan = isNaN(value);
-        if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
-            return nan ? '' : value;
-        }
-        return parseFloat(value).toFixed(this.decimalPrecision);
+    addChild : function(cn)
+    {
+        this.cn.push(cn);
     },
-
-    setValue : function(v){
-        v = this.fixPrecision(v);
-        Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
+    // only for images really...
+    toDataURL : function()
+    {
+        var mimetype = false;
+        switch(true) {
+            case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
+                mimetype = "image/png";
+                break;
+             case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
+                mimetype = "image/jpeg";
+                break;
+            default :
+                return 'about:blank'; // ?? error?
+        }
+        
+        
+        var hexstring = this.content[this.content.length-1].value;
+        
+        return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
+            return String.fromCharCode(parseInt(a, 16));
+        }).join(""));
+    }
+    
+};
+// this looks like it's normally the {rtf{ .... }}
+Roo.rtf.Document = function()
+{
+    // we dont want to acutally store parent - it will make debug a nightmare..
+    this.rtlch  = [];
+    this.content = [];
+    this.cn = [];
+    
+};
+Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
+    addChild : function(cn)
+    {
+        this.cn.push(cn);
+        switch(cn.type) {
+            case 'rtlch': // most content seems to be inside this??
+            case 'listtext':
+            case 'shpinst':
+                this.rtlch.push(cn);
+                return;
+            default:
+                this[cn.type] = cn;
+        }
+        
     },
-
-    // private
-    decimalPrecisionFcn : function(v){
-        return Math.floor(v);
+    
+    getElementsByType : function(type)
+    {
+        var ret =  [];
+        this._getElementsByType(type, ret, this.cn, 'rtf');
+        return ret;
     },
-
-    beforeBlur : function(){
-        var v = this.parseValue(this.getRawValue());
-        if(v){
-            this.setValue(v);
-        }
+    _getElementsByType : function (type, ret, search_array, path)
+    {
+        search_array.forEach(function(n,i) {
+            if (n.type == type) {
+                n.path = path + '/' + n.type + ':' + i;
+                ret.push(n);
+            }
+            if (n.cn.length > 0) {
+                this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
+            }
+        },this);
     }
-});/*
- * Based on:
- * Ext JS Library 1.1.1
- * Copyright(c) 2006-2007, Ext JS, LLC.
+    
+});
+Roo.rtf.Ctrl = function(opts)
+{
+    this.value = opts.value;
+    this.param = opts.param;
+};
+/**
  *
- * Originally Released Under LGPL - original licence link has changed is not relivant.
  *
- * Fork - LGPL
- * <script type="text/javascript">
+ * based on this https://github.com/iarna/rtf-parser
+ * it's really only designed to extract pict from pasted RTF 
+ *
+ * usage:
+ *
+ *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
+ *  
+ *
  */
+
  
-/**
- * @class Roo.form.DateField
- * @extends Roo.form.TriggerField
- * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
-* @constructor
-* Create a new DateField
-* @param {Object} config
- */
-Roo.form.DateField = function(config)
-{
-    Roo.form.DateField.superclass.constructor.call(this, config);
+
+
+
+Roo.rtf.Parser = function(text) {
+    //super({objectMode: true})
+    this.text = '';
+    this.parserState = this.parseText;
     
-      this.addEvents({
-         
-        /**
-         * @event select
-         * Fires when a date is selected
-            * @param {Roo.form.DateField} combo This combo box
-            * @param {Date} date The date selected
-            */
-        'select' : true
-         
-    });
+    // these are for interpeter...
+    this.doc = {};
+    ///this.parserState = this.parseTop
+    this.groupStack = [];
+    this.hexStore = [];
+    this.doc = false;
     
+    this.groups = []; // where we put the return.
     
-    if(typeof this.minValue == "string") {
-        this.minValue = this.parseDate(this.minValue);
-    }
-    if(typeof this.maxValue == "string") {
-        this.maxValue = this.parseDate(this.maxValue);
-    }
-    this.ddMatch = null;
-    if(this.disabledDates){
-        var dd = this.disabledDates;
-        var re = "(?:";
-        for(var i = 0; i < dd.length; i++){
-            re += dd[i];
-            if(i != dd.length-1) {
-                re += "|";
-            }
+    for (var ii = 0; ii < text.length; ++ii) {
+        ++this.cpos;
+        
+        if (text[ii] === '\n') {
+            ++this.row;
+            this.col = 1;
+        } else {
+            ++this.col;
         }
-        this.ddMatch = new RegExp(re + ")");
+        this.parserState(text[ii]);
     }
-};
-
-Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
-    /**
-     * @cfg {String} format
-     * The default date format string which can be overriden for localization support.  The format must be
-     * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
-     */
-    format : "m/d/y",
-    /**
-     * @cfg {String} altFormats
-     * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
-     * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
-     */
-    altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
-    /**
-     * @cfg {Array} disabledDays
-     * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
-     */
-    disabledDays : null,
-    /**
-     * @cfg {String} disabledDaysText
-     * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
-     */
-    disabledDaysText : "Disabled",
-    /**
-     * @cfg {Array} disabledDates
-     * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
-     * expression so they are very powerful. Some examples:
-     * <ul>
-     * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
-     * <li>["03/08", "09/16"] would disable those days for every year</li>
-     * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
-     * <li>["03/../2006"] would disable every day in March 2006</li>
-     * <li>["^03"] would disable every day in every March</li>
-     * </ul>
-     * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
-     * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
-     */
-    disabledDates : null,
-    /**
-     * @cfg {String} disabledDatesText
-     * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
-     */
-    disabledDatesText : "Disabled",
-    /**
-     * @cfg {Date/String} minValue
-     * The minimum allowed date. Can be either a Javascript date object or a string date in a
-     * valid format (defaults to null).
-     */
-    minValue : null,
-    /**
-     * @cfg {Date/String} maxValue
-     * The maximum allowed date. Can be either a Javascript date object or a string date in a
-     * valid format (defaults to null).
-     */
-    maxValue : null,
-    /**
-     * @cfg {String} minText
-     * The error text to display when the date in the cell is before minValue (defaults to
-     * 'The date in this field must be after {minValue}').
-     */
-    minText : "The date in this field must be equal to or after {0}",
-    /**
-     * @cfg {String} maxText
-     * The error text to display when the date in the cell is after maxValue (defaults to
-     * 'The date in this field must be before {maxValue}').
-     */
-    maxText : "The date in this field must be equal to or before {0}",
-    /**
-     * @cfg {String} invalidText
-     * The error text to display when the date in the field is invalid (defaults to
-     * '{value} is not a valid date - it must be in the format {format}').
-     */
-    invalidText : "{0} is not a valid date - it must be in the format {1}",
-    /**
-     * @cfg {String} triggerClass
-     * An additional CSS class used to style the trigger button.  The trigger will always get the
-     * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
-     * which displays a calendar icon).
-     */
-    triggerClass : 'x-form-date-trigger',
     
-
-    /**
-     * @cfg {Boolean} useIso
-     * if enabled, then the date field will use a hidden field to store the 
-     * real value as iso formated date. default (false)
-     */ 
-    useIso : false,
-    /**
-     * @cfg {String/Object} autoCreate
-     * A DomHelper element spec, or true for a default element spec (defaults to
-     * {tag: "input", type: "text", size: "10", autocomplete: "off"})
-     */ 
-    // private
-    defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
     
-    // private
-    hiddenField: false,
     
-    onRender : function(ct, position)
+};
+Roo.rtf.Parser.prototype = {
+    text : '', // string being parsed..
+    controlWord : '',
+    controlWordParam :  '',
+    hexChar : '',
+    doc : false,
+    group: false,
+    groupStack : false,
+    hexStore : false,
+    
+    
+    cpos : 0, 
+    row : 1, // reportin?
+    col : 1, //
+
+     
+    push : function (el)
     {
-        Roo.form.DateField.superclass.onRender.call(this, ct, position);
-        if (this.useIso) {
-            //this.el.dom.removeAttribute('name'); 
-            Roo.log("Changing name?");
-            this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
-            this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
-                    'before', true);
-            this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
-            // prevent input submission
-            this.hiddenName = this.name;
+        var m = 'cmd'+ el.type;
+        if (typeof(this[m]) == 'undefined') {
+            Roo.log('invalid cmd:' + el.type);
+            return;
         }
+        this[m](el);
+        //Roo.log(el);
+    },
+    flushHexStore : function()
+    {
+        if (this.hexStore.length < 1) {
+            return;
+        }
+        var hexstr = this.hexStore.map(
+            function(cmd) {
+                return cmd.value;
+        }).join('');
+        
+        this.group.addContent( new Roo.rtf.Hex( hexstr ));
+              
             
-            
+        this.hexStore.splice(0)
+        
     },
     
-    // private
-    validateValue : function(value)
+    cmdgroupstart : function()
     {
-        value = this.formatDate(value);
-        if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
-            Roo.log('super failed');
-            return false;
+        this.flushHexStore();
+        if (this.group) {
+            this.groupStack.push(this.group);
         }
-        if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
-             return true;
+         // parent..
+        if (this.doc === false) {
+            this.group = this.doc = new Roo.rtf.Document();
+            return;
+            
         }
-        var svalue = value;
-        value = this.parseDate(value);
-        if(!value){
-            Roo.log('parse date failed' + svalue);
-            this.markInvalid(String.format(this.invalidText, svalue, this.format));
-            return false;
+        this.group = new Roo.rtf.Group(this.group);
+    },
+    cmdignorable : function()
+    {
+        this.flushHexStore();
+        this.group.ignorable = true;
+    },
+    cmdendparagraph : function()
+    {
+        this.flushHexStore();
+        this.group.addContent(new Roo.rtf.Paragraph());
+    },
+    cmdgroupend : function ()
+    {
+        this.flushHexStore();
+        var endingGroup = this.group;
+        
+        
+        this.group = this.groupStack.pop();
+        if (this.group) {
+            this.group.addChild(endingGroup);
         }
-        var time = value.getTime();
-        if(this.minValue && time < this.minValue.getTime()){
-            this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
-            return false;
+        
+        
+        
+        var doc = this.group || this.doc;
+        //if (endingGroup instanceof FontTable) {
+        //  doc.fonts = endingGroup.table
+        //} else if (endingGroup instanceof ColorTable) {
+        //  doc.colors = endingGroup.table
+        //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
+        if (endingGroup.ignorable === false) {
+            //code
+            this.groups.push(endingGroup);
+           // Roo.log( endingGroup );
         }
-        if(this.maxValue && time > this.maxValue.getTime()){
-            this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
-            return false;
+            //Roo.each(endingGroup.content, function(item)) {
+            //    doc.addContent(item);
+            //}
+            //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
+        //}
+    },
+    cmdtext : function (cmd)
+    {
+        this.flushHexStore();
+        if (!this.group) { // an RTF fragment, missing the {\rtf1 header
+            //this.group = this.doc
+            return;  // we really don't care about stray text...
         }
-        if(this.disabledDays){
-            var day = value.getDay();
-            for(var i = 0; i < this.disabledDays.length; i++) {
-               if(day === this.disabledDays[i]){
-                   this.markInvalid(this.disabledDaysText);
-                    return false;
-               }
-            }
+        this.group.addContent(new Roo.rtf.Span(cmd));
+    },
+    cmdcontrolword : function (cmd)
+    {
+        this.flushHexStore();
+        if (!this.group.type) {
+            this.group.type = cmd.value;
+            return;
         }
-        var fvalue = this.formatDate(value);
-        if(this.ddMatch && this.ddMatch.test(fvalue)){
-            this.markInvalid(String.format(this.disabledDatesText, fvalue));
-            return false;
+        this.group.addContent(new Roo.rtf.Ctrl(cmd));
+        // we actually don't care about ctrl words...
+        return ;
+        /*
+        var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
+        if (this[method]) {
+            this[method](cmd.param)
+        } else {
+            if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
         }
-        return true;
+        */
     },
-
-    // private
-    // Provides logic to override the default TriggerField.validateBlur which just returns true
-    validateBlur : function(){
-        return !this.menu || !this.menu.isVisible();
+    cmdhexchar : function(cmd) {
+        this.hexStore.push(cmd);
+    },
+    cmderror : function(cmd) {
+        throw cmd.value;
     },
     
-    getName: function()
+    /*
+      _flush (done) {
+        if (this.text !== '\u0000') this.emitText()
+        done()
+      }
+      */
+      
+      
+    parseText : function(c)
     {
-        // returns hidden if it's set..
-        if (!this.rendered) {return ''};
-        return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
-        
-    },
-
-    /**
-     * Returns the current date value of the date field.
-     * @return {Date} The date value
-     */
-    getValue : function(){
-        
-        return  this.hiddenField ?
-                this.hiddenField.value :
-                this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
-    },
-
-    /**
-     * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
-     * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
-     * (the default format used is "m/d/y").
-     * <br />Usage:
-     * <pre><code>
-//All of these calls set the same date value (May 4, 2006)
-
-//Pass a date object:
-var dt = new Date('5/4/06');
-dateField.setValue(dt);
-
-//Pass a date string (default format):
-dateField.setValue('5/4/06');
-
-//Pass a date string (custom format):
-dateField.format = 'Y-m-d';
-dateField.setValue('2006-5-4');
-</code></pre>
-     * @param {String/Date} date The date or valid date string
-     */
-    setValue : function(date){
-        if (this.hiddenField) {
-            this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
+        if (c === '\\') {
+            this.parserState = this.parseEscapes;
+        } else if (c === '{') {
+            this.emitStartGroup();
+        } else if (c === '}') {
+            this.emitEndGroup();
+        } else if (c === '\x0A' || c === '\x0D') {
+            // cr/lf are noise chars
+        } else {
+            this.text += c;
         }
-        Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
-        // make sure the value field is always stored as a date..
-        this.value = this.parseDate(date);
-        
-        
     },
-
-    // private
-    parseDate : function(value){
-        if(!value || value instanceof Date){
-            return value;
+    
+    parseEscapes: function (c)
+    {
+        if (c === '\\' || c === '{' || c === '}') {
+            this.text += c;
+            this.parserState = this.parseText;
+        } else {
+            this.parserState = this.parseControlSymbol;
+            this.parseControlSymbol(c);
         }
-        var v = Date.parseDate(value, this.format);
-         if (!v && this.useIso) {
-            v = Date.parseDate(value, 'Y-m-d');
+    },
+    parseControlSymbol: function(c)
+    {
+        if (c === '~') {
+            this.text += '\u00a0'; // nbsp
+            this.parserState = this.parseText
+        } else if (c === '-') {
+             this.text += '\u00ad'; // soft hyphen
+        } else if (c === '_') {
+            this.text += '\u2011'; // non-breaking hyphen
+        } else if (c === '*') {
+            this.emitIgnorable();
+            this.parserState = this.parseText;
+        } else if (c === "'") {
+            this.parserState = this.parseHexChar;
+        } else if (c === '|') { // formula cacter
+            this.emitFormula();
+            this.parserState = this.parseText;
+        } else if (c === ':') { // subentry in an index entry
+            this.emitIndexSubEntry();
+            this.parserState = this.parseText;
+        } else if (c === '\x0a') {
+            this.emitEndParagraph();
+            this.parserState = this.parseText;
+        } else if (c === '\x0d') {
+            this.emitEndParagraph();
+            this.parserState = this.parseText;
+        } else {
+            this.parserState = this.parseControlWord;
+            this.parseControlWord(c);
         }
-        if(!v && this.altFormats){
-            if(!this.altFormatsArray){
-                this.altFormatsArray = this.altFormats.split("|");
-            }
-            for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
-                v = Date.parseDate(value, this.altFormatsArray[i]);
+    },
+    parseHexChar: function (c)
+    {
+        if (/^[A-Fa-f0-9]$/.test(c)) {
+            this.hexChar += c;
+            if (this.hexChar.length >= 2) {
+              this.emitHexChar();
+              this.parserState = this.parseText;
             }
+            return;
         }
-        return v;
+        this.emitError("Invalid character \"" + c + "\" in hex literal.");
+        this.parserState = this.parseText;
+        
     },
-
-    // private
-    formatDate : function(date, fmt){
-        return (!date || !(date instanceof Date)) ?
-               date : date.dateFormat(fmt || this.format);
+    parseControlWord : function(c)
+    {
+        if (c === ' ') {
+            this.emitControlWord();
+            this.parserState = this.parseText;
+        } else if (/^[-\d]$/.test(c)) {
+            this.parserState = this.parseControlWordParam;
+            this.controlWordParam += c;
+        } else if (/^[A-Za-z]$/.test(c)) {
+          this.controlWord += c;
+        } else {
+          this.emitControlWord();
+          this.parserState = this.parseText;
+          this.parseText(c);
+        }
     },
-
-    // private
-    menuListeners : {
-        select: function(m, d){
-            
-            this.setValue(d);
-            this.fireEvent('select', this, d);
-        },
-        show : function(){ // retain focus styling
-            this.onFocus();
-        },
-        hide : function(){
-            this.focus.defer(10, this);
-            var ml = this.menuListeners;
-            this.menu.un("select", ml.select,  this);
-            this.menu.un("show", ml.show,  this);
-            this.menu.un("hide", ml.hide,  this);
+    parseControlWordParam : function (c) {
+        if (/^\d$/.test(c)) {
+          this.controlWordParam += c;
+        } else if (c === ' ') {
+          this.emitControlWord();
+          this.parserState = this.parseText;
+        } else {
+          this.emitControlWord();
+          this.parserState = this.parseText;
+          this.parseText(c);
         }
     },
-
-    // private
-    // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
-    onTriggerClick : function(){
-        if(this.disabled){
+    
+    
+    
+    
+    emitText : function () {
+        if (this.text === '') {
             return;
         }
-        if(this.menu == null){
-            this.menu = new Roo.menu.DateMenu();
+        this.push({
+            type: 'text',
+            value: this.text,
+            pos: this.cpos,
+            row: this.row,
+            col: this.col
+        });
+        this.text = ''
+    },
+    emitControlWord : function ()
+    {
+        this.emitText();
+        if (this.controlWord === '') {
+            // do we want to track this - it seems just to cause problems.
+            //this.emitError('empty control word');
+        } else {
+            this.push({
+                  type: 'controlword',
+                  value: this.controlWord,
+                  param: this.controlWordParam !== '' && Number(this.controlWordParam),
+                  pos: this.cpos,
+                  row: this.row,
+                  col: this.col
+            });
         }
-        Roo.apply(this.menu.picker,  {
-            showClear: this.allowBlank,
-            minDate : this.minValue,
-            maxDate : this.maxValue,
-            disabledDatesRE : this.ddMatch,
-            disabledDatesText : this.disabledDatesText,
-            disabledDays : this.disabledDays,
-            disabledDaysText : this.disabledDaysText,
-            format : this.useIso ? 'Y-m-d' : this.format,
-            minText : String.format(this.minText, this.formatDate(this.minValue)),
-            maxText : String.format(this.maxText, this.formatDate(this.maxValue))
+        this.controlWord = '';
+        this.controlWordParam = '';
+    },
+    emitStartGroup : function ()
+    {
+        this.emitText();
+        this.push({
+            type: 'groupstart',
+            pos: this.cpos,
+            row: this.row,
+            col: this.col
+        });
+    },
+    emitEndGroup : function ()
+    {
+        this.emitText();
+        this.push({
+            type: 'groupend',
+            pos: this.cpos,
+            row: this.row,
+            col: this.col
+        });
+    },
+    emitIgnorable : function ()
+    {
+        this.emitText();
+        this.push({
+            type: 'ignorable',
+            pos: this.cpos,
+            row: this.row,
+            col: this.col
+        });
+    },
+    emitHexChar : function ()
+    {
+        this.emitText();
+        this.push({
+            type: 'hexchar',
+            value: this.hexChar,
+            pos: this.cpos,
+            row: this.row,
+            col: this.col
+        });
+        this.hexChar = ''
+    },
+    emitError : function (message)
+    {
+      this.emitText();
+      this.push({
+            type: 'error',
+            value: message,
+            row: this.row,
+            col: this.col,
+            char: this.cpos //,
+            //stack: new Error().stack
         });
-        this.menu.on(Roo.apply({}, this.menuListeners, {
-            scope:this
-        }));
-        this.menu.picker.setValue(this.getValue() || new Date());
-        this.menu.show(this.el, "tl-bl?");
     },
+    emitEndParagraph : function () {
+        this.emitText();
+        this.push({
+            type: 'endparagraph',
+            pos: this.cpos,
+            row: this.row,
+            col: this.col
+        });
+    }
+     
+} ;
+Roo.htmleditor = {};
+/**
+ * @class Roo.htmleditor.Filter
+ * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
+ * @cfg {DomElement} node The node to iterate and filter
+ * @cfg {boolean|String|Array} tag Tags to replace 
+ * @constructor
+ * Create a new Filter.
+ * @param {Object} config Configuration options
+ */
 
-    beforeBlur : function(){
-        var v = this.parseDate(this.getRawValue());
-        if(v){
-            this.setValue(v);
+
+
+Roo.htmleditor.Filter = function(cfg) {
+    Roo.apply(this.cfg);
+    // this does not actually call walk as it's really just a abstract class
+}
+
+
+Roo.htmleditor.Filter.prototype = {
+    
+    node: false,
+    
+    tag: false,
+
+    // overrride to do replace comments.
+    replaceComment : false,
+    
+    // overrride to do replace or do stuff with tags..
+    replaceTag : false,
+    
+    walk : function(dom)
+    {
+        Roo.each( Array.from(dom.childNodes), function( e ) {
+            switch(true) {
+                
+                case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
+                    this.replaceComment(e);
+                    return;
+                
+                case e.nodeType != 1: //not a node.
+                    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)) {
+                        return;
+                    }
+                    if (e.hasChildNodes()) {
+                        this.walk(e);
+                    }
+                    return;
+                
+                default:    // tags .. that do not match.
+                    if (e.hasChildNodes()) {
+                        this.walk(e);
+                    }
+            }
+            
+        }, this);
+        
+    }
+}; 
+
+/**
+ * @class Roo.htmleditor.FilterAttributes
+ * clean attributes and  styles including http:// etc.. in attribute
+ * @constructor
+* Run a new Attribute Filter
+* @param {Object} config Configuration options
+ */
+Roo.htmleditor.FilterAttributes = function(cfg)
+{
+    Roo.apply(this, cfg);
+    this.attrib_black = this.attrib_black || [];
+    this.attrib_white = this.attrib_white || [];
+
+    this.attrib_clean = this.attrib_clean || [];
+    this.style_white = this.style_white || [];
+    this.style_black = this.style_black || [];
+    this.walk(cfg.node);
+}
+
+Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
+{
+    tag: true, // all tags
+    
+    attrib_black : false, // array
+    attrib_clean : false,
+    attrib_white : false,
+
+    style_white : false,
+    style_black : false,
+     
+     
+    replaceTag : function(node)
+    {
+        if (!node.attributes || !node.attributes.length) {
+            return true;
+        }
+        
+        for (var i = node.attributes.length-1; i > -1 ; i--) {
+            var a = node.attributes[i];
+            //console.log(a);
+            if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
+                node.removeAttribute(a.name);
+                continue;
+            }
+            
+            
+            
+            if (a.name.toLowerCase().substr(0,2)=='on')  {
+                node.removeAttribute(a.name);
+                continue;
+            }
+            
+            
+            if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
+                node.removeAttribute(a.name);
+                continue;
+            }
+            if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
+                this.cleanAttr(node,a.name,a.value); // fixme..
+                continue;
+            }
+            if (a.name == 'style') {
+                this.cleanStyle(node,a.name,a.value);
+                continue;
+            }
+            /// clean up MS crap..
+            // tecnically this should be a list of valid class'es..
+            
+            
+            if (a.name == 'class') {
+                if (a.value.match(/^Mso/)) {
+                    node.removeAttribute('class');
+                }
+                
+                if (a.value.match(/^body$/)) {
+                    node.removeAttribute('class');
+                }
+                continue;
+            }
+            
+            
+            // style cleanup!?
+            // class cleanup?
+            
         }
+        return true; // clean children
     },
-
-    /*@
-     * overide
-     * 
-     */
-    isDirty : function() {
-        if(this.disabled) {
-            return false;
-        }
         
-        if(typeof(this.startValue) === 'undefined'){
-            return false;
-        }
+    cleanAttr: function(node, n,v)
+    {
         
-        return String(this.getValue()) !== String(this.startValue);
+        if (v.match(/^\./) || v.match(/^\//)) {
+            return;
+        }
+        if (v.match(/^(http|https):\/\//)
+            || v.match(/^mailto:/) 
+            || v.match(/^ftp:/)
+            || v.match(/^data:/)
+            ) {
+            return;
+        }
+        if (v.match(/^#/)) {
+            return;
+        }
+        if (v.match(/^\{/)) { // allow template editing.
+            return;
+        }
+//            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
+        node.removeAttribute(n);
         
     },
-    // @overide
-    cleanLeadingSpace : function(e)
+    cleanStyle : function(node,  n,v)
     {
-       return;
+        if (v.match(/expression/)) { //XSS?? should we even bother..
+            node.removeAttribute(n);
+            return;
+        }
+        
+        var parts = v.split(/;/);
+        var clean = [];
+        
+        Roo.each(parts, function(p) {
+            p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
+            if (!p.length) {
+                return true;
+            }
+            var l = p.split(':').shift().replace(/\s+/g,'');
+            l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
+            
+            if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
+                return true;
+            }
+            //Roo.log()
+            // only allow 'c whitelisted system attributes'
+            if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
+                return true;
+            }
+            
+            
+            clean.push(p);
+            return true;
+        },this);
+        if (clean.length) { 
+            node.setAttribute(n, clean.join(';'));
+        } else {
+            node.removeAttribute(n);
+        }
+        
     }
+        
+        
+        
     
-});/*
- * 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.htmleditor.FilterBlack
+ * remove blacklisted elements.
+ * @constructor
+ * Run a new Blacklisted Filter
+ * @param {Object} config Configuration options
  */
+
+Roo.htmleditor.FilterBlack = function(cfg)
+{
+    Roo.apply(this, cfg);
+    this.walk(cfg.node);
+}
+
+Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
+{
+    tag : true, // all elements.
+   
+    replaceTag : function(n)
+    {
+        n.parentNode.removeChild(n);
+    }
+});
 /**
- * @class Roo.form.MonthField
- * @extends Roo.form.TriggerField
- * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
-* @constructor
-* Create a new MonthField
-* @param {Object} config
+ * @class Roo.htmleditor.FilterComment
+ * remove comments.
+ * @constructor
+* Run a new Comments Filter
+* @param {Object} config Configuration options
  */
-Roo.form.MonthField = function(config){
-    
-    Roo.form.MonthField.superclass.constructor.call(this, config);
-    
-      this.addEvents({
-         
-        /**
-         * @event select
-         * Fires when a date is selected
-            * @param {Roo.form.MonthFieeld} combo This combo box
-            * @param {Date} date The date selected
-            */
-        'select' : true
-         
-    });
-    
-    
-    if(typeof this.minValue == "string") {
-        this.minValue = this.parseDate(this.minValue);
+Roo.htmleditor.FilterComment = function(cfg)
+{
+    this.walk(cfg.node);
+}
+
+Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
+{
+  
+    replaceComment : function(n)
+    {
+        n.parentNode.removeChild(n);
     }
-    if(typeof this.maxValue == "string") {
-        this.maxValue = this.parseDate(this.maxValue);
+});/**
+ * @class Roo.htmleditor.FilterKeepChildren
+ * remove tags but keep children
+ * @constructor
+ * Run a new Keep Children Filter
+ * @param {Object} config Configuration options
+ */
+
+Roo.htmleditor.FilterKeepChildren = function(cfg)
+{
+    Roo.apply(this, 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 )
     }
-    this.ddMatch = null;
-    if(this.disabledDates){
-        var dd = this.disabledDates;
-        var re = "(?:";
-        for(var i = 0; i < dd.length; i++){
-            re += dd[i];
-            if(i != dd.length-1) {
-                re += "|";
+    this.walk(cfg.node);
+}
+
+Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
+{
+    
+  
+    replaceTag : function(node)
+    {
+        // walk children...
+        //Roo.log(node);
+        var ar = Array.from(node.childNodes);
+        //remove first..
+        for (var i = 0; i < ar.length; i++) {
+            if (ar[i].nodeType == 1) {
+                if (
+                    (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
+                    || // array and it matches
+                    (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
+                ) {
+                    this.replaceTag(ar[i]); // child is blacklisted as well...
+                    continue;
+                }
+            }
+        }  
+        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);
+            if (this.tag !== false) {
+                this.walk(ar[i]);
+                
             }
         }
-        this.ddMatch = new RegExp(re + ")");
+        node.parentNode.removeChild(node);
+        return false; // don't walk children
+        
+        
     }
-};
+});/**
+ * @class Roo.htmleditor.FilterParagraph
+ * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
+ * like on 'push' to remove the <p> tags and replace them with line breaks.
+ * @constructor
+ * Run a new Paragraph Filter
+ * @param {Object} config Configuration options
+ */
 
-Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
-    /**
-     * @cfg {String} format
-     * The default date format string which can be overriden for localization support.  The format must be
-     * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
-     */
-    format : "M Y",
-    /**
-     * @cfg {String} altFormats
-     * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
-     * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
-     */
-    altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
-    /**
-     * @cfg {Array} disabledDays
-     * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
-     */
-    disabledDays : [0,1,2,3,4,5,6],
-    /**
-     * @cfg {String} disabledDaysText
-     * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
-     */
-    disabledDaysText : "Disabled",
-    /**
-     * @cfg {Array} disabledDates
-     * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
-     * expression so they are very powerful. Some examples:
-     * <ul>
-     * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
-     * <li>["03/08", "09/16"] would disable those days for every year</li>
-     * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
-     * <li>["03/../2006"] would disable every day in March 2006</li>
-     * <li>["^03"] would disable every day in every March</li>
-     * </ul>
-     * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
-     * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
-     */
-    disabledDates : null,
-    /**
-     * @cfg {String} disabledDatesText
-     * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
-     */
-    disabledDatesText : "Disabled",
-    /**
-     * @cfg {Date/String} minValue
-     * The minimum allowed date. Can be either a Javascript date object or a string date in a
-     * valid format (defaults to null).
-     */
-    minValue : null,
-    /**
-     * @cfg {Date/String} maxValue
-     * The maximum allowed date. Can be either a Javascript date object or a string date in a
-     * valid format (defaults to null).
-     */
-    maxValue : null,
-    /**
-     * @cfg {String} minText
-     * The error text to display when the date in the cell is before minValue (defaults to
-     * 'The date in this field must be after {minValue}').
-     */
-    minText : "The date in this field must be equal to or after {0}",
-    /**
-     * @cfg {String} maxTextf
-     * The error text to display when the date in the cell is after maxValue (defaults to
-     * 'The date in this field must be before {maxValue}').
-     */
-    maxText : "The date in this field must be equal to or before {0}",
-    /**
-     * @cfg {String} invalidText
-     * The error text to display when the date in the field is invalid (defaults to
-     * '{value} is not a valid date - it must be in the format {format}').
-     */
-    invalidText : "{0} is not a valid date - it must be in the format {1}",
-    /**
-     * @cfg {String} triggerClass
-     * An additional CSS class used to style the trigger button.  The trigger will always get the
-     * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
-     * which displays a calendar icon).
-     */
-    triggerClass : 'x-form-date-trigger',
-    
+Roo.htmleditor.FilterParagraph = function(cfg)
+{
+    // no need to apply config.
+    this.walk(cfg.node);
+}
 
-    /**
-     * @cfg {Boolean} useIso
-     * if enabled, then the date field will use a hidden field to store the 
-     * real value as iso formated date. default (true)
-     */ 
-    useIso : true,
-    /**
-     * @cfg {String/Object} autoCreate
-     * A DomHelper element spec, or true for a default element spec (defaults to
-     * {tag: "input", type: "text", size: "10", autocomplete: "off"})
-     */ 
-    // private
-    defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
-    
-    // private
-    hiddenField: false,
+Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
+{
     
-    hideMonthPicker : false,
+     
+    tag : 'P',
     
-    onRender : function(ct, position)
+     
+    replaceTag : function(node)
     {
-        Roo.form.MonthField.superclass.onRender.call(this, ct, position);
-        if (this.useIso) {
-            this.el.dom.removeAttribute('name'); 
-            this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
-                    'before', true);
-            this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
-            // prevent input submission
-            this.hiddenName = this.name;
+        
+        if (node.childNodes.length == 1 &&
+            node.childNodes[0].nodeType == 3 &&
+            node.childNodes[0].textContent.trim().length < 1
+            ) {
+            // remove and replace with '<BR>';
+            node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
+            return false; // no need to walk..
         }
-            
-            
-    },
+        var 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);
+        }
+        // now what about this?
+        // <p> &nbsp; </p>
+        
+        // double BR.
+        node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
+        node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
+        node.parentNode.removeChild(node);
+        
+        return false;
+
+    }
     
-    // private
-    validateValue : function(value)
+});/**
+ * @class Roo.htmleditor.FilterSpan
+ * filter span's with no attributes out..
+ * @constructor
+ * Run a new Span Filter
+ * @param {Object} config Configuration options
+ */
+
+Roo.htmleditor.FilterSpan = function(cfg)
+{
+    // no need to apply config.
+    this.walk(cfg.node);
+}
+
+Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
+{
+     
+    tag : 'SPAN',
+     
+    replaceTag : function(node)
     {
-        value = this.formatDate(value);
-        if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
-            return false;
-        }
-        if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
-             return true;
-        }
-        var svalue = value;
-        value = this.parseDate(value);
-        if(!value){
-            this.markInvalid(String.format(this.invalidText, svalue, this.format));
-            return false;
-        }
-        var time = value.getTime();
-        if(this.minValue && time < this.minValue.getTime()){
-            this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
-            return false;
-        }
-        if(this.maxValue && time > this.maxValue.getTime()){
-            this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
-            return false;
-        }
-        /*if(this.disabledDays){
-            var day = value.getDay();
-            for(var i = 0; i < this.disabledDays.length; i++) {
-               if(day === this.disabledDays[i]){
-                   this.markInvalid(this.disabledDaysText);
-                    return false;
-               }
-            }
+        if (node.attributes && node.attributes.length > 0) {
+            return true; // walk if there are any.
         }
-        */
-        var fvalue = this.formatDate(value);
-        /*if(this.ddMatch && this.ddMatch.test(fvalue)){
-            this.markInvalid(String.format(this.disabledDatesText, fvalue));
-            return false;
-        }
-        */
-        return true;
-    },
+        Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
+        return false;
+     
+    }
+    
+});/**
+ * @class Roo.htmleditor.FilterTableWidth
+  try and remove table width data - as that frequently messes up other stuff.
+ * 
+ *      was cleanTableWidths.
+ *
+ * Quite often pasting from word etc.. results in tables with column and widths.
+ * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
+ *
+ * @constructor
+ * Run a new Table Filter
+ * @param {Object} config Configuration options
+ */
 
-    // private
-    // Provides logic to override the default TriggerField.validateBlur which just returns true
-    validateBlur : function(){
-        return !this.menu || !this.menu.isVisible();
-    },
+Roo.htmleditor.FilterTableWidth = function(cfg)
+{
+    // no need to apply config.
+    this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
+    this.walk(cfg.node);
+}
 
-    /**
-     * Returns the current date value of the date field.
-     * @return {Date} The date value
-     */
-    getValue : function(){
+Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
+{
+     
+     
+    
+    replaceTag: function(node) {
         
         
+      
+        if (node.hasAttribute('width')) {
+            node.removeAttribute('width');
+        }
         
-        return  this.hiddenField ?
-                this.hiddenField.value :
-                this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
-    },
-
-    /**
-     * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
-     * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
-     * (the default format used is "m/d/y").
-     * <br />Usage:
-     * <pre><code>
-//All of these calls set the same date value (May 4, 2006)
-
-//Pass a date object:
-var dt = new Date('5/4/06');
-monthField.setValue(dt);
+         
+        if (node.hasAttribute("style")) {
+            // pretty basic...
+            
+            var styles = node.getAttribute("style").split(";");
+            var nstyle = [];
+            Roo.each(styles, function(s) {
+                if (!s.match(/:/)) {
+                    return;
+                }
+                var kv = s.split(":");
+                if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
+                    return;
+                }
+                // what ever is left... we allow.
+                nstyle.push(s);
+            });
+            node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
+            if (!nstyle.length) {
+                node.removeAttribute('style');
+            }
+        }
+        
+        return true; // continue doing children..
+    }
+});/**
+ * @class Roo.htmleditor.FilterWord
+ * try and clean up all the mess that Word generates.
+ * 
+ * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
+ * @constructor
+ * Run a new Span Filter
+ * @param {Object} config Configuration options
+ */
 
-//Pass a date string (default format):
-monthField.setValue('5/4/06');
+Roo.htmleditor.FilterWord = function(cfg)
+{
+    // no need to apply config.
+    this.replaceDocBullets(cfg.node);
+    
+    // this is disabled as the removal is done by other filters;
+   // this.walk(cfg.node);
+    
+    
+}
 
-//Pass a date string (custom format):
-monthField.format = 'Y-m-d';
-monthField.setValue('2006-5-4');
-</code></pre>
-     * @param {String/Date} date The date or valid date string
+Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
+{
+    tag: true,
+     
+    
+    /**
+     * Clean up MS wordisms...
      */
-    setValue : function(date){
-        Roo.log('month setValue' + date);
-        // can only be first of month..
+    replaceTag : function(node)
+    {
+         
+        // no idea what this does - span with text, replaceds with just text.
+        if(
+                node.nodeName == 'SPAN' &&
+                !node.hasAttributes() &&
+                node.childNodes.length == 1 &&
+                node.firstChild.nodeName == "#text"  
+        ) {
+            var textNode = node.firstChild;
+            node.removeChild(textNode);
+            if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
+                node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
+            }
+            node.parentNode.insertBefore(textNode, node);
+            if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
+                node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
+            }
+            
+            node.parentNode.removeChild(node);
+            return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
+        }
         
-        var val = this.parseDate(date);
+   
         
-        if (this.hiddenField) {
-            this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
+        if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
+            node.parentNode.removeChild(node);
+            return false; // dont do chidlren
         }
-        Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
-        this.value = this.parseDate(date);
-    },
-
-    // private
-    parseDate : function(value){
-        if(!value || value instanceof Date){
-            value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
-            return value;
+        //Roo.log(node.tagName);
+        // remove - but keep children..
+        if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
+            //Roo.log('-- removed');
+            while (node.childNodes.length) {
+                var cn = node.childNodes[0];
+                node.removeChild(cn);
+                node.parentNode.insertBefore(cn, node);
+                // move node to parent - and clean it..
+                if (cn.nodeType == 1) {
+                    this.replaceTag(cn);
+                }
+                
+            }
+            node.parentNode.removeChild(node);
+            /// no need to iterate chidlren = it's got none..
+            //this.iterateChildren(node, this.cleanWord);
+            return false; // no need to iterate children.
         }
-        var v = Date.parseDate(value, this.format);
-        if (!v && this.useIso) {
-            v = Date.parseDate(value, 'Y-m-d');
+        // clean styles
+        if (node.className.length) {
+            
+            var cn = node.className.split(/\W+/);
+            var cna = [];
+            Roo.each(cn, function(cls) {
+                if (cls.match(/Mso[a-zA-Z]+/)) {
+                    return;
+                }
+                cna.push(cls);
+            });
+            node.className = cna.length ? cna.join(' ') : '';
+            if (!cna.length) {
+                node.removeAttribute("class");
+            }
         }
-        if (v) {
-            // 
-            v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
+        
+        if (node.hasAttribute("lang")) {
+            node.removeAttribute("lang");
+        }
+        
+        if (node.hasAttribute("style")) {
+            
+            var styles = node.getAttribute("style").split(";");
+            var nstyle = [];
+            Roo.each(styles, function(s) {
+                if (!s.match(/:/)) {
+                    return;
+                }
+                var kv = s.split(":");
+                if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
+                    return;
+                }
+                // what ever is left... we allow.
+                nstyle.push(s);
+            });
+            node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
+            if (!nstyle.length) {
+                node.removeAttribute('style');
+            }
         }
+        return true; // do children
         
         
-        if(!v && this.altFormats){
-            if(!this.altFormatsArray){
-                this.altFormatsArray = this.altFormats.split("|");
+        
+    },
+    
+    styleToObject: function(node)
+    {
+        var styles = (node.getAttribute("style") || '').split(";");
+        var ret = {};
+        Roo.each(styles, function(s) {
+            if (!s.match(/:/)) {
+                return;
             }
-            for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
-                v = Date.parseDate(value, this.altFormatsArray[i]);
+            var kv = s.split(":");
+             
+            // what ever is left... we allow.
+            ret[kv[0].trim()] = kv[1];
+        });
+        return ret;
+    },
+     
+    replaceDocBullets : function(doc)
+    {
+        // this is a bit odd - but it appears some indents use ql-indent-1
+        //Roo.log(doc.innerHTML);
+        
+        var listpara = doc.getElementsByClassName('MsoListParagraphCxSpFirst');
+        for( var i = 0; i < listpara.length; i ++) {
+            listpara.item(i).className = "MsoListParagraph";
+        }
+        // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
+        var htwo = doc.getElementsByTagName('h2');
+        for( var i = 0; i < htwo.length; i ++) {
+            if (htwo.item(i).hasAttribute('style') && htwo.item(i).getAttribute('style').match(/mso-list:/)) {
+                htwo.item(i).className = "MsoListParagraph";
             }
         }
-        return v;
-    },
-
-    // private
-    formatDate : function(date, fmt){
-        return (!date || !(date instanceof Date)) ?
-               date : date.dateFormat(fmt || this.format);
-    },
-
-    // private
-    menuListeners : {
-        select: function(m, d){
-            this.setValue(d);
-            this.fireEvent('select', this, d);
-        },
-        show : function(){ // retain focus styling
-            this.onFocus();
-        },
-        hide : function(){
-            this.focus.defer(10, this);
-            var ml = this.menuListeners;
-            this.menu.un("select", ml.select,  this);
-            this.menu.un("show", ml.show,  this);
-            this.menu.un("hide", ml.hide,  this);
+        listpara = doc.getElementsByClassName('MsoNormal');
+        while(listpara.length) {
+            if (listpara.item(0).hasAttribute('style') && listpara.item(0).getAttribute('style').match(/mso-list:/)) {
+                listpara.item(0).className = "MsoListParagraph";
+            } else {
+                listpara.item(0).className = "MsoNormalx";
+            }
         }
-    },
-    // private
-    // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
-    onTriggerClick : function(){
-        if(this.disabled){
-            return;
+        listpara = doc.getElementsByClassName('ql-indent-1');
+        while(listpara.length) {
+            this.replaceDocBullet(listpara.item(0));
         }
-        if(this.menu == null){
-            this.menu = new Roo.menu.DateMenu();
-           
+        listpara = doc.getElementsByClassName('MsoListParagraph');
+        while(listpara.length) {
+            
+            this.replaceDocBullet(listpara.item(0));
         }
-        
-        Roo.apply(this.menu.picker,  {
+      
+    },
+    
+     
+    
+    replaceDocBullet : function(p)
+    {
+        // gather all the siblings.
+        var ns = p,
+            parent = p.parentNode,
+            doc = parent.ownerDocument,
+            items = [];
             
-            showClear: this.allowBlank,
-            minDate : this.minValue,
-            maxDate : this.maxValue,
-            disabledDatesRE : this.ddMatch,
-            disabledDatesText : this.disabledDatesText,
             
-            format : this.useIso ? 'Y-m-d' : this.format,
-            minText : String.format(this.minText, this.formatDate(this.minValue)),
-            maxText : String.format(this.maxText, this.formatDate(this.maxValue))
+        while (ns) {
+            if (ns.nodeType != 1) {
+                ns = ns.nextSibling;
+                continue;
+            }
+            if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
+                break;
+            }
+            if (ns.getAttribute('style').match(/mso-list/)) {
+                items.push(ns);
+                ns = ns.nextSibling;
+                has_list = true;
+                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].getAttribute('style').match(/mso-list/)) {
+                    has_list = true;
+                    break;
+                }
+            }
+            if (!has_list) {
+                break;
+            }
+            items.push(ns);
+            ns = ns.nextSibling;
             
-        });
-         this.menu.on(Roo.apply({}, this.menuListeners, {
-            scope:this
-        }));
-       
+            
+        }
+        if (!items.length) {
+            ns.className = "";
+            return;
+        }
         
-        var m = this.menu;
-        var p = m.picker;
+        var ul = parent.ownerDocument.createElement('ul'); // what about number lists...
+        parent.insertBefore(ul, p);
+        var lvl = 0;
+        var stack = [ ul ];
+        var last_li = false;
         
-        // hide month picker get's called when we called by 'before hide';
+        var margin_to_depth = {};
+        max_margins = -1;
         
-        var ignorehide = true;
-        p.hideMonthPicker  = function(disableAnim){
-            if (ignorehide) {
+        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 style = {};
+            for(var i = 0; i < spans.length; i++) {
+            
+                style = this.styleToObject(spans[i]);
+                if (typeof(style['mso-list']) == 'undefined') {
+                    continue;
+                }
+                
+                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;
             }
-             if(this.monthPicker){
-                Roo.log("hideMonthPicker called");
-                if(disableAnim === true){
-                    this.monthPicker.hide();
-                }else{
-                    this.monthPicker.slideOut('t', {duration:.2});
-                    p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
-                    p.fireEvent("select", this, this.value);
-                    m.hide();
+            
+            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('ul'); // what about number lists...
+                if (!last_li) {
+                    last_li = doc.createElement('li');
+                    stack[lvl].appendChild(last_li);
                 }
+                last_li.appendChild(nul);
+                stack[nlvl] = nul;
+                
             }
-        }
-        
-        Roo.log('picker set value');
-        Roo.log(this.getValue());
-        p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
-        m.show(this.el, 'tl-bl?');
-        ignorehide  = false;
-        // this will trigger hideMonthPicker..
-        
-        
-        // hidden the day picker
-        Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
-        
+            lvl = nlvl;
+            
+            var nli = stack[nlvl].appendChild(doc.createElement('li'));
+            last_li = nli;
+            nli.innerHTML = n.innerHTML;
+            //Roo.log("innerHTML = " + n.innerHTML);
+            parent.removeChild(n);
+            
+             
+             
+            
+        },this);
         
         
-      
         
-        p.showMonthPicker.defer(100, p);
-    
         
-       
-    },
-
-    beforeBlur : function(){
-        var v = this.parseDate(this.getRawValue());
-        if(v){
-            this.setValue(v);
-        }
     }
-
-    /** @cfg {Boolean} grow @hide */
-    /** @cfg {Number} growMin @hide */
-    /** @cfg {Number} growMax @hide */
-    /**
-     * @hide
-     * @method autoSize
-     */
-});/*
- * 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.form.ComboBox
- * @extends Roo.form.TriggerField
- * A combobox control with support for autocomplete, remote-loading, paging and many other features.
+ * @class Roo.htmleditor.FilterStyleToTag
+ * part of the word stuff... - certain 'styles' should be converted to tags.
+ * eg.
+ *   font-weight: bold -> bold
+ *   ?? super / subscrit etc..
+ * 
  * @constructor
- * Create a new ComboBox.
- * @param {Object} config Configuration options
+* Run a new style to tag filter.
+* @param {Object} config Configuration options
  */
-Roo.form.ComboBox = function(config){
-    Roo.form.ComboBox.superclass.constructor.call(this, config);
-    this.addEvents({
-        /**
-         * @event expand
-         * Fires when the dropdown list is expanded
-            * @param {Roo.form.ComboBox} combo This combo box
-            */
-        'expand' : true,
-        /**
-         * @event collapse
-         * Fires when the dropdown list is collapsed
-            * @param {Roo.form.ComboBox} combo This combo box
-            */
-        'collapse' : true,
-        /**
-         * @event beforeselect
-         * Fires before a list item is selected. Return false to cancel the selection.
-            * @param {Roo.form.ComboBox} combo This combo box
-            * @param {Roo.data.Record} record The data record returned from the underlying store
-            * @param {Number} index The index of the selected item in the dropdown list
-            */
-        'beforeselect' : true,
-        /**
-         * @event select
-         * Fires when a list item is selected
-            * @param {Roo.form.ComboBox} combo This combo box
-            * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
-            * @param {Number} index The index of the selected item in the dropdown list
-            */
-        'select' : true,
-        /**
-         * @event beforequery
-         * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
-         * The event object passed has these properties:
-            * @param {Roo.form.ComboBox} combo This combo box
-            * @param {String} query The query
-            * @param {Boolean} forceAll true to force "all" query
-            * @param {Boolean} cancel true to cancel the query
-            * @param {Object} e The query event object
-            */
-        'beforequery': true,
-         /**
-         * @event add
-         * Fires when the 'add' icon is pressed (add a listener to enable add button)
-            * @param {Roo.form.ComboBox} combo This combo box
-            */
-        'add' : true,
-        /**
-         * @event edit
-         * Fires when the 'edit' icon is pressed (add a listener to enable add button)
-            * @param {Roo.form.ComboBox} combo This combo box
-            * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
-            */
-        'edit' : true
+Roo.htmleditor.FilterStyleToTag = function(cfg)
+{
+    
+    this.tags = {
+        B  : [ 'fontWeight' , 'bold'],
+        I :  [ 'fontStyle' , 'italic'],
+        //pre :  [ 'font-style' , 'italic'],
+        // h1.. h6 ?? font-size?
+        SUP : [ 'verticalAlign' , 'super' ],
+        SUB : [ 'verticalAlign' , 'sub' ]
         
         
-    });
-    if(this.transform){
-        this.allowDomMove = false;
-        var s = Roo.getDom(this.transform);
-        if(!this.hiddenName){
-            this.hiddenName = s.name;
-        }
-        if(!this.store){
-            this.mode = 'local';
-            var d = [], opts = s.options;
-            for(var i = 0, len = opts.length;i < len; i++){
-                var o = opts[i];
-                var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
-                if(o.selected) {
-                    this.value = value;
-                }
-                d.push([value, o.text]);
-            }
-            this.store = new Roo.data.SimpleStore({
-                'id': 0,
-                fields: ['value', 'text'],
-                data : d
-            });
-            this.valueField = 'value';
-            this.displayField = 'text';
-        }
-        s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
-        if(!this.lazyRender){
-            this.target = true;
-            this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
-            s.parentNode.removeChild(s); // remove it
-            this.render(this.el.parentNode);
-        }else{
-            s.parentNode.removeChild(s); // remove it
-        }
-
-    }
-    if (this.store) {
-        this.store = Roo.factory(this.store, Roo.data);
-    }
+    };
     
-    this.selectedIndex = -1;
-    if(this.mode == 'local'){
-        if(config.queryDelay === undefined){
-            this.queryDelay = 10;
-        }
-        if(config.minChars === undefined){
-            this.minChars = 0;
-        }
-    }
-};
-
-Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
-    /**
-     * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
-     */
-    /**
-     * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
-     * rendering into an Roo.Editor, defaults to false)
-     */
-    /**
-     * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
-     * {tag: "input", type: "text", size: "24", autocomplete: "off"})
-     */
-    /**
-     * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
-     */
-    /**
-     * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
-     * the dropdown list (defaults to undefined, with no header element)
-     */
-
-     /**
-     * @cfg {String/Roo.Template} tpl The template to use to render the output
-     */
+    Roo.apply(this, cfg);
      
-    // private
-    defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
-    /**
-     * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
-     */
-    listWidth: undefined,
-    /**
-     * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
-     * mode = 'remote' or 'text' if mode = 'local')
-     */
-    displayField: undefined,
-    /**
-     * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
-     * mode = 'remote' or 'value' if mode = 'local'). 
-     * Note: use of a valueField requires the user make a selection
-     * in order for a value to be mapped.
-     */
-    valueField: undefined,
     
+    this.walk(cfg.node);
     
-    /**
-     * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
-     * field's data value (defaults to the underlying DOM element's name)
-     */
-    hiddenName: undefined,
-    /**
-     * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
-     */
-    listClass: '',
-    /**
-     * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
-     */
-    selectedClass: 'x-combo-selected',
-    /**
-     * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
-     * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
-     * which displays a downward arrow icon).
-     */
-    triggerClass : 'x-form-arrow-trigger',
-    /**
-     * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
-     */
-    shadow:'sides',
-    /**
-     * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
-     * anchor positions (defaults to 'tl-bl')
-     */
-    listAlign: 'tl-bl?',
-    /**
-     * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
-     */
-    maxHeight: 300,
-    /**
-     * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
-     * query specified by the allQuery config option (defaults to 'query')
-     */
-    triggerAction: 'query',
-    /**
-     * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
-     * (defaults to 4, does not apply if editable = false)
-     */
-    minChars : 4,
-    /**
-     * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
-     * delay (typeAheadDelay) if it matches a known value (defaults to false)
-     */
-    typeAhead: false,
-    /**
-     * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
-     * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
-     */
-    queryDelay: 500,
-    /**
-     * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
-     * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
-     */
-    pageSize: 0,
-    /**
-     * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
-     * when editable = true (defaults to false)
-     */
-    selectOnFocus:false,
-    /**
-     * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
-     */
-    queryParam: 'query',
-    /**
-     * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
-     * when mode = 'remote' (defaults to 'Loading...')
-     */
-    loadingText: 'Loading...',
-    /**
-     * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
-     */
-    resizable: false,
-    /**
-     * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
-     */
-    handleHeight : 8,
-    /**
-     * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
-     * traditional select (defaults to true)
-     */
-    editable: true,
-    /**
-     * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
-     */
-    allQuery: '',
-    /**
-     * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
-     */
-    mode: 'remote',
-    /**
-     * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
-     * listWidth has a higher value)
-     */
-    minListWidth : 70,
-    /**
-     * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
-     * allow the user to set arbitrary text into the field (defaults to false)
-     */
-    forceSelection:false,
-    /**
-     * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
-     * if typeAhead = true (defaults to 250)
-     */
-    typeAheadDelay : 250,
-    /**
-     * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
-     * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
-     */
-    valueNotFoundText : undefined,
-    /**
-     * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
-     */
-    blockFocus : false,
     
-    /**
-     * @cfg {Boolean} disableClear Disable showing of clear button.
-     */
-    disableClear : false,
-    /**
-     * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
-     */
-    alwaysQuery : false,
     
-    //private
-    addicon : false,
-    editicon: false,
+}
+
+
+Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
+{
+    tag: true, // all tags
     
-    // element that contains real text value.. (when hidden is used..)
-     
-    // private
-    onRender : function(ct, position)
+    tags : false,
+    
+    
+    replaceTag : function(node)
     {
-        Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
         
-       if(this.hiddenName){
-            this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
-                    'before', true);
-            this.hiddenField.value =
-                this.hiddenValue !== undefined ? this.hiddenValue :
-                this.value !== undefined ? this.value : '';
-
-            // prevent input submission
-            this.el.dom.removeAttribute('name');
-             
-             
+        
+        if (node.getAttribute("style") === null) {
+            return true;
         }
-       
-        if(Roo.isGecko){
-            this.el.dom.setAttribute('autocomplete', 'off');
+        var inject = [];
+        for (var k in this.tags) {
+            if (node.style[this.tags[k][0]] == this.tags[k][1]) {
+                inject.push(k);
+                node.style.removeProperty(this.tags[k][0]);
+            }
         }
-
-        var cls = 'x-combo-list';
-
-        this.list = new Roo.Layer({
-            shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
+        if (!inject.length) {
+            return true; 
+        }
+        var cn = Array.from(node.childNodes);
+        var nn = node;
+        Roo.each(inject, function(t) {
+            var nc = node.ownerDocument.createElement(t);
+            nn.appendChild(nc);
+            nn = nc;
         });
+        for(var i = 0;i < cn.length;cn++) {
+            node.removeChild(cn[i]);
+            nn.appendChild(cn[i]);
+        }
+        return true /// iterate thru
+    }
+    
+})/**
+ * @class Roo.htmleditor.FilterLongBr
+ * BR/BR/BR - keep a maximum of 2...
+ * @constructor
+ * Run a new Long BR Filter
+ * @param {Object} config Configuration options
+ */
 
-        var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
-        this.list.setWidth(lw);
-        this.list.swallowEvent('mousewheel');
-        this.assetHeight = 0;
+Roo.htmleditor.FilterLongBr = function(cfg)
+{
+    // no need to apply config.
+    this.walk(cfg.node);
+}
 
-        if(this.title){
-            this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
-            this.assetHeight += this.header.getHeight();
+Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
+{
+    
+     
+    tag : 'BR',
+    
+     
+    replaceTag : function(node)
+    {
+        
+        var ps = node.nextSibling;
+        while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
+            ps = ps.nextSibling;
         }
-
-        this.innerList = this.list.createChild({cls:cls+'-inner'});
-        this.innerList.on('mouseover', this.onViewOver, this);
-        this.innerList.on('mousemove', this.onViewMove, this);
-        this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
         
-        if(this.allowBlank && !this.pageSize && !this.disableClear){
-            this.footer = this.list.createChild({cls:cls+'-ft'});
-            this.pageTb = new Roo.Toolbar(this.footer);
-           
+        if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
+            node.parentNode.removeChild(node); // remove last BR inside one fo these tags
+            return false;
         }
-        if(this.pageSize){
-            this.footer = this.list.createChild({cls:cls+'-ft'});
-            this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
-                    {pageSize: this.pageSize});
-            
+        
+        if (!ps || ps.nodeType != 1) {
+            return false;
         }
         
-        if (this.pageTb && this.allowBlank && !this.disableClear) {
-            var _this = this;
-            this.pageTb.add(new Roo.Toolbar.Fill(), {
-                cls: 'x-btn-icon x-btn-clear',
-                text: '&#160;',
-                handler: function()
-                {
-                    _this.collapse();
-                    _this.clearValue();
-                    _this.onSelect(false, -1);
-                }
-            });
+        if (!ps || ps.tagName != 'BR') {
+           
+            return false;
         }
-        if (this.footer) {
-            this.assetHeight += this.footer.getHeight();
+        
+        
+        
+        
+        
+        if (!node.previousSibling) {
+            return false;
         }
+        var ps = node.previousSibling;
         
-
-        if(!this.tpl){
-            this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
+        while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
+            ps = ps.previousSibling;
         }
-
-        this.view = new Roo.View(this.innerList, this.tpl, {
-            singleSelect:true,
-           store: this.store,
-           selectedClass: this.selectedClass
-        });
-
-        this.view.on('click', this.onViewClick, this);
-
-        this.store.on('beforeload', this.onBeforeLoad, this);
-        this.store.on('load', this.onLoad, this);
-        this.store.on('loadexception', this.onLoadException, this);
-
-        if(this.resizable){
-            this.resizer = new Roo.Resizable(this.list,  {
-               pinned:true, handles:'se'
-            });
-            this.resizer.on('resize', function(r, w, h){
-                this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
-                this.listWidth = w;
-                this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
-                this.restrictHeight();
-            }, this);
-            this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
+        if (!ps || ps.nodeType != 1) {
+            return false;
+        }
+        // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
+        if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
+            return false;
         }
-        if(!this.editable){
-            this.editable = true;
-            this.setEditable(false);
-        }  
         
+        node.parentNode.removeChild(node); // remove me...
         
-        if (typeof(this.events.add.listeners) != 'undefined') {
-            
-            this.addicon = this.wrap.createChild(
-                {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
-       
-            this.addicon.on('click', function(e) {
-                this.fireEvent('add', this);
-            }, this);
-        }
-        if (typeof(this.events.edit.listeners) != 'undefined') {
-            
-            this.editicon = this.wrap.createChild(
-                {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
-            if (this.addicon) {
-                this.editicon.setStyle('margin-left', '40px');
-            }
-            this.editicon.on('click', function(e) {
-                
-                // we fire even  if inothing is selected..
-                this.fireEvent('edit', this, this.lastData );
-                
-            }, this);
+        return false; // no need to do children
+
+    }
+    
+}); 
+
+/**
+ * @class Roo.htmleditor.FilterBlock
+ * removes id / data-block and contenteditable that are associated with blocks
+ * usage should be done on a cloned copy of the dom
+ * @constructor
+* Run a new Attribute Filter { node : xxxx }}
+* @param {Object} config Configuration options
+ */
+Roo.htmleditor.FilterBlock = function(cfg)
+{
+    Roo.apply(this, cfg);
+    var qa = cfg.node.querySelectorAll;
+    this.removeAttributes('data-block');
+    this.removeAttributes('contenteditable');
+    this.removeAttributes('id');
+    
+}
+
+Roo.apply(Roo.htmleditor.FilterBlock.prototype,
+{
+    node: true, // all tags
+     
+     
+    removeAttributes : function(attr)
+    {
+        var ar = this.node.querySelectorAll('*[' + attr + ']');
+        for (var i =0;i<ar.length;i++) {
+            ar[i].removeAttribute(attr);
         }
+    }
         
         
         
-    },
+    
+});
+/***
+ * This is based loosely on tinymce 
+ * @class Roo.htmleditor.TidySerializer
+ * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
+ * @constructor
+ * @method Serializer
+ * @param {Object} settings Name/value settings object.
+ */
 
-    // private
-    initEvents : function(){
-        Roo.form.ComboBox.superclass.initEvents.call(this);
 
-        this.keyNav = new Roo.KeyNav(this.el, {
-            "up" : function(e){
-                this.inKeyMode = true;
-                this.selectPrev();
-            },
+Roo.htmleditor.TidySerializer = function(settings)
+{
+    Roo.apply(this, settings);
+    
+    this.writer = new Roo.htmleditor.TidyWriter(settings);
+    
+    
 
-            "down" : function(e){
-                if(!this.isExpanded()){
-                    this.onTriggerClick();
-                }else{
-                    this.inKeyMode = true;
-                    this.selectNext();
-                }
+};
+Roo.htmleditor.TidySerializer.prototype = {
+    
+    /**
+     * @param {boolean} inner do the inner of the node.
+     */
+    inner : false,
+    
+    writer : false,
+    
+    /**
+    * Serializes the specified node into a string.
+    *
+    * @example
+    * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
+    * @method serialize
+    * @param {DomElement} node Node instance to serialize.
+    * @return {String} String with HTML based on DOM tree.
+    */
+    serialize : function(node) {
+        
+        // = settings.validate;
+        var writer = this.writer;
+        var self  = this;
+        this.handlers = {
+            // #text
+            3: function(node) {
+                
+                writer.text(node.nodeValue, node);
             },
-
-            "enter" : function(e){
-                this.onViewClick();
-                //return true;
+            // #comment
+            8: function(node) {
+                writer.comment(node.nodeValue);
             },
-
-            "esc" : function(e){
-                this.collapse();
+            // Processing instruction
+            7: function(node) {
+                writer.pi(node.name, node.nodeValue);
             },
-
-            "tab" : function(e){
-                this.onViewClick(false);
-                this.fireEvent("specialkey", this, e);
-                return true;
+            // Doctype
+            10: function(node) {
+                writer.doctype(node.nodeValue);
             },
-
-            scope : this,
-
-            doRelay : function(foo, bar, hname){
-                if(hname == 'down' || this.scope.isExpanded()){
-                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
-                }
-                return true;
+            // CDATA
+            4: function(node) {
+                writer.cdata(node.nodeValue);
             },
-
-            forceKeyDown: true
-        });
-        this.queryDelay = Math.max(this.queryDelay || 10,
-                this.mode == 'local' ? 10 : 250);
-        this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
-        if(this.typeAhead){
-            this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
-        }
-        if(this.editable !== false){
-            this.el.on("keyup", this.onKeyUp, this);
-        }
-        if(this.forceSelection){
-            this.on('blur', this.doForce, this);
-        }
+            // Document fragment
+            11: function(node) {
+                node = node.firstChild;
+                if (!node) {
+                    return;
+                }
+                while(node) {
+                    self.walk(node);
+                    node = node.nextSibling
+                }
+            }
+        };
+        writer.reset();
+        1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
+        return writer.getContent();
     },
 
-    onDestroy : function(){
-        if(this.view){
-            this.view.setStore(null);
-            this.view.el.removeAllListeners();
-            this.view.el.remove();
-            this.view.purgeListeners();
+    walk: function(node)
+    {
+        var attrName, attrValue, sortedAttrs, i, l, elementRule,
+            handler = this.handlers[node.nodeType];
+            
+        if (handler) {
+            handler(node);
+            return;
         }
-        if(this.list){
-            this.list.destroy();
+    
+        var name = node.nodeName;
+        var isEmpty = node.childNodes.length < 1;
+      
+        var writer = this.writer;
+        var attrs = node.attributes;
+        // Sort attributes
+        
+        writer.start(node.nodeName, attrs, isEmpty, node);
+        if (isEmpty) {
+            return;
         }
-        if(this.store){
-            this.store.un('beforeload', this.onBeforeLoad, this);
-            this.store.un('load', this.onLoad, this);
-            this.store.un('loadexception', this.onLoadException, this);
+        node = node.firstChild;
+        if (!node) {
+            writer.end(name);
+            return;
         }
-        Roo.form.ComboBox.superclass.onDestroy.call(this);
-    },
+        while (node) {
+            this.walk(node);
+            node = node.nextSibling;
+        }
+        writer.end(name);
+        
+    
+    }
+    // Serialize element and treat all non elements as fragments
+   
+}; 
 
-    // private
-    fireKey : function(e){
-        if(e.isNavKeyPress() && !this.list.isVisible()){
-            this.fireEvent("specialkey", this, e);
+/***
+ * This is based loosely on tinymce 
+ * @class Roo.htmleditor.TidyWriter
+ * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
+ *
+ * Known issues?
+ * - not tested much with 'PRE' formated elements.
+ * 
+ *
+ *
+ */
+
+Roo.htmleditor.TidyWriter = function(settings)
+{
+    
+    // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
+    Roo.apply(this, settings);
+    this.html = [];
+    this.state = [];
+     
+    this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
+  
+}
+Roo.htmleditor.TidyWriter.prototype = {
+
+    state : false,
+    
+    indent :  '  ',
+    
+    // part of state...
+    indentstr : '',
+    in_pre: false,
+    in_inline : false,
+    last_inline : false,
+    encode : false,
+     
+    
+            /**
+    * Writes the a start element such as <p id="a">.
+    *
+    * @method start
+    * @param {String} name Name of the element.
+    * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
+    * @param {Boolean} empty Optional empty state if the tag should end like <br />.
+    */
+    start: function(name, attrs, empty, node)
+    {
+        var i, l, attr, value;
+        
+        // there are some situations where adding line break && indentation will not work. will not work.
+        // <span / b / i ... formating?
+        
+        var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
+        var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
+        
+        var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
+        
+        var add_lb = name == 'BR' ? false : in_inline;
+        
+        if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
+            i_inline = false;
         }
-    },
 
-    // private
-    onResize: function(w, h){
-        Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
+        var indentstr =  this.indentstr;
         
-        if(typeof w != 'number'){
-            // we do not handle it!?!?
-            return;
+        // e_inline = elements that can be inline, but still allow \n before and after?
+        // only 'BR' ??? any others?
+        
+        // ADD LINE BEFORE tage
+        if (!this.in_pre) {
+            if (in_inline) {
+                //code
+                if (name == 'BR') {
+                    this.addLine();
+                } else if (this.lastElementEndsWS()) {
+                    this.addLine();
+                } else{
+                    // otherwise - no new line. (and dont indent.)
+                    indentstr = '';
+                }
+                
+            } else {
+                this.addLine();
+            }
+        } else {
+            indentstr = '';
         }
-        var tw = this.trigger.getWidth();
-        tw += this.addicon ? this.addicon.getWidth() : 0;
-        tw += this.editicon ? this.editicon.getWidth() : 0;
-        var x = w - tw;
-        this.el.setWidth( this.adjustWidth('input', x));
+        
+        this.html.push(indentstr + '<', name.toLowerCase());
+        
+        if (attrs) {
+            for (i = 0, l = attrs.length; i < l; i++) {
+                attr = attrs[i];
+                this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
+            }
+        }
+     
+        if (empty) {
+            if (is_short) {
+                this.html[this.html.length] = '/>';
+            } else {
+                this.html[this.html.length] = '></' + name.toLowerCase() + '>';
+            }
+            var e_inline = name == 'BR' ? false : this.in_inline;
             
-        this.trigger.setStyle('left', x+'px');
+            if (!e_inline && !this.in_pre) {
+                this.addLine();
+            }
+            return;
         
-        if(this.list && this.listWidth === undefined){
-            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
-            this.list.setWidth(lw);
-            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
+        }
+        // not empty..
+        this.html[this.html.length] = '>';
+        
+        // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
+        /*
+        if (!in_inline && !in_pre) {
+            var cn = node.firstChild;
+            while(cn) {
+                if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
+                    in_inline = true
+                    break;
+                }
+                cn = cn.nextSibling;
+            }
+             
+        }
+        */
+        
+        
+        this.pushState({
+            indentstr : in_pre   ? '' : (this.indentstr + this.indent),
+            in_pre : in_pre,
+            in_inline :  in_inline
+        });
+        // add a line after if we are not in a
+        
+        if (!in_inline && !in_pre) {
+            this.addLine();
         }
         
+            
+         
+        
+    },
     
+    lastElementEndsWS : function()
+    {
+        var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
+        if (value === false) {
+            return true;
+        }
+        return value.match(/\s+$/);
         
     },
-
+    
     /**
-     * Allow or prevent the user from directly editing the field text.  If false is passed,
-     * the user will only be able to select from the items defined in the dropdown list.  This method
-     * is the runtime equivalent of setting the 'editable' config option at config time.
-     * @param {Boolean} value True to allow the user to directly edit the field text
+     * Writes the a end element such as </p>.
+     *
+     * @method end
+     * @param {String} name Name of the element.
      */
-    setEditable : function(value){
-        if(value == this.editable){
-            return;
-        }
-        this.editable = value;
-        if(!value){
-            this.el.dom.setAttribute('readOnly', true);
-            this.el.on('mousedown', this.onTriggerClick,  this);
-            this.el.addClass('x-combo-noedit');
-        }else{
-            this.el.dom.setAttribute('readOnly', false);
-            this.el.un('mousedown', this.onTriggerClick,  this);
-            this.el.removeClass('x-combo-noedit');
+    end: function(name) {
+        var value;
+        this.popState();
+        var indentstr = '';
+        var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
+        
+        if (!this.in_pre && !in_inline) {
+            this.addLine();
+            indentstr  = this.indentstr;
         }
+        this.html.push(indentstr + '</', name.toLowerCase(), '>');
+        this.last_inline = in_inline;
+        
+        // pop the indent state..
     },
-
-    // private
-    onBeforeLoad : function(){
-        if(!this.hasFocus){
+    /**
+     * Writes a text node.
+     *
+     * In pre - we should not mess with the contents.
+     * 
+     *
+     * @method text
+     * @param {String} text String to write out.
+     * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
+     */
+    text: function(in_text, node)
+    {
+        // if not in whitespace critical
+        if (in_text.length < 1) {
             return;
         }
-        this.innerList.update(this.loadingText ?
-               '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
-        this.restrictHeight();
-        this.selectedIndex = -1;
-    },
-
-    // private
-    onLoad : function(){
-        if(!this.hasFocus){
-            return;
+        var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
+        
+        if (this.in_pre) {
+            this.html[this.html.length] =  text;
+            return;   
         }
-        if(this.store.getCount() > 0){
-            this.expand();
-            this.restrictHeight();
-            if(this.lastQuery == this.allQuery){
-                if(this.editable){
-                    this.el.dom.select();
+        
+        if (this.in_inline) {
+            text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
+            if (text != ' ') {
+                text = text.replace(/\s+/,' ');  // all white space to single white space
+                
+                    
+                // if next tag is '<BR>', then we can trim right..
+                if (node.nextSibling &&
+                    node.nextSibling.nodeType == 1 &&
+                    node.nextSibling.nodeName == 'BR' )
+                {
+                    text = text.replace(/\s+$/g,'');
                 }
-                if(!this.selectByValue(this.value, true)){
-                    this.select(0, true);
+                // if previous tag was a BR, we can also trim..
+                if (node.previousSibling &&
+                    node.previousSibling.nodeType == 1 &&
+                    node.previousSibling.nodeName == 'BR' )
+                {
+                    text = this.indentstr +  text.replace(/^\s+/g,'');
                 }
-            }else{
-                this.selectNext();
-                if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
-                    this.taTask.delay(this.typeAheadDelay);
+                if (text.match(/\n/)) {
+                    text = text.replace(
+                        /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
+                    );
+                    // remoeve the last whitespace / line break.
+                    text = text.replace(/\n\s+$/,'');
                 }
+                // repace long lines
+                
             }
-        }else{
-            this.onEmptyResults();
+             
+            this.html[this.html.length] =  text;
+            return;   
         }
-        //this.el.focus();
-    },
-    // private
-    onLoadException : function()
-    {
-        this.collapse();
-        Roo.log(this.store.reader.jsonData);
-        if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
-            Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
+        // see if previous element was a inline element.
+        var indentstr = this.indentstr;
+   
+        text = text.replace(/\s+/g," "); // all whitespace into single white space.
+        
+        // should trim left?
+        if (node.previousSibling &&
+            node.previousSibling.nodeType == 1 &&
+            Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
+        {
+            indentstr = '';
+            
+        } else {
+            this.addLine();
+            text = text.replace(/^\s+/,''); // trim left
+          
+        }
+        // should trim right?
+        if (node.nextSibling &&
+            node.nextSibling.nodeType == 1 &&
+            Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
+        {
+          // noop
+            
+        }  else {
+            text = text.replace(/\s+$/,''); // trim right
         }
+         
+              
         
         
-    },
-    // private
-    onTypeAhead : function(){
-        if(this.store.getCount() > 0){
-            var r = this.store.getAt(0);
-            var newValue = r.data[this.displayField];
-            var len = newValue.length;
-            var selStart = this.getRawValue().length;
-            if(selStart != len){
-                this.setRawValue(newValue);
-                this.selectText(selStart, newValue.length);
-            }
+        
+        if (text.length < 1) {
+            return;
         }
-    },
-
-    // private
-    onSelect : function(record, index){
-        if(this.fireEvent('beforeselect', this, record, index) !== false){
-            this.setFromData(index > -1 ? record.data : false);
-            this.collapse();
-            this.fireEvent('select', this, record, index);
+        if (!text.match(/\n/)) {
+            this.html.push(indentstr + text);
+            return;
         }
+        
+        text = this.indentstr + text.replace(
+            /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
+        );
+        // remoeve the last whitespace / line break.
+        text = text.replace(/\s+$/,''); 
+        
+        this.html.push(text);
+        
+        // split and indent..
+        
+        
     },
-
     /**
-     * Returns the currently selected field value or empty string if no value is set.
-     * @return {String} value The selected value
+     * Writes a cdata node such as <![CDATA[data]]>.
+     *
+     * @method cdata
+     * @param {String} text String to write out inside the cdata.
      */
-    getValue : function(){
-        if(this.valueField){
-            return typeof this.value != 'undefined' ? this.value : '';
-        }
-        return Roo.form.ComboBox.superclass.getValue.call(this);
+    cdata: function(text) {
+        this.html.push('<![CDATA[', text, ']]>');
     },
-
     /**
-     * Clears any text/value currently set in the field
-     */
-    clearValue : function(){
-        if(this.hiddenField){
-            this.hiddenField.value = '';
-        }
-        this.value = '';
-        this.setRawValue('');
-        this.lastSelectionText = '';
-        
-    },
-
+    * Writes a comment node such as <!-- Comment -->.
+    *
+    * @method cdata
+    * @param {String} text String to write out inside the comment.
+    */
+   comment: function(text) {
+       this.html.push('<!--', text, '-->');
+   },
     /**
-     * Sets the specified value into the field.  If the value finds a match, the corresponding record text
-     * will be displayed in the field.  If the value does not match the data value of an existing item,
-     * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
-     * Otherwise the field will be blank (although the value will still be set).
-     * @param {String} value The value to match
+     * Writes a PI node such as <?xml attr="value" ?>.
+     *
+     * @method pi
+     * @param {String} name Name of the pi.
+     * @param {String} text String to write out inside the pi.
      */
-    setValue : function(v){
-        var text = v;
-        if(this.valueField){
-            var r = this.findRecord(this.valueField, v);
-            if(r){
-                text = r.data[this.displayField];
-            }else if(this.valueNotFoundText !== undefined){
-                text = this.valueNotFoundText;
-            }
-        }
-        this.lastSelectionText = text;
-        if(this.hiddenField){
-            this.hiddenField.value = v;
-        }
-        Roo.form.ComboBox.superclass.setValue.call(this, text);
-        this.value = v;
+    pi: function(name, text) {
+        text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
+        this.indent != '' && this.html.push('\n');
     },
     /**
-     * @property {Object} the last set data for the element
-     */
-    
-    lastData : false,
-    /**
-     * Sets the value of the field based on a object which is related to the record format for the store.
-     * @param {Object} value the value to set as. or false on reset?
+     * Writes a doctype node such as <!DOCTYPE data>.
+     *
+     * @method doctype
+     * @param {String} text String to write out inside the doctype.
      */
-    setFromData : function(o){
-        var dv = ''; // display value
-        var vv = ''; // value value..
-        this.lastData = o;
-        if (this.displayField) {
-            dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
-        } else {
-            // this is an error condition!!!
-            Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
-        }
-        
-        if(this.valueField){
-            vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
-        }
-        if(this.hiddenField){
-            this.hiddenField.value = vv;
-            
-            this.lastSelectionText = dv;
-            Roo.form.ComboBox.superclass.setValue.call(this, dv);
-            this.value = vv;
-            return;
-        }
-        // no hidden field.. - we store the value in 'value', but still display
-        // display field!!!!
-        this.lastSelectionText = dv;
-        Roo.form.ComboBox.superclass.setValue.call(this, dv);
-        this.value = vv;
-        
-        
+    doctype: function(text) {
+        this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
     },
-    // private
-    reset : function(){
-        // overridden so that last data is reset..
-        this.setValue(this.resetValue);
-        this.originalValue = this.getValue();
-        this.clearInvalid();
-        this.lastData = false;
-        if (this.view) {
-            this.view.clearSelections();
-        }
+    /**
+     * Resets the internal buffer if one wants to reuse the writer.
+     *
+     * @method reset
+     */
+    reset: function() {
+        this.html.length = 0;
+        this.state = [];
+        this.pushState({
+            indentstr : '',
+            in_pre : false, 
+            in_inline : false
+        })
     },
-    // private
-    findRecord : function(prop, value){
-        var record;
-        if(this.store.getCount() > 0){
-            this.store.each(function(r){
-                if(r.data[prop] == value){
-                    record = r;
-                    return false;
-                }
-                return true;
-            });
-        }
-        return record;
+    /**
+     * Returns the contents that got serialized.
+     *
+     * @method getContent
+     * @return {String} HTML contents that got written down.
+     */
+    getContent: function() {
+        return this.html.join('').replace(/\n$/, '');
     },
     
-    getName: function()
+    pushState : function(cfg)
     {
-        // returns hidden if it's set..
-        if (!this.rendered) {return ''};
-        return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
-        
-    },
-    // private
-    onViewMove : function(e, t){
-        this.inKeyMode = false;
+        this.state.push(cfg);
+        Roo.apply(this, cfg);
     },
-
-    // private
-    onViewOver : function(e, t){
-        if(this.inKeyMode){ // prevent key nav and mouse over conflicts
-            return;
+    
+    popState : function()
+    {
+        if (this.state.length < 1) {
+            return; // nothing to push
         }
-        var item = this.view.findItemFromChild(t);
-        if(item){
-            var index = this.view.indexOf(item);
-            this.select(index, false);
+        var cfg = {
+            in_pre: false,
+            indentstr : ''
+        };
+        this.state.pop();
+        if (this.state.length > 0) {
+            cfg = this.state[this.state.length-1]; 
         }
+        Roo.apply(this, cfg);
     },
-
-    // private
-    onViewClick : function(doFocus)
+    
+    addLine: function()
     {
-        var index = this.view.getSelectedIndexes()[0];
-        var r = this.store.getAt(index);
-        if(r){
-            this.onSelect(r, index);
+        if (this.html.length < 1) {
+            return;
         }
-        if(doFocus !== false && !this.blockFocus){
-            this.el.focus();
+        
+        
+        var value = this.html[this.html.length - 1];
+        if (value.length > 0 && '\n' !== value) {
+            this.html.push('\n');
         }
-    },
+    }
+    
+    
+//'pre script noscript style textarea video audio iframe object code'
+// shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
+// inline 
+};
 
-    // private
-    restrictHeight : function(){
-        this.innerList.dom.style.height = '';
-        var inner = this.innerList.dom;
-        var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
-        this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
-        this.list.beginUpdate();
-        this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
-        this.list.alignTo(this.el, this.listAlign);
-        this.list.endUpdate();
-    },
+Roo.htmleditor.TidyWriter.inline_elements = [
+        'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
+        'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
+];
+Roo.htmleditor.TidyWriter.shortend_elements = [
+    'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
+    'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
+];
 
-    // private
-    onEmptyResults : function(){
-        this.collapse();
-    },
+Roo.htmleditor.TidyWriter.whitespace_elements = [
+    'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
+];/***
+ * This is based loosely on tinymce 
+ * @class Roo.htmleditor.TidyEntities
+ * @static
+ * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
+ *
+ * Not 100% sure this is actually used or needed.
+ */
 
+Roo.htmleditor.TidyEntities = {
+    
     /**
-     * Returns true if the dropdown list is expanded, else false.
+     * initialize data..
      */
-    isExpanded : function(){
-        return this.list.isVisible();
+    init : function (){
+     
+        this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
+       
     },
 
+
+    buildEntitiesLookup: function(items, radix) {
+        var i, chr, entity, lookup = {};
+        if (!items) {
+            return {};
+        }
+        items = typeof(items) == 'string' ? items.split(',') : items;
+        radix = radix || 10;
+        // Build entities lookup table
+        for (i = 0; i < items.length; i += 2) {
+            chr = String.fromCharCode(parseInt(items[i], radix));
+            // Only add non base entities
+            if (!this.baseEntities[chr]) {
+                entity = '&' + items[i + 1] + ';';
+                lookup[chr] = entity;
+                lookup[entity] = chr;
+            }
+        }
+        return lookup;
+        
+    },
+    
+    asciiMap : {
+            128: '€',
+            130: '‚',
+            131: 'ƒ',
+            132: '„',
+            133: '…',
+            134: '†',
+            135: '‡',
+            136: 'ˆ',
+            137: '‰',
+            138: 'Š',
+            139: '‹',
+            140: 'Œ',
+            142: 'Ž',
+            145: '‘',
+            146: '’',
+            147: '“',
+            148: '”',
+            149: '•',
+            150: '–',
+            151: '—',
+            152: '˜',
+            153: '™',
+            154: 'š',
+            155: '›',
+            156: 'œ',
+            158: 'ž',
+            159: 'Ÿ'
+    },
+    // Raw entities
+    baseEntities : {
+        '"': '&quot;',
+        // Needs to be escaped since the YUI compressor would otherwise break the code
+        '\'': '&#39;',
+        '<': '&lt;',
+        '>': '&gt;',
+        '&': '&amp;',
+        '`': '&#96;'
+    },
+    // Reverse lookup table for raw entities
+    reverseEntities : {
+        '&lt;': '<',
+        '&gt;': '>',
+        '&amp;': '&',
+        '&quot;': '"',
+        '&apos;': '\''
+    },
+    
+    attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
+    textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
+    rawCharsRegExp : /[<>&\"\']/g,
+    entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
+    namedEntities  : false,
+    namedEntitiesData : [ 
+        '50',
+        'nbsp',
+        '51',
+        'iexcl',
+        '52',
+        'cent',
+        '53',
+        'pound',
+        '54',
+        'curren',
+        '55',
+        'yen',
+        '56',
+        'brvbar',
+        '57',
+        'sect',
+        '58',
+        'uml',
+        '59',
+        'copy',
+        '5a',
+        'ordf',
+        '5b',
+        'laquo',
+        '5c',
+        'not',
+        '5d',
+        'shy',
+        '5e',
+        'reg',
+        '5f',
+        'macr',
+        '5g',
+        'deg',
+        '5h',
+        'plusmn',
+        '5i',
+        'sup2',
+        '5j',
+        'sup3',
+        '5k',
+        'acute',
+        '5l',
+        'micro',
+        '5m',
+        'para',
+        '5n',
+        'middot',
+        '5o',
+        'cedil',
+        '5p',
+        'sup1',
+        '5q',
+        'ordm',
+        '5r',
+        'raquo',
+        '5s',
+        'frac14',
+        '5t',
+        'frac12',
+        '5u',
+        'frac34',
+        '5v',
+        'iquest',
+        '60',
+        'Agrave',
+        '61',
+        'Aacute',
+        '62',
+        'Acirc',
+        '63',
+        'Atilde',
+        '64',
+        'Auml',
+        '65',
+        'Aring',
+        '66',
+        'AElig',
+        '67',
+        'Ccedil',
+        '68',
+        'Egrave',
+        '69',
+        'Eacute',
+        '6a',
+        'Ecirc',
+        '6b',
+        'Euml',
+        '6c',
+        'Igrave',
+        '6d',
+        'Iacute',
+        '6e',
+        'Icirc',
+        '6f',
+        'Iuml',
+        '6g',
+        'ETH',
+        '6h',
+        'Ntilde',
+        '6i',
+        'Ograve',
+        '6j',
+        'Oacute',
+        '6k',
+        'Ocirc',
+        '6l',
+        'Otilde',
+        '6m',
+        'Ouml',
+        '6n',
+        'times',
+        '6o',
+        'Oslash',
+        '6p',
+        'Ugrave',
+        '6q',
+        'Uacute',
+        '6r',
+        'Ucirc',
+        '6s',
+        'Uuml',
+        '6t',
+        'Yacute',
+        '6u',
+        'THORN',
+        '6v',
+        'szlig',
+        '70',
+        'agrave',
+        '71',
+        'aacute',
+        '72',
+        'acirc',
+        '73',
+        'atilde',
+        '74',
+        'auml',
+        '75',
+        'aring',
+        '76',
+        'aelig',
+        '77',
+        'ccedil',
+        '78',
+        'egrave',
+        '79',
+        'eacute',
+        '7a',
+        'ecirc',
+        '7b',
+        'euml',
+        '7c',
+        'igrave',
+        '7d',
+        'iacute',
+        '7e',
+        'icirc',
+        '7f',
+        'iuml',
+        '7g',
+        'eth',
+        '7h',
+        'ntilde',
+        '7i',
+        'ograve',
+        '7j',
+        'oacute',
+        '7k',
+        'ocirc',
+        '7l',
+        'otilde',
+        '7m',
+        'ouml',
+        '7n',
+        'divide',
+        '7o',
+        'oslash',
+        '7p',
+        'ugrave',
+        '7q',
+        'uacute',
+        '7r',
+        'ucirc',
+        '7s',
+        'uuml',
+        '7t',
+        'yacute',
+        '7u',
+        'thorn',
+        '7v',
+        'yuml',
+        'ci',
+        'fnof',
+        'sh',
+        'Alpha',
+        'si',
+        'Beta',
+        'sj',
+        'Gamma',
+        'sk',
+        'Delta',
+        'sl',
+        'Epsilon',
+        'sm',
+        'Zeta',
+        'sn',
+        'Eta',
+        'so',
+        'Theta',
+        'sp',
+        'Iota',
+        'sq',
+        'Kappa',
+        'sr',
+        'Lambda',
+        'ss',
+        'Mu',
+        'st',
+        'Nu',
+        'su',
+        'Xi',
+        'sv',
+        'Omicron',
+        't0',
+        'Pi',
+        't1',
+        'Rho',
+        't3',
+        'Sigma',
+        't4',
+        'Tau',
+        't5',
+        'Upsilon',
+        't6',
+        'Phi',
+        't7',
+        'Chi',
+        't8',
+        'Psi',
+        't9',
+        'Omega',
+        'th',
+        'alpha',
+        'ti',
+        'beta',
+        'tj',
+        'gamma',
+        'tk',
+        'delta',
+        'tl',
+        'epsilon',
+        'tm',
+        'zeta',
+        'tn',
+        'eta',
+        'to',
+        'theta',
+        'tp',
+        'iota',
+        'tq',
+        'kappa',
+        'tr',
+        'lambda',
+        'ts',
+        'mu',
+        'tt',
+        'nu',
+        'tu',
+        'xi',
+        'tv',
+        'omicron',
+        'u0',
+        'pi',
+        'u1',
+        'rho',
+        'u2',
+        'sigmaf',
+        'u3',
+        'sigma',
+        'u4',
+        'tau',
+        'u5',
+        'upsilon',
+        'u6',
+        'phi',
+        'u7',
+        'chi',
+        'u8',
+        'psi',
+        'u9',
+        'omega',
+        'uh',
+        'thetasym',
+        'ui',
+        'upsih',
+        'um',
+        'piv',
+        '812',
+        'bull',
+        '816',
+        'hellip',
+        '81i',
+        'prime',
+        '81j',
+        'Prime',
+        '81u',
+        'oline',
+        '824',
+        'frasl',
+        '88o',
+        'weierp',
+        '88h',
+        'image',
+        '88s',
+        'real',
+        '892',
+        'trade',
+        '89l',
+        'alefsym',
+        '8cg',
+        'larr',
+        '8ch',
+        'uarr',
+        '8ci',
+        'rarr',
+        '8cj',
+        'darr',
+        '8ck',
+        'harr',
+        '8dl',
+        'crarr',
+        '8eg',
+        'lArr',
+        '8eh',
+        'uArr',
+        '8ei',
+        'rArr',
+        '8ej',
+        'dArr',
+        '8ek',
+        'hArr',
+        '8g0',
+        'forall',
+        '8g2',
+        'part',
+        '8g3',
+        'exist',
+        '8g5',
+        'empty',
+        '8g7',
+        'nabla',
+        '8g8',
+        'isin',
+        '8g9',
+        'notin',
+        '8gb',
+        'ni',
+        '8gf',
+        'prod',
+        '8gh',
+        'sum',
+        '8gi',
+        'minus',
+        '8gn',
+        'lowast',
+        '8gq',
+        'radic',
+        '8gt',
+        'prop',
+        '8gu',
+        'infin',
+        '8h0',
+        'ang',
+        '8h7',
+        'and',
+        '8h8',
+        'or',
+        '8h9',
+        'cap',
+        '8ha',
+        'cup',
+        '8hb',
+        'int',
+        '8hk',
+        'there4',
+        '8hs',
+        'sim',
+        '8i5',
+        'cong',
+        '8i8',
+        'asymp',
+        '8j0',
+        'ne',
+        '8j1',
+        'equiv',
+        '8j4',
+        'le',
+        '8j5',
+        'ge',
+        '8k2',
+        'sub',
+        '8k3',
+        'sup',
+        '8k4',
+        'nsub',
+        '8k6',
+        'sube',
+        '8k7',
+        'supe',
+        '8kl',
+        'oplus',
+        '8kn',
+        'otimes',
+        '8l5',
+        'perp',
+        '8m5',
+        'sdot',
+        '8o8',
+        'lceil',
+        '8o9',
+        'rceil',
+        '8oa',
+        'lfloor',
+        '8ob',
+        'rfloor',
+        '8p9',
+        'lang',
+        '8pa',
+        'rang',
+        '9ea',
+        'loz',
+        '9j0',
+        'spades',
+        '9j3',
+        'clubs',
+        '9j5',
+        'hearts',
+        '9j6',
+        'diams',
+        'ai',
+        'OElig',
+        'aj',
+        'oelig',
+        'b0',
+        'Scaron',
+        'b1',
+        'scaron',
+        'bo',
+        'Yuml',
+        'm6',
+        'circ',
+        'ms',
+        'tilde',
+        '802',
+        'ensp',
+        '803',
+        'emsp',
+        '809',
+        'thinsp',
+        '80c',
+        'zwnj',
+        '80d',
+        'zwj',
+        '80e',
+        'lrm',
+        '80f',
+        'rlm',
+        '80j',
+        'ndash',
+        '80k',
+        'mdash',
+        '80o',
+        'lsquo',
+        '80p',
+        'rsquo',
+        '80q',
+        'sbquo',
+        '80s',
+        'ldquo',
+        '80t',
+        'rdquo',
+        '80u',
+        'bdquo',
+        '810',
+        'dagger',
+        '811',
+        'Dagger',
+        '81g',
+        'permil',
+        '81p',
+        'lsaquo',
+        '81q',
+        'rsaquo',
+        '85c',
+        'euro'
+    ],
+
+         
     /**
-     * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
-     * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
-     * @param {String} value The data value of the item to select
-     * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
-     * selected item if it is not currently in view (defaults to true)
-     * @return {Boolean} True if the value matched an item in the list, else false
+     * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
+     *
+     * @method encodeRaw
+     * @param {String} text Text to encode.
+     * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
+     * @return {String} Entity encoded text.
      */
-    selectByValue : function(v, scrollIntoView){
-        if(v !== undefined && v !== null){
-            var r = this.findRecord(this.valueField || this.displayField, v);
-            if(r){
-                this.select(this.store.indexOf(r), scrollIntoView);
-                return true;
-            }
-        }
-        return false;
+    encodeRaw: function(text, attr)
+    {
+        var t = this;
+        return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
+            return t.baseEntities[chr] || chr;
+        });
     },
-
     /**
-     * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
-     * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
-     * @param {Number} index The zero-based index of the list item to select
-     * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
-     * selected item if it is not currently in view (defaults to true)
+     * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
+     * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
+     * and is exposed as the DOMUtils.encode function.
+     *
+     * @method encodeAllRaw
+     * @param {String} text Text to encode.
+     * @return {String} Entity encoded text.
      */
-    select : function(index, scrollIntoView){
-        this.selectedIndex = index;
-        this.view.select(index);
-        if(scrollIntoView !== false){
-            var el = this.view.getNode(index);
-            if(el){
-                this.innerList.scrollChildIntoView(el, false);
-            }
-        }
-    },
-
-    // private
-    selectNext : function(){
-        var ct = this.store.getCount();
-        if(ct > 0){
-            if(this.selectedIndex == -1){
-                this.select(0);
-            }else if(this.selectedIndex < ct-1){
-                this.select(this.selectedIndex+1);
-            }
-        }
+    encodeAllRaw: function(text) {
+        var t = this;
+        return ('' + text).replace(this.rawCharsRegExp, function(chr) {
+            return t.baseEntities[chr] || chr;
+        });
     },
-
-    // private
-    selectPrev : function(){
-        var ct = this.store.getCount();
-        if(ct > 0){
-            if(this.selectedIndex == -1){
-                this.select(0);
-            }else if(this.selectedIndex != 0){
-                this.select(this.selectedIndex-1);
+    /**
+     * Encodes the specified string using numeric entities. The core entities will be
+     * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
+     *
+     * @method encodeNumeric
+     * @param {String} text Text to encode.
+     * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
+     * @return {String} Entity encoded text.
+     */
+    encodeNumeric: function(text, attr) {
+        var t = this;
+        return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
+            // Multi byte sequence convert it to a single entity
+            if (chr.length > 1) {
+                return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
             }
-        }
-    },
-
-    // private
-    onKeyUp : function(e){
-        if(this.editable !== false && !e.isSpecialKey()){
-            this.lastKey = e.getKey();
-            this.dqTask.delay(this.queryDelay);
-        }
-    },
-
-    // private
-    validateBlur : function(){
-        return !this.list || !this.list.isVisible();   
-    },
-
-    // private
-    initQuery : function(){
-        this.doQuery(this.getRawValue());
+            return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
+        });
     },
-
-    // private
-    doForce : function(){
-        if(this.el.dom.value.length > 0){
-            this.el.dom.value =
-                this.lastSelectionText === undefined ? '' : this.lastSelectionText;
-             
-        }
+    /**
+     * Encodes the specified string using named entities. The core entities will be encoded
+     * as named ones but all non lower ascii characters will be encoded into named entities.
+     *
+     * @method encodeNamed
+     * @param {String} text Text to encode.
+     * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
+     * @param {Object} entities Optional parameter with entities to use.
+     * @return {String} Entity encoded text.
+     */
+    encodeNamed: function(text, attr, entities) {
+        var t = this;
+        entities = entities || this.namedEntities;
+        return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
+            return t.baseEntities[chr] || entities[chr] || chr;
+        });
     },
-
     /**
-     * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
-     * query allowing the query action to be canceled if needed.
-     * @param {String} query The SQL query to execute
-     * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
-     * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
-     * saved in the current store (defaults to false)
+     * Returns an encode function based on the name(s) and it's optional entities.
+     *
+     * @method getEncodeFunc
+     * @param {String} name Comma separated list of encoders for example named,numeric.
+     * @param {String} entities Optional parameter with entities to use instead of the built in set.
+     * @return {function} Encode function to be used.
      */
-    doQuery : function(q, forceAll){
-        if(q === undefined || q === null){
-            q = '';
+    getEncodeFunc: function(name, entities) {
+        entities = this.buildEntitiesLookup(entities) || this.namedEntities;
+        var t = this;
+        function encodeNamedAndNumeric(text, attr) {
+            return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
+                return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
+            });
         }
-        var qe = {
-            query: q,
-            forceAll: forceAll,
-            combo: this,
-            cancel:false
-        };
-        if(this.fireEvent('beforequery', qe)===false || qe.cancel){
-            return false;
+
+        function encodeCustomNamed(text, attr) {
+            return t.encodeNamed(text, attr, entities);
         }
-        q = qe.query;
-        forceAll = qe.forceAll;
-        if(forceAll === true || (q.length >= this.minChars)){
-            if(this.lastQuery != q || this.alwaysQuery){
-                this.lastQuery = q;
-                if(this.mode == 'local'){
-                    this.selectedIndex = -1;
-                    if(forceAll){
-                        this.store.clearFilter();
-                    }else{
-                        this.store.filter(this.displayField, q);
-                    }
-                    this.onLoad();
-                }else{
-                    this.store.baseParams[this.queryParam] = q;
-                    this.store.load({
-                        params: this.getParams(q)
-                    });
-                    this.expand();
-                }
-            }else{
-                this.selectedIndex = -1;
-                this.onLoad();   
+        // Replace + with , to be compatible with previous TinyMCE versions
+        name = this.makeMap(name.replace(/\+/g, ','));
+        // Named and numeric encoder
+        if (name.named && name.numeric) {
+            return this.encodeNamedAndNumeric;
+        }
+        // Named encoder
+        if (name.named) {
+            // Custom names
+            if (entities) {
+                return encodeCustomNamed;
             }
+            return this.encodeNamed;
         }
-    },
-
-    // private
-    getParams : function(q){
-        var p = {};
-        //p[this.queryParam] = q;
-        if(this.pageSize){
-            p.start = 0;
-            p.limit = this.pageSize;
+        // Numeric
+        if (name.numeric) {
+            return this.encodeNumeric;
         }
-        return p;
+        // Raw encoder
+        return this.encodeRaw;
     },
-
     /**
-     * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
+     * Decodes the specified string, this will replace entities with raw UTF characters.
+     *
+     * @method decode
+     * @param {String} text Text to entity decode.
+     * @return {String} Entity decoded string.
      */
-    collapse : function(){
-        if(!this.isExpanded()){
-            return;
-        }
-        this.list.hide();
-        Roo.get(document).un('mousedown', this.collapseIf, this);
-        Roo.get(document).un('mousewheel', this.collapseIf, this);
-        if (!this.editable) {
-            Roo.get(document).un('keydown', this.listKeyPress, this);
-        }
-        this.fireEvent('collapse', this);
+    decode: function(text)
+    {
+        var  t = this;
+        return text.replace(this.entityRegExp, function(all, numeric) {
+            if (numeric) {
+                numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
+                // Support upper UTF
+                if (numeric > 65535) {
+                    numeric -= 65536;
+                    return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
+                }
+                return t.asciiMap[numeric] || String.fromCharCode(numeric);
+            }
+            return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
+        });
     },
-
-    // private
-    collapseIf : function(e){
-        if(!e.within(this.wrap) && !e.within(this.list)){
-            this.collapse();
-        }
+    nativeDecode : function (text) {
+        return text;
     },
+    makeMap : function (items, delim, map) {
+               var i;
+               items = items || [];
+               delim = delim || ',';
+               if (typeof items == "string") {
+                       items = items.split(delim);
+               }
+               map = map || {};
+               i = items.length;
+               while (i--) {
+                       map[items[i]] = {};
+               }
+               return map;
+       }
+};
+    
+    
+    
+Roo.htmleditor.TidyEntities.init();
+/**
+ * @class Roo.htmleditor.KeyEnter
+ * Handle Enter press..
+ * @cfg {Roo.HtmlEditorCore} core the editor.
+ * @constructor
+ * Create a new Filter.
+ * @param {Object} config Configuration options
+ */
 
-    /**
-     * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
-     */
-    expand : function(){
-        if(this.isExpanded() || !this.hasFocus){
-            return;
-        }
-        this.list.alignTo(this.el, this.listAlign);
-        this.list.show();
-        Roo.get(document).on('mousedown', this.collapseIf, this);
-        Roo.get(document).on('mousewheel', this.collapseIf, this);
-        if (!this.editable) {
-            Roo.get(document).on('keydown', this.listKeyPress, this);
-        }
-        
-        this.fireEvent('expand', this);
-    },
 
-    // private
-    // Implements the default empty TriggerField.onTriggerClick function
-    onTriggerClick : function(){
-        if(this.disabled){
-            return;
+
+
+
+Roo.htmleditor.KeyEnter = function(cfg) {
+    Roo.apply(this, cfg);
+    // this does not actually call walk as it's really just a abstract class
+    Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
+}
+
+//Roo.htmleditor.KeyEnter.i = 0;
+
+
+Roo.htmleditor.KeyEnter.prototype = {
+    
+    core : false,
+    
+    keypress : function(e)
+    {
+        if (e.charCode != 13 && e.charCode != 10) {
+            Roo.log([e.charCode,e]);
+            return true;
         }
-        if(this.isExpanded()){
-            this.collapse();
-            if (!this.blockFocus) {
-                this.el.focus();
-            }
-            
-        }else {
-            this.hasFocus = true;
-            if(this.triggerAction == 'all') {
-                this.doQuery(this.allQuery, true);
+        e.preventDefault();
+        // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
+        var doc = this.core.doc;
+          //add a new line
+       
+    
+        var sel = this.core.getSelection();
+        var range = sel.getRangeAt(0);
+        var n = range.commonAncestorContainer;
+        var pc = range.closest([ 'ol', 'ul']);
+        var pli = range.closest('li');
+        if (!pc || e.ctrlKey) {
+            // on it list, or ctrl pressed.
+            if (!e.ctrlKey) {
+                sel.insertNode('br', 'after'); 
             } else {
-                this.doQuery(this.getRawValue());
-            }
-            if (!this.blockFocus) {
-                this.el.focus();
+                // 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);
+            return false;
         }
-    },
-    listKeyPress : function(e)
-    {
-        //Roo.log('listkeypress');
-        // scroll to first matching element based on key pres..
-        if (e.isSpecialKey()) {
+        
+        // deal with <li> insetion
+        if (pli.innerText.trim() == '' &&
+            pli.previousSibling &&
+            pli.previousSibling.nodeName == 'LI' &&
+            pli.previousSibling.innerText.trim() ==  '') {
+            pli.parentNode.removeChild(pli.previousSibling);
+            sel.cursorAfter(pc);
+            this.core.undoManager.addEvent();
+            this.core.fireEditorEvent(e);
             return false;
         }
-        var k = String.fromCharCode(e.getKey()).toUpperCase();
-        //Roo.log(k);
-        var match  = false;
-        var csel = this.view.getSelectedNodes();
-        var cselitem = false;
-        if (csel.length) {
-            var ix = this.view.indexOf(csel[0]);
-            cselitem  = this.store.getAt(ix);
-            if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
-                cselitem = false;
-            }
-            
+    
+        var li = doc.createElement('LI');
+        li.innerHTML = '&nbsp;';
+        if (!pli || !pli.firstSibling) {
+            pc.appendChild(li);
+        } else {
+            pli.parentNode.insertBefore(li, pli.firstSibling);
         }
+        sel.cursorText (li.firstChild);
+      
+        this.core.undoManager.addEvent();
+        this.core.fireEditorEvent(e);
+
+        return false;
         
-        this.store.each(function(v) { 
-            if (cselitem) {
-                // start at existing selection.
-                if (cselitem.id == v.id) {
-                    cselitem = false;
-                }
-                return;
-            }
-                
-            if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
-                match = this.store.indexOf(v);
-                return false;
-            }
-        }, this);
+    
         
-        if (match === false) {
-            return true; // no more action?
-        }
-        // scroll to?
-        this.view.select(match);
-        var sn = Roo.get(this.view.getSelectedNodes()[0]);
-        sn.scrollIntoView(sn.dom.parentNode, false);
-    } 
-
-    /** 
-    * @cfg {Boolean} grow 
-    * @hide 
-    */
-    /** 
-    * @cfg {Number} growMin 
-    * @hide 
-    */
-    /** 
-    * @cfg {Number} growMax 
-    * @hide 
-    */
-    /**
-     * @hide
-     * @method autoSize
-     */
-});/*
- * Copyright(c) 2010-2012, Roo J Solutions Limited
- *
- * Licence LGPL
- *
- */
-
+        
+         
+    }
+};
+     
 /**
- * @class Roo.form.ComboBoxArray
- * @extends Roo.form.TextField
- * A facebook style adder... for lists of email / people / countries  etc...
- * pick multiple items from a combo box, and shows each one.
- *
- *  Fred [x]  Brian [x]  [Pick another |v]
- *
- *
- *  For this to work: it needs various extra information
- *    - normal combo problay has
- *      name, hiddenName
- *    + displayField, valueField
- *
- *    For our purpose...
- *
- *
- *   If we change from 'extends' to wrapping...
- *   
- *  
- *
+ * @class Roo.htmleditor.Block
+ * Base class for html editor blocks - do not use it directly .. extend it..
+ * @cfg {DomElement} node The node to apply stuff to.
+ * @cfg {String} friendly_name the name that appears in the context bar about this block
+ * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
  
  * @constructor
- * Create a new ComboBoxArray.
+ * Create a new Filter.
  * @param {Object} config Configuration options
  */
 
-Roo.form.ComboBoxArray = function(config)
+Roo.htmleditor.Block  = function(cfg)
 {
-    this.addEvents({
-        /**
-         * @event beforeremove
-         * Fires before remove the value from the list
-            * @param {Roo.form.ComboBoxArray} _self This combo box array
-             * @param {Roo.form.ComboBoxArray.Item} item removed item
-            */
-        'beforeremove' : true,
-        /**
-         * @event remove
-         * Fires when remove the value from the list
-            * @param {Roo.form.ComboBoxArray} _self This combo box array
-             * @param {Roo.form.ComboBoxArray.Item} item removed item
-            */
-        'remove' : true
-        
-        
-    });
-    
-    Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
-    
-    this.items = new Roo.util.MixedCollection(false);
-    
-    // construct the child combo...
-    
+    // do nothing .. should not be called really.
+}
+/**
+ * factory method to get the block from an element (using cache if necessary)
+ * @static
+ * @param {HtmlElement} the dom element
+ */
+Roo.htmleditor.Block.factory = function(node)
+{
+    var cc = Roo.htmleditor.Block.cache;
+    var id = Roo.get(node).id;
+    if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
+        Roo.htmleditor.Block.cache[id].readElement(node);
+        return Roo.htmleditor.Block.cache[id];
+    }
+    var db  = node.getAttribute('data-block');
+    if (!db) {
+        db = node.nodeName.toLowerCase().toUpperCaseFirst();
+    }
+    var cls = Roo.htmleditor['Block' + db];
+    if (typeof(cls) == 'undefined') {
+        //Roo.log(node.getAttribute('data-block'));
+        Roo.log("OOps missing block : " + 'Block' + db);
+        return false;
+    }
+    Roo.htmleditor.Block.cache[id] = new cls({ node: node });
+    return Roo.htmleditor.Block.cache[id];  /// should trigger update element
+};
+
+/**
+ * initalize all Elements from content that are 'blockable'
+ * @static
+ * @param the body element
+ */
+Roo.htmleditor.Block.initAll = function(body, type)
+{
+    if (typeof(type) == 'undefined') {
+        var ia = Roo.htmleditor.Block.initAll;
+        ia(body,'table');
+        ia(body,'td');
+        ia(body,'figure');
+        return;
+    }
+    Roo.each(Roo.get(body).query(type), function(e) {
+        Roo.htmleditor.Block.factory(e);    
+    },this);
+};
+// question goes here... do we need to clear out this cache sometimes?
+// or show we make it relivant to the htmleditor.
+Roo.htmleditor.Block.cache = {};
+
+Roo.htmleditor.Block.prototype = {
     
+    node : false,
     
+     // used by context menu
+    friendly_name : 'Based Block',
     
-   
+    // text for button to delete this element
+    deleteTitle : false,
     
-}
-
-Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
-{ 
+    context : false,
+    /**
+     * Update a node with values from this object
+     * @param {DomElement} node
+     */
+    updateElement : function(node)
+    {
+        Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
+    },
+     /**
+     * convert to plain HTML for calling insertAtCursor..
+     */
+    toHTML : function()
+    {
+        return Roo.DomHelper.markup(this.toObject());
+    },
+    /**
+     * used by readEleemnt to extract data from a node
+     * may need improving as it's pretty basic
+     
+     * @param {DomElement} node
+     * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
+     * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
+     * @param {String} style the style property - eg. text-align
+     */
+    getVal : function(node, tag, attr, style)
+    {
+        var n = node;
+        if (tag !== true && n.tagName != tag.toUpperCase()) {
+            // in theory we could do figure[3] << 3rd figure? or some more complex search..?
+            // but kiss for now.
+            n = node.getElementsByTagName(tag).item(0);
+        }
+        if (!n) {
+            return '';
+        }
+        if (attr === false) {
+            return n;
+        }
+        if (attr == 'html') {
+            return n.innerHTML;
+        }
+        if (attr == 'style') {
+            return n.style[style]; 
+        }
+        
+        return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
+            
+    },
     /**
-     * @cfg {Roo.form.Combo} combo The combo box that is wrapped
+     * create a DomHelper friendly object - for use with 
+     * Roo.DomHelper.markup / overwrite / etc..
+     * (override this)
+     */
+    toObject : function()
+    {
+        return {};
+    },
+      /**
+     * Read a node that has a 'data-block' property - and extract the values from it.
+     * @param {DomElement} node - the node
      */
+    readElement : function(node)
+    {
+        
+    } 
     
-    lastData : false,
     
-    // behavies liek a hiddne field
-    inputType:      'hidden',
-    /**
-     * @cfg {Number} width The width of the box that displays the selected element
-     */ 
-    width:          300,
+};
 
+
+/**
+ * @class Roo.htmleditor.BlockFigure
+ * Block that has an image and a figcaption
+ * @cfg {String} image_src the url for the image
+ * @cfg {String} align (left|right) alignment for the block default left
+ * @cfg {String} caption the text to appear below  (and in the alt tag)
+ * @cfg {String} caption_display (block|none) display or not the caption
+ * @cfg {String|number} image_width the width of the image number or %?
+ * @cfg {String|number} image_height the height of the image number or %?
+ * 
+ * @constructor
+ * Create a new Filter.
+ * @param {Object} config Configuration options
+ */
+
+Roo.htmleditor.BlockFigure = function(cfg)
+{
+    if (cfg.node) {
+        this.readElement(cfg.node);
+        this.updateElement(cfg.node);
+    }
+    Roo.apply(this, cfg);
+}
+Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
     
+    // setable values.
+    image_src: '',
+    align: 'center',
+    caption : '',
+    caption_display : 'block',
+    width : '100%',
+    cls : '',
+    href: '',
+    video_url : '',
     
-    /**
-     * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
-     */
-    name : false,
-    /**
-     * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
-     */
-    hiddenName : false,
-    
+    // margin: '2%', not used
     
-    // private the array of items that are displayed..
-    items  : false,
-    // private - the hidden field el.
-    hiddenEl : false,
-    // private - the filed el..
-    el : false,
+    text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
+
     
-    //validateValue : function() { return true; }, // all values are ok!
-    //onAddClick: function() { },
+    // used by context menu
+    friendly_name : 'Image with caption',
+    deleteTitle : "Delete Image and Caption",
     
-    onRender : function(ct, position) 
+    contextMenu : function(toolbar)
     {
         
-        // create the standard hidden element
-        //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
-        
-        
-        // give fake names to child combo;
-        this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
-        this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
-        
-        this.combo = Roo.factory(this.combo, Roo.form);
-        this.combo.onRender(ct, position);
-        if (typeof(this.combo.width) != 'undefined') {
-            this.combo.onResize(this.combo.width,0);
-        }
-        
-        this.combo.initEvents();
-        
-        // assigned so form know we need to do this..
-        this.store          = this.combo.store;
-        this.valueField     = this.combo.valueField;
-        this.displayField   = this.combo.displayField ;
-        
-        
-        this.combo.wrap.addClass('x-cbarray-grp');
-        
-        var cbwrap = this.combo.wrap.createChild(
-            {tag: 'div', cls: 'x-cbarray-cb'},
-            this.combo.el.dom
-        );
-        
-             
-        this.hiddenEl = this.combo.wrap.createChild({
-            tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
-        });
-        this.el = this.combo.wrap.createChild({
-            tag: 'input',  type:'hidden' , name: this.name, value : ''
-        });
-         //   this.el.dom.removeAttribute("name");
-        
-        
-        this.outerWrap = this.combo.wrap;
-        this.wrap = cbwrap;
-        
-        this.outerWrap.setWidth(this.width);
-        this.outerWrap.dom.removeChild(this.el.dom);
+        var block = function() {
+            return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
+        };
         
-        this.wrap.dom.appendChild(this.el.dom);
-        this.outerWrap.dom.removeChild(this.combo.trigger.dom);
-        this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
         
-        this.combo.trigger.setStyle('position','relative');
-        this.combo.trigger.setStyle('left', '0px');
-        this.combo.trigger.setStyle('top', '2px');
+        var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
         
-        this.combo.el.setStyle('vertical-align', 'text-bottom');
+        var syncValue = toolbar.editorcore.syncValue;
         
-        //this.trigger.setStyle('vertical-align', 'top');
+        var fields = {};
         
-        // this should use the code from combo really... on('add' ....)
-        if (this.adder) {
+        return [
+             {
+                xtype : 'TextItem',
+                text : "Source: ",
+                xns : rooui.Toolbar  //Boostrap?
+            },
+            {
+                xtype : 'Button',
+                text: 'Change Image URL',
+                 
+                listeners : {
+                    click: function (btn, state)
+                    {
+                        var b = block();
+                        
+                        Roo.MessageBox.show({
+                            title : "Image Source URL",
+                            msg : "Enter the url for the image",
+                            buttons: Roo.MessageBox.OKCANCEL,
+                            fn: function(btn, val){
+                                if (btn != 'ok') {
+                                    return;
+                                }
+                                b.image_src = val;
+                                b.updateElement();
+                                syncValue();
+                                toolbar.editorcore.onEditorEvent();
+                            },
+                            minWidth:250,
+                            prompt:true,
+                            //multiline: multiline,
+                            modal : true,
+                            value : b.image_src
+                        });
+                    }
+                },
+                xns : rooui.Toolbar
+            },
+         
+            {
+                xtype : 'Button',
+                text: 'Change Link URL',
+                 
+                listeners : {
+                    click: function (btn, state)
+                    {
+                        var b = block();
+                        
+                        Roo.MessageBox.show({
+                            title : "Link URL",
+                            msg : "Enter the url for the link - leave blank to have no link",
+                            buttons: Roo.MessageBox.OKCANCEL,
+                            fn: function(btn, val){
+                                if (btn != 'ok') {
+                                    return;
+                                }
+                                b.href = val;
+                                b.updateElement();
+                                syncValue();
+                                toolbar.editorcore.onEditorEvent();
+                            },
+                            minWidth:250,
+                            prompt:true,
+                            //multiline: multiline,
+                            modal : true,
+                            value : b.href
+                        });
+                    }
+                },
+                xns : rooui.Toolbar
+            },
+            {
+                xtype : 'Button',
+                text: 'Show Video URL',
+                 
+                listeners : {
+                    click: function (btn, state)
+                    {
+                        Roo.MessageBox.alert("Video URL",
+                            block().video_url == '' ? 'This image is not linked ot a video' :
+                                'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
+                    }
+                },
+                xns : rooui.Toolbar
+            },
+            
+            
+            {
+                xtype : 'TextItem',
+                text : "Width: ",
+                xns : rooui.Toolbar  //Boostrap?
+            },
+            {
+                xtype : 'ComboBox',
+                allowBlank : false,
+                displayField : 'val',
+                editable : true,
+                listWidth : 100,
+                triggerAction : 'all',
+                typeAhead : true,
+                valueField : 'val',
+                width : 70,
+                name : 'width',
+                listeners : {
+                    select : function (combo, r, index)
+                    {
+                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
+                        var b = block();
+                        b.width = r.get('val');
+                        b.updateElement();
+                        syncValue();
+                        toolbar.editorcore.onEditorEvent();
+                    }
+                },
+                xns : rooui.form,
+                store : {
+                    xtype : 'SimpleStore',
+                    data : [
+                        ['100%'],
+                        ['80%'],
+                        ['50%'],
+                        ['20%'],
+                        ['10%']
+                    ],
+                    fields : [ 'val'],
+                    xns : Roo.data
+                }
+            },
+            {
+                xtype : 'TextItem',
+                text : "Align: ",
+                xns : rooui.Toolbar  //Boostrap?
+            },
+            {
+                xtype : 'ComboBox',
+                allowBlank : false,
+                displayField : 'val',
+                editable : true,
+                listWidth : 100,
+                triggerAction : 'all',
+                typeAhead : true,
+                valueField : 'val',
+                width : 70,
+                name : 'align',
+                listeners : {
+                    select : function (combo, r, index)
+                    {
+                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
+                        var b = block();
+                        b.align = r.get('val');
+                        b.updateElement();
+                        syncValue();
+                        toolbar.editorcore.onEditorEvent();
+                    }
+                },
+                xns : rooui.form,
+                store : {
+                    xtype : 'SimpleStore',
+                    data : [
+                        ['left'],
+                        ['right'],
+                        ['center']
+                    ],
+                    fields : [ 'val'],
+                    xns : Roo.data
+                }
+            },
             
+            
+            {
+                xtype : 'Button',
+                text: 'Hide Caption',
+                name : 'caption_display',
+                pressed : false,
+                enableToggle : true,
+                setValue : function(v) {
+                    // this trigger toggle.
+                     
+                    this.setText(v ? "Hide Caption" : "Show Caption");
+                    this.setPressed(v != 'block');
+                },
+                listeners : {
+                    toggle: function (btn, state)
+                    {
+                        var b  = block();
+                        b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
+                        this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
+                        b.updateElement();
+                        syncValue();
+                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
+                        toolbar.editorcore.onEditorEvent();
+                    }
+                },
+                xns : rooui.Toolbar
+            }
+        ];
         
-            this.adder = this.outerWrap.createChild(
-                {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
-            var _t = this;
-            this.adder.on('click', function(e) {
-                _t.fireEvent('adderclick', this, e);
-            }, _t);
+    },
+    /**
+     * create a DomHelper friendly object - for use with
+     * Roo.DomHelper.markup / overwrite / etc..
+     */
+    toObject : function()
+    {
+        var d = document.createElement('div');
+        d.innerHTML = this.caption;
+        
+        var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
+        
+        var iw = this.align == 'center' ? this.width : '100%';
+        var img =   {
+            tag : 'img',
+            contenteditable : 'false',
+            src : this.image_src,
+            alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
+            style: {
+                width : iw,
+                maxWidth : iw + ' !important', // this is not getting rendered?
+                margin : m  
+                
+            }
+        };
+        /*
+        '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
+                    '<a href="{2}">' + 
+                        '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
+                    '</a>' + 
+                '</div>',
+        */
+                
+        if (this.href.length > 0) {
+            img = {
+                tag : 'a',
+                href: this.href,
+                contenteditable : 'true',
+                cn : [
+                    img
+                ]
+            };
         }
-        //var _t = this;
-        //this.adder.on('click', this.onAddClick, _t);
         
         
-        this.combo.on('select', function(cb, rec, ix) {
-            this.addItem(rec.data);
+        if (this.video_url.length > 0) {
+            img = {
+                tag : 'div',
+                cls : this.cls,
+                frameborder : 0,
+                allowfullscreen : true,
+                width : 420,  // these are for video tricks - that we replace the outer
+                height : 315,
+                src : this.video_url,
+                cn : [
+                    img
+                ]
+            };
+        }
+        // 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");
+        
+  
+        var ret =   {
+            tag: 'figure',
+            'data-block' : 'Figure',
+            'data-width' : this.width, 
+            contenteditable : 'false',
             
-            cb.setValue('');
-            cb.el.dom.value = '';
-            //cb.lastData = rec.data;
-            // add to list
+            style : {
+                display: 'block',
+                float :  this.align ,
+                maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
+                width : this.align == 'center' ? '100%' : this.width,
+                margin:  '0px',
+                padding: this.align == 'center' ? '0' : '0 10px' ,
+                textAlign : this.align   // seems to work for email..
+                
+            },
+           
             
-        }, this);
-        
-        
+            align : this.align,
+            cn : [
+                img,
+              
+                {
+                    tag: 'figcaption',
+                    'data-display' : this.caption_display,
+                    style : {
+                        textAlign : 'left',
+                        fontSize : '16px',
+                        lineHeight : '24px',
+                        display : this.caption_display,
+                        maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
+                        margin: m,
+                        width: this.align == 'center' ?  this.width : '100%' 
+                    
+                         
+                    },
+                    cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
+                    cn : [
+                        {
+                            tag: 'div',
+                            style  : {
+                                marginTop : '16px',
+                                textAlign : 'left'
+                            },
+                            align: 'left',
+                            cn : [
+                                {
+                                    // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
+                                    tag : 'i',
+                                    contenteditable : true,
+                                    html : captionhtml
+                                }
+                                
+                            ]
+                        }
+                        
+                    ]
+                    
+                }
+            ]
+        };
+        return ret;
+         
     },
     
-    
-    getName: function()
+    readElement : function(node)
     {
-        // returns hidden if it's set..
-        if (!this.rendered) {return ''};
-        return  this.hiddenName ? this.hiddenName : this.name;
+        // this should not really come from the link...
+        this.video_url = this.getVal(node, 'div', 'src');
+        this.cls = this.getVal(node, 'div', 'class');
+        this.href = this.getVal(node, 'a', 'href');
         
-    },
-    
-    
-    onResize: function(w, h){
-        
-        return;
-        // not sure if this is needed..
-        //this.combo.onResize(w,h);
-        
-        if(typeof w != 'number'){
-            // we do not handle it!?!?
-            return;
-        }
-        var tw = this.combo.trigger.getWidth();
-        tw += this.addicon ? this.addicon.getWidth() : 0;
-        tw += this.editicon ? this.editicon.getWidth() : 0;
-        var x = w - tw;
-        this.combo.el.setWidth( this.combo.adjustWidth('input', x));
-            
-        this.combo.trigger.setStyle('left', '0px');
         
-        if(this.list && this.listWidth === undefined){
-            var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
-            this.list.setWidth(lw);
-            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
+        this.image_src = this.getVal(node, 'img', 'src');
+         
+        this.align = this.getVal(node, 'figure', 'align');
+        var figcaption = this.getVal(node, 'figcaption', false);
+        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, true, 'data-width');
+        //this.margin = this.getVal(node, 'figure', 'style', 'margin');
         
     },
-    
-    addItem: function(rec)
+    removeNode : function()
     {
-        var valueField = this.combo.valueField;
-        var displayField = this.combo.displayField;
-       
-        if (this.items.indexOfKey(rec[valueField]) > -1) {
-            //console.log("GOT " + rec.data.id);
-            return;
-        }
-        
-        var x = new Roo.form.ComboBoxArray.Item({
-            //id : rec[this.idField],
-            data : rec,
-            displayField : displayField ,
-            tipField : displayField ,
-            cb : this
-        });
-        // use the 
-        this.items.add(rec[valueField],x);
-        // add it before the element..
-        this.updateHiddenEl();
-        x.render(this.outerWrap, this.wrap.dom);
-        // add the image handler..
-    },
+        return this.node;
+    }
     
-    updateHiddenEl : function()
-    {
-        this.validate();
-        if (!this.hiddenEl) {
-            return;
+  
+   
+     
+    
+    
+    
+    
+})
+
+
+/**
+ * @class Roo.htmleditor.BlockTable
+ * Block that manages a table
+ * 
+ * @constructor
+ * Create a new Filter.
+ * @param {Object} config Configuration options
+ */
+
+Roo.htmleditor.BlockTable = function(cfg)
+{
+    if (cfg.node) {
+        this.readElement(cfg.node);
+        this.updateElement(cfg.node);
+    }
+    Roo.apply(this, cfg);
+    if (!cfg.node) {
+        this.rows = [];
+        for(var r = 0; r < this.no_row; r++) {
+            this.rows[r] = [];
+            for(var c = 0; c < this.no_col; c++) {
+                this.rows[r][c] = this.emptyCell();
+            }
         }
-        var ar = [];
-        var idField = this.combo.valueField;
-        
-        this.items.each(function(f) {
-            ar.push(f.data[idField]);
-        });
-        this.hiddenEl.dom.value = ar.join(',');
-        this.validate();
-    },
+    }
     
-    reset : function()
+    
+}
+Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
+    rows : false,
+    no_col : 1,
+    no_row : 1,
+    
+    
+    width: '100%',
+    
+    // used by context menu
+    friendly_name : 'Table',
+    deleteTitle : 'Delete Table',
+    // context menu is drawn once..
+    
+    contextMenu : function(toolbar)
     {
-        this.items.clear();
         
-        Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
-           el.remove();
-        });
+        var block = function() {
+            return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
+        };
         
-        this.el.dom.value = '';
-        if (this.hiddenEl) {
-            this.hiddenEl.dom.value = '';
-        }
+        
+        var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
+        
+        var syncValue = toolbar.editorcore.syncValue;
+        
+        var fields = {};
+        
+        return [
+            {
+                xtype : 'TextItem',
+                text : "Width: ",
+                xns : rooui.Toolbar  //Boostrap?
+            },
+            {
+                xtype : 'ComboBox',
+                allowBlank : false,
+                displayField : 'val',
+                editable : true,
+                listWidth : 100,
+                triggerAction : 'all',
+                typeAhead : true,
+                valueField : 'val',
+                width : 100,
+                name : 'width',
+                listeners : {
+                    select : function (combo, r, index)
+                    {
+                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
+                        var b = block();
+                        b.width = r.get('val');
+                        b.updateElement();
+                        syncValue();
+                        toolbar.editorcore.onEditorEvent();
+                    }
+                },
+                xns : rooui.form,
+                store : {
+                    xtype : 'SimpleStore',
+                    data : [
+                        ['100%'],
+                        ['auto']
+                    ],
+                    fields : [ 'val'],
+                    xns : Roo.data
+                }
+            },
+            // -------- Cols
+            
+            {
+                xtype : 'TextItem',
+                text : "Columns: ",
+                xns : rooui.Toolbar  //Boostrap?
+            },
+         
+            {
+                xtype : 'Button',
+                text: '-',
+                listeners : {
+                    click : function (_self, e)
+                    {
+                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
+                        block().removeColumn();
+                        syncValue();
+                        toolbar.editorcore.onEditorEvent();
+                    }
+                },
+                xns : rooui.Toolbar
+            },
+            {
+                xtype : 'Button',
+                text: '+',
+                listeners : {
+                    click : function (_self, e)
+                    {
+                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
+                        block().addColumn();
+                        syncValue();
+                        toolbar.editorcore.onEditorEvent();
+                    }
+                },
+                xns : rooui.Toolbar
+            },
+            // -------- ROWS
+            {
+                xtype : 'TextItem',
+                text : "Rows: ",
+                xns : rooui.Toolbar  //Boostrap?
+            },
+         
+            {
+                xtype : 'Button',
+                text: '-',
+                listeners : {
+                    click : function (_self, e)
+                    {
+                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
+                        block().removeRow();
+                        syncValue();
+                        toolbar.editorcore.onEditorEvent();
+                    }
+                },
+                xns : rooui.Toolbar
+            },
+            {
+                xtype : 'Button',
+                text: '+',
+                listeners : {
+                    click : function (_self, e)
+                    {
+                        block().addRow();
+                        syncValue();
+                        toolbar.editorcore.onEditorEvent();
+                    }
+                },
+                xns : rooui.Toolbar
+            },
+            // -------- ROWS
+            {
+                xtype : 'Button',
+                text: 'Reset Column Widths',
+                listeners : {
+                    
+                    click : function (_self, e)
+                    {
+                        block().resetWidths();
+                        syncValue();
+                        toolbar.editorcore.onEditorEvent();
+                    }
+                },
+                xns : rooui.Toolbar
+            } 
+            
+            
+            
+        ];
         
     },
-    getValue: function()
-    {
-        return this.hiddenEl ? this.hiddenEl.dom.value : '';
-    },
-    setValue: function(v) // not a valid action - must use addItems..
+    
+    
+  /**
+     * create a DomHelper friendly object - for use with
+     * Roo.DomHelper.markup / overwrite / etc..
+     * ?? should it be called with option to hide all editing features?
+     */
+    toObject : function()
     {
         
-        this.reset();
-         
-        if (this.store.isLocal && (typeof(v) == 'string')) {
-            // then we can use the store to find the values..
-            // comma seperated at present.. this needs to allow JSON based encoding..
-            this.hiddenEl.value  = v;
-            var v_ar = [];
-            Roo.each(v.split(','), function(k) {
-                Roo.log("CHECK " + this.valueField + ',' + k);
-                var li = this.store.query(this.valueField, k);
-                if (!li.length) {
-                    return;
+        var ret = {
+            tag : 'table',
+            contenteditable : 'false', // this stops cell selection from picking the table.
+            'data-block' : 'Table',
+            style : {
+                width:  this.width,
+                border : 'solid 1px #000', // ??? hard coded?
+                'border-collapse' : 'collapse' 
+            },
+            cn : [
+                { tag : 'tbody' , cn : [] }
+            ]
+        };
+        
+        // do we have a head = not really 
+        var ncols = 0;
+        Roo.each(this.rows, function( row ) {
+            var tr = {
+                tag: 'tr',
+                style : {
+                    margin: '6px',
+                    border : 'solid 1px #000',
+                    textAlign : 'left' 
+                },
+                cn : [ ]
+            };
+            
+            ret.cn[0].cn.push(tr);
+            // does the row have any properties? ?? height?
+            var nc = 0;
+            Roo.each(row, function( cell ) {
+                
+                var td = {
+                    tag : 'td',
+                    contenteditable :  'true',
+                    'data-block' : 'Td',
+                    html : cell.html,
+                    style : cell.style
+                };
+                if (cell.colspan > 1) {
+                    td.colspan = cell.colspan ;
+                    nc += cell.colspan;
+                } else {
+                    nc++;
+                }
+                if (cell.rowspan > 1) {
+                    td.rowspan = cell.rowspan ;
                 }
-                var add = {};
-                add[this.valueField] = k;
-                add[this.displayField] = li.item(0).data[this.displayField];
                 
-                this.addItem(add);
-            }, this) 
-             
-        }
-        if (typeof(v) == 'object' ) {
-            // then let's assume it's an array of objects..
-            Roo.each(v, function(l) {
-                this.addItem(l);
+                
+                // widths ?
+                tr.cn.push(td);
+                    
+                
             }, this);
-             
-        }
+            ncols = Math.max(nc, ncols);
+            
+            
+        }, this);
+        // add the header row..
         
+        ncols++;
+         
         
+        return ret;
+         
     },
-    setFromData: function(v)
+    
+    readElement : function(node)
     {
-        // this recieves an object, if setValues is called.
-        this.reset();
-        this.el.dom.value = v[this.displayField];
-        this.hiddenEl.dom.value = v[this.valueField];
-        if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
-            return;
-        }
-        var kv = v[this.valueField];
-        var dv = v[this.displayField];
-        kv = typeof(kv) != 'string' ? '' : kv;
-        dv = typeof(dv) != 'string' ? '' : dv;
-        
-        
-        var keys = kv.split(',');
-        var display = dv.split(',');
-        for (var i = 0 ; i < keys.length; i++) {
+        node  = node ? node : this.node ;
+        this.width = this.getVal(node, true, 'style', 'width') || '100%';
+        
+        this.rows = [];
+        this.no_row = 0;
+        var trs = Array.from(node.rows);
+        trs.forEach(function(tr) {
+            var row =  [];
+            this.rows.push(row);
             
-            add = {};
-            add[this.valueField] = keys[i];
-            add[this.displayField] = display[i];
-            this.addItem(add);
-        }
-      
+            this.no_row++;
+            var no_column = 0;
+            Array.from(tr.cells).forEach(function(td) {
+                
+                var add = {
+                    colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
+                    rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
+                    style : td.hasAttribute('style') ? td.getAttribute('style') : '',
+                    html : td.innerHTML
+                };
+                no_column += add.colspan;
+                     
+                
+                row.push(add);
+                
+                
+            },this);
+            this.no_col = Math.max(this.no_col, no_column);
+            
+            
+        },this);
+        
         
     },
+    normalizeRows: function()
+    {
+        var ret= [];
+        var rid = -1;
+        this.rows.forEach(function(row) {
+            rid++;
+            ret[rid] = [];
+            row = this.normalizeRow(row);
+            var cid = 0;
+            row.forEach(function(c) {
+                while (typeof(ret[rid][cid]) != 'undefined') {
+                    cid++;
+                }
+                if (typeof(ret[rid]) == 'undefined') {
+                    ret[rid] = [];
+                }
+                ret[rid][cid] = c;
+                c.row = rid;
+                c.col = cid;
+                if (c.rowspan < 2) {
+                    return;
+                }
+                
+                for(var i = 1 ;i < c.rowspan; i++) {
+                    if (typeof(ret[rid+i]) == 'undefined') {
+                        ret[rid+i] = [];
+                    }
+                    ret[rid+i][cid] = c;
+                }
+            });
+        }, this);
+        return ret;
     
-    /**
-     * Validates the combox array value
-     * @return {Boolean} True if the value is valid, else false
-     */
-    validate : function(){
-        if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
-            this.clearInvalid();
-            return true;
-        }
-        return false;
     },
     
-    validateValue : function(value){
-        return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
-        
+    normalizeRow: function(row)
+    {
+        var ret= [];
+        row.forEach(function(c) {
+            if (c.colspan < 2) {
+                ret.push(c);
+                return;
+            }
+            for(var i =0 ;i < c.colspan; i++) {
+                ret.push(c);
+            }
+        });
+        return ret;
+    
     },
     
-    /*@
-     * overide
-     * 
-     */
-    isDirty : function() {
-        if(this.disabled) {
-            return false;
-        }
-        
-        try {
-            var d = Roo.decode(String(this.originalValue));
-        } catch (e) {
-            return String(this.getValue()) !== String(this.originalValue);
+    deleteColumn : function(sel)
+    {
+        if (!sel || sel.type != 'col') {
+            return;
         }
-        
-        var originalValue = [];
-        
-        for (var i = 0; i < d.length; i++){
-            originalValue.push(d[i][this.valueField]);
+        if (this.no_col < 2) {
+            return;
         }
         
-        return String(this.getValue()) !== String(originalValue.join(','));
+        this.rows.forEach(function(row) {
+            var cols = this.normalizeRow(row);
+            var col = cols[sel.col];
+            if (col.colspan > 1) {
+                col.colspan --;
+            } else {
+                row.remove(col);
+            }
+            
+        }, this);
+        this.no_col--;
         
-    }
-    
-});
-
-
-
-/**
- * @class Roo.form.ComboBoxArray.Item
- * @extends Roo.BoxComponent
- * A selected item in the list
- *  Fred [x]  Brian [x]  [Pick another |v]
- * 
- * @constructor
- * Create a new item.
- * @param {Object} config Configuration options
- */
-Roo.form.ComboBoxArray.Item = function(config) {
-    config.id = Roo.id();
-    Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
-}
-
-Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
-    data : {},
-    cb: false,
-    displayField : false,
-    tipField : false,
-    
+    },
+    removeColumn : function()
+    {
+        this.deleteColumn({
+            type: 'col',
+            col : this.no_col-1
+        });
+        this.updateElement();
+    },
     
-    defaultAutoCreate : {
-        tag: 'div',
-        cls: 'x-cbarray-item',
-        cn : [ 
-            { tag: 'div' },
-            {
-                tag: 'img',
-                width:16,
-                height : 16,
-                src : Roo.BLANK_IMAGE_URL ,
-                align: 'center'
-            }
-        ]
+     
+    addColumn : function()
+    {
         
+        this.rows.forEach(function(row) {
+            row.push(this.emptyCell());
+           
+        }, this);
+        this.updateElement();
     },
     
-    onRender : function(ct, position)
+    deleteRow : function(sel)
     {
-        Roo.form.Field.superclass.onRender.call(this, ct, position);
+        if (!sel || sel.type != 'row') {
+            return;
+        }
         
-        if(!this.el){
-            var cfg = this.getAutoCreate();
-            this.el = ct.createChild(cfg, position);
+        if (this.no_row < 2) {
+            return;
         }
         
-        this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
+        var rows = this.normalizeRows();
         
-        this.el.child('div').dom.innerHTML = this.cb.renderer ? 
-            this.cb.renderer(this.data) :
-            String.format('{0}',this.data[this.displayField]);
         
+        rows[sel.row].forEach(function(col) {
+            if (col.rowspan > 1) {
+                col.rowspan--;
+            } else {
+                col.remove = 1; // flage it as removed.
+            }
             
-        this.el.child('div').dom.setAttribute('qtip',
-                        String.format('{0}',this.data[this.tipField])
-        );
+        }, this);
+        var newrows = [];
+        this.rows.forEach(function(row) {
+            newrow = [];
+            row.forEach(function(c) {
+                if (typeof(c.remove) == 'undefined') {
+                    newrow.push(c);
+                }
+                
+            });
+            if (newrow.length > 0) {
+                newrows.push(row);
+            }
+        });
+        this.rows =  newrows;
+        
         
-        this.el.child('img').on('click', this.remove, this);
+        
+        this.no_row--;
+        this.updateElement();
         
     },
-   
-    remove : function()
+    removeRow : function()
     {
-        if(this.cb.disabled){
-            return;
-        }
+        this.deleteRow({
+            type: 'row',
+            row : this.no_row-1
+        });
         
-        if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
-            this.cb.items.remove(this);
-            this.el.child('img').un('click', this.remove, this);
-            this.el.remove();
-            this.cb.updateHiddenEl();
-
-            this.cb.fireEvent('remove', this.cb, this);
+    },
+    
+     
+    addRow : function()
+    {
+        
+        var row = [];
+        for (var i = 0; i < this.no_col; i++ ) {
+            
+            row.push(this.emptyCell());
+           
         }
+        this.rows.push(row);
+        this.updateElement();
+        
+    },
+     
+    // the default cell object... at present...
+    emptyCell : function() {
+        return (new Roo.htmleditor.BlockTd({})).toObject();
         
+     
+    },
+    
+    removeNode : function()
+    {
+        return this.node;
+    },
+    
+    
+    
+    resetWidths : function()
+    {
+        Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
+            var nn = Roo.htmleditor.Block.factory(n);
+            nn.width = '';
+            nn.updateElement(n);
+        });
     }
-});/*
- * RooJS Library 1.1.1
- * Copyright(c) 2008-2011  Alan Knowles
+    
+    
+    
+    
+})
+
+/**
+ *
+ * editing a TD?
+ *
+ * since selections really work on the table cell, then editing really should work from there
+ *
+ * The original plan was to support merging etc... - but that may not be needed yet..
+ *
+ * So this simple version will support:
+ *   add/remove cols
+ *   adjust the width +/-
+ *   reset the width...
+ *   
  *
- * License - LGPL
  */
+
+
  
 
 /**
- * @class Roo.form.ComboNested
- * @extends Roo.form.ComboBox
- * A combobox for that allows selection of nested items in a list,
- * eg.
- *
- *  Book
- *    -> red
- *    -> green
- *  Table
- *    -> square
- *      ->red
- *      ->green
- *    -> rectangle
- *      ->green
- *      
+ * @class Roo.htmleditor.BlockTable
+ * Block that manages a table
  * 
  * @constructor
- * Create a new ComboNested
+ * Create a new Filter.
  * @param {Object} config Configuration options
  */
-Roo.form.ComboNested = function(config){
-    Roo.form.ComboCheck.superclass.constructor.call(this, config);
-    // should verify some data...
-    // like
-    // hiddenName = required..
-    // displayField = required
-    // valudField == required
-    var req= [ 'hiddenName', 'displayField', 'valueField' ];
-    var _t = this;
-    Roo.each(req, function(e) {
-        if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
-            throw "Roo.form.ComboNested : missing value for: " + e;
-        }
-    });
+
+Roo.htmleditor.BlockTd = function(cfg)
+{
+    if (cfg.node) {
+        this.readElement(cfg.node);
+        this.updateElement(cfg.node);
+    }
+    Roo.apply(this, cfg);
      
     
-};
-
-Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
-   
-    /*
-     * @config {Number} max Number of columns to show
-     */
     
-    maxColumns : 3,
-   
-    list : null, // the outermost div..
-    innerLists : null, // the
-    views : null,
-    stores : null,
-    // private
-    loadingChildren : false,
+}
+Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
+    node : false,
     
-    onRender : function(ct, position)
+    width: '',
+    textAlign : 'left',
+    valign : 'top',
+    
+    colspan : 1,
+    rowspan : 1,
+    
+    
+    // used by context menu
+    friendly_name : 'Table Cell',
+    deleteTitle : false, // use our customer delete
+    
+    // context menu is drawn once..
+    
+    contextMenu : function(toolbar)
     {
-        Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
         
-        if(this.hiddenName){
-            this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
-                    'before', true);
-            this.hiddenField.value =
-                this.hiddenValue !== undefined ? this.hiddenValue :
-                this.value !== undefined ? this.value : '';
-
-            // prevent input submission
-            this.el.dom.removeAttribute('name');
-             
-             
-        }
-       
-        if(Roo.isGecko){
-            this.el.dom.setAttribute('autocomplete', 'off');
-        }
-
-        var cls = 'x-combo-list';
-
-        this.list = new Roo.Layer({
-            shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
-        });
-
-        var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
-        this.list.setWidth(lw);
-        this.list.swallowEvent('mousewheel');
-        this.assetHeight = 0;
-
-        if(this.title){
-            this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
-            this.assetHeight += this.header.getHeight();
-        }
-        this.innerLists = [];
-        this.views = [];
-        this.stores = [];
-        for (var i =0 ; i < this.maxColumns; i++) {
-            this.onRenderList( cls, i);
-        }
+        var cell = function() {
+            return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
+        };
         
-        // always needs footer, as we are going to have an 'OK' button.
-        this.footer = this.list.createChild({cls:cls+'-ft'});
-        this.pageTb = new Roo.Toolbar(this.footer);  
-        var _this = this;
-        this.pageTb.add(  {
-            
-            text: 'Done',
-            handler: function()
-            {
-                _this.collapse();
-            }
-        });
+        var table = function() {
+            return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
+        };
         
-        if ( this.allowBlank && !this.disableClear) {
-            
-            this.pageTb.add(new Roo.Toolbar.Fill(), {
-                cls: 'x-btn-icon x-btn-clear',
-                text: '&#160;',
-                handler: function()
-                {
-                    _this.collapse();
-                    _this.clearValue();
-                    _this.onSelect(false, -1);
-                }
-            });
+        var lr = false;
+        var saveSel = function()
+        {
+            lr = toolbar.editorcore.getSelection().getRangeAt(0);
         }
-        if (this.footer) {
-            this.assetHeight += this.footer.getHeight();
+        var restoreSel = function()
+        {
+            if (lr) {
+                (function() {
+                    toolbar.editorcore.focus();
+                    var cr = toolbar.editorcore.getSelection();
+                    cr.removeAllRanges();
+                    cr.addRange(lr);
+                    toolbar.editorcore.onEditorEvent();
+                }).defer(10, this);
+                
+                
+            }
         }
         
-    },
-    onRenderList : function (  cls, i)
-    {
+        var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
         
-        var lw = Math.floor(
-                ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
-        );
+        var syncValue = toolbar.editorcore.syncValue;
         
-        this.list.setWidth(lw); // default to '1'
-
-        var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
-        //il.on('mouseover', this.onViewOver, this, { list:  i });
-        //il.on('mousemove', this.onViewMove, this, { list:  i });
-        il.setWidth(lw);
-        il.setStyle({ 'overflow-x' : 'hidden'});
-
-        if(!this.tpl){
-            this.tpl = new Roo.Template({
-                html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
-                isEmpty: function (value, allValues) {
-                    //Roo.log(value);
-                    var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
-                    return dl ? 'has-children' : 'no-children'
-                }
-            });
-        }
+        var fields = {};
         
-        var store  = this.store;
-        if (i > 0) {
-            store  = new Roo.data.SimpleStore({
-                //fields : this.store.reader.meta.fields,
-                reader : this.store.reader,
-                data : [ ]
-            });
-        }
-        this.stores[i]  = store;
-                  
-        var view = this.views[i] = new Roo.View(
-            il,
-            this.tpl,
+        return [
             {
-                singleSelect:true,
-                store: store,
-                selectedClass: this.selectedClass
-            }
-        );
-        view.getEl().setWidth(lw);
-        view.getEl().setStyle({
-            position: i < 1 ? 'relative' : 'absolute',
-            top: 0,
-            left: (i * lw ) + 'px',
-            display : i > 0 ? 'none' : 'block'
-        });
-        view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
-        view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
-        //view.on('click', this.onViewClick, this, { list : i });
-
-        store.on('beforeload', this.onBeforeLoad, this);
-        store.on('load',  this.onLoad, this, { list  : i});
-        store.on('loadexception', this.onLoadException, this);
-
-        // hide the other vies..
-        
-        
-        
-    },
-      
-    restrictHeight : function()
-    {
-        var mh = 0;
-        Roo.each(this.innerLists, function(il,i) {
-            var el = this.views[i].getEl();
-            el.dom.style.height = '';
-            var inner = el.dom;
-            var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
-            // only adjust heights on other ones..
-            mh = Math.max(h, mh);
-            if (i < 1) {
+                xtype : 'Button',
+                text : 'Edit Table',
+                listeners : {
+                    click : function() {
+                        var t = toolbar.tb.selectedNode.closest('table');
+                        toolbar.editorcore.selectNode(t);
+                        toolbar.editorcore.onEditorEvent();                        
+                    }
+                }
                 
-                el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
-                il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
+            },
+              
+           
+             
+            {
+                xtype : 'TextItem',
+                text : "Column Width: ",
+                 xns : rooui.Toolbar 
                
-            }
+            },
+            {
+                xtype : 'Button',
+                text: '-',
+                listeners : {
+                    click : function (_self, e)
+                    {
+                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
+                        cell().shrinkColumn();
+                        syncValue();
+                         toolbar.editorcore.onEditorEvent();
+                    }
+                },
+                xns : rooui.Toolbar
+            },
+            {
+                xtype : 'Button',
+                text: '+',
+                listeners : {
+                    click : function (_self, e)
+                    {
+                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
+                        cell().growColumn();
+                        syncValue();
+                        toolbar.editorcore.onEditorEvent();
+                    }
+                },
+                xns : rooui.Toolbar
+            },
             
+            {
+                xtype : 'TextItem',
+                text : "Vertical Align: ",
+                xns : rooui.Toolbar  //Boostrap?
+            },
+            {
+                xtype : 'ComboBox',
+                allowBlank : false,
+                displayField : 'val',
+                editable : true,
+                listWidth : 100,
+                triggerAction : 'all',
+                typeAhead : true,
+                valueField : 'val',
+                width : 100,
+                name : 'valign',
+                listeners : {
+                    select : function (combo, r, index)
+                    {
+                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
+                        var b = cell();
+                        b.valign = r.get('val');
+                        b.updateElement();
+                        syncValue();
+                        toolbar.editorcore.onEditorEvent();
+                    }
+                },
+                xns : rooui.form,
+                store : {
+                    xtype : 'SimpleStore',
+                    data : [
+                        ['top'],
+                        ['middle'],
+                        ['bottom'] // there are afew more... 
+                    ],
+                    fields : [ 'val'],
+                    xns : Roo.data
+                }
+            },
             
-        }, this);
-        
-        this.list.beginUpdate();
-        this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
-        this.list.alignTo(this.el, this.listAlign);
-        this.list.endUpdate();
-        
-    },
-     
-    
-    // -- store handlers..
-    // private
-    onBeforeLoad : function()
-    {
-        if(!this.hasFocus){
-            return;
-        }
-        this.innerLists[0].update(this.loadingText ?
-               '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
-        this.restrictHeight();
-        this.selectedIndex = -1;
-    },
-    // private
-    onLoad : function(a,b,c,d)
-    {
-        if (!this.loadingChildren) {
-            // then we are loading the top level. - hide the children
-            for (var i = 1;i < this.views.length; i++) {
-                this.views[i].getEl().setStyle({ display : 'none' });
-            }
-            var lw = Math.floor(
-                ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
-            );
-        
-             this.list.setWidth(lw); // default to '1'
-
+            {
+                xtype : 'TextItem',
+                text : "Merge Cells: ",
+                 xns : rooui.Toolbar 
+               
+            },
             
-        }
-        if(!this.hasFocus){
-            return;
-        }
-        
-        if(this.store.getCount() > 0) {
-            this.expand();
-            this.restrictHeight();   
-        } else {
-            this.onEmptyResults();
-        }
-        
-        if (!this.loadingChildren) {
-            this.selectActive();
-        }
-        /*
-        this.stores[1].loadData([]);
-        this.stores[2].loadData([]);
-        this.views
-        */    
-    
-        //this.el.focus();
-    },
-    
-    
-    // private
-    onLoadException : function()
-    {
-        this.collapse();
-        Roo.log(this.store.reader.jsonData);
-        if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
-            Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
-        }
-        
+            
+            {
+                xtype : 'Button',
+                text: 'Right',
+                listeners : {
+                    click : function (_self, e)
+                    {
+                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
+                        cell().mergeRight();
+                        //block().growColumn();
+                        syncValue();
+                        toolbar.editorcore.onEditorEvent();
+                    }
+                },
+                xns : rooui.Toolbar
+            },
+             
+            {
+                xtype : 'Button',
+                text: 'Below',
+                listeners : {
+                    click : function (_self, e)
+                    {
+                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
+                        cell().mergeBelow();
+                        //block().growColumn();
+                        syncValue();
+                        toolbar.editorcore.onEditorEvent();
+                    }
+                },
+                xns : rooui.Toolbar
+            },
+            {
+                xtype : 'TextItem',
+                text : "| ",
+                 xns : rooui.Toolbar 
+               
+            },
+            
+            {
+                xtype : 'Button',
+                text: 'Split',
+                listeners : {
+                    click : function (_self, e)
+                    {
+                        //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
+                        cell().split();
+                        syncValue();
+                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
+                        toolbar.editorcore.onEditorEvent();
+                                             
+                    }
+                },
+                xns : rooui.Toolbar
+            },
+            {
+                xtype : 'Fill',
+                xns : rooui.Toolbar 
+               
+            },
         
-    },
-    // no cleaning of leading spaces on blur here.
-    cleanLeadingSpace : function(e) { },
-    
-
-    onSelectChange : function (view, sels, opts )
-    {
-        var ix = view.getSelectedIndexes();
-         
-        if (opts.list > this.maxColumns - 2) {
-            if (view.store.getCount()<  1) {
-                this.views[opts.list ].getEl().setStyle({ display :   'none' });
-
-            } else  {
-                if (ix.length) {
-                    // used to clear ?? but if we are loading unselected 
-                    this.setFromData(view.store.getAt(ix[0]).data);
+          
+            {
+                xtype : 'Button',
+                text: 'Delete',
+                 
+                xns : rooui.Toolbar,
+                menu : {
+                    xtype : 'Menu',
+                    xns : rooui.menu,
+                    items : [
+                        {
+                            xtype : 'Item',
+                            html: 'Column',
+                            listeners : {
+                                click : function (_self, e)
+                                {
+                                    var t = table();
+                                    
+                                    cell().deleteColumn();
+                                    syncValue();
+                                    toolbar.editorcore.selectNode(t.node);
+                                    toolbar.editorcore.onEditorEvent();   
+                                }
+                            },
+                            xns : rooui.menu
+                        },
+                        {
+                            xtype : 'Item',
+                            html: 'Row',
+                            listeners : {
+                                click : function (_self, e)
+                                {
+                                    var t = table();
+                                    cell().deleteRow();
+                                    syncValue();
+                                    
+                                    toolbar.editorcore.selectNode(t.node);
+                                    toolbar.editorcore.onEditorEvent();   
+                                                         
+                                }
+                            },
+                            xns : rooui.menu
+                        },
+                       {
+                            xtype : 'Separator',
+                            xns : rooui.menu
+                        },
+                        {
+                            xtype : 'Item',
+                            html: 'Table',
+                            listeners : {
+                                click : function (_self, e)
+                                {
+                                    var t = table();
+                                    var nn = t.node.nextSibling || t.node.previousSibling;
+                                    t.node.parentNode.removeChild(t.node);
+                                    if (nn) { 
+                                        toolbar.editorcore.selectNode(nn, true);
+                                    }
+                                    toolbar.editorcore.onEditorEvent();   
+                                                         
+                                }
+                            },
+                            xns : rooui.menu
+                        }
+                    ]
                 }
-                
             }
             
-            return;
-        }
+            // align... << fixme
+            
+        ];
         
-        if (!ix.length) {
-            // this get's fired when trigger opens..
-           // this.setFromData({});
-            var str = this.stores[opts.list+1];
-            str.data.clear(); // removeall wihtout the fire events..
-            return;
+    },
+    
+    
+  /**
+     * create a DomHelper friendly object - for use with
+     * Roo.DomHelper.markup / overwrite / etc..
+     * ?? should it be called with option to hide all editing features?
+     */
+ /**
+     * create a DomHelper friendly object - for use with
+     * Roo.DomHelper.markup / overwrite / etc..
+     * ?? should it be called with option to hide all editing features?
+     */
+    toObject : function()
+    {
+        var ret = {
+            tag : 'td',
+            contenteditable : 'true', // this stops cell selection from picking the table.
+            'data-block' : 'Td',
+            valign : this.valign,
+            style : {  
+                'text-align' :  this.textAlign,
+                border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
+                'border-collapse' : 'collapse',
+                padding : '6px', // 8 for desktop / 4 for mobile
+                'vertical-align': this.valign
+            },
+            html : this.html
+        };
+        if (this.width != '') {
+            ret.width = this.width;
+            ret.style.width = this.width;
         }
         
-        var rec = view.store.getAt(ix[0]);
-         
-        this.setFromData(rec.data);
-        this.fireEvent('select', this, rec, ix[0]);
-        
-        var lw = Math.floor(
-             (
-                (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
-             ) / this.maxColumns
-        );
-        this.loadingChildren = true;
-        this.stores[opts.list+1].loadDataFromChildren( rec );
-        this.loadingChildren = false;
-        var dl = this.stores[opts.list+1]. getTotalCount();
-        
-        this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
         
-        this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
-        for (var i = opts.list+2; i < this.views.length;i++) {
-            this.views[i].getEl().setStyle({ display : 'none' });
+        if (this.colspan > 1) {
+            ret.colspan = this.colspan ;
+        } 
+        if (this.rowspan > 1) {
+            ret.rowspan = this.rowspan ;
         }
         
-        this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
-        this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
+           
         
-        if (this.isLoading) {
-           // this.selectActive(opts.list);
-        }
+        return ret;
          
     },
     
-    
-    
-    
-    onDoubleClick : function()
+    readElement : function(node)
     {
-        this.collapse(); //??
+        node  = node ? node : this.node ;
+        this.width = node.style.width;
+        this.colspan = Math.max(1,1*node.getAttribute('colspan'));
+        this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
+        this.html = node.innerHTML;
+        
+        
     },
-    
      
+    // the default cell object... at present...
+    emptyCell : function() {
+        return {
+            colspan :  1,
+            rowspan :  1,
+            textAlign : 'left',
+            html : "&nbsp;" // is this going to be editable now?
+        };
+     
+    },
     
+    removeNode : function()
+    {
+        return this.node.closest('table');
+         
+    },
     
+    cellData : false,
     
-    // private
-    recordToStack : function(store, prop, value, stack)
+    colWidths : false,
+    
+    toTableArray  : function()
     {
-        var cstore = new Roo.data.SimpleStore({
-            //fields : this.store.reader.meta.fields, // we need array reader.. for
-            reader : this.store.reader,
-            data : [ ]
+        var ret = [];
+        var tab = this.node.closest('tr').closest('table');
+        Array.from(tab.rows).forEach(function(r, ri){
+            ret[ri] = [];
         });
-        var _this = this;
-        var record  = false;
-        var srec = false;
-        if(store.getCount() < 1){
-            return false;
-        }
-        store.each(function(r){
-            if(r.data[prop] == value){
-                record = r;
-            srec = r;
-                return false;
-            }
-            if (r.data.cn && r.data.cn.length) {
-                cstore.loadDataFromChildren( r);
-                var cret = _this.recordToStack(cstore, prop, value, stack);
-                if (cret !== false) {
-                    record = cret;
-                    srec = r;
-                    return false;
+        var rn = 0;
+        this.colWidths = [];
+        var all_auto = true;
+        Array.from(tab.rows).forEach(function(r, ri){
+            
+            var cn = 0;
+            Array.from(r.cells).forEach(function(ce, ci){
+                var c =  {
+                    cell : ce,
+                    row : rn,
+                    col: cn,
+                    colspan : ce.colSpan,
+                    rowspan : ce.rowSpan
+                };
+                if (ce.isEqualNode(this.node)) {
+                    this.cellData = c;
                 }
-            }
-             
-            return true;
-        });
-        if (record == false) {
-            return false
+                // if we have been filled up by a row?
+                if (typeof(ret[rn][cn]) != 'undefined') {
+                    while(typeof(ret[rn][cn]) != 'undefined') {
+                        cn++;
+                    }
+                    c.col = cn;
+                }
+                
+                if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
+                    this.colWidths[cn] =   ce.style.width;
+                    if (this.colWidths[cn] != '') {
+                        all_auto = false;
+                    }
+                }
+                
+                
+                if (c.colspan < 2 && c.rowspan < 2 ) {
+                    ret[rn][cn] = c;
+                    cn++;
+                    return;
+                }
+                for(var j = 0; j < c.rowspan; j++) {
+                    if (typeof(ret[rn+j]) == 'undefined') {
+                        continue; // we have a problem..
+                    }
+                    ret[rn+j][cn] = c;
+                    for(var i = 0; i < c.colspan; i++) {
+                        ret[rn+j][cn+i] = c;
+                    }
+                }
+                
+                cn += c.colspan;
+            }, this);
+            rn++;
+        }, this);
+        
+        // initalize widths.?
+        // either all widths or no widths..
+        if (all_auto) {
+            this.colWidths[0] = false; // no widths flag.
         }
-        stack.unshift(srec);
-        return record;
+        
+        
+        return ret;
+        
     },
     
-    /*
-     * find the stack of stores that match our value.
-     *
-     * 
-     */
-    
-    selectActive : function ()
-    {
-       // if store is not loaded, then we will need to wait for that to happen first.
-        var stack = [];
-        this.recordToStack(this.store, this.valueField, this.getValue(), stack);
-        for (var i = 0; i < stack.length; i++ ) {
-            this.views[i].select(stack[i].store.indexOf(stack[i]), false, 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.form.Checkbox
- * @extends Roo.form.Field
- * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
- * @constructor
- * Creates a new Checkbox
- * @param {Object} config Configuration options
- */
-Roo.form.Checkbox = function(config){
-    Roo.form.Checkbox.superclass.constructor.call(this, config);
-    this.addEvents({
-        /**
-         * @event check
-         * Fires when the checkbox is checked or unchecked.
-            * @param {Roo.form.Checkbox} this This checkbox
-            * @param {Boolean} checked The new checked value
-            */
-        check : true
-    });
-};
-
-Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
-    /**
-     * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
-     */
-    focusClass : undefined,
-    /**
-     * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
-     */
-    fieldClass: "x-form-field",
-    /**
-     * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
-     */
-    checked: false,
-    /**
-     * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
-     * {tag: "input", type: "checkbox", autocomplete: "off"})
-     */
-    defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
-    /**
-     * @cfg {String} boxLabel The text that appears beside the checkbox
-     */
-    boxLabel : "",
-    /**
-     * @cfg {String} inputValue The value that should go into the generated input element's value attribute
-     */  
-    inputValue : '1',
-    /**
-     * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
-     */
-     valueOff: '0', // value when not checked..
-
-    actionMode : 'viewEl', 
-    //
-    // private
-    itemCls : 'x-menu-check-item x-form-item',
-    groupClass : 'x-menu-group-item',
-    inputType : 'hidden',
-    
     
-    inSetChecked: false, // check that we are not calling self...
     
-    inputElement: false, // real input element?
-    basedOn: false, // ????
     
-    isFormField: true, // not sure where this is needed!!!!
-
-    onResize : function(){
-        Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
-        if(!this.boxLabel){
-            this.el.alignTo(this.wrap, 'c-c');
-        }
-    },
-
-    initEvents : function(){
-        Roo.form.Checkbox.superclass.initEvents.call(this);
-        this.el.on("click", this.onClick,  this);
-        this.el.on("change", this.onClick,  this);
-    },
-
-
-    getResizeEl : function(){
-        return this.wrap;
-    },
-
-    getPositionEl : function(){
-        return this.wrap;
-    },
-
-    // private
-    onRender : function(ct, position){
-        Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
-        /*
-        if(this.inputValue !== undefined){
-            this.el.dom.value = this.inputValue;
+    mergeRight: function()
+    {
+         
+        // get the contents of the next cell along..
+        var tr = this.node.closest('tr');
+        var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
+        if (i >= tr.childNodes.length - 1) {
+            return; // no cells on right to merge with.
         }
-        */
-        //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
-        this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
-        var viewEl = this.wrap.createChild({ 
-            tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
-        this.viewEl = viewEl;   
-        this.wrap.on('click', this.onClick,  this); 
+        var table = this.toTableArray();
         
-        this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
-        this.el.on('propertychange', this.setFromHidden,  this);  //ie
+        if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
+            return; // nothing right?
+        }
+        var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
+        // right cell - must be same rowspan and on the same row.
+        if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
+            return; // right hand side is not same rowspan.
+        }
         
         
         
-        if(this.boxLabel){
-            this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
-        //    viewEl.on('click', this.onClick,  this); 
-        }
-        //if(this.checked){
-            this.setChecked(this.checked);
-        //}else{
-            //this.checked = this.el.dom;
-        //}
+        this.node.innerHTML += ' ' + rc.cell.innerHTML;
+        tr.removeChild(rc.cell);
+        this.colspan += rc.colspan;
+        this.node.setAttribute('colspan', this.colspan);
 
+        var table = this.toTableArray();
+        this.normalizeWidths(table);
+        this.updateWidths(table);
     },
-
-    // private
-    initValue : Roo.emptyFn,
-
-    /**
-     * Returns the checked state of the checkbox.
-     * @return {Boolean} True if checked, else false
-     */
-    getValue : function(){
-        if(this.el){
-            return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
+    
+    
+    mergeBelow : function()
+    {
+        var table = this.toTableArray();
+        if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
+            return; // no row below
         }
-        return this.valueOff;
+        if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
+            return; // nothing right?
+        }
+        var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
         
+        if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
+            return; // right hand side is not same rowspan.
+        }
+        this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
+        rc.cell.parentNode.removeChild(rc.cell);
+        this.rowspan += rc.rowspan;
+        this.node.setAttribute('rowspan', this.rowspan);
     },
-
-       // private
-    onClick : function(){ 
-        if (this.disabled) {
+    
+    split: function()
+    {
+        if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
             return;
         }
-        this.setChecked(!this.checked);
-
-        //if(this.el.dom.checked != this.checked){
-        //    this.setValue(this.el.dom.checked);
-       // }
+        var table = this.toTableArray();
+        var cd = this.cellData;
+        this.rowspan = 1;
+        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');
+                }
+                 
+                var ntd = this.node.cloneNode(); // which col/row should be 0..
+                ntd.removeAttribute('id'); 
+                ntd.style.width  = this.colWidths[c];
+                ntd.innerHTML = '';
+                table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
+            }
+            
+        }
+        this.redrawAllCells(table);
+        
     },
-
-    /**
-     * 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){
+    
+    
+    
+    redrawAllCells: function(table)
+    {
         
+         
+        var tab = this.node.closest('tr').closest('table');
+        var ctr = tab.rows[0].parentNode;
+        Array.from(tab.rows).forEach(function(r, ri){
+            
+            Array.from(r.cells).forEach(function(ce, ci){
+                ce.parentNode.removeChild(ce);
+            });
+            r.parentNode.removeChild(r);
+        });
+        for(var r = 0 ; r < table.length; r++) {
+            var re = tab.rows[r];
+            
+            var re = tab.ownerDocument.createElement('tr');
+            ctr.appendChild(re);
+            for(var c = 0 ; c < table[r].length; c++) {
+                if (table[r][c].cell === false) {
+                    continue;
+                }
+                
+                re.appendChild(table[r][c].cell);
+                 
+                table[r][c].cell = false;
+            }
+        }
         
-        //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.fireEvent("check", this, this.checked);
     },
-    // private..
-    setChecked : function(state,suppressEvent)
+    updateWidths : function(table)
     {
-        if (this.inSetChecked) {
-            this.checked = state;
+        for(var r = 0 ; r < table.length; r++) {
+           
+            for(var c = 0 ; c < table[r].length; c++) {
+                if (table[r][c].cell === false) {
+                    continue;
+                }
+                
+                if (this.colWidths[0] != false && table[r][c].colspan < 2) {
+                    var el = Roo.htmleditor.Block.factory(table[r][c].cell);
+                    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) {
+                this.colWidths[i] = nw;
+            },this);
             return;
         }
-        
     
-        if(this.wrap){
-            this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
-        }
-        this.checked = state;
-        if(suppressEvent !== true){
-            this.fireEvent('check', this, state);
+        var t = 0, missing = [];
+        
+        this.colWidths.forEach(function(w,i) {
+            //if you mix % and
+            this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
+            var add =  this.colWidths[i];
+            if (add > 0) {
+                t+=add;
+                return;
+            }
+            missing.push(i);
+            
+            
+        },this);
+        var nc = this.colWidths.length;
+        if (missing.length) {
+            var mult = (nc - missing.length) / (1.0 * nc);
+            var t = mult * t;
+            var ew = (100 -t) / (1.0 * missing.length);
+            this.colWidths.forEach(function(w,i) {
+                if (w > 0) {
+                    this.colWidths[i] = w * mult;
+                    return;
+                }
+                
+                this.colWidths[i] = ew;
+            }, this);
+            // have to make up numbers..
+             
         }
-        this.inSetChecked = true;
-        this.el.dom.value = state ? this.inputValue : this.valueOff;
-        this.inSetChecked = false;
+        // now we should have all the widths..
         
+    
     },
-    // handle setting of hidden value by some other method!!?!?
-    setFromHidden: function()
+    
+    shrinkColumn : function()
     {
-        if(!this.el){
+        var table = this.toTableArray();
+        this.normalizeWidths(table);
+        var col = this.cellData.col;
+        var nw = this.colWidths[col] * 0.8;
+        if (nw < 5) {
             return;
         }
-        //console.log("SET FROM HIDDEN");
-        //alert('setFrom hidden');
-        this.setValue(this.el.dom.value);
+        var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
+        this.colWidths.forEach(function(w,i) {
+            if (i == col) {
+                 this.colWidths[i] = nw;
+                return;
+            }
+            this.colWidths[i] += otherAdd
+        }, this);
+        this.updateWidths(table);
+         
     },
-    
-    onDestroy : function()
+    growColumn : function()
     {
-        if(this.viewEl){
-            Roo.get(this.viewEl).remove();
+        var table = this.toTableArray();
+        this.normalizeWidths(table);
+        var col = this.cellData.col;
+        var nw = this.colWidths[col] * 1.2;
+        if (nw > 90) {
+            return;
         }
+        var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
+        this.colWidths.forEach(function(w,i) {
+            if (i == col) {
+                this.colWidths[i] = nw;
+                return;
+            }
+            this.colWidths[i] -= otherSub
+        }, this);
+        this.updateWidths(table);
          
-        Roo.form.Checkbox.superclass.onDestroy.call(this);
     },
-    
-    setBoxLabel : function(str)
+    deleteRow : function()
     {
-        this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
-    }
-
-});/*
- * 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.form.Radio
- * @extends Roo.form.Checkbox
- * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
- * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
- * @constructor
- * Creates a new Radio
- * @param {Object} config Configuration options
- */
-Roo.form.Radio = function(){
-    Roo.form.Radio.superclass.constructor.apply(this, arguments);
-};
-Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
-    inputType: 'radio',
-
-    /**
-     * If this radio is part of a group, it will return the selected value
-     * @return {String}
-     */
-    getGroupValue : function(){
-        return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
-    },
-    
-    
-    onRender : function(ct, position){
-        Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
-        
-        if(this.inputValue !== undefined){
-            this.el.dom.value = this.inputValue;
+        // delete this rows 'tr'
+        // if any of the cells in this row have a rowspan > 1 && row!= this row..
+        // then reduce the rowspan.
+        var table = this.toTableArray();
+        // this.cellData.row;
+        for (var i =0;i< table[this.cellData.row].length ; i++) {
+            var c = table[this.cellData.row][i];
+            if (c.row != this.cellData.row) {
+                
+                c.rowspan--;
+                c.cell.setAttribute('rowspan', c.rowspan);
+                continue;
+            }
+            if (c.rowspan > 1) {
+                c.rowspan--;
+                c.cell.setAttribute('rowspan', c.rowspan);
+            }
         }
-         
-        this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
-        //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
-        //var viewEl = this.wrap.createChild({ 
-        //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
-        //this.viewEl = viewEl;   
-        //this.wrap.on('click', this.onClick,  this); 
-        
-        //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
-        //this.el.on('propertychange', this.setFromHidden,  this);  //ie
-        
+        table.splice(this.cellData.row,1);
+        this.redrawAllCells(table);
         
+    },
+    deleteColumn : function()
+    {
+        var table = this.toTableArray();
         
-        if(this.boxLabel){
-            this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
-        //    viewEl.on('click', this.onClick,  this); 
-        }
-         if(this.checked){
-            this.el.dom.checked =   'checked' ;
+        for (var i =0;i< table.length ; i++) {
+            var c = table[i][this.cellData.col];
+            if (c.col != this.cellData.col) {
+                table[i][this.cellData.col].colspan--;
+            } else if (c.colspan > 1) {
+                c.colspan--;
+                c.cell.setAttribute('colspan', c.colspan);
+            }
+            table[i].splice(this.cellData.col,1);
         }
-         
-    } 
+        
+        this.redrawAllCells(table);
+    }
+    
     
     
-});//<script type="text/javascript">
+    
+})
+
+//<script type="text/javascript">
 
 /*
  * Based  Ext JS Library 1.1.1
@@ -43490,7 +49802,8 @@ Roo.HtmlEditorCore = function(config){
          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
          * @param {Roo.HtmlEditorCore} this
          */
-        editorevent: true
+        editorevent: true 
+         
         
     });
     
@@ -43526,13 +49839,32 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
      * @cfg {Number} width (in pixels)
      */   
     width: 500,
+     /**
+     * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
+     *         if you are doing an email editor, this probably needs disabling, it's designed
+     */
+    autoClean: true,
     
+    /**
+     * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
+     */
+    enableBlocks : true,
     /**
      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
      * 
      */
     stylesheets: false,
+     /**
+     * @cfg {String} language default en - language of text (usefull for rtl languages)
+     * 
+     */
+    language: 'en',
     
+    /**
+     * @cfg {boolean} allowComments - default false - allow comments in HTML source
+     *          - by default they are stripped - if you are editing email you may need this.
+     */
+    allowComments: false,
     // id of frame..
     frameId: false,
     
@@ -43554,6 +49886,8 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
      
     bodyCls : '',
 
+    
+    undoManager : false,
     /**
      * Protected method that will not generally be called directly. It
      * is called when the editor initializes the iframe with HTML contents. Override this method if you
@@ -43579,27 +49913,33 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
                 st = '<style type="text/css">' +
                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
                    '</style>';
-        } else { 
-            st = '<style type="text/css">' +
-                    this.stylesheets +
-                '</style>';
+        } else {
+            for (var i in this.stylesheets) {
+                if (typeof(this.stylesheets[i]) != 'string') {
+                    continue;
+                }
+                st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
+            }
+            
         }
         
         st +=  '<style type="text/css">' +
             'IMG { cursor: pointer } ' +
         '</style>';
-
-        var cls = 'roo-htmleditor-body';
+        
+        st += '<meta name="google" content="notranslate">';
+        
+        var cls = 'notranslate roo-htmleditor-body';
         
         if(this.bodyCls.length){
             cls += ' ' + this.bodyCls;
         }
         
-        return '<html><head>' + st  +
+        return '<html  class="notranslate" translate="no"><head>' + st  +
             //<style type="text/css">' +
             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
             //'</style>' +
-            ' </head><body class="' +  cls + '"></body></html>';
+            ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
     },
 
     // private
@@ -43638,7 +49978,7 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
         
         this.iframe = iframe.dom;
 
-         this.assignDocWin();
+        this.assignDocWin();
         
         this.doc.designMode = 'on';
        
@@ -43654,6 +49994,7 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
                 if(this.doc.body || this.doc.readyState == 'complete'){
                     try {
                         this.doc.designMode="on";
+                        
                     } catch (e) {
                         return;
                     }
@@ -43701,10 +50042,10 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
         
         if(this.sourceEditMode){
  
-            Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
+            Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
             
         }else{
-            Roo.get(this.iframe).removeClass(['x-hidden','hide']);
+            Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
             //this.iframe.className = '';
             this.deferFocus();
         }
@@ -43721,7 +50062,8 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
      * @param {String} html The HTML to be cleaned
      * return {String} The cleaned HTML
      */
-    cleanHtml : function(html){
+    cleanHtml : function(html)
+    {
         html = String(html);
         if(html.length > 5){
             if(Roo.isSafari){ // strip safari nonsense
@@ -43739,11 +50081,40 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
      * Protected method that will not generally be called directly. Syncs the contents
      * of the editor iframe with the textarea.
      */
-    syncValue : function(){
+    syncValue : function()
+    {
+        //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
         if(this.initialized){
+            
+            if (this.undoManager) {
+                this.undoManager.addEvent();
+            }
+
+            
             var bd = (this.doc.body || this.doc.documentElement);
-            //this.cleanUpPaste(); -- this is done else where and causes havoc..
-            var html = bd.innerHTML;
+           
+            
+            var sel = this.win.getSelection();
+            
+            var div = document.createElement('div');
+            div.innerHTML = bd.innerHTML;
+            var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
+            if (gtx.length > 0) {
+                var rm = gtx.item(0).parentNode;
+                rm.parentNode.removeChild(rm);
+            }
+            
+           
+            if (this.enableBlocks) {
+                new Roo.htmleditor.FilterBlock({ node : div });
+            }
+            //?? tidy?
+            var tidy = new Roo.htmleditor.TidySerializer({
+                inner:  true
+            });
+            var html  = tidy.serialize(div);
+            
+            
             if(Roo.isSafari){
                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
@@ -43788,24 +50159,41 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
     },
 
     /**
+     * TEXTAREA -> EDITABLE
      * Protected method that will not generally be called directly. Pushes the value of the textarea
      * into the iframe editor.
      */
-    pushValue : function(){
+    pushValue : function()
+    {
+        //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
         if(this.initialized){
             var v = this.el.dom.value.trim();
             
-//            if(v.length < 1){
-//                v = '&#160;';
-//            }
             
             if(this.owner.fireEvent('beforepush', this, v) !== false){
                 var d = (this.doc.body || this.doc.documentElement);
                 d.innerHTML = v;
-                this.cleanUpPaste();
+                 
                 this.el.dom.value = d.innerHTML;
                 this.owner.fireEvent('push', this, v);
             }
+            if (this.autoClean) {
+                new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
+                new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
+            }
+            if (this.enableBlocks) {
+                Roo.htmleditor.Block.initAll(this.doc.body);
+            }
+            
+            this.updateLanguage();
+            
+            var lc = this.doc.body.lastChild;
+            if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
+                // add an extra line at the end.
+                this.doc.body.appendChild(this.doc.createElement('br'));
+            }
+            
+            
         }
     },
 
@@ -43867,28 +50255,146 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
         //ss['background-attachment'] = 'fixed'; // w3c
         dbody.bgProperties = 'fixed'; // ie
+        dbody.setAttribute("translate", "no");
+        
         //Roo.DomHelper.applyStyles(dbody, ss);
         Roo.EventManager.on(this.doc, {
-            //'mousedown': this.onEditorEvent,
+             
             'mouseup': this.onEditorEvent,
             'dblclick': this.onEditorEvent,
             'click': this.onEditorEvent,
             'keyup': this.onEditorEvent,
+            
             buffer:100,
             scope: this
         });
+        Roo.EventManager.on(this.doc, {
+            'paste': this.onPasteEvent,
+            scope : this
+        });
         if(Roo.isGecko){
             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
         }
+        //??? needed???
         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
         }
         this.initialized = true;
 
+        
+        // initialize special key events - enter
+        new Roo.htmleditor.KeyEnter({core : this});
+        
+         
+        
         this.owner.fireEvent('initialize', this);
         this.pushValue();
     },
-
+    // this is to prevent a href clicks resulting in a redirect?
+   
+    onPasteEvent : function(e,v)
+    {
+        // I think we better assume paste is going to be a dirty load of rubish from word..
+        
+        // even pasting into a 'email version' of this widget will have to clean up that mess.
+        var cd = (e.browserEvent.clipboardData || window.clipboardData);
+        
+        // check what type of paste - if it's an image, then handle it differently.
+        if (cd.files && cd.files.length > 0) {
+            // pasting images?
+            var urlAPI = (window.createObjectURL && window) || 
+                (window.URL && URL.revokeObjectURL && URL) || 
+                (window.webkitURL && webkitURL);
+    
+            var url = urlAPI.createObjectURL( cd.files[0]);
+            this.insertAtCursor('<img src=" + url + ">');
+            return false;
+        }
+        if (cd.types.indexOf('text/html') < 0 ) {
+            return false;
+        }
+        var images = [];
+        var html = cd.getData('text/html'); // clipboard event
+        if (cd.types.indexOf('text/rtf') > -1) {
+            var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
+            images = parser.doc ? parser.doc.getElementsByType('pict') : [];
+        }
+        //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'; });
+        
+        
+        html = this.cleanWordChars(html);
+        
+        var d = (new DOMParser().parseFromString(html, 'text/html')).body;
+        
+        
+        var sn = this.getParentElement();
+        // check if d contains a table, and prevent nesting??
+        //Roo.log(d.getElementsByTagName('table'));
+        //Roo.log(sn);
+        //Roo.log(sn.closest('table'));
+        if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
+            e.preventDefault();
+            this.insertAtCursor("You can not nest tables");
+            //Roo.log("prevent?"); // fixme - 
+            return false;
+        }
+        
+        if (images.length > 0) {
+            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', 'colspan', 'rowspan', 'data-display', 'data-width'],
+                attrib_clean : ['href', 'src' ] 
+            });
+            new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
+            // should be fonts..
+            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) {
+                
+            Array.from(d.getElementsByTagName('img')).forEach(function(img) {
+                if (img.closest('figure')) { // assume!! that it's aready
+                    return;
+                }
+                var fig  = new Roo.htmleditor.BlockFigure({
+                    image_src  : img.src
+                });
+                fig.updateElement(img); // replace it..
+                
+            });
+        }
+        
+        
+        this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
+        if (this.enableBlocks) {
+            Roo.htmleditor.Block.initAll(this.doc.body);
+        }
+         
+        
+        e.preventDefault();
+        return false;
+        // default behaveiour should be our local cleanup paste? (optional?)
+        // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
+        //this.owner.fireEvent('paste', e, v);
+    },
     // private
     onDestroy : function(){
         
@@ -43910,7 +50416,7 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
     onFirstFocus : function(){
         
         this.assignDocWin();
-        
+        this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
         
         this.activated = true;
          
@@ -43955,10 +50461,48 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
 
     onEditorEvent : function(e)
     {
-        this.owner.fireEvent('editorevent', this, e);
+         
+        
+        if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
+            return; // we do not handle this.. (undo manager does..)
+        }
+        // in theory this detects if the last element is not a br, then we try and do that.
+        // its so clicking in space at bottom triggers adding a br and moving the cursor.
+        if (e &&
+            e.target.nodeName == 'BODY' &&
+            e.type == "mouseup" &&
+            this.doc.body.lastChild
+           ) {
+            var lc = this.doc.body.lastChild;
+            // gtx-trans is google translate plugin adding crap.
+            while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
+                lc = lc.previousSibling;
+            }
+            if (lc.nodeType == 1 && lc.nodeName != 'BR') {
+            // if last element is <BR> - then dont do anything.
+            
+                var ns = this.doc.createElement('br');
+                this.doc.body.appendChild(ns);
+                range = this.doc.createRange();
+                range.setStartAfter(ns);
+                range.collapse(true);
+                var sel = this.win.getSelection();
+                sel.removeAllRanges();
+                sel.addRange(range);
+            }
+        }
+        
+        
+        
+        this.fireEditorEvent(e);
       //  this.updateToolbar();
         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
     },
+    
+    fireEditorEvent: function(e)
+    {
+        this.owner.fireEvent('editorevent', this, e);
+    },
 
     insertTag : function(tg)
     {
@@ -43980,7 +50524,7 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
             
         }
         this.execCmd("formatblock",   tg);
-        
+        this.undoManager.addEvent(); 
     },
     
     insertText : function(txt)
@@ -43992,6 +50536,7 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
                //alert(Sender.getAttribute('label'));
                
         range.insertNode(this.doc.createTextNode(txt));
+        this.undoManager.addEvent();
     } ,
     
      
@@ -44002,7 +50547,37 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
      * @param {String} cmd The Midas command
      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
      */
-    relayCmd : function(cmd, value){
+    relayCmd : function(cmd, value)
+    {
+        
+        switch (cmd) {
+            case 'justifyleft':
+            case 'justifyright':
+            case 'justifycenter':
+                // if we are in a cell, then we will adjust the
+                var n = this.getParentElement();
+                var td = n.closest('td');
+                if (td) {
+                    var bl = Roo.htmleditor.Block.factory(td);
+                    bl.textAlign = cmd.replace('justify','');
+                    bl.updateElement();
+                    this.owner.fireEvent('editorevent', this);
+                    return;
+                }
+                this.execCmd('styleWithCSS', true); // 
+                break;
+            case 'bold':
+            case 'italic':
+                // if there is no selection, then we insert, and set the curson inside it..
+                this.execCmd('styleWithCSS', false); 
+                break;
+                
+        
+            default:
+                break;
+        }
+        
+        
         this.win.focus();
         this.execCmd(cmd, value);
         this.owner.fireEvent('editorevent', this);
@@ -44035,20 +50610,7 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
         if(!this.activated){
             return;
         }
-        /*
-        if(Roo.isIE){
-            this.win.focus();
-            var r = this.doc.selection.createRange();
-            if(r){
-                r.collapse(true);
-                r.pasteHTML(text);
-                this.syncValue();
-                this.deferFocus();
-            
-            }
-            return;
-        }
-        */
+         
         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
             this.win.focus();
             
@@ -44058,19 +50620,31 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
             var win = this.win;
             
             if (win.getSelection && win.getSelection().getRangeAt) {
+                
+                // delete the existing?
+                
+                this.createRange(this.getSelection()).deleteContents();
                 range = win.getSelection().getRangeAt(0);
                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
                 range.insertNode(node);
+                range = range.cloneRange();
+                range.collapse(false);
+                 
+                win.getSelection().removeAllRanges();
+                win.getSelection().addRange(range);
+                
+                
+                
             } else if (win.document.selection && win.document.selection.createRange) {
                 // no firefox support
                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
                 win.document.selection.createRange().pasteHTML(txt);
+            
             } else {
                 // no firefox support
                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
                 this.execCmd('InsertHTML', txt);
             } 
-            
             this.syncValue();
             
             this.deferFocus();
@@ -44095,15 +50669,17 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
                         cmd = 'underline';
                         break;
                     
-                    case 'v':
-                        this.cleanUpPaste.defer(100, this);
-                        return;
+                    //case 'v':
+                      //  this.cleanUpPaste.defer(100, this);
+                      //  return;
                         
                 }
                 if(cmd){
-                    this.win.focus();
-                    this.execCmd(cmd);
-                    this.deferFocus();
+                    
+                    this.relayCmd(cmd);
+                    //this.win.focus();
+                    //this.execCmd(cmd);
+                    //this.deferFocus();
                     e.preventDefault();
                 }
                 
@@ -44113,6 +50689,8 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
 
     // private
     fixKeys : function(){ // load time branching for fastest keydown performance
+        
+        
         if(Roo.isIE){
             return function(e){
                 var k = e.getKey(), r;
@@ -44126,23 +50704,25 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
                     }
                     return;
                 }
-                
+                /// this is handled by Roo.htmleditor.KeyEnter
+                 /*
                 if(k == e.ENTER){
                     r = this.doc.selection.createRange();
                     if(r){
                         var target = r.parentElement();
                         if(!target || target.tagName.toLowerCase() != 'li'){
                             e.stopEvent();
-                            r.pasteHTML('<br />');
+                            r.pasteHTML('<br/>');
                             r.collapse(false);
                             r.select();
                         }
                     }
                 }
-                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
-                    this.cleanUpPaste.defer(100, this);
-                    return;
-                }
+                */
+                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
+                //    this.cleanUpPaste.defer(100, this);
+                //    return;
+                //}
                 
                 
             };
@@ -44155,10 +50735,11 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
                     this.deferFocus();
                 }
-                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
-                    this.cleanUpPaste.defer(100, this);
-                    return;
-                }
+               
+                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
+                //    this.cleanUpPaste.defer(100, this);
+                 //   return;
+                //}
                 
             };
         }else if(Roo.isSafari){
@@ -44171,10 +50752,12 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
                     this.deferFocus();
                     return;
                 }
-               if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
-                    this.cleanUpPaste.defer(100, this);
-                    return;
-                }
+                 this.mozKeyPress(e);
+                
+               //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
+                 //   this.cleanUpPaste.defer(100, this);
+                 //   return;
+               // }
                 
              };
         }
@@ -44204,7 +50787,27 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
     getSelection : function() 
     {
         this.assignDocWin();
-        return Roo.isIE ? this.doc.selection : this.win.getSelection();
+        return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
+    },
+    /**
+     * Select a dom node
+     * @param {DomElement} node the node to select
+     */
+    selectNode : function(node, collapse)
+    {
+        var nodeRange = node.ownerDocument.createRange();
+        try {
+            nodeRange.selectNode(node);
+        } catch (e) {
+            nodeRange.selectNodeContents(node);
+        }
+        if (collapse === true) {
+            nodeRange.collapse(true);
+        }
+        //
+        var s = this.win.getSelection();
+        s.removeAllRanges();
+        s.addRange(nodeRange);
     },
     
     getSelectedNode: function() 
@@ -44213,8 +50816,7 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
         
         // should we cache this!!!!
         
-        
-        
+         
          
         var range = this.createRange(this.getSelection()).cloneRange();
         
@@ -44234,671 +50836,247 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
             return parent;
         }
         
-        // is ancestor a text element.
-        var ac =  range.commonAncestorContainer;
-        if (ac.nodeType == 3) {
-            ac = ac.parentNode;
-        }
-        
-        var ar = ac.childNodes;
-         
-        var nodes = [];
-        var other_nodes = [];
-        var has_other_nodes = false;
-        for (var i=0;i<ar.length;i++) {
-            if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
-                continue;
-            }
-            // fullly contained node.
-            
-            if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
-                nodes.push(ar[i]);
-                continue;
-            }
-            
-            // probably selected..
-            if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
-                other_nodes.push(ar[i]);
-                continue;
-            }
-            // outer..
-            if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
-                continue;
-            }
-            
-            
-            has_other_nodes = true;
-        }
-        if (!nodes.length && other_nodes.length) {
-            nodes= other_nodes;
-        }
-        if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
-            return false;
-        }
-        
-        return nodes[0];
-    },
-    createRange: function(sel)
-    {
-        // this has strange effects when using with 
-        // top toolbar - not sure if it's a great idea.
-        //this.editor.contentWindow.focus();
-        if (typeof sel != "undefined") {
-            try {
-                return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
-            } catch(e) {
-                return this.doc.createRange();
-            }
-        } else {
-            return this.doc.createRange();
-        }
-    },
-    getParentElement: function()
-    {
-        
-        this.assignDocWin();
-        var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
-        
-        var range = this.createRange(sel);
-         
-        try {
-            var p = range.commonAncestorContainer;
-            while (p.nodeType == 3) { // text node
-                p = p.parentNode;
-            }
-            return p;
-        } catch (e) {
-            return null;
-        }
-    
-    },
-    /***
-     *
-     * Range intersection.. the hard stuff...
-     *  '-1' = before
-     *  '0' = hits..
-     *  '1' = after.
-     *         [ -- selected range --- ]
-     *   [fail]                        [fail]
-     *
-     *    basically..
-     *      if end is before start or  hits it. fail.
-     *      if start is after end or hits it fail.
-     *
-     *   if either hits (but other is outside. - then it's not 
-     *   
-     *    
-     **/
-    
-    
-    // @see http://www.thismuchiknow.co.uk/?p=64.
-    rangeIntersectsNode : function(range, node)
-    {
-        var nodeRange = node.ownerDocument.createRange();
-        try {
-            nodeRange.selectNode(node);
-        } catch (e) {
-            nodeRange.selectNodeContents(node);
-        }
-    
-        var rangeStartRange = range.cloneRange();
-        rangeStartRange.collapse(true);
-    
-        var rangeEndRange = range.cloneRange();
-        rangeEndRange.collapse(false);
-    
-        var nodeStartRange = nodeRange.cloneRange();
-        nodeStartRange.collapse(true);
-    
-        var nodeEndRange = nodeRange.cloneRange();
-        nodeEndRange.collapse(false);
-    
-        return rangeStartRange.compareBoundaryPoints(
-                 Range.START_TO_START, nodeEndRange) == -1 &&
-               rangeEndRange.compareBoundaryPoints(
-                 Range.START_TO_START, nodeStartRange) == 1;
-        
-         
-    },
-    rangeCompareNode : function(range, node)
-    {
-        var nodeRange = node.ownerDocument.createRange();
-        try {
-            nodeRange.selectNode(node);
-        } catch (e) {
-            nodeRange.selectNodeContents(node);
-        }
-        
-        
-        range.collapse(true);
-    
-        nodeRange.collapse(true);
-     
-        var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
-        var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
-         
-        //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
-        
-        var nodeIsBefore   =  ss == 1;
-        var nodeIsAfter    = ee == -1;
-        
-        if (nodeIsBefore && nodeIsAfter) {
-            return 0; // outer
-        }
-        if (!nodeIsBefore && nodeIsAfter) {
-            return 1; //right trailed.
-        }
-        
-        if (nodeIsBefore && !nodeIsAfter) {
-            return 2;  // left trailed.
-        }
-        // fully contined.
-        return 3;
-    },
-
-    // private? - in a new class?
-    cleanUpPaste :  function()
-    {
-        // cleans up the whole document..
-        Roo.log('cleanuppaste');
-        
-        this.cleanUpChildren(this.doc.body);
-        var clean = this.cleanWordChars(this.doc.body.innerHTML);
-        if (clean != this.doc.body.innerHTML) {
-            this.doc.body.innerHTML = clean;
-        }
-        
-    },
-    
-    cleanWordChars : function(input) {// change the chars to hex code
-        var he = Roo.HtmlEditorCore;
-        
-        var output = input;
-        Roo.each(he.swapCodes, function(sw) { 
-            var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
-            
-            output = output.replace(swapper, sw[1]);
-        });
-        
-        return output;
-    },
-    
-    
-    cleanUpChildren : function (n)
-    {
-        if (!n.childNodes.length) {
-            return;
-        }
-        for (var i = n.childNodes.length-1; i > -1 ; i--) {
-           this.cleanUpChild(n.childNodes[i]);
-        }
-    },
-    
-    
-        
-    
-    cleanUpChild : function (node)
-    {
-        var ed = this;
-        //console.log(node);
-        if (node.nodeName == "#text") {
-            // clean up silly Windows -- stuff?
-            return; 
-        }
-        if (node.nodeName == "#comment") {
-            node.parentNode.removeChild(node);
-            // clean up silly Windows -- stuff?
-            return; 
-        }
-        var lcname = node.tagName.toLowerCase();
-        // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
-        // whitelist of tags..
-        
-        if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
-            // remove node.
-            node.parentNode.removeChild(node);
-            return;
-            
-        }
-        
-        var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
-        
-        // spans with no attributes - just remove them..
-        if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
-            remove_keep_children = true;
-        }
-        
-        // remove <a name=....> as rendering on yahoo mailer is borked with this.
-        // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
-        
-        //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
-        //    remove_keep_children = true;
-        //}
-        
-        if (remove_keep_children) {
-            this.cleanUpChildren(node);
-            // inserts everything just before this node...
-            while (node.childNodes.length) {
-                var cn = node.childNodes[0];
-                node.removeChild(cn);
-                node.parentNode.insertBefore(cn, node);
-            }
-            node.parentNode.removeChild(node);
-            return;
-        }
-        
-        if (!node.attributes || !node.attributes.length) {
-            
-          
-            
-            
-            this.cleanUpChildren(node);
-            return;
-        }
-        
-        function cleanAttr(n,v)
-        {
-            
-            if (v.match(/^\./) || v.match(/^\//)) {
-                return;
-            }
-            if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
-                return;
-            }
-            if (v.match(/^#/)) {
-                return;
-            }
-//            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
-            node.removeAttribute(n);
-            
-        }
-        
-        var cwhite = this.cwhite;
-        var cblack = this.cblack;
-            
-        function cleanStyle(n,v)
-        {
-            if (v.match(/expression/)) { //XSS?? should we even bother..
-                node.removeAttribute(n);
-                return;
-            }
-            
-            var parts = v.split(/;/);
-            var clean = [];
-            
-            Roo.each(parts, function(p) {
-                p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
-                if (!p.length) {
-                    return true;
-                }
-                var l = p.split(':').shift().replace(/\s+/g,'');
-                l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
-                
-                if ( cwhite.length && cblack.indexOf(l) > -1) {
-//                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
-                    //node.removeAttribute(n);
-                    return true;
-                }
-                //Roo.log()
-                // only allow 'c whitelisted system attributes'
-                if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
-//                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
-                    //node.removeAttribute(n);
-                    return true;
-                }
-                
-                
-                 
-                
-                clean.push(p);
-                return true;
-            });
-            if (clean.length) { 
-                node.setAttribute(n, clean.join(';'));
-            } else {
-                node.removeAttribute(n);
-            }
-            
-        }
-        
-        
-        for (var i = node.attributes.length-1; i > -1 ; i--) {
-            var a = node.attributes[i];
-            //console.log(a);
-            
-            if (a.name.toLowerCase().substr(0,2)=='on')  {
-                node.removeAttribute(a.name);
-                continue;
-            }
-            if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
-                node.removeAttribute(a.name);
-                continue;
-            }
-            if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
-                cleanAttr(a.name,a.value); // fixme..
-                continue;
-            }
-            if (a.name == 'style') {
-                cleanStyle(a.name,a.value);
-                continue;
-            }
-            /// clean up MS crap..
-            // tecnically this should be a list of valid class'es..
-            
-            
-            if (a.name == 'class') {
-                if (a.value.match(/^Mso/)) {
-                    node.removeAttribute('class');
-                }
-                
-                if (a.value.match(/^body$/)) {
-                    node.removeAttribute('class');
-                }
-                continue;
-            }
-            
-            // style cleanup!?
-            // class cleanup?
-            
-        }
-        
-        
-        this.cleanUpChildren(node);
-        
-        
-    },
-    
-    /**
-     * Clean up MS wordisms...
-     */
-    cleanWord : function(node)
-    {
-        if (!node) {
-            this.cleanWord(this.doc.body);
-            return;
-        }
-        
-        if(
-                node.nodeName == 'SPAN' &&
-                !node.hasAttributes() &&
-                node.childNodes.length == 1 &&
-                node.firstChild.nodeName == "#text"  
-        ) {
-            var textNode = node.firstChild;
-            node.removeChild(textNode);
-            if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
-                node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
-            }
-            node.parentNode.insertBefore(textNode, node);
-            if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
-                node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
-            }
-            node.parentNode.removeChild(node);
-        }
-        
-        if (node.nodeName == "#text") {
-            // clean up silly Windows -- stuff?
-            return; 
-        }
-        if (node.nodeName == "#comment") {
-            node.parentNode.removeChild(node);
-            // clean up silly Windows -- stuff?
-            return; 
-        }
-        
-        if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
-            node.parentNode.removeChild(node);
-            return;
-        }
-        //Roo.log(node.tagName);
-        // remove - but keep children..
-        if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
-            //Roo.log('-- removed');
-            while (node.childNodes.length) {
-                var cn = node.childNodes[0];
-                node.removeChild(cn);
-                node.parentNode.insertBefore(cn, node);
-                // move node to parent - and clean it..
-                this.cleanWord(cn);
-            }
-            node.parentNode.removeChild(node);
-            /// no need to iterate chidlren = it's got none..
-            //this.iterateChildren(node, this.cleanWord);
-            return;
-        }
-        // clean styles
-        if (node.className.length) {
-            
-            var cn = node.className.split(/\W+/);
-            var cna = [];
-            Roo.each(cn, function(cls) {
-                if (cls.match(/Mso[a-zA-Z]+/)) {
-                    return;
-                }
-                cna.push(cls);
-            });
-            node.className = cna.length ? cna.join(' ') : '';
-            if (!cna.length) {
-                node.removeAttribute("class");
-            }
-        }
-        
-        if (node.hasAttribute("lang")) {
-            node.removeAttribute("lang");
+        // is ancestor a text element.
+        var ac =  range.commonAncestorContainer;
+        if (ac.nodeType == 3) {
+            ac = ac.parentNode;
         }
         
-        if (node.hasAttribute("style")) {
+        var ar = ac.childNodes;
+         
+        var nodes = [];
+        var other_nodes = [];
+        var has_other_nodes = false;
+        for (var i=0;i<ar.length;i++) {
+            if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
+                continue;
+            }
+            // fullly contained node.
             
-            var styles = node.getAttribute("style").split(";");
-            var nstyle = [];
-            Roo.each(styles, function(s) {
-                if (!s.match(/:/)) {
-                    return;
-                }
-                var kv = s.split(":");
-                if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
-                    return;
-                }
-                // what ever is left... we allow.
-                nstyle.push(s);
-            });
-            node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
-            if (!nstyle.length) {
-                node.removeAttribute('style');
+            if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
+                nodes.push(ar[i]);
+                continue;
+            }
+            
+            // probably selected..
+            if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
+                other_nodes.push(ar[i]);
+                continue;
+            }
+            // outer..
+            if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
+                continue;
             }
+            
+            
+            has_other_nodes = true;
         }
-        this.iterateChildren(node, this.cleanWord);
-        
-        
-        
-    },
-    /**
-     * iterateChildren of a Node, calling fn each time, using this as the scole..
-     * @param {DomNode} node node to iterate children of.
-     * @param {Function} fn method of this class to call on each item.
-     */
-    iterateChildren : function(node, fn)
-    {
-        if (!node.childNodes.length) {
-                return;
+        if (!nodes.length && other_nodes.length) {
+            nodes= other_nodes;
         }
-        for (var i = node.childNodes.length-1; i > -1 ; i--) {
-           fn.call(this, node.childNodes[i])
+        if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
+            return false;
         }
+        
+        return nodes[0];
     },
     
     
-    /**
-     * cleanTableWidths.
-     *
-     * Quite often pasting from word etc.. results in tables with column and widths.
-     * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
-     *
-     */
-    cleanTableWidths : function(node)
+    createRange: function(sel)
     {
-         
-         
-        if (!node) {
-            this.cleanTableWidths(this.doc.body);
-            return;
+        // this has strange effects when using with 
+        // top toolbar - not sure if it's a great idea.
+        //this.editor.contentWindow.focus();
+        if (typeof sel != "undefined") {
+            try {
+                return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
+            } catch(e) {
+                return this.doc.createRange();
+            }
+        } else {
+            return this.doc.createRange();
         }
+    },
+    getParentElement: function()
+    {
         
-        // ignore list...
-        if (node.nodeName == "#text" || node.nodeName == "#comment") {
-            return; 
-        }
-        Roo.log(node.tagName);
-        if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
-            this.iterateChildren(node, this.cleanTableWidths);
-            return;
-        }
-        if (node.hasAttribute('width')) {
-            node.removeAttribute('width');
-        }
+        this.assignDocWin();
+        var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
         
+        var range = this.createRange(sel);
          
-        if (node.hasAttribute("style")) {
-            // pretty basic...
-            
-            var styles = node.getAttribute("style").split(";");
-            var nstyle = [];
-            Roo.each(styles, function(s) {
-                if (!s.match(/:/)) {
-                    return;
-                }
-                var kv = s.split(":");
-                if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
-                    return;
-                }
-                // what ever is left... we allow.
-                nstyle.push(s);
-            });
-            node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
-            if (!nstyle.length) {
-                node.removeAttribute('style');
+        try {
+            var p = range.commonAncestorContainer;
+            while (p.nodeType == 3) { // text node
+                p = p.parentNode;
             }
+            return p;
+        } catch (e) {
+            return null;
         }
-        
-        this.iterateChildren(node, this.cleanTableWidths);
-        
-        
+    
     },
+    /***
+     *
+     * Range intersection.. the hard stuff...
+     *  '-1' = before
+     *  '0' = hits..
+     *  '1' = after.
+     *         [ -- selected range --- ]
+     *   [fail]                        [fail]
+     *
+     *    basically..
+     *      if end is before start or  hits it. fail.
+     *      if start is after end or hits it fail.
+     *
+     *   if either hits (but other is outside. - then it's not 
+     *   
+     *    
+     **/
+    
     
+    // @see http://www.thismuchiknow.co.uk/?p=64.
+    rangeIntersectsNode : function(range, node)
+    {
+        var nodeRange = node.ownerDocument.createRange();
+        try {
+            nodeRange.selectNode(node);
+        } catch (e) {
+            nodeRange.selectNodeContents(node);
+        }
     
+        var rangeStartRange = range.cloneRange();
+        rangeStartRange.collapse(true);
     
+        var rangeEndRange = range.cloneRange();
+        rangeEndRange.collapse(false);
     
-    domToHTML : function(currentElement, depth, nopadtext) {
-        
-        depth = depth || 0;
-        nopadtext = nopadtext || false;
+        var nodeStartRange = nodeRange.cloneRange();
+        nodeStartRange.collapse(true);
     
-        if (!currentElement) {
-            return this.domToHTML(this.doc.body);
+        var nodeEndRange = nodeRange.cloneRange();
+        nodeEndRange.collapse(false);
+    
+        return rangeStartRange.compareBoundaryPoints(
+                 Range.START_TO_START, nodeEndRange) == -1 &&
+               rangeEndRange.compareBoundaryPoints(
+                 Range.START_TO_START, nodeStartRange) == 1;
+        
+         
+    },
+    rangeCompareNode : function(range, node)
+    {
+        var nodeRange = node.ownerDocument.createRange();
+        try {
+            nodeRange.selectNode(node);
+        } catch (e) {
+            nodeRange.selectNodeContents(node);
         }
         
-        //Roo.log(currentElement);
-        var j;
-        var allText = false;
-        var nodeName = currentElement.nodeName;
-        var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
         
-        if  (nodeName == '#text') {
-            
-            return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
-        }
+        range.collapse(true);
+    
+        nodeRange.collapse(true);
+     
+        var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
+        var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
+         
+        //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
         
+        var nodeIsBefore   =  ss == 1;
+        var nodeIsAfter    = ee == -1;
         
-        var ret = '';
-        if (nodeName != 'BODY') {
-             
-            var i = 0;
-            // Prints the node tagName, such as <A>, <IMG>, etc
-            if (tagName) {
-                var attr = [];
-                for(i = 0; i < currentElement.attributes.length;i++) {
-                    // quoting?
-                    var aname = currentElement.attributes.item(i).name;
-                    if (!currentElement.attributes.item(i).value.length) {
-                        continue;
-                    }
-                    attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
-                }
-                
-                ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
-            } 
-            else {
-                
-                // eack
-            }
-        } else {
-            tagName = false;
-        }
-        if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
-            return ret;
+        if (nodeIsBefore && nodeIsAfter) {
+            return 0; // outer
         }
-        if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
-            nopadtext = true;
+        if (!nodeIsBefore && nodeIsAfter) {
+            return 1; //right trailed.
         }
         
+        if (nodeIsBefore && !nodeIsAfter) {
+            return 2;  // left trailed.
+        }
+        // fully contined.
+        return 3;
+    },
+    cleanWordChars : function(input) {// change the chars to hex code
         
-        // Traverse the tree
-        i = 0;
-        var currentElementChild = currentElement.childNodes.item(i);
-        var allText = true;
-        var innerHTML  = '';
-        lastnode = '';
-        while (currentElementChild) {
-            // Formatting code (indent the tree so it looks nice on the screen)
-            var nopad = nopadtext;
-            if (lastnode == 'SPAN') {
-                nopad  = true;
-            }
-            // text
-            if  (currentElementChild.nodeName == '#text') {
-                var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
-                toadd = nopadtext ? toadd : toadd.trim();
-                if (!nopad && toadd.length > 80) {
-                    innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
-                }
-                innerHTML  += toadd;
-                
-                i++;
-                currentElementChild = currentElement.childNodes.item(i);
-                lastNode = '';
-                continue;
-            }
-            allText = false;
+       var swapCodes  = [ 
+            [    8211, "&#8211;" ], 
+            [    8212, "&#8212;" ], 
+            [    8216,  "'" ],  
+            [    8217, "'" ],  
+            [    8220, '"' ],  
+            [    8221, '"' ],  
+            [    8226, "*" ],  
+            [    8230, "..." ]
+        ]; 
+        var output = input;
+        Roo.each(swapCodes, function(sw) { 
+            var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
             
-            innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
-                
-            // Recursively traverse the tree structure of the child node
-            innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
-            lastnode = currentElementChild.nodeName;
-            i++;
-            currentElementChild=currentElement.childNodes.item(i);
-        }
+            output = output.replace(swapper, sw[1]);
+        });
         
-        ret += innerHTML;
+        return output;
+    },
+    
+     
+    
         
-        if (!allText) {
-                // The remaining code is mostly for formatting the tree
-            ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
-        }
+    
+    cleanUpChild : function (node)
+    {
         
+        new Roo.htmleditor.FilterComment({node : node});
+        new Roo.htmleditor.FilterAttributes({
+                node : node,
+                attrib_black : this.ablack,
+                attrib_clean : this.aclean,
+                style_white : this.cwhite,
+                style_black : this.cblack
+        });
+        new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
+        new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
+         
         
-        if (tagName) {
-            ret+= "</"+tagName+">";
-        }
-        return ret;
+    },
+    
+    /**
+     * Clean up MS wordisms...
+     * @deprecated - use filter directly
+     */
+    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', ':' ]} );
+        
+    },
+   
+    
+    /**
+
+     * @deprecated - use filters
+     */
+    cleanTableWidths : function(node)
+    {
+        new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
         
     },
+    
+     
         
     applyBlacklists : function()
     {
         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
         
+        this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
+        this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
+        this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
+        
         this.white = [];
         this.black = [];
         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
@@ -45016,6 +51194,16 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
         
     },
     
+    
+    updateLanguage : function()
+    {
+        if (!this.iframe || !this.iframe.contentDocument) {
+            return;
+        }
+        Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
+    },
+    
+    
     removeStylesheets : function()
     {
         var _this = this;
@@ -45080,36 +51268,40 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
 });
 
 Roo.HtmlEditorCore.white = [
-        'area', 'br', 'img', 'input', 'hr', 'wbr',
+        'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
         
-       'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
-       'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
-       'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
-       'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
-       'table',   'ul',         'xmp', 
+       'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
+       'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
+       'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
+       'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
+       'TABLE',   'UL',         'XMP', 
        
-       'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
-      'thead',   'tr', 
+       'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
+      'THEAD',   'TR', 
      
-      'dir', 'menu', 'ol', 'ul', 'dl',
+      'DIR', 'MENU', 'OL', 'UL', 'DL',
        
-      'embed',  'object'
+      'EMBED',  'OBJECT'
 ];
 
 
 Roo.HtmlEditorCore.black = [
     //    'embed',  'object', // enable - backend responsiblity to clean thiese
-        'applet', // 
-        'base',   'basefont', 'bgsound', 'blink',  'body', 
-        'frame',  'frameset', 'head',    'html',   'ilayer', 
-        'iframe', 'layer',  'link',     'meta',    'object',   
-        'script', 'style' ,'title',  'xml' // clean later..
+        'APPLET', // 
+        'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
+        'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
+        'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
+        'SCRIPT', 'STYLE' ,'TITLE',  'XML',
+        //'FONT' // CLEAN LATER..
+        'COLGROUP', 'COL'   // messy tables.
+        
+        
 ];
-Roo.HtmlEditorCore.clean = [
-    'script', 'style', 'title', 'xml'
+Roo.HtmlEditorCore.clean = [ // ?? needed???
+     'SCRIPT', 'STYLE', 'TITLE', 'XML'
 ];
-Roo.HtmlEditorCore.remove = [
-    'font'
+Roo.HtmlEditorCore.tag_remove = [
+    'FONT', 'TBODY'  
 ];
 // attributes..
 
@@ -45140,16 +51332,7 @@ Roo.HtmlEditorCore.cblack= [
 ];
 
 
-Roo.HtmlEditorCore.swapCodes   =[ 
-    [    8211, "--" ], 
-    [    8212, "--" ], 
-    [    8216,  "'" ],  
-    [    8217, "'" ],  
-    [    8220, '"' ],  
-    [    8221, '"' ],  
-    [    8226, "*" ],  
-    [    8230, "..." ]
-]; 
+
 
     //<script type="text/javascript">
 
@@ -45212,7 +51395,7 @@ Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
     width: 500,
     
     /**
-     * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
+     * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
      * 
      */
     stylesheets: false,
@@ -45239,7 +51422,31 @@ Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
      * 
      */
     white: false,
+    /**
+     * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
+     */
+    allowComments: false,
+    /**
+     * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
+     */
+    enableBlocks : true,
     
+    /**
+     * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
+     *         if you are doing an email editor, this probably needs disabling, it's designed
+     */
+    autoClean: true,
+    /**
+     * @cfg {string} bodyCls default '' default classes to add to body of editable area - usually undoreset is a good start..
+     */
+    bodyCls : '',
+    /**
+     * @cfg {String} language default en - language of text (usefull for rtl languages)
+     * 
+     */
+    language: 'en',
+    
+     
     // id of frame..
     frameId: false,
     
@@ -45344,7 +51551,13 @@ Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
             * Fires when press the Sytlesheets button
             * @param {Roo.HtmlEditorCore} this
             */
-            stylesheetsclick: true
+            stylesheetsclick: true,
+            /**
+            * @event paste
+            * Fires when press user pastes into the editor
+            * @param {Roo.HtmlEditorCore} this
+            */
+            paste: true 
         });
         this.defaultAutoCreate =  {
             tag: "textarea",
@@ -45375,8 +51588,19 @@ Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
          
         
     },
-
-     
+    /**
+     * get the Context selected node
+     * @returns {DomElement|boolean} selected node if active or false if none
+     * 
+     */
+    getSelectedNode : function()
+    {
+        if (this.toolbars.length < 2 || !this.toolbars[1].tb) {
+            return false;
+        }
+        return this.toolbars[1].tb.selectedNode;
+    
+    },
     // private
     onRender : function(ct, position)
     {
@@ -45597,6 +51821,8 @@ Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
             this.el.removeClass('x-hidden');
             this.el.dom.removeAttribute('tabIndex');
             this.el.focus();
+            this.el.dom.scrollTop = 0;
+            
             
             for (var i = 0; i < this.toolbars.length; i++) {
                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
@@ -45664,7 +51890,17 @@ Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
         this.editorcore.pushValue();
     },
 
+    /**
+     * update the language in the body - really done by core
+     * @param {String} language - eg. en / ar / zh-CN etc..
+     */
+    updateLanguage : function(lang)
+    {
+        this.language = lang;
+        this.editorcore.language = lang;
+        this.editorcore.updateLanguage();
      
+    },
     // private
     deferFocus : function(){
         this.focus.defer(10, this);
@@ -45769,8 +52005,7 @@ Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
      */
 });
  
-    // <script type="text/javascript">
-/*
+    /*
  * Based on
  * Ext JS Library 1.1.1
  * Copyright(c) 2006-2007, Ext JS, LLC.
@@ -45779,9 +52014,9 @@ Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
  */
 
 /**
- * @class Roo.form.HtmlEditorToolbar1
+ * @class Roo.form.HtmlEditor.ToolbarStandard
  * Basic Toolbar
- * 
+
  * Usage:
  *
  new Roo.form.HtmlEditor({
@@ -45795,7 +52030,7 @@ Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
      
  * 
  * @cfg {Object} disable List of elements to disable..
- * @cfg {Array} btns List of additional buttons.
+ * @cfg {Roo.Toolbar.Item|Roo.Toolbar.Button|Roo.Toolbar.SplitButton|Roo.form.Field} btns[] List of additional buttons.
  * 
  * 
  * NEEDS Extra CSS? 
@@ -45820,7 +52055,7 @@ Roo.form.HtmlEditor.ToolbarStandard = function(config)
     // dont call parent... till later.
 }
 
-Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
+Roo.form.HtmlEditor.ToolbarStandard.prototype = {
     
     tb: false,
     
@@ -46166,7 +52401,7 @@ Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
                     tabIndex:-1
                 });
             }
-             cmenu.menu.items.push({
+            cmenu.menu.items.push({
                 actiontype : 'tablewidths',
                 html: 'Remove Table Widths',
                 handler: function(a,b) {
@@ -46218,7 +52453,7 @@ Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
                 actiontype : 'tidy',
                 html: 'Tidy HTML Source',
                 handler: function(a,b) {
-                    editorcore.doc.body.innerHTML = editorcore.domToHTML();
+                    new Roo.htmleditor.Tidy(editorcore.doc.body);
                     editorcore.syncValue();
                 },
                 tabIndex:-1
@@ -46255,7 +52490,7 @@ Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
         
         if (this.btns) {
             for(var i =0; i< this.btns.length;i++) {
-                var b = Roo.factory(this.btns[i],Roo.form);
+                var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
                 b.cls =  'x-edit-none';
                 
                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
@@ -46296,11 +52531,45 @@ Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
     },
     // private used internally
     createLink : function(){
-        Roo.log("create link?");
-        var url = prompt(this.createLinkText, this.defaultLinkValue);
-        if(url && url != 'http:/'+'/'){
-            this.editorcore.relayCmd('createlink', url);
+        //Roo.log("create link?");
+        var ec = this.editorcore;
+        var ar = ec.getAllAncestors();
+        var n = false;
+        for(var i = 0;i< ar.length;i++) {
+            if (ar[i] && ar[i].nodeName == 'A') {
+                n = ar[i];
+                break;
+            }
         }
+        
+        (function() {
+            
+            Roo.MessageBox.show({
+                title : "Add / Edit Link URL",
+                msg : "Enter the url for the link",
+                buttons: Roo.MessageBox.OKCANCEL,
+                fn: function(btn, url){
+                    if (btn != 'ok') {
+                        return;
+                    }
+                    if(url && url != 'http:/'+'/'){
+                        if (n) {
+                            n.setAttribute('href', url);
+                        } else {
+                            ec.relayCmd('createlink', url);
+                        }
+                    }
+                },
+                minWidth:250,
+                prompt:true,
+                //multiline: multiline,
+                modal : true,
+                value :  n  ? n.getAttribute('href') : '' 
+            });
+            
+             
+        }).defer(100, this); // we have to defer this , otherwise the mouse click gives focus to the main window.
+        
     },
 
     
@@ -46413,6 +52682,11 @@ Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
                 this.tb.items.each(function(item){
                     item.enable();
                 });
+                // initialize 'blocks'
+                Roo.each(Roo.get(this.editorcore.doc.body).query('*[data-block]'), function(e) {
+                    Roo.htmleditor.Block.factory(e).updateElement(e);
+                },this);
+            
             }
             
         }
@@ -46539,7 +52813,7 @@ Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
            item.enable();
         });
     }
-});
+};
 
 
 
@@ -46587,189 +52861,138 @@ Roo.form.HtmlEditor.ToolbarContext = function(config)
  
 
 Roo.form.HtmlEditor.ToolbarContext.types = {
-    'IMG' : {
-        width : {
+    'IMG' : [
+        {
+            name : 'width',
             title: "Width",
             width: 40
         },
-        height:  {
+        {
+            name : 'height',
             title: "Height",
             width: 40
         },
-        align: {
+        {
+            name : 'align',
             title: "Align",
             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
             width : 80
             
         },
-        border: {
+        {
+            name : 'border',
             title: "Border",
             width: 40
         },
-        alt: {
+        {
+            name : 'alt',
             title: "Alt",
             width: 120
         },
-        src : {
+        {
+            name : 'src',
             title: "Src",
             width: 220
         }
         
-    },
-    'A' : {
-        name : {
+    ],
+    
+    'FIGURE' : [
+        {
+            name : 'align',
+            title: "Align",
+            opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
+            width : 80  
+        }
+    ],
+    'A' : [
+        {
+            name : 'name',
             title: "Name",
             width: 50
         },
-        target:  {
+        {
+            name : 'target',
             title: "Target",
             width: 120
         },
-        href:  {
+        {
+            name : 'href',
             title: "Href",
             width: 220
         } // border?
         
-    },
-    'TABLE' : {
-        rows : {
-            title: "Rows",
-            width: 20
-        },
-        cols : {
-            title: "Cols",
-            width: 20
-        },
-        width : {
-            title: "Width",
-            width: 40
-        },
-        height : {
-            title: "Height",
-            width: 40
-        },
-        border : {
-            title: "Border",
-            width: 20
-        }
-    },
-    'TD' : {
-        width : {
-            title: "Width",
-            width: 40
-        },
-        height : {
-            title: "Height",
-            width: 40
-        },   
-        align: {
-            title: "Align",
-            opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
-            width: 80
-        },
-        valign: {
-            title: "Valign",
-            opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
-            width: 80
-        },
-        colspan: {
-            title: "Colspan",
-            width: 20
-            
-        },
-         'font-family'  : {
-            title : "Font",
-            style : 'fontFamily',
-            displayField: 'display',
-            optname : 'font-family',
-            width: 140
-        }
-    },
-    'INPUT' : {
-        name : {
+    ],
+    
+    'INPUT' : [
+        {
+            name : 'name',
             title: "name",
             width: 120
         },
-        value : {
+        {
+            name : 'value',
             title: "Value",
             width: 120
         },
-        width : {
+        {
+            name : 'width',
             title: "Width",
             width: 40
         }
-    },
-    'LABEL' : {
-        'for' : {
+    ],
+    'LABEL' : [
+         {
+            name : 'for',
             title: "For",
             width: 120
         }
-    },
-    'TEXTAREA' : {
-          name : {
+    ],
+    'TEXTAREA' : [
+        {
+            name : 'name',
             title: "name",
             width: 120
         },
-        rows : {
+        {
+            name : 'rows',
             title: "Rows",
             width: 20
         },
-        cols : {
+        {
+            name : 'cols',
             title: "Cols",
             width: 20
         }
-    },
-    'SELECT' : {
-        name : {
+    ],
+    'SELECT' : [
+        {
+            name : 'name',
             title: "name",
             width: 120
         },
-        selectoptions : {
+        {
+            name : 'selectoptions',
             title: "Options",
             width: 200
         }
-    },
+    ],
     
     // should we really allow this??
     // should this just be 
-    'BODY' : {
-        title : {
+    'BODY' : [
+        
+        {
+            name : 'title',
             title: "Title",
             width: 200,
             disabled : true
         }
-    },
-    'SPAN' : {
-        'font-family'  : {
-            title : "Font",
-            style : 'fontFamily',
-            displayField: 'display',
-            optname : 'font-family',
-            width: 140
-        }
-    },
-    'DIV' : {
-        'font-family'  : {
-            title : "Font",
-            style : 'fontFamily',
-            displayField: 'display',
-            optname : 'font-family',
-            width: 140
-        }
-    },
-     'P' : {
-        'font-family'  : {
-            title : "Font",
-            style : 'fontFamily',
-            displayField: 'display',
-            optname : 'font-family',
-            width: 140
-        }
-    },
-    
-    '*' : {
-        // empty..
-    }
+    ],
+    '*' : [
+        // empty.
+    ]
 
 };
 
@@ -46855,9 +53078,9 @@ Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
         // disable everything...
         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
         this.toolbars = {};
-           
+        // block toolbars are built in updateToolbar when needed.
         for (var i in  ty) {
-          
+            
             this.toolbars[i] = this.buildToolbar(ty[i],i);
         }
         this.tb = this.toolbars.BODY;
@@ -46884,8 +53107,13 @@ Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
      *
      * Note you can force an update by calling on('editorevent', scope, false)
      */
-    updateToolbar: function(editor,ev,sel){
-
+    updateToolbar: function(editor ,ev, sel)
+    {
+        
+        if (ev) {
+            ev.stopEvent(); // se if we can stop this looping with mutiple events.
+        }
+        
         //Roo.log(ev);
         // capture mouse up - this is handy for selecting images..
         // perhaps should go somewhere else...
@@ -46893,38 +53121,40 @@ Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
              this.editor.onFirstFocus();
             return;
         }
-        
+        //Roo.log(ev ? ev.target : 'NOTARGET');
         
         
         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
         // selectNode - might want to handle IE?
+        
+        
+        
         if (ev &&
             (ev.type == 'mouseup' || ev.type == 'click' ) &&
-            ev.target && ev.target.tagName == 'IMG') {
+            ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
             // they have click on an image...
             // let's see if we can change the selection...
             sel = ev.target;
-         
-              var nodeRange = sel.ownerDocument.createRange();
-            try {
-                nodeRange.selectNode(sel);
-            } catch (e) {
-                nodeRange.selectNodeContents(sel);
-            }
-            //nodeRange.collapse(true);
-            var s = this.editorcore.win.getSelection();
-            s.removeAllRanges();
-            s.addRange(nodeRange);
-        }  
+            
+            // this triggers looping?
+            //this.editorcore.selectNode(sel);
+             
+        }
         
+        // this forces an id..
+        Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
+             e.classList.remove('roo-ed-selection');
+        });
+        //Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
+        //Roo.get(node).addClass('roo-ed-selection');
       
-        var updateFooter = sel ? false : true;
+        //var updateFooter = sel ? false : true; 
         
         
         var ans = this.editorcore.getAllAncestors();
         
         // pick
-        var ty= Roo.form.HtmlEditor.ToolbarContext.types;
+        var ty = Roo.form.HtmlEditor.ToolbarContext.types;
         
         if (!sel) { 
             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
@@ -46932,86 +53162,145 @@ Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
             
         }
-        // pick a menu that exists..
-        var tn = sel.tagName.toUpperCase();
-        //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
-        
-        tn = sel.tagName.toUpperCase();
         
+        var tn = sel.tagName.toUpperCase();
         var lastSel = this.tb.selectedNode;
-        
         this.tb.selectedNode = sel;
+        var left_label = tn;
         
-        // if current menu does not match..
+        // ok see if we are editing a block?
         
-        if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
-                
-            this.tb.el.hide();
-            ///console.log("show: " + tn);
-            this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
-            this.tb.el.show();
-            // update name
-            this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
-            
-            
-            // update attributes
-            if (this.tb.fields) {
-                this.tb.fields.each(function(e) {
-                    if (e.stylename) {
-                        e.setValue(sel.style[e.stylename]);
-                        return;
-                    } 
-                   e.setValue(sel.getAttribute(e.attrname));
-                });
-            }
+        var db = false;
+        // you are not actually selecting the block.
+        if (sel && sel.hasAttribute('data-block')) {
+            db = sel;
+        } else if (sel && sel.closest('[data-block]')) {
             
-            var hasStyles = false;
-            for(var i in this.styles) {
-                hasStyles = true;
-                break;
-            }
+            db = sel.closest('[data-block]');
+            //var cepar = sel.closest('[contenteditable=true]');
+            //if (db && cepar && cepar.tagName != 'BODY') {
+            //   db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
+            //}   
+        }
+        
+        
+        var block = false;
+        //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
+        if (db && this.editorcore.enableBlocks) {
+            block = Roo.htmleditor.Block.factory(db);
             
-            // update styles
-            if (hasStyles) { 
-                var st = this.tb.fields.item(0);
-                
-                st.store.removeAll();
-               
-                
-                var cn = sel.className.split(/\s+/);
+            
+            if (block) {
+                 db.className = (
+                        db.classList.length > 0  ? db.className + ' ' : ''
+                    )  + 'roo-ed-selection';
+                 
+                 // since we removed it earlier... its not there..
+                tn = 'BLOCK.' + db.getAttribute('data-block');
                 
-                var avs = [];
-                if (this.styles['*']) {
-                    
-                    Roo.each(this.styles['*'], function(v) {
-                        avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
-                    });
-                }
-                if (this.styles[tn]) { 
-                    Roo.each(this.styles[tn], function(v) {
-                        avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
-                    });
+                //this.editorcore.selectNode(db);
+                if (typeof(this.toolbars[tn]) == 'undefined') {
+                   this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
                 }
-                
-                st.store.loadData(avs);
-                st.collapse();
-                st.setValue(cn);
+                this.toolbars[tn].selectedNode = db;
+                left_label = block.friendly_name;
+                ans = this.editorcore.getAllAncestors();
             }
-            // flag our selected Node.
-            this.tb.selectedNode = sel;
-           
-           
-            Roo.menu.MenuMgr.hideAll();
-
+            
+                
+            
         }
         
-        if (!updateFooter) {
-            //this.footDisp.dom.innerHTML = ''; 
-            return;
+        
+        if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
+            return; // no change?
+        }
+        
+        
+          
+        this.tb.el.hide();
+        ///console.log("show: " + tn);
+        this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
+        
+        this.tb.el.show();
+        // update name
+        this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
+        
+        
+        // update attributes
+        if (block && this.tb.fields) {
+             
+            this.tb.fields.each(function(e) {
+                e.setValue(block[e.name]);
+            });
+            
+            
+        } else  if (this.tb.fields && this.tb.selectedNode) {
+            this.tb.fields.each( function(e) {
+                if (e.stylename) {
+                    e.setValue(this.tb.selectedNode.style[e.stylename]);
+                    return;
+                } 
+                e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
+            }, this);
+            this.updateToolbarStyles(this.tb.selectedNode);  
         }
+        
+        
+       
+        Roo.menu.MenuMgr.hideAll();
+
+        
+        
+    
         // update the footer
         //
+        this.updateFooter(ans);
+             
+    },
+    
+    updateToolbarStyles : function(sel)
+    {
+        var hasStyles = false;
+        for(var i in this.styles) {
+            hasStyles = true;
+            break;
+        }
+        
+        // update styles
+        if (hasStyles && this.tb.hasStyles) { 
+            var st = this.tb.fields.item(0);
+            
+            st.store.removeAll();
+            var cn = sel.className.split(/\s+/);
+            
+            var avs = [];
+            if (this.styles['*']) {
+                
+                Roo.each(this.styles['*'], function(v) {
+                    avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
+                });
+            }
+            if (this.styles[tn]) { 
+                Roo.each(this.styles[tn], function(v) {
+                    avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
+                });
+            }
+            
+            st.store.loadData(avs);
+            st.collapse();
+            st.setValue(cn);
+        }
+    },
+    
+     
+    updateFooter : function(ans)
+    {
         var html = '';
+        if (ans === false) {
+            this.footDisp.dom.innerHTML = '';
+            return;
+        }
         
         this.footerEls = ans.reverse();
         Roo.each(this.footerEls, function(a,i) {
@@ -47031,10 +53320,8 @@ Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
         
         this.footDisp.dom.innerHTML = html;
             
-        //this.editorsyncValue();
+        
     },
-     
-    
    
        
     // private
@@ -47059,7 +53346,7 @@ Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
            item.enable();
         });
     },
-    buildToolbar: function(tlist, nm)
+    buildToolbar: function(tlist, nm, friendly_name, block)
     {
         var editor = this.editor;
         var editorcore = this.editorcore;
@@ -47070,18 +53357,22 @@ Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
         
        
         var tb = new Roo.Toolbar(wdiv);
-        // add the name..
+        ///this.tb = tb; // << this sets the active toolbar..
+        if (tlist === false && block) {
+            tlist = block.contextMenu(this);
+        }
         
-        tb.add(nm+ ":&nbsp;");
+        tb.hasStyles = false;
+        tb.name = nm;
+        
+        tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
+        
+        var styles = Array.from(this.styles);
         
-        var styles = [];
-        for(var i in this.styles) {
-            styles.push(i);
-        }
         
         // styles...
         if (styles && styles.length) {
-            
+            tb.hasStyles = true;
             // this needs a multi-select checkbox...
             tb.addField( new Roo.form.ComboBox({
                 store: new Roo.data.SimpleStore({
@@ -47111,9 +53402,18 @@ Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
         }
         
         var tbc = Roo.form.HtmlEditor.ToolbarContext;
-        var tbops = tbc.options;
         
-        for (var i in tlist) {
+        
+        for (var i = 0; i < tlist.length; i++) {
+            
+            // newer versions will use xtype cfg to create menus.
+            if (typeof(tlist[i].xtype) != 'undefined') {
+                
+                tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
+                
+                
+                continue;
+            }
             
             var item = tlist[i];
             tb.add(item.title + ":&nbsp;");
@@ -47121,8 +53421,8 @@ Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
             
             //optname == used so you can configure the options available..
             var opts = item.opts ? item.opts : false;
-            if (item.optname) {
-                opts = tbops[item.optname];
+            if (item.optname) { // use the b
+                opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
            
             }
             
@@ -47134,13 +53434,15 @@ Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
                         fields: ['val', 'display'],
                         data : opts  
                     }),
-                    name : '-roo-edit-' + i,
-                    attrname : i,
+                    name : '-roo-edit-' + tlist[i].name,
+                    
+                    attrname : tlist[i].name,
                     stylename : item.style ? item.style : false,
+                    
                     displayField: item.displayField ? item.displayField : 'val',
                     valueField :  'val',
                     typeAhead: false,
-                    mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
+                    mode: typeof(tbc.stores[tlist[i].name]) != 'undefined'  ? 'remote' : 'local',
                     editable : false,
                     triggerAction: 'all',
                     emptyText:'Select',
@@ -47148,11 +53450,20 @@ Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
                     width: item.width ? item.width  : 130,
                     listeners : {
                         'select': function(c, r, i) {
+                             
+                            
                             if (c.stylename) {
                                 tb.selectedNode.style[c.stylename] =  r.get('val');
+                                editorcore.syncValue();
+                                return;
+                            }
+                            if (r === false) {
+                                tb.selectedNode.removeAttribute(c.attrname);
+                                editorcore.syncValue();
                                 return;
                             }
                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
+                            editorcore.syncValue();
                         }
                     }
 
@@ -47160,7 +53471,7 @@ Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
                 continue;
                     
                  
-                
+                /*
                 tb.addField( new Roo.form.TextField({
                     name: i,
                     width: 100,
@@ -47168,16 +53479,19 @@ Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
                     value: ''
                 }));
                 continue;
+                */
             }
             tb.addField( new Roo.form.TextField({
-                name: '-roo-edit-' + i,
-                attrname : i,
+                name: '-roo-edit-' + tlist[i].name,
+                attrname : tlist[i].name,
                 
                 width: item.width,
                 //allowBlank:true,
                 value: '',
                 listeners: {
                     'change' : function(f, nv, ov) {
+                        
+                         
                         tb.selectedNode.setAttribute(f.attrname, nv);
                         editorcore.syncValue();
                     }
@@ -47187,8 +53501,9 @@ Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
         }
         
         var _this = this;
-        
+        var show_delete = !block || block.deleteTitle !== false;
         if(nm == 'BODY'){
+            show_delete = false;
             tb.addSeparator();
         
             tb.addButton( {
@@ -47204,60 +53519,61 @@ Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
         }
         
         tb.addFill();
-        tb.addButton( {
-            text: 'Remove Tag',
-    
-            listeners : {
-                click : function ()
-                {
-                    // remove
-                    // undo does not work.
-                     
-                    var sn = tb.selectedNode;
-                    
-                    var pn = sn.parentNode;
-                    
-                    var stn =  sn.childNodes[0];
-                    var en = sn.childNodes[sn.childNodes.length - 1 ];
-                    while (sn.childNodes.length) {
-                        var node = sn.childNodes[0];
-                        sn.removeChild(node);
-                        //Roo.log(node);
-                        pn.insertBefore(node, sn);
+        if (show_delete) {
+            tb.addButton({
+                text: block && block.deleteTitle ? block.deleteTitle  : 'Remove Block or Formating', // remove the tag, and puts the children outside...
+        
+                listeners : {
+                    click : function ()
+                    {
+                        var sn = tb.selectedNode;
+                        if (block) {
+                            sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
+                            
+                        }
+                        if (!sn) {
+                            return;
+                        }
+                        var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
+                        if (sn.hasAttribute('data-block')) {
+                            stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
+                            sn.parentNode.removeChild(sn);
+                            
+                        } else if (sn && sn.tagName != 'BODY') {
+                            // remove and keep parents.
+                            a = new Roo.htmleditor.FilterKeepChildren({tag : false});
+                            a.replaceTag(sn);
+                        }
+                        
+                        
+                        var range = editorcore.createRange();
+            
+                        range.setStart(stn,0);
+                        range.setEnd(stn,0); 
+                        var selection = editorcore.getSelection();
+                        selection.removeAllRanges();
+                        selection.addRange(range);
+                        
+                        
+                        //_this.updateToolbar(null, null, pn);
+                        _this.updateToolbar(null, null, null);
+                        _this.updateFooter(false);
                         
                     }
-                    pn.removeChild(sn);
-                    var range = editorcore.createRange();
-        
-                    range.setStart(stn,0);
-                    range.setEnd(en,0); //????
-                    //range.selectNode(sel);
-                    
-                    
-                    var selection = editorcore.getSelection();
-                    selection.removeAllRanges();
-                    selection.addRange(range);
-                    
-                    
-                    
-                    //_this.updateToolbar(null, null, pn);
-                    _this.updateToolbar(null, null, null);
-                    _this.footDisp.dom.innerHTML = ''; 
                 }
-            }
-            
+                
+                        
                     
                 
-            
-        });
-        
+            });
+        }    
         
         tb.el.on('click', function(e){
             e.preventDefault(); // what does this do?
         });
         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
         tb.el.hide();
-        tb.name = nm;
+        
         // dont need to disable them... as they will get hidden
         return tb;
          
@@ -47301,6 +53617,7 @@ Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
         
         
     },
+    // when the footer contect changes
     onContextClick : function (ev,dom)
     {
         ev.preventDefault();
@@ -47313,17 +53630,7 @@ Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
         var ans = this.footerEls;
         var sel = ans[n];
         
-         // pick
-        var range = this.editorcore.createRange();
-        
-        range.selectNodeContents(sel);
-        //range.selectNode(sel);
-        
-        
-        var selection = this.editorcore.getSelection();
-        selection.removeAllRanges();
-        selection.addRange(range);
-        
+        this.editorcore.selectNode(sel);
         
         
         this.updateToolbar(null, null, sel);
@@ -47521,7 +53828,26 @@ Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
         
         return valid;
     },
-
+    /**
+     * Returns array of invalid form fields.
+     * @return Array
+     */
+    
+    invalidFields : function()
+    {
+        var ret = [];
+        this.items.each(function(f){
+            if(f.validate()){
+                return;
+            }
+            ret.push(f);
+            
+        });
+        
+        return ret;
+    },
+    
+    
     /**
      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
      * @return Boolean
@@ -47873,11 +54199,12 @@ clientValidation  Boolean          Applies to submit only.  Pass true to call fo
      * @param {Boolean} asString
      * @return {Object}
      */
-    getValues : function(asString){
+    getValues : function(asString)
+    {
         if (this.childForms) {
             // copy values from the child forms
             Roo.each(this.childForms, function (f) {
-                this.setValues(f.getValues());
+                this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
             }, this);
         }
         
@@ -47910,21 +54237,31 @@ clientValidation  Boolean          Applies to submit only.  Pass true to call fo
     /**
      * Returns the fields in this form as an object with key/value pairs. 
      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
+     * Normally this will not return readOnly data 
+     * @param {Boolean} with_readonly return readonly field data.
      * @return {Object}
      */
-    getFieldValues : function(with_hidden)
+    getFieldValues : function(with_readonly)
     {
         if (this.childForms) {
             // copy values from the child forms
             // should this call getFieldValues - probably not as we do not currently copy
             // hidden fields when we generate..
             Roo.each(this.childForms, function (f) {
-                this.setValues(f.getValues());
+                this.setValues(f.getFieldValues());
             }, this);
         }
         
         var ret = {};
         this.items.each(function(f){
+            
+            if (f.readOnly && with_readonly !== true) {
+                return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
+                        // if a subform contains a copy of them.
+                        // if you have subforms with the same editable data, you will need to copy the data back
+                        // and forth.
+            }
+            
             if (!f.getName()) {
                 return;
             }
@@ -48213,6 +54550,7 @@ Roo.apply(Roo.form.BasicForm, {
 /**
  * @class Roo.form.Form
  * @extends Roo.form.BasicForm
+ * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
  * @constructor
  * @param {Object} config Configuration options
@@ -48270,6 +54608,10 @@ Roo.form.Form = function(config){
 };
 
 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
+     /**
+     * @cfg {Roo.Button} buttons[] buttons at bottom of form
+     */
+    
     /**
      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
      */
@@ -48277,7 +54619,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',
 
@@ -48287,7 +54629,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',
@@ -49009,6 +55351,7 @@ Roo.form.Action.ACTION_TYPES = {
 /**
  * @class Roo.form.Layout
  * @extends Roo.Component
+ * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
  * @constructor
  * @param {Object} config Configuration options
@@ -49036,7 +55379,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")
      */
     /**
@@ -49156,9 +55499,11 @@ Roo.extend(Roo.form.Layout, Roo.Component, {
     }
 });
 
+
 /**
  * @class Roo.form.Column
  * @extends Roo.form.Layout
+ * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
  * @constructor
  * @param {Object} config Configuration options
@@ -49189,10 +55534,10 @@ Roo.extend(Roo.form.Column, Roo.form.Layout, {
     }
 });
 
-
 /**
  * @class Roo.form.Row
  * @extends Roo.form.Layout
+ * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
  * @constructor
  * @param {Object} config Configuration options
@@ -49269,6 +55614,7 @@ Roo.extend(Roo.form.Row, Roo.form.Layout, {
 /**
  * @class Roo.form.FieldSet
  * @extends Roo.form.Layout
+ * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
  * @constructor
  * @param {Object} config Configuration options
@@ -49317,7 +55663,7 @@ Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
 /**
  * @class Roo.form.VTypes
  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
- * @singleton
+ * @static
  */
 Roo.form.VTypes = function(){
     // closure these in so they are only created once.
@@ -51369,10 +57715,10 @@ Roo.DDView = function(container, tpl, config) {
     this.getEl().setStyle("outline", "0px none");
     this.getEl().unselectable();
     if (this.dragGroup) {
-               this.setDraggable(this.dragGroup.split(","));
+       this.setDraggable(this.dragGroup.split(","));
     }
     if (this.dropGroup) {
-               this.setDroppable(this.dropGroup.split(","));
+       this.setDroppable(this.dropGroup.split(","));
     }
     if (this.deletable) {
        this.setDeletable();
@@ -51914,6 +58260,7 @@ Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
 /**
  * @class Roo.BorderLayout
  * @extends Roo.LayoutManager
+ * @children Roo.ContentPanel
  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
  * please see: <br><br>
  * <a href="http://www.jackslocum.com/yui/2006/10/19/cross-browser-web-20-layouts-with-yahoo-ui/">Cross Browser Layouts - Part 1</a><br>
@@ -51995,6 +58342,22 @@ Roo.BorderLayout = function(container, config){
 };
 
 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
+       
+       /**
+        * @cfg {Roo.LayoutRegion} east
+        */
+       /**
+        * @cfg {Roo.LayoutRegion} west
+        */
+       /**
+        * @cfg {Roo.LayoutRegion} north
+        */
+       /**
+        * @cfg {Roo.LayoutRegion} south
+        */
+       /**
+        * @cfg {Roo.LayoutRegion} center
+        */
     /**
      * Creates and adds a new region if it doesn't already exist.
      * @param {String} target The target region key (north, south, east, west or center).
@@ -54099,22 +60462,26 @@ Roo.LayoutStateManager.prototype = {
 /**
  * @class Roo.ContentPanel
  * @extends Roo.util.Observable
+ * @children Roo.form.Form Roo.JsonView Roo.View
+ * @parent Roo.BorderLayout Roo.LayoutDialog builder
  * A basic ContentPanel element.
  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
- * @cfg {Boolean/Object} autoCreate True to auto generate the DOM element for this panel, or a {@link Roo.DomHelper} config of the element to create
+ * @cfg {Boolean|Object} autoCreate True to auto generate the DOM element for this panel, or a {@link Roo.DomHelper} config of the element to create
  * @cfg {Boolean}   closable      True if the panel can be closed/removed
  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
- * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
- * @cfg {Toolbar}   toolbar       A toolbar for this panel
+ * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
+ * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
  * @cfg {String} title          The title for this panel
  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
  * @cfg {String} url            Calls {@link #setUrl} with this value
- * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
- * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
+ * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
+ * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
+ * @cfg {String}    style  Extra style to add to the content panel
+ * @cfg {Roo.menu.Menu} menu  popup menu
 
  * @constructor
  * Create a new ContentPanel.
@@ -54152,6 +60519,8 @@ Roo.ContentPanel = function(el, config, content){
                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
         }
     }
+    
+    
     this.closable = false;
     this.loaded = false;
     this.active = false;
@@ -54542,17 +60911,32 @@ layout.addxtype({
     }
 });
 
+
+
+
+
+
+
+
+
+
+
+
 /**
  * @class Roo.GridPanel
  * @extends Roo.ContentPanel
+ * @parent Roo.BorderLayout Roo.LayoutDialog builder
  * @constructor
  * Create a new GridPanel.
- * @param {Roo.grid.Grid} grid The grid for this panel
- * @param {String/Object} config A string to set only the panel's title, or a config object
+ * @cfg {Roo.grid.Grid} grid The grid for this panel
  */
 Roo.GridPanel = function(grid, config){
     
-  
+    // universal ctor...
+    if (typeof(grid.grid) != 'undefined') {
+        config = grid;
+        grid = config.grid;
+    }
     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
         
@@ -54620,11 +61004,15 @@ Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
 /**
  * @class Roo.NestedLayoutPanel
  * @extends Roo.ContentPanel
+ * @parent Roo.BorderLayout Roo.LayoutDialog builder
+ * @cfg {Roo.BorderLayout} layout   [required] The layout for this panel
+ *
+ * 
  * @constructor
  * Create a new NestedLayoutPanel.
  * 
  * 
- * @param {Roo.BorderLayout} layout The layout for this panel
+ * @param {Roo.BorderLayout} layout [required] The layout for this panel
  * @param {String/Object} config A string to set only the title or a config object
  */
 Roo.NestedLayoutPanel = function(layout, config)
@@ -54656,6 +61044,8 @@ Roo.NestedLayoutPanel = function(layout, config)
 
 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
 
+    layout : false,
+
     setSize : function(width, height){
         if(!this.ignoreResize(width, height)){
             var size = this.adjustForComponents(width, height);
@@ -54706,7 +61096,7 @@ Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
     
     /**
      * Returns the nested BorderLayout for this panel
-     * @return {Roo.BorderLayout} 
+     * @return {Roo.BorderLayout}
      */
     getLayout : function(){
         return this.layout;
@@ -54805,19 +61195,15 @@ Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
 
 
 
-
-
-
-
-
-
 /**
  * @class Roo.TreePanel
  * @extends Roo.ContentPanel
+ * @parent Roo.BorderLayout Roo.LayoutDialog builder
+ * Treepanel component
+ * 
  * @constructor
  * Create a new TreePanel. - defaults to fit/scoll contents.
  * @param {String/Object} config A string to set only the panel's title, or a config object
- * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
  */
 Roo.TreePanel = function(config){
     var el = config.el;
@@ -54860,19 +61246,13 @@ Roo.TreePanel = function(config){
 
 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
     fitToFrame : true,
-    autoScroll : true
-});
-
-
-
-
-
-
-
-
-
-
+    autoScroll : true,
+    /*
+     * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
+     */
+    tree : false
 
+});
 /*
  * Based on:
  * Ext JS Library 1.1.1
@@ -55301,8 +61681,26 @@ Roo.grid.Grid = function(container, config){
 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
     
     /**
+        * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
+        */
+       /**
+        * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
+        */
+       /**
+        * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
+        */
+       /**
+        * @cfg {Roo.data.Store} ds The data store for the grid
+        */
+       /**
+        * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
+        */
+       /**
      * @cfg {String} ddGroup - drag drop group.
      */
+      /**
+     * @cfg {String} dragGroup - drag group (?? not sure if needed.)
+     */
 
     /**
      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
@@ -55339,6 +61737,9 @@ Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
 
     /**
     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
+    */
+      /**
+    * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
     */
     
     /**
@@ -55400,8 +61801,10 @@ Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
     */
     dropTarget: false,
-    
-   
+     /**
+    * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
+    */ 
+    sortColMenu : false,
     
     // private
     rendered : false,
@@ -55413,6 +61816,15 @@ Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
     /**
     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
     */
+    
+    
+    /**
+    * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
+    * %0 is replaced with the number of selected rows.
+    */
+    ddText : "{0} selected row{1}",
+    
+    
     /**
      * Called once after all setup has been completed and the grid is ready to be rendered.
      * @return {Roo.grid.Grid} this
@@ -55723,11 +62135,17 @@ Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
     getView : function(){
         if(!this.view){
             this.view = new Roo.grid.GridView(this.viewConfig);
+           this.relayEvents(this.view, [
+               "beforerowremoved", "beforerowsinserted",
+               "beforerefresh", "rowremoved",
+               "rowsinserted", "rowupdated" ,"refresh"
+           ]);
         }
         return this.view;
     },
     /**
      * Called to get grid's drag proxy text, by default returns this.ddText.
+     * Override this to put something different in the dragged text.
      * @return {String}
      */
     getDragDropText : function(){
@@ -55735,12 +62153,7 @@ Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
         return String.format(this.ddText, count, count == 1 ? '' : 's');
     }
 });
-/**
- * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
- * %0 is replaced with the number of selected rows.
- * @type String
- */
-Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
+/*
  * Based on:
  * Ext JS Library 1.1.1
  * Copyright(c) 2006-2007, Ext JS, LLC.
@@ -55750,7 +62163,13 @@ Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
  * Fork - LGPL
  * <script type="text/javascript">
  */
+ /**
+ * @class Roo.grid.AbstractGridView
+ * @extends Roo.util.Observable
+ * @abstract
+ * Abstract base class for grid Views
+ * @constructor
+ */
 Roo.grid.AbstractGridView = function(){
        this.grid = null;
        
@@ -56655,7 +63074,7 @@ Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
                 );
         */
         if(ctop < stop){
-             c.scrollTop = ctop;
+            c.scrollTop = ctop;
             //Roo.log("set scrolltop to ctop DISABLE?");
         }else if(cbot > sbot){
             //Roo.log("set scrolltop to cbot-ch");
@@ -57447,13 +63866,28 @@ Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
     beforeColMenuShow : function(){
         var cm = this.cm,  colCount = cm.getColumnCount();
         this.colMenu.removeAll();
+        
+        var items = [];
         for(var i = 0; i < colCount; i++){
-            this.colMenu.add(new Roo.menu.CheckItem({
+            items.push({
                 id: "col-"+cm.getColumnId(i),
                 text: cm.getColumnHeader(i),
                 checked: !cm.isHidden(i),
                 hideOnClick:false
-            }));
+            });
+        }
+        
+        if (this.grid.sortColMenu) {
+            items.sort(function(a,b) {
+                if (a.text == b.text) {
+                    return 0;
+                }
+                return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
+            });
+        }
+        
+        for(var i = 0; i < colCount; i++){
+            this.colMenu.add(new Roo.menu.CheckItem(items[i]));
         }
     },
 
@@ -57650,7 +64084,8 @@ Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
         }
     },
 
-    layout : function(initialRender, is2ndPass){
+    layout : function(initialRender, is2ndPass)
+    {
         var g = this.grid;
         var auto = g.autoHeight;
         var scrollOffset = 16;
@@ -57807,19 +64242,34 @@ Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
  * Fork - LGPL
  * <script type="text/javascript">
  */
+ /**
+ * @extends Roo.dd.DDProxy
+ * @class Roo.grid.SplitDragZone
+ * Support for Column Header resizing
+ * @constructor
+ * @param {Object} config
+ */
 // private
 // This is a support class used internally by the Grid components
 Roo.grid.SplitDragZone = function(grid, hd, hd2){
     this.grid = grid;
     this.view = grid.getView();
     this.proxy = this.view.resizeProxy;
-    Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
-        "gridSplitters" + this.grid.getGridEl().id, {
-        dragElId : Roo.id(this.proxy.dom), resizeFrame:false
-    });
+    Roo.grid.SplitDragZone.superclass.constructor.call(
+        this,
+        hd, // ID
+        "gridSplitters" + this.grid.getGridEl().id, // SGROUP
+        {  // CONFIG
+            dragElId : Roo.id(this.proxy.dom),
+            resizeFrame:false
+        }
+    );
+    
     this.setHandleElId(Roo.id(hd));
-    this.setOuterHandleElId(Roo.id(hd2));
+    if (hd2 !== false) {
+        this.setOuterHandleElId(Roo.id(hd2));
+    }
+    
     this.scroll = false;
 };
 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
@@ -57827,8 +64277,25 @@ Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
 
     b4StartDrag : function(x, y){
         this.view.headersDisabled = true;
-        this.proxy.setHeight(this.view.mainWrap.getHeight());
+        var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
+                    this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
+        );
+        this.proxy.setHeight(h);
+        
+        // for old system colWidth really stored the actual width?
+        // in bootstrap we tried using xs/ms/etc.. to do % sizing?
+        // which in reality did not work.. - it worked only for fixed sizes
+        // for resizable we need to use actual sizes.
         var w = this.cm.getColumnWidth(this.cellIndex);
+        if (!this.view.mainWrap) {
+            // bootstrap.
+            w = this.view.getHeaderIndex(this.cellIndex).getWidth();
+        }
+        
+        
+        
+        // this was w-this.grid.minColumnWidth;
+        // doesnt really make sense? - w = thie curren width or the rendered one?
         var minw = Math.max(w-this.grid.minColumnWidth, 0);
         this.resetConstraints();
         this.setXConstraint(minw, 1000);
@@ -57836,6 +64303,10 @@ Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
         this.minX = x - minw;
         this.maxX = x + 1000;
         this.startPos = x;
+        if (!this.view.mainWrap) { // this is Bootstrap code..
+            this.getDragEl().style.display='block';
+        }
+        
         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
     },
 
@@ -57857,7 +64328,12 @@ Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
         this.view.headersDisabled = false;
         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
         var diff = endX - this.startPos;
-        this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
+        // 
+        var w = this.cm.getColumnWidth(this.cellIndex);
+        if (!this.view.mainWrap) {
+            w = 0;
+        }
+        this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
     },
 
     autoOffset : function(){
@@ -57909,7 +64385,12 @@ Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
             }
         
         }
+        if (sm.getSelections && sm.getSelections().length < 1) {
+            return false;
+        }
         
+        
+        // before it used to all dragging of unseleted... - now we dont do that.
         if(rowIndex !== false){
             
             // if editorgrid.. 
@@ -57930,14 +64411,14 @@ Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
                 grid: this.grid,
                 ddel: this.ddel,
                 rowIndex: rowIndex,
-                selections:sm.getSelections ? sm.getSelections() : (
-                    sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
-                )
+                selections: sm.getSelections ? sm.getSelections() : (
+                    sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
             };
         }
         return false;
     },
-
+    
+    
     onInitDrag : function(e){
         var data = this.dragData;
         this.ddel.innerHTML = this.grid.getDragDropText();
@@ -58005,30 +64486,15 @@ Roo.grid.ColumnModel = function(config){
        /**
      * The config passed into the constructor
      */
-    this.config = config;
+    this.config = []; //config;
     this.lookup = {};
 
     // if no id, create one
     // if the column does not have a dataIndex mapping,
     // map it to the order it is in the config
     for(var i = 0, len = config.length; i < len; i++){
-        var c = config[i];
-        if(typeof c.dataIndex == "undefined"){
-            c.dataIndex = i;
-        }
-        if(typeof c.renderer == "string"){
-            c.renderer = Roo.util.Format[c.renderer];
-        }
-        if(typeof c.id == "undefined"){
-            c.id = Roo.id();
-        }
-        if(c.editor && c.editor.xtype){
-            c.editor  = Roo.factory(c.editor, Roo.grid);
-        }
-        if(c.editor && c.editor.isFormField){
-            c.editor = new Roo.grid.GridEditor(c.editor);
-        }
-        this.lookup[c.id] = c;
+       this.addColumn(config[i]);
+       
     }
 
     /**
@@ -58089,66 +64555,84 @@ 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)
+     */
+       /**
+     * @cfg {String} smHeader Header at Bootsrap Small width
+     */
+       /**
+     * @cfg {String} mdHeader Header at Bootsrap Medium width
+     */
+       /**
+     * @cfg {String} lgHeader Header at Bootsrap Large width
+     */
+       /**
+     * @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)
+     * @cfg {Number} xs  can be '0' for hidden at this size (number less than 12)
      */
     /**
-     * @cfg {Number} sm (Optional)
+     * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
      */
     /**
-     * @cfg {Number} md (Optional)
+     * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
      */
     /**
-     * @cfg {Number} lg (Optional)
+     * @cfg {Number} lg   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.
@@ -58170,7 +64654,7 @@ Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
 
     
     /**
-     * Returns the column for a specified dataIndex.
+     * Returns the column Object for a specified dataIndex.
      * @param {String} dataIndex The column dataIndex
      * @return {Object|Boolean} the column or false if not found
      */
@@ -58331,10 +64815,29 @@ Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
     /**
      * Returns the width for the specified column.
      * @param {Number} col The column index
+     * @param (optional) {String} gridSize bootstrap width size.
      * @return {Number}
      */
-    getColumnWidth : function(col){
-        return this.config[col].width * 1 || this.defaultWidth;
+    getColumnWidth : function(col, gridSize)
+       {
+               var cfg = this.config[col];
+               
+               if (typeof(gridSize) == 'undefined') {
+                       return cfg.width * 1 || this.defaultWidth;
+               }
+               if (gridSize === false) { // if we set it..
+                       return cfg.width || false;
+               }
+               var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
+               
+               for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
+                       if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
+                               continue;
+                       }
+                       return cfg[ sizes[i] ];
+               }
+               return 1;
+               
     },
 
     /**
@@ -58496,7 +64999,35 @@ Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
      */
     setEditor : function(col, editor){
         this.config[col].editor = editor;
+    },
+    /**
+     * Add a column (experimental...) - defaults to adding to the end..
+     * @param {Object} config 
+    */
+    addColumn : function(c)
+    {
+    
+       var i = this.config.length;
+       this.config[i] = c;
+       
+       if(typeof c.dataIndex == "undefined"){
+            c.dataIndex = i;
+        }
+        if(typeof c.renderer == "string"){
+            c.renderer = Roo.util.Format[c.renderer];
+        }
+        if(typeof c.id == "undefined"){
+            c.id = Roo.id();
+        }
+        if(c.editor && c.editor.xtype){
+            c.editor  = Roo.factory(c.editor, Roo.grid);
+        }
+        if(c.editor && c.editor.isFormField){
+            c.editor = new Roo.grid.GridEditor(c.editor);
+        }
+        this.lookup[c.id] = c;
     }
+    
 });
 
 Roo.grid.ColumnModel.defaultRenderer = function(value)
@@ -58527,6 +65058,7 @@ Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
 /**
  * @class Roo.grid.AbstractSelectionModel
  * @extends Roo.util.Observable
+ * @abstract
  * Abstract base class for grid SelectionModels.  It provides the interface that should be
  * implemented by descendant classes.  This class should not be directly instantiated.
  * @constructor
@@ -58593,39 +65125,39 @@ Roo.grid.RowSelectionModel = function(config){
 
     this.addEvents({
         /**
-            * @event selectionchange
-            * Fires when the selection changes
-            * @param {SelectionModel} this
-            */
-           "selectionchange" : true,
-        /**
-            * @event afterselectionchange
-            * Fires after the selection changes (eg. by key press or clicking)
-            * @param {SelectionModel} this
-            */
-           "afterselectionchange" : true,
-        /**
-            * @event beforerowselect
-            * Fires when a row is selected being selected, return false to cancel.
-            * @param {SelectionModel} this
-            * @param {Number} rowIndex The selected index
-            * @param {Boolean} keepExisting False if other selections will be cleared
-            */
-           "beforerowselect" : true,
-        /**
-            * @event rowselect
-            * Fires when a row is selected.
-            * @param {SelectionModel} this
-            * @param {Number} rowIndex The selected index
-            * @param {Roo.data.Record} r The record
-            */
-           "rowselect" : true,
-        /**
-            * @event rowdeselect
-            * Fires when a row is deselected.
-            * @param {SelectionModel} this
-            * @param {Number} rowIndex The selected index
-            */
+        * @event selectionchange
+        * Fires when the selection changes
+        * @param {SelectionModel} this
+        */
+       "selectionchange" : true,
+       /**
+        * @event afterselectionchange
+        * Fires after the selection changes (eg. by key press or clicking)
+        * @param {SelectionModel} this
+        */
+       "afterselectionchange" : true,
+       /**
+        * @event beforerowselect
+        * Fires when a row is selected being selected, return false to cancel.
+        * @param {SelectionModel} this
+        * @param {Number} rowIndex The selected index
+        * @param {Boolean} keepExisting False if other selections will be cleared
+        */
+       "beforerowselect" : true,
+       /**
+        * @event rowselect
+        * Fires when a row is selected.
+        * @param {SelectionModel} this
+        * @param {Number} rowIndex The selected index
+        * @param {Roo.data.Record} r The record
+        */
+       "rowselect" : true,
+       /**
+        * @event rowdeselect
+        * Fires when a row is deselected.
+        * @param {SelectionModel} this
+        * @param {Number} rowIndex The selected index
+        */
         "rowdeselect" : true
     });
     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
@@ -58647,7 +65179,8 @@ Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
         }else{ // allow click to work like normal
             this.grid.on("rowclick", this.handleDragableRowClick, this);
         }
-
+        // bootstrap does not have a view..
+        var view = this.grid.view ? this.grid.view : this.grid;
         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
             "up" : function(e){
                 if(!e.shiftKey){
@@ -58655,7 +65188,7 @@ Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
                 }else if(this.last !== false && this.lastActive !== false){
                     var last = this.last;
                     this.selectRange(this.last,  this.lastActive-1);
-                    this.grid.getView().focusRow(this.lastActive);
+                    view.focusRow(this.lastActive);
                     if(last !== false){
                         this.last = last;
                     }
@@ -58670,7 +65203,7 @@ Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
                 }else if(this.last !== false && this.lastActive !== false){
                     var last = this.last;
                     this.selectRange(this.last,  this.lastActive+1);
-                    this.grid.getView().focusRow(this.lastActive);
+                    view.focusRow(this.lastActive);
                     if(last !== false){
                         this.last = last;
                     }
@@ -58682,7 +65215,7 @@ Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
             scope: this
         });
 
-        var view = this.grid.view;
+         
         view.on("refresh", this.onRefresh, this);
         view.on("rowupdated", this.onRowUpdated, this);
         view.on("rowremoved", this.onRemove, this);
@@ -58690,7 +65223,7 @@ Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
 
     // private
     onRefresh : function(){
-        var ds = this.grid.dataSource, i, v = this.grid.view;
+        var ds = this.grid.ds, i, v = this.grid.view;
         var s = this.selections;
         s.each(function(r){
             if((i = ds.indexOfId(r.id)) != -1){
@@ -58723,7 +65256,7 @@ Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
         if(!keepExisting){
             this.clearSelections();
         }
-        var ds = this.grid.dataSource;
+        var ds = this.grid.ds;
         for(var i = 0, len = records.length; i < len; i++){
             this.selectRow(ds.indexOf(records[i]), true);
         }
@@ -58749,7 +65282,7 @@ Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
      * @param {Boolean} keepExisting (optional) True to keep existing selections
      */
     selectLastRow : function(keepExisting){
-        this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
+        this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
     },
 
     /**
@@ -58757,9 +65290,10 @@ Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
      * @param {Boolean} keepExisting (optional) True to keep existing selections
      */
     selectNext : function(keepExisting){
-        if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
+        if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
             this.selectRow(this.last+1, keepExisting);
-            this.grid.getView().focusRow(this.last);
+            var view = this.grid.view ? this.grid.view : this.grid;
+            view.focusRow(this.last);
         }
     },
 
@@ -58770,7 +65304,8 @@ Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
     selectPrevious : function(keepExisting){
         if(this.last){
             this.selectRow(this.last-1, keepExisting);
-            this.grid.getView().focusRow(this.last);
+            var view = this.grid.view ? this.grid.view : this.grid;
+            view.focusRow(this.last);
         }
     },
 
@@ -58799,7 +65334,7 @@ Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
             return;
         }
         if(fast !== true){
-            var ds = this.grid.dataSource;
+            var ds = this.grid.ds;
             var s = this.selections;
             s.each(function(r){
                 this.deselectRow(ds.indexOfId(r.id));
@@ -58820,7 +65355,7 @@ Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
             return;
         }
         this.selections.clear();
-        for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
+        for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
             this.selectRow(i, true);
         }
     },
@@ -58839,7 +65374,7 @@ Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
      * @return {Boolean}
      */
     isSelected : function(index){
-        var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
+        var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
         return (r && this.selections.key(r.id) ? true : false);
     },
 
@@ -58853,8 +65388,10 @@ Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
     },
 
     // private
-    handleMouseDown : function(e, t){
-        var view = this.grid.getView(), rowIndex;
+    handleMouseDown : function(e, t)
+    {
+        var view = this.grid.view ? this.grid.view : this.grid;
+        var rowIndex;
         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
             return;
         };
@@ -58881,7 +65418,8 @@ Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
     {
         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
             this.selectRow(rowIndex, false);
-            grid.view.focusRow(rowIndex);
+            var view = this.grid.view ? this.grid.view : this.grid;
+            view.focusRow(rowIndex);
              this.fireEvent("afterselectionchange", this);
         }
     },
@@ -58944,18 +65482,19 @@ Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
      * @param {Boolean} keepExisting (optional) True to keep existing selections
      */
     selectRow : function(index, keepExisting, preventViewNotify){
-        if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
+        if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
             return;
         }
         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
             if(!keepExisting || this.singleSelect){
                 this.clearSelections();
             }
-            var r = this.grid.dataSource.getAt(index);
+            var r = this.grid.ds.getAt(index);
             this.selections.add(r);
             this.last = this.lastActive = index;
             if(!preventViewNotify){
-                this.grid.getView().onRowSelect(index);
+                var view = this.grid.view ? this.grid.view : this.grid;
+                view.onRowSelect(index);
             }
             this.fireEvent("rowselect", this, index, r);
             this.fireEvent("selectionchange", this);
@@ -58976,10 +65515,11 @@ Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
         if(this.lastActive == index){
             this.lastActive = false;
         }
-        var r = this.grid.dataSource.getAt(index);
+        var r = this.grid.ds.getAt(index);
         this.selections.remove(r);
         if(!preventViewNotify){
-            this.grid.getView().onRowDeselect(index);
+            var view = this.grid.view ? this.grid.view : this.grid;
+            view.onRowDeselect(index);
         }
         this.fireEvent("rowdeselect", this, index);
         this.fireEvent("selectionchange", this);
@@ -59878,7 +66418,7 @@ Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
  
 /**
  * @class Roo.grid.Calendar
- * @extends Roo.util.Grid
+ * @extends Roo.grid.Grid
  * This class extends the Grid to provide a calendar widget
  * <br><br>Usage:<pre><code>
  var grid = new Roo.grid.Calendar("my-container-id", {
@@ -61193,6 +67733,7 @@ Roo.LoadMask.prototype = {
      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
      */
+    removeMask : false,
     /**
      * @cfg {String} msg
      * The text to display in a centered loading message box (defaults to 'Loading...')