Roo/form/ComboBoxArray.js
[roojs1] / roojs-ui-debug.js
index 735c2f8..1d73780 100644 (file)
@@ -3040,6 +3040,8 @@ Roo.dd.ScrollManager = function(){
     var dragEl = null;
     var proc = {};
     
+    
+    
     var onStop = function(e){
         dragEl = null;
         clearProc();
@@ -3074,6 +3076,7 @@ Roo.dd.ScrollManager = function(){
     };
     
     var startProc = function(el, dir){
+         Roo.log('scroll startproc');
         clearProc();
         proc.el = el;
         proc.dir = dir;
@@ -3081,6 +3084,7 @@ Roo.dd.ScrollManager = function(){
     };
     
     var onFire = function(e, isDrop){
+       
         if(isDrop || !ddm.dragCurrent){ return; }
         var dds = Roo.dd.ScrollManager;
         if(!dragEl || dragEl != ddm.dragCurrent){
@@ -3137,6 +3141,7 @@ Roo.dd.ScrollManager = function(){
                 el = Roo.get(el);
                 els[el.id] = el;
             }
+            Roo.dd.ScrollManager.els = els;
         },
         
         /**
@@ -3940,6 +3945,7 @@ Roo.dd.DropTarget = function(el, config){
          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
          * 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
@@ -6671,6 +6677,8 @@ Roo.data.Node = function(attributes){
         this.id = Roo.id(null, "ynode-");
         this.attributes.id = this.id;
     }
+     
+    
     /**
      * All child nodes of this node. @type Array
      */
@@ -10328,6 +10336,15 @@ Roo.extend(Roo.DatePicker, Roo.Component, {
      */
     setValue : function(value){
         var old = this.value;
+        
+        if (typeof(value) == 'string') {
+         
+            value = Date.parseDate(value, this.format);
+        }
+        if (!value) {
+            value = new Date();
+        }
+        
         this.value = value.clearTime(true);
         if(this.el){
             this.update(this.value);
@@ -10349,8 +10366,9 @@ Roo.extend(Roo.DatePicker, Roo.Component, {
         }
     },
 
-    // private
+    // privateval
     onRender : function(container, position){
+        
         var m = [
              '<table cellspacing="0">',
                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
@@ -16928,8 +16946,8 @@ Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
         }
         this.getSelectionModel().init(this);
         if (!this.root) {
-            console.log("ROOT not set in tree");
-            return;
+            Roo.log("ROOT not set in tree");
+            return this;
         }
         this.root.render();
         if(!this.rootVisible){
@@ -18512,6 +18530,11 @@ Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
     * 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.
     */
@@ -18924,6 +18947,7 @@ Roo.tree.TreeDropZone = function(tree, config){
 
 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
     ddGroup : "TreeDD",
+    scroll:  true,
     
     expandDelay : 1000,
     
@@ -18975,7 +18999,8 @@ Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
         return overEvent.cancel === false && result !== false;
     },
     
-    getDropPoint : function(e, n, dd){
+    getDropPoint : function(e, n, dd)
+    {
         var tn = n.node;
         if(tn.isRoot){
             return tn.allowChildren !== false ? "append" : false; // always append for root
@@ -19004,11 +19029,14 @@ Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
         }
     },
     
-    onNodeEnter : function(n, dd, e, data){
+    onNodeEnter : function(n, dd, e, data)
+    {
         this.cancelExpand();
     },
     
-    onNodeOver : function(n, dd, e, data){
+    onNodeOver : function(n, dd, e, data)
+    {
+       
         var pt = this.getDropPoint(e, n, dd);
         var node = n.node;
         
@@ -19045,6 +19073,7 @@ Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
     },
     
     onNodeOut : function(n, dd, e, data){
+        
         this.cancelExpand();
         this.removeDropIndicators(n);
     },
@@ -19143,7 +19172,8 @@ Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
             data.node.ui.highlight();
         }
         this.hideProxy();
-    }    
+    } 
+    
 });
 
 }
@@ -19167,11 +19197,12 @@ Roo.tree.TreeDragZone = function(tree, config){
 
 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;
@@ -19187,6 +19218,8 @@ Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
     
     onEndDrag : function(data, e){
         this.tree.fireEvent("enddrag", this.tree, data.node, e);
+        
+        
     },
     
     onValidDrop : function(dd, e, id){
@@ -21039,10 +21072,9 @@ Roo.menu.DateMenu = function(config){
      * @param {Date} date
      */
     this.relayEvents(di, ["select"]);
-
     this.on('beforeshow', function(){
         if(this.picker){
-            this.picker.hideMonthPicker(true);
+            this.picker.hideMonthPicker(false);
         }
     }, this);
 };
@@ -22660,7 +22692,7 @@ Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
     
 
     /**
-     * @cfg {bool} useIso
+     * @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)
      */ 
@@ -22683,7 +22715,7 @@ Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
             this.el.dom.removeAttribute('name'); 
             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
                     'before', true);
-            this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
+            this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
             // prevent input submission
             this.hiddenName = this.name;
         }
@@ -22696,6 +22728,7 @@ Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
     {
         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
@@ -22704,6 +22737,7 @@ Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
         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;
         }
@@ -22745,7 +22779,9 @@ Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
      */
     getValue : function(){
         
-        return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
+        return  this.hiddenField ?
+                this.hiddenField.value :
+                this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
     },
 
     /**
@@ -22774,6 +22810,10 @@ dateField.setValue('2006-5-4');
             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
@@ -22782,6 +22822,9 @@ dateField.setValue('2006-5-4');
             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("|");
@@ -22802,6 +22845,7 @@ dateField.setValue('2006-5-4');
     // private
     menuListeners : {
         select: function(m, d){
+            
             this.setValue(d);
             this.fireEvent('select', this, d);
         },
@@ -22834,7 +22878,7 @@ dateField.setValue('2006-5-4');
             disabledDatesText : this.disabledDatesText,
             disabledDays : this.disabledDays,
             disabledDaysText : this.disabledDaysText,
-            format : this.format,
+            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))
         });
@@ -22870,6 +22914,406 @@ dateField.setValue('2006-5-4');
  * <script type="text/javascript">
  */
  
+/**
+ * @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.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);
+    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.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',
+    
+
+    /**
+     * @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: "off"},
+    
+    // private
+    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;
+        }
+            
+            
+    },
+    
+    // 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(){
+        
+        
+        
+        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);
+
+//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
+     */
+    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
+    parseDate : function(value){
+        if(!value || value instanceof Date){
+            value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
+            return value;
+        }
+        var v = Date.parseDate(value, this.format);
+        if (!v && this.useIso) {
+            v = Date.parseDate(value, 'Y-m-d');
+        }
+        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 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);
+        }
+    },
+    // private
+    // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
+    onTriggerClick : function(){
+        if(this.disabled){
+            return;
+        }
+        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);
+    
+        
+       
+    },
+
+    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
@@ -23591,6 +24035,9 @@ Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
         this.setValue(this.originalValue);
         this.clearInvalid();
         this.lastData = false;
+        if (this.view) {
+            this.view.clearSelections();
+        }
     },
     // private
     findRecord : function(prop, value){
@@ -23941,6 +24388,422 @@ Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
      * @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...
+ *   
+ *  
+ *
+ * @constructor
+ * Create a new ComboBoxArray.
+ * @param {Object} config Configuration options
+ */
+
+Roo.form.ComboBoxArray = function(config)
+{
+    
+    Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
+    
+    this.items = new Roo.util.MixedCollection(false);
+    
+    // construct the child combo...
+    
+    
+    
+    
+   
+    
+}
+
+Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
+{ 
+    /**
+     * @cfg {Roo.form.Combo} combo The combo box that is wrapped
+     */
+    
+    lastData : false,
+    
+    // behavies liek a hiddne field
+    inputType:      'hidden',
+    /**
+     * @cfg {Number} width The width of the box that displays the selected element
+     */ 
+    width:          300,
+
+    
+    
+    /**
+     * @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,
+    
+    
+    // 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) 
+    {
+        
+        // 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);
+        
+        // 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);
+        
+        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);
+        
+        
+    },
+    
+    
+    getName: function()
+    {
+        // returns hidden if it's set..
+        if (!this.rendered) {return ''};
+        return  this.hiddenName ? this.hiddenName : this.name;
+        
+    },
+    
+    
+    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'));
+        }
+        
+    
+        
+    },
+    
+    addItem: function(rec)
+    {
+        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..
+    },
+    
+    updateHiddenEl : function()
+    {
+        this.validate();
+        if (!this.hiddenEl) {
+            return;
+        }
+        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.form.ComboBoxArray.superclass.reset.call(this); 
+        this.items.each(function(f) {
+           f.remove(); 
+        });
+        this.el.dom.value = '';
+        if (this.hiddenEl) {
+            this.hiddenEl.dom.value = '';
+        }
+        
+    },
+    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(','), function(k) {
+                Roo.log("CHECK " + this.valueField + ',' + k);
+                var li = this.store.query(this.valueField, k);
+                if (!li.length) {
+                    return;
+                }
+                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);
+            }, this);
+             
+        }
+        
+        
+    },
+    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(',');
+        var display = dv.split(',');
+        for (var i = 0 ; i < keys.length; i++) {
+            
+            add = {};
+            add[this.valueField] = keys[i];
+            add[this.displayField] = display[i];
+            this.addItem(add);
+        }
+      
+        
+    },
+    
+    
+    validateValue : function(value){
+        return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
+        
+    }
+    
+});
+
+
+
+/**
+ * @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,
+    
+    
+    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()
+    {
+        
+        this.cb.items.remove(this);
+        this.el.child('img').un('click', this.remove, this);
+        this.el.remove();
+        this.cb.updateHiddenEl();
+    }
+    
+    
 });/*
  * Based on:
  * Ext JS Library 1.1.1
@@ -24398,7 +25261,7 @@ Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
             //<style type="text/css">' +
             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
             //'</style>' +
-            ' </head><body></body></html>';
+            ' </head><body class="roo-htmleditor-body"></body></html>';
     },
 
     // private
@@ -25323,6 +26186,9 @@ Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
                 return;
             }
+            if (v.match(/^#/)) {
+                return;
+            }
             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
             node.removeAttribute(n);
             
@@ -25362,14 +26228,15 @@ Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
             //console.log(a);
             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
                 node.removeAttribute(a.name);
-                return;
+                continue;
             }
             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
                 cleanAttr(a.name,a.value); // fixme..
-                return;
+                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..
@@ -25383,6 +26250,7 @@ Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
                 if (a.value.match(/body/)) {
                     node.className = '';
                 }
+                continue;
             }
             
             // style cleanup!?
@@ -25877,7 +26745,7 @@ Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
         
         if (this.btns) {
             for(var i =0; i< this.btns.length;i++) {
-                var b = this.btns[i];
+                var b = Roo.factory(this.btns[i],Roo.form);
                 b.cls =  'x-edit-none';
                 b.scope = editor;
                 tb.add(b);
@@ -26388,6 +27256,9 @@ Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
         this.tb.el.show();
         this.buildFooter();
         this.footer.show();
+        editor.on('hide', function( ) { this.footer.hide() }, this);
+        editor.on('show', function( ) { this.footer.show() }, this);
+        
          
         this.rendered = true;
         
@@ -26473,7 +27344,7 @@ Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
             // update attributes
             if (this.tb.fields) {
                 this.tb.fields.each(function(e) {
-                   e.setValue(sel.getAttribute(e.name));
+                   e.setValue(sel.getAttribute(e.attrname));
                 });
             }
             
@@ -26597,7 +27468,8 @@ Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
                     fields: ['val', 'selected'],
                     data : [] 
                 }),
-                name : 'className',
+                name : '-roo-edit-className',
+                attrname : 'className',
                 displayField:'val',
                 typeAhead: false,
                 mode: 'local',
@@ -26635,7 +27507,8 @@ Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
                         fields: ['val'],
                         data : item.opts  
                     }),
-                    name : i,
+                    name : '-roo-edit-' + i,
+                    attrname : i,
                     displayField:'val',
                     typeAhead: false,
                     mode: 'local',
@@ -26646,7 +27519,7 @@ Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
                     width: item.width ? item.width  : 130,
                     listeners : {
                         'select': function(c, r, i) {
-                            tb.selectedNode.setAttribute(c.name, r.get('val'));
+                            tb.selectedNode.setAttribute(c.attrname, r.get('val'));
                         }
                     }
 
@@ -26664,13 +27537,15 @@ Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
                 continue;
             }
             tb.addField( new Roo.form.TextField({
-                name: i,
+                name: '-roo-edit-' + i,
+                attrname : i,
+                
                 width: item.width,
                 //allowBlank:true,
                 value: '',
                 listeners: {
                     'change' : function(f, nv, ov) {
-                        tb.selectedNode.setAttribute(f.name, nv);
+                        tb.selectedNode.setAttribute(f.attrname, nv);
                     }
                 }
             }));
@@ -27051,7 +27926,11 @@ clientValidation  Boolean          Applies to submit only.  Pass true to call fo
             // we have a scenario where updates need confirming.
             // eg. if a locking scenario exists..
             // we look for { errors : { needs_confirm : true }} in the response.
-            if (typeof(action.result.errors.needs_confirm) != 'undefined') {
+            if (
+                (typeof(action.result) != 'undefined')  &&
+                (typeof(action.result.errors) != 'undefined')  &&
+                (typeof(action.result.errors.needs_confirm) != 'undefined')
+           ){
                 var _t = this;
                 Roo.MessageBox.confirm(
                     "Change requires confirmation",
@@ -27076,7 +27955,7 @@ clientValidation  Boolean          Applies to submit only.  Pass true to call fo
                 Roo.MessageBox.alert("Error",
                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
                         action.result.errorMsg :
-                        "Saving Failed, please check your entries"
+                        "Saving Failed, please check your entries or try again"
                 );
             }
             
@@ -27990,6 +28869,7 @@ Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
                 }
                 if (!rdata || !rdata.success) {
                     Roo.log(rdata);
+                    Roo.MessageBox.alert(Roo.encode(rdata));
                     return;
                 }
                 var data = rdata.data;
@@ -32314,7 +33194,17 @@ Roo.ContentPanel = function(el, config, content){
         this.toolbar = new Roo.Toolbar(this.toolbar);
     }
     
+    // xtype created footer. - not sure if will work as we normally have to render first..
+    if (this.footer && !this.footer.el && this.footer.xtype) {
+        if (!this.wrapEl) {
+            this.wrapEl = this.el.wrap();
+        }
     
+        this.footer.container = this.wrapEl.createChild();
+         
+        this.footer = Roo.factory(this.footer, Roo);
+        
+    }
     
     if(this.resizeEl){
         this.resizeEl = Roo.get(this.resizeEl, true);
@@ -32502,7 +33392,9 @@ panel.load({
         return this.wrapEl || this.el;
     },
     
-    adjustForComponents : function(width, height){
+    adjustForComponents : function(width, height)
+    {
+        Roo.log('adjustForComponents ');
         if(this.resizeEl != this.el){
             width -= this.el.getFrameWidth('lr');
             height -= this.el.getFrameWidth('tb');
@@ -32512,6 +33404,15 @@ panel.load({
             height -= te.getHeight();
             te.setWidth(width);
         }
+        if(this.footer){
+            var te = this.footer.getEl();
+            Roo.log("footer:" + te.getHeight());
+            
+            height -= te.getHeight();
+            te.setWidth(width);
+        }
+        
+        
         if(this.adjustments){
             width += this.adjustments[0];
             height += this.adjustments[1];
@@ -32620,7 +33521,13 @@ layout.addxtype({
     addxtype : function(cfg) {
         // add form..
         if (cfg.xtype.match(/^Form$/)) {
-            var el = this.el.createChild();
+            
+            var el;
+            //if (this.footer) {
+            //    el = this.footer.container.insertSibling(false, 'before');
+            //} else {
+                el = this.el.createChild();
+            //}
 
             this.form = new  Roo.form.Form(cfg);
             
@@ -32946,12 +33853,14 @@ Roo.TreePanel = function(config){
         //console.log('render tree');
         this.tree.render();
     });
+    // this should not be needed.. - it's actually the 'el' that resizes?
+    // actuall it breaks the containerScroll - dragging nodes auto scroll at top
     
-    this.on('resize',  function (cp, w, h) {
-            this.tree.innerCt.setWidth(w);
-            this.tree.innerCt.setHeight(h);
-            this.tree.innerCt.setStyle('overflow-y', 'auto');
-    });
+    //this.on('resize',  function (cp, w, h) {
+    //        this.tree.innerCt.setWidth(w);
+    //        this.tree.innerCt.setHeight(h);
+    //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
+    //});
 
         
     
@@ -36919,4 +37828,2432 @@ Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
             g.startEditing(newCell[0], newCell[1]);
         }
     }
-});
\ No newline at end of file
+});/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+/**
+ * @class Roo.grid.CellSelectionModel
+ * @extends Roo.grid.AbstractSelectionModel
+ * This class provides the basic implementation for cell selection in a grid.
+ * @constructor
+ * @param {Object} config The object containing the configuration of this model.
+ * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
+ */
+Roo.grid.CellSelectionModel = function(config){
+    Roo.apply(this, config);
+
+    this.selection = null;
+
+    this.addEvents({
+        /**
+            * @event beforerowselect
+            * Fires before a cell is selected.
+            * @param {SelectionModel} this
+            * @param {Number} rowIndex The selected row index
+            * @param {Number} colIndex The selected cell index
+            */
+           "beforecellselect" : true,
+        /**
+            * @event cellselect
+            * Fires when a cell is selected.
+            * @param {SelectionModel} this
+            * @param {Number} rowIndex The selected row index
+            * @param {Number} colIndex The selected cell index
+            */
+           "cellselect" : true,
+        /**
+            * @event selectionchange
+            * Fires when the active selection changes.
+            * @param {SelectionModel} this
+            * @param {Object} selection null for no selection or an object (o) with two properties
+               <ul>
+               <li>o.record: the record object for the row the selection is in</li>
+               <li>o.cell: An array of [rowIndex, columnIndex]</li>
+               </ul>
+            */
+           "selectionchange" : true,
+        /**
+            * @event tabend
+            * Fires when the tab (or enter) was pressed on the last editable cell
+            * You can use this to trigger add new row.
+            * @param {SelectionModel} this
+            */
+           "tabend" : true,
+         /**
+            * @event beforeeditnext
+            * Fires before the next editable sell is made active
+            * You can use this to skip to another cell or fire the tabend
+            *    if you set cell to false
+            * @param {Object} eventdata object : { cell : [ row, col ] } 
+            */
+           "beforeeditnext" : true
+    });
+    Roo.grid.CellSelectionModel.superclass.constructor.call(this);
+};
+
+Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
+    
+    enter_is_tab: false,
+
+    /** @ignore */
+    initEvents : function(){
+        this.grid.on("mousedown", this.handleMouseDown, this);
+        this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
+        var view = this.grid.view;
+        view.on("refresh", this.onViewChange, this);
+        view.on("rowupdated", this.onRowUpdated, this);
+        view.on("beforerowremoved", this.clearSelections, this);
+        view.on("beforerowsinserted", this.clearSelections, this);
+        if(this.grid.isEditor){
+            this.grid.on("beforeedit", this.beforeEdit,  this);
+        }
+    },
+
+       //private
+    beforeEdit : function(e){
+        this.select(e.row, e.column, false, true, e.record);
+    },
+
+       //private
+    onRowUpdated : function(v, index, r){
+        if(this.selection && this.selection.record == r){
+            v.onCellSelect(index, this.selection.cell[1]);
+        }
+    },
+
+       //private
+    onViewChange : function(){
+        this.clearSelections(true);
+    },
+
+       /**
+        * Returns the currently selected cell,.
+        * @return {Array} The selected cell (row, column) or null if none selected.
+        */
+    getSelectedCell : function(){
+        return this.selection ? this.selection.cell : null;
+    },
+
+    /**
+     * Clears all selections.
+     * @param {Boolean} true to prevent the gridview from being notified about the change.
+     */
+    clearSelections : function(preventNotify){
+        var s = this.selection;
+        if(s){
+            if(preventNotify !== true){
+                this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
+            }
+            this.selection = null;
+            this.fireEvent("selectionchange", this, null);
+        }
+    },
+
+    /**
+     * Returns true if there is a selection.
+     * @return {Boolean}
+     */
+    hasSelection : function(){
+        return this.selection ? true : false;
+    },
+
+    /** @ignore */
+    handleMouseDown : function(e, t){
+        var v = this.grid.getView();
+        if(this.isLocked()){
+            return;
+        };
+        var row = v.findRowIndex(t);
+        var cell = v.findCellIndex(t);
+        if(row !== false && cell !== false){
+            this.select(row, cell);
+        }
+    },
+
+    /**
+     * Selects a cell.
+     * @param {Number} rowIndex
+     * @param {Number} collIndex
+     */
+    select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
+        if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
+            this.clearSelections();
+            r = r || this.grid.dataSource.getAt(rowIndex);
+            this.selection = {
+                record : r,
+                cell : [rowIndex, colIndex]
+            };
+            if(!preventViewNotify){
+                var v = this.grid.getView();
+                v.onCellSelect(rowIndex, colIndex);
+                if(preventFocus !== true){
+                    v.focusCell(rowIndex, colIndex);
+                }
+            }
+            this.fireEvent("cellselect", this, rowIndex, colIndex);
+            this.fireEvent("selectionchange", this, this.selection);
+        }
+    },
+
+       //private
+    isSelectable : function(rowIndex, colIndex, cm){
+        return !cm.isHidden(colIndex);
+    },
+
+    /** @ignore */
+    handleKeyDown : function(e){
+        //Roo.log('Cell Sel Model handleKeyDown');
+        if(!e.isNavKeyPress()){
+            return;
+        }
+        var g = this.grid, s = this.selection;
+        if(!s){
+            e.stopEvent();
+            var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
+            if(cell){
+                this.select(cell[0], cell[1]);
+            }
+            return;
+        }
+        var sm = this;
+        var walk = function(row, col, step){
+            return g.walkCells(row, col, step, sm.isSelectable,  sm);
+        };
+        var k = e.getKey(), r = s.cell[0], c = s.cell[1];
+        var newCell;
+
+      
+
+        switch(k){
+            case e.TAB:
+                // handled by onEditorKey
+                if (g.isEditor && g.editing) {
+                    return;
+                }
+                if(e.shiftKey) {
+                    newCell = walk(r, c-1, -1);
+                } else {
+                    newCell = walk(r, c+1, 1);
+                }
+                break;
+            
+            case e.DOWN:
+               newCell = walk(r+1, c, 1);
+                break;
+            
+            case e.UP:
+                newCell = walk(r-1, c, -1);
+                break;
+            
+            case e.RIGHT:
+                newCell = walk(r, c+1, 1);
+                break;
+            
+            case e.LEFT:
+                newCell = walk(r, c-1, -1);
+                break;
+            
+            case e.ENTER:
+                
+                if(g.isEditor && !g.editing){
+                   g.startEditing(r, c);
+                   e.stopEvent();
+                   return;
+                }
+                
+                
+             break;
+        };
+        if(newCell){
+            this.select(newCell[0], newCell[1]);
+            e.stopEvent();
+            
+        }
+    },
+
+    acceptsNav : function(row, col, cm){
+        return !cm.isHidden(col) && cm.isCellEditable(col, row);
+    },
+    /**
+     * Selects a cell.
+     * @param {Number} field (not used) - as it's normally used as a listener
+     * @param {Number} e - event - fake it by using
+     *
+     * var e = Roo.EventObjectImpl.prototype;
+     * e.keyCode = e.TAB
+     *
+     * 
+     */
+    onEditorKey : function(field, e){
+        
+        var k = e.getKey(),
+            newCell,
+            g = this.grid,
+            ed = g.activeEditor,
+            forward = false;
+        ///Roo.log('onEditorKey' + k);
+        
+        
+        if (this.enter_is_tab && k == e.ENTER) {
+            k = e.TAB;
+        }
+        
+        if(k == e.TAB){
+            if(e.shiftKey){
+                newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
+            }else{
+                newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
+                forward = true;
+            }
+            
+            e.stopEvent();
+            
+        } else if(k == e.ENTER &&  !e.ctrlKey){
+            ed.completeEdit();
+            e.stopEvent();
+            newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
+        
+               } else if(k == e.ESC){
+            ed.cancelEdit();
+        }
+               
+        if (newCell) {
+            var ecall = { cell : newCell, forward : forward };
+            this.fireEvent('beforeeditnext', ecall );
+            newCell = ecall.cell;
+                       forward = ecall.forward;
+        }
+               
+        if(newCell){
+            //Roo.log('next cell after edit');
+            g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
+        } else if (forward) {
+            // tabbed past last
+            this.fireEvent.defer(100, this, ['tabend',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.grid.EditorGrid
+ * @extends Roo.grid.Grid
+ * Class for creating and editable grid.
+ * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
+ * The container MUST have some type of size defined for the grid to fill. The container will be 
+ * automatically set to position relative if it isn't already.
+ * @param {Object} dataSource The data model to bind to
+ * @param {Object} colModel The column model with info about this grid's columns
+ */
+Roo.grid.EditorGrid = function(container, config){
+    Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
+    this.getGridEl().addClass("xedit-grid");
+
+    if(!this.selModel){
+        this.selModel = new Roo.grid.CellSelectionModel();
+    }
+
+    this.activeEditor = null;
+
+       this.addEvents({
+           /**
+            * @event beforeedit
+            * Fires before cell editing is triggered. The edit event object has the following properties <br />
+            * <ul style="padding:5px;padding-left:16px;">
+            * <li>grid - This grid</li>
+            * <li>record - The record being edited</li>
+            * <li>field - The field name being edited</li>
+            * <li>value - The value for the field being edited.</li>
+            * <li>row - The grid row index</li>
+            * <li>column - The grid column index</li>
+            * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
+            * </ul>
+            * @param {Object} e An edit event (see above for description)
+            */
+           "beforeedit" : true,
+           /**
+            * @event afteredit
+            * Fires after a cell is edited. <br />
+            * <ul style="padding:5px;padding-left:16px;">
+            * <li>grid - This grid</li>
+            * <li>record - The record being edited</li>
+            * <li>field - The field name being edited</li>
+            * <li>value - The value being set</li>
+            * <li>originalValue - The original value for the field, before the edit.</li>
+            * <li>row - The grid row index</li>
+            * <li>column - The grid column index</li>
+            * </ul>
+            * @param {Object} e An edit event (see above for description)
+            */
+           "afteredit" : true,
+           /**
+            * @event validateedit
+            * Fires after a cell is edited, but before the value is set in the record. 
+         * You can use this to modify the value being set in the field, Return false
+            * to cancel the change. The edit event object has the following properties <br />
+            * <ul style="padding:5px;padding-left:16px;">
+         * <li>editor - This editor</li>
+            * <li>grid - This grid</li>
+            * <li>record - The record being edited</li>
+            * <li>field - The field name being edited</li>
+            * <li>value - The value being set</li>
+            * <li>originalValue - The original value for the field, before the edit.</li>
+            * <li>row - The grid row index</li>
+            * <li>column - The grid column index</li>
+            * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
+            * </ul>
+            * @param {Object} e An edit event (see above for description)
+            */
+           "validateedit" : true
+       });
+    this.on("bodyscroll", this.stopEditing,  this);
+    this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
+};
+
+Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
+    /**
+     * @cfg {Number} clicksToEdit
+     * The number of clicks on a cell required to display the cell's editor (defaults to 2)
+     */
+    clicksToEdit: 2,
+
+    // private
+    isEditor : true,
+    // private
+    trackMouseOver: false, // causes very odd FF errors
+
+    onCellDblClick : function(g, row, col){
+        this.startEditing(row, col);
+    },
+
+    onEditComplete : function(ed, value, startValue){
+        this.editing = false;
+        this.activeEditor = null;
+        ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
+        var r = ed.record;
+        var field = this.colModel.getDataIndex(ed.col);
+        var e = {
+            grid: this,
+            record: r,
+            field: field,
+            originalValue: startValue,
+            value: value,
+            row: ed.row,
+            column: ed.col,
+            cancel:false,
+            editor: ed
+        };
+        var cell = Roo.get(this.view.getCell(ed.row,ed.col))
+        cell.show();
+          
+        if(String(value) !== String(startValue)){
+            
+            if(this.fireEvent("validateedit", e) !== false && !e.cancel){
+                r.set(field, e.value);
+                // if we are dealing with a combo box..
+                // then we also set the 'name' colum to be the displayField
+                if (ed.field.displayField && ed.field.name) {
+                    r.set(ed.field.name, ed.field.el.dom.value);
+                }
+                
+                delete e.cancel; //?? why!!!
+                this.fireEvent("afteredit", e);
+            }
+        } else {
+            this.fireEvent("afteredit", e); // always fire it!
+        }
+        this.view.focusCell(ed.row, ed.col);
+    },
+
+    /**
+     * Starts editing the specified for the specified row/column
+     * @param {Number} rowIndex
+     * @param {Number} colIndex
+     */
+    startEditing : function(row, col){
+        this.stopEditing();
+        if(this.colModel.isCellEditable(col, row)){
+            this.view.ensureVisible(row, col, true);
+          
+            var r = this.dataSource.getAt(row);
+            var field = this.colModel.getDataIndex(col);
+            var cell = Roo.get(this.view.getCell(row,col));
+            var e = {
+                grid: this,
+                record: r,
+                field: field,
+                value: r.data[field],
+                row: row,
+                column: col,
+                cancel:false 
+            };
+            if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
+                this.editing = true;
+                var ed = this.colModel.getCellEditor(col, row);
+                
+                if (!ed) {
+                    return;
+                }
+                if(!ed.rendered){
+                    ed.render(ed.parentEl || document.body);
+                }
+                ed.field.reset();
+               
+                cell.hide();
+                
+                (function(){ // complex but required for focus issues in safari, ie and opera
+                    ed.row = row;
+                    ed.col = col;
+                    ed.record = r;
+                    ed.on("complete",   this.onEditComplete,        this,       {single: true});
+                    ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
+                    this.activeEditor = ed;
+                    var v = r.data[field];
+                    ed.startEdit(this.view.getCell(row, col), v);
+                    // combo's with 'displayField and name set
+                    if (ed.field.displayField && ed.field.name) {
+                        ed.field.el.dom.value = r.data[ed.field.name];
+                    }
+                    
+                    
+                }).defer(50, this);
+            }
+        }
+    },
+        
+    /**
+     * Stops any active editing
+     */
+    stopEditing : function(){
+        if(this.activeEditor){
+            this.activeEditor.completeEdit();
+        }
+        this.activeEditor = null;
+    }
+});/*
+ * 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 - not really -- you end up using it !
+// This is a support class used internally by the Grid components
+
+/**
+ * @class Roo.grid.GridEditor
+ * @extends Roo.Editor
+ * Class for creating and editable grid elements.
+ * @param {Object} config any settings (must include field)
+ */
+Roo.grid.GridEditor = function(field, config){
+    if (!config && field.field) {
+        config = field;
+        field = Roo.factory(config.field, Roo.form);
+    }
+    Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
+    field.monitorTab = false;
+};
+
+Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
+    
+    /**
+     * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
+     */
+    
+    alignment: "tl-tl",
+    autoSize: "width",
+    hideEl : false,
+    cls: "x-small-editor x-grid-editor",
+    shim:false,
+    shadow:"frame"
+});/*
+ * 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.grid.PropertyRecord = Roo.data.Record.create([
+    {name:'name',type:'string'},  'value'
+]);
+
+
+Roo.grid.PropertyStore = function(grid, source){
+    this.grid = grid;
+    this.store = new Roo.data.Store({
+        recordType : Roo.grid.PropertyRecord
+    });
+    this.store.on('update', this.onUpdate,  this);
+    if(source){
+        this.setSource(source);
+    }
+    Roo.grid.PropertyStore.superclass.constructor.call(this);
+};
+
+
+
+Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
+    setSource : function(o){
+        this.source = o;
+        this.store.removeAll();
+        var data = [];
+        for(var k in o){
+            if(this.isEditableValue(o[k])){
+                data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
+            }
+        }
+        this.store.loadRecords({records: data}, {}, true);
+    },
+
+    onUpdate : function(ds, record, type){
+        if(type == Roo.data.Record.EDIT){
+            var v = record.data['value'];
+            var oldValue = record.modified['value'];
+            if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
+                this.source[record.id] = v;
+                record.commit();
+                this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
+            }else{
+                record.reject();
+            }
+        }
+    },
+
+    getProperty : function(row){
+       return this.store.getAt(row);
+    },
+
+    isEditableValue: function(val){
+        if(val && val instanceof Date){
+            return true;
+        }else if(typeof val == 'object' || typeof val == 'function'){
+            return false;
+        }
+        return true;
+    },
+
+    setValue : function(prop, value){
+        this.source[prop] = value;
+        this.store.getById(prop).set('value', value);
+    },
+
+    getSource : function(){
+        return this.source;
+    }
+});
+
+Roo.grid.PropertyColumnModel = function(grid, store){
+    this.grid = grid;
+    var g = Roo.grid;
+    g.PropertyColumnModel.superclass.constructor.call(this, [
+        {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
+        {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
+    ]);
+    this.store = store;
+    this.bselect = Roo.DomHelper.append(document.body, {
+        tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
+            {tag: 'option', value: 'true', html: 'true'},
+            {tag: 'option', value: 'false', html: 'false'}
+        ]
+    });
+    Roo.id(this.bselect);
+    var f = Roo.form;
+    this.editors = {
+        'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
+        'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
+        'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
+        'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
+        'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
+    };
+    this.renderCellDelegate = this.renderCell.createDelegate(this);
+    this.renderPropDelegate = this.renderProp.createDelegate(this);
+};
+
+Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
+    
+    
+    nameText : 'Name',
+    valueText : 'Value',
+    
+    dateFormat : 'm/j/Y',
+    
+    
+    renderDate : function(dateVal){
+        return dateVal.dateFormat(this.dateFormat);
+    },
+
+    renderBool : function(bVal){
+        return bVal ? 'true' : 'false';
+    },
+
+    isCellEditable : function(colIndex, rowIndex){
+        return colIndex == 1;
+    },
+
+    getRenderer : function(col){
+        return col == 1 ?
+            this.renderCellDelegate : this.renderPropDelegate;
+    },
+
+    renderProp : function(v){
+        return this.getPropertyName(v);
+    },
+
+    renderCell : function(val){
+        var rv = val;
+        if(val instanceof Date){
+            rv = this.renderDate(val);
+        }else if(typeof val == 'boolean'){
+            rv = this.renderBool(val);
+        }
+        return Roo.util.Format.htmlEncode(rv);
+    },
+
+    getPropertyName : function(name){
+        var pn = this.grid.propertyNames;
+        return pn && pn[name] ? pn[name] : name;
+    },
+
+    getCellEditor : function(colIndex, rowIndex){
+        var p = this.store.getProperty(rowIndex);
+        var n = p.data['name'], val = p.data['value'];
+        
+        if(typeof(this.grid.customEditors[n]) == 'string'){
+            return this.editors[this.grid.customEditors[n]];
+        }
+        if(typeof(this.grid.customEditors[n]) != 'undefined'){
+            return this.grid.customEditors[n];
+        }
+        if(val instanceof Date){
+            return this.editors['date'];
+        }else if(typeof val == 'number'){
+            return this.editors['number'];
+        }else if(typeof val == 'boolean'){
+            return this.editors['boolean'];
+        }else{
+            return this.editors['string'];
+        }
+    }
+});
+
+/**
+ * @class Roo.grid.PropertyGrid
+ * @extends Roo.grid.EditorGrid
+ * This class represents the  interface of a component based property grid control.
+ * <br><br>Usage:<pre><code>
+ var grid = new Roo.grid.PropertyGrid("my-container-id", {
+      
+ });
+ // set any options
+ grid.render();
+ * </code></pre>
+  
+ * @constructor
+ * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
+ * The container MUST have some type of size defined for the grid to fill. The container will be
+ * automatically set to position relative if it isn't already.
+ * @param {Object} config A config object that sets properties on this grid.
+ */
+Roo.grid.PropertyGrid = function(container, config){
+    config = config || {};
+    var store = new Roo.grid.PropertyStore(this);
+    this.store = store;
+    var cm = new Roo.grid.PropertyColumnModel(this, store);
+    store.store.sort('name', 'ASC');
+    Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
+        ds: store.store,
+        cm: cm,
+        enableColLock:false,
+        enableColumnMove:false,
+        stripeRows:false,
+        trackMouseOver: false,
+        clicksToEdit:1
+    }, config));
+    this.getGridEl().addClass('x-props-grid');
+    this.lastEditRow = null;
+    this.on('columnresize', this.onColumnResize, this);
+    this.addEvents({
+         /**
+            * @event beforepropertychange
+            * Fires before a property changes (return false to stop?)
+            * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
+            * @param {String} id Record Id
+            * @param {String} newval New Value
+         * @param {String} oldval Old Value
+            */
+        "beforepropertychange": true,
+        /**
+            * @event propertychange
+            * Fires after a property changes
+            * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
+            * @param {String} id Record Id
+            * @param {String} newval New Value
+         * @param {String} oldval Old Value
+            */
+        "propertychange": true
+    });
+    this.customEditors = this.customEditors || {};
+};
+Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
+    
+     /**
+     * @cfg {Object} customEditors map of colnames=> custom editors.
+     * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
+     * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
+     * false disables editing of the field.
+        */
+    
+      /**
+     * @cfg {Object} propertyNames map of property Names to their displayed value
+        */
+    
+    render : function(){
+        Roo.grid.PropertyGrid.superclass.render.call(this);
+        this.autoSize.defer(100, this);
+    },
+
+    autoSize : function(){
+        Roo.grid.PropertyGrid.superclass.autoSize.call(this);
+        if(this.view){
+            this.view.fitColumns();
+        }
+    },
+
+    onColumnResize : function(){
+        this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
+        this.autoSize();
+    },
+    /**
+     * Sets the data for the Grid
+     * accepts a Key => Value object of all the elements avaiable.
+     * @param {Object} data  to appear in grid.
+     */
+    setSource : function(source){
+        this.store.setSource(source);
+        //this.autoSize();
+    },
+    /**
+     * Gets all the data from the grid.
+     * @return {Object} data  data stored in grid
+     */
+    getSource : function(){
+        return this.store.getSource();
+    }
+});/*
+ * 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.LoadMask
+ * A simple utility class for generically masking elements while loading data.  If the element being masked has
+ * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
+ * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
+ * element's UpdateManager load indicator and will be destroyed after the initial load.
+ * @constructor
+ * Create a new LoadMask
+ * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
+ * @param {Object} config The config object
+ */
+Roo.LoadMask = function(el, config){
+    this.el = Roo.get(el);
+    Roo.apply(this, config);
+    if(this.store){
+        this.store.on('beforeload', this.onBeforeLoad, this);
+        this.store.on('load', this.onLoad, this);
+        this.store.on('loadexception', this.onLoadException, this);
+        this.removeMask = false;
+    }else{
+        var um = this.el.getUpdateManager();
+        um.showLoadIndicator = false; // disable the default indicator
+        um.on('beforeupdate', this.onBeforeLoad, this);
+        um.on('update', this.onLoad, this);
+        um.on('failure', this.onLoad, this);
+        this.removeMask = true;
+    }
+};
+
+Roo.LoadMask.prototype = {
+    /**
+     * @cfg {Boolean} removeMask
+     * 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.
+     */
+    /**
+     * @cfg {String} msg
+     * The text to display in a centered loading message box (defaults to 'Loading...')
+     */
+    msg : 'Loading...',
+    /**
+     * @cfg {String} msgCls
+     * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
+     */
+    msgCls : 'x-mask-loading',
+
+    /**
+     * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
+     * @type Boolean
+     */
+    disabled: false,
+
+    /**
+     * Disables the mask to prevent it from being displayed
+     */
+    disable : function(){
+       this.disabled = true;
+    },
+
+    /**
+     * Enables the mask so that it can be displayed
+     */
+    enable : function(){
+        this.disabled = false;
+    },
+    
+    onLoadException : function()
+    {
+        if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
+            Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
+        }
+        this.el.unmask(this.removeMask);
+    },
+    // private
+    onLoad : function()
+    {
+        this.el.unmask(this.removeMask);
+    },
+
+    // private
+    onBeforeLoad : function(){
+        if(!this.disabled){
+            this.el.mask(this.msg, this.msgCls);
+        }
+    },
+
+    // private
+    destroy : function(){
+        if(this.store){
+            this.store.un('beforeload', this.onBeforeLoad, this);
+            this.store.un('load', this.onLoad, this);
+            this.store.un('loadexception', this.onLoadException, this);
+        }else{
+            var um = this.el.getUpdateManager();
+            um.un('beforeupdate', this.onBeforeLoad, this);
+            um.un('update', this.onLoad, this);
+            um.un('failure', this.onLoad, 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.XTemplate
+ * @extends Roo.Template
+ * Provides a template that can have nested templates for loops or conditionals. The syntax is:
+<pre><code>
+var t = new Roo.XTemplate(
+       '&lt;select name="{name}"&gt;',
+               '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
+       '&lt;/select&gt;'
+);
+// then append, applying the master template values
+ </code></pre>
+ *
+ * Supported features:
+ *
+ *  Tags:
+
+<pre><code>
+      {a_variable} - output encoded.
+      {a_variable.format:("Y-m-d")} - call a method on the variable
+      {a_variable:raw} - unencoded output
+      {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
+      {a_variable:this.method_on_template(...)} - call a method on the template object.
+</code></pre>
+ *  The tpl tag:
+<pre><code>
+        &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
+        &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
+        &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
+        &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
+  
+        &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
+        &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
+</code></pre>
+ *      
+ */
+Roo.XTemplate = function()
+{
+    Roo.XTemplate.superclass.constructor.apply(this, arguments);
+    if (this.html) {
+        this.compile();
+    }
+};
+
+
+Roo.extend(Roo.XTemplate, Roo.Template, {
+
+    /**
+     * The various sub templates
+     */
+    tpls : false,
+    /**
+     *
+     * basic tag replacing syntax
+     * WORD:WORD()
+     *
+     * // you can fake an object call by doing this
+     *  x.t:(test,tesT) 
+     * 
+     */
+    re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
+
+    /**
+     * compile the template
+     *
+     * This is not recursive, so I'm not sure how nested templates are really going to be handled..
+     *
+     */
+    compile: function()
+    {
+        var s = this.html;
+     
+        s = ['<tpl>', s, '</tpl>'].join('');
+    
+        var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
+            nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
+            ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
+            execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
+            namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
+            m,
+            id     = 0,
+            tpls   = [];
+    
+        while(true == !!(m = s.match(re))){
+            var forMatch   = m[0].match(nameRe),
+                ifMatch   = m[0].match(ifRe),
+                execMatch   = m[0].match(execRe),
+                namedMatch   = m[0].match(namedRe),
+                
+                exp  = null, 
+                fn   = null,
+                exec = null,
+                name = forMatch && forMatch[1] ? forMatch[1] : '';
+                
+            if (ifMatch) {
+                // if - puts fn into test..
+                exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
+                if(exp){
+                   fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
+                }
+            }
+            
+            if (execMatch) {
+                // exec - calls a function... returns empty if true is  returned.
+                exp = execMatch && execMatch[1] ? execMatch[1] : null;
+                if(exp){
+                   exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
+                }
+            }
+            
+            
+            if (name) {
+                // for = 
+                switch(name){
+                    case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
+                    case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
+                    default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
+                }
+            }
+            var uid = namedMatch ? namedMatch[1] : id;
+            
+            
+            tpls.push({
+                id:     namedMatch ? namedMatch[1] : id,
+                target: name,
+                exec:   exec,
+                test:   fn,
+                body:   m[1] || ''
+            });
+            if (namedMatch) {
+                s = s.replace(m[0], '');
+            } else { 
+                s = s.replace(m[0], '{xtpl'+ id + '}');
+            }
+            ++id;
+        }
+        this.tpls = [];
+        for(var i = tpls.length-1; i >= 0; --i){
+            this.compileTpl(tpls[i]);
+            this.tpls[tpls[i].id] = tpls[i];
+        }
+        this.master = tpls[tpls.length-1];
+        return this;
+    },
+    /**
+     * same as applyTemplate, except it's done to one of the subTemplates
+     * when using named templates, you can do:
+     *
+     * var str = pl.applySubTemplate('your-name', values);
+     *
+     * 
+     * @param {Number} id of the template
+     * @param {Object} values to apply to template
+     * @param {Object} parent (normaly the instance of this object)
+     */
+    applySubTemplate : function(id, values, parent)
+    {
+        
+        
+        var t = this.tpls[id];
+        
+        
+        try { 
+            if(t.test && !t.test.call(this, values, parent)){
+                return '';
+            }
+        } catch(e) {
+            Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
+            Roo.log(e.toString());
+            Roo.log(t.test);
+            return ''
+        }
+        try { 
+            
+            if(t.exec && t.exec.call(this, values, parent)){
+                return '';
+            }
+        } catch(e) {
+            Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
+            Roo.log(e.toString());
+            Roo.log(t.exec);
+            return ''
+        }
+        try {
+            var vs = t.target ? t.target.call(this, values, parent) : values;
+            parent = t.target ? values : parent;
+            if(t.target && vs instanceof Array){
+                var buf = [];
+                for(var i = 0, len = vs.length; i < len; i++){
+                    buf[buf.length] = t.compiled.call(this, vs[i], parent);
+                }
+                return buf.join('');
+            }
+            return t.compiled.call(this, vs, parent);
+        } catch (e) {
+            Roo.log("Xtemplate.applySubTemplate : Exception thrown");
+            Roo.log(e.toString());
+            Roo.log(t.compiled);
+            return '';
+        }
+    },
+
+    compileTpl : function(tpl)
+    {
+        var fm = Roo.util.Format;
+        var useF = this.disableFormats !== true;
+        var sep = Roo.isGecko ? "+" : ",";
+        var undef = function(str) {
+            Roo.log("Property not found :"  + str);
+            return '';
+        };
+        
+        var fn = function(m, name, format, args)
+        {
+            //Roo.log(arguments);
+            args = args ? args.replace(/\\'/g,"'") : args;
+            //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
+            if (typeof(format) == 'undefined') {
+                format= 'htmlEncode';
+            }
+            if (format == 'raw' ) {
+                format = false;
+            }
+            
+            if(name.substr(0, 4) == 'xtpl'){
+                return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
+            }
+            
+            // build an array of options to determine if value is undefined..
+            
+            // basically get 'xxxx.yyyy' then do
+            // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
+            //    (function () { Roo.log("Property not found"); return ''; })() :
+            //    ......
+            
+            var udef_ar = [];
+            var lookfor = '';
+            Roo.each(name.split('.'), function(st) {
+                lookfor += (lookfor.length ? '.': '') + st;
+                udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
+            });
+            
+            var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
+            
+            
+            if(format && useF){
+                
+                args = args ? ',' + args : "";
+                 
+                if(format.substr(0, 5) != "this."){
+                    format = "fm." + format + '(';
+                }else{
+                    format = 'this.call("'+ format.substr(5) + '", ';
+                    args = ", values";
+                }
+                
+                return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
+            }
+             
+            if (args.length) {
+                // called with xxyx.yuu:(test,test)
+                // change to ()
+                return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
+            }
+            // raw.. - :raw modifier..
+            return "'"+ sep + udef_st  + name + ")"+sep+"'";
+            
+        };
+        var body;
+        // branched to use + in gecko and [].join() in others
+        if(Roo.isGecko){
+            body = "tpl.compiled = function(values, parent){  with(values) { return '" +
+                   tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
+                    "';};};";
+        }else{
+            body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
+            body.push(tpl.body.replace(/(\r\n|\n)/g,
+                            '\\n').replace(/'/g, "\\'").replace(this.re, fn));
+            body.push("'].join('');};};");
+            body = body.join('');
+        }
+        
+        Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
+       
+        /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
+        eval(body);
+        
+        return this;
+    },
+
+    applyTemplate : function(values){
+        return this.master.compiled.call(this, values, {});
+        //var s = this.subs;
+    },
+
+    apply : function(){
+        return this.applyTemplate.apply(this, arguments);
+    }
+
+ });
+
+Roo.XTemplate.from = function(el){
+    el = Roo.getDom(el);
+    return new Roo.XTemplate(el.value || el.innerHTML);
+};/*
+ * 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.
+ * 
+ * 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 )
+ * 
+ * @extends Roo.util.Observable
+ * @constructor
+ * @param cfg {Object} configuration of component
+ * 
+ */
+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;
+        
+        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;
+            el = Roo.get(ename);
+            if (!el) {
+                Roo.log("Warning - element can not be found :#" + ename );
+                return;
+            }
+        }
+        
+        
+        if (!this.parent) {
+            
+            el = el ? Roo.get(el) : false;     
+            
+            // 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' 
+            
+        var tree = this._tree ? this._tree() : this.tree();
+        tree.region = tree.region || this.region;
+        this.el = this.parent.el.addxtype(tree);
+        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 : [],
+
+    
+    /**
+     * 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) {
+                        return;
+                }
+                break;
+        }
+               
+        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;
+        }
+        if (str.substring(0,1) == '#') {
+            return str;
+        }
+
+        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;
+        }
+        Roo.each(ar, function(e) {
+            if (typeof(o[e]) == 'undefined') {
+                throw "Module not found : " + str;
+            }
+            o = o[e];
+        });
+        
+        return o;
+        
+    },
+    
+    
+    /**
+     * 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.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.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);
+    },
+    
+     /**
+     * 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)
+        });
+
+        
+        // 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.keySort('ASC',  cmp );
+            this.topModule.modules.each(addMod);
+        }
+        return mods;
+    },
+    
+     /**
+     * Build the registered modules.
+     * @param {Object} parent element.
+     * @param {Function} optional method to call after module has been added.
+     * 
+     */ 
+   
+    build : function() 
+    {
+        
+        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.show({ title: 'loading' });
+            Roo.MessageBox.show({
+               title: "Please wait...",
+               msg: msg,
+               width:450,
+               progress:true,
+               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.hide();
+                }
+                Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
+                
+                // THE END...
+                return false;   
+            }
+            
+            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.updateProgress(  (total  - mods.length)/total, msg  );
+            }
+            
+         
+            // 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!
+            }
+            
+            // now build 
+            
+                       
+                       
+            m.render();
+            // it's 10 on top level, and 1 on others??? why...
+            return progressRun.defer(10, _this);
+             
+        }
+        progressRun.defer(1, _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
+                       
+               }
+});
+
+Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
+ //<script type="text/javascript">
+
+
+/**
+ * @class Roo.Login
+ * @extends Roo.LayoutDialog
+ * A generic Login Dialog..... - only one needed in theory!?!?
+ *
+ * Fires XComponent builder on success...
+ * 
+ * Sends 
+ *    username,password, lang = for login actions.
+ *    check = 1 for periodic checking that sesion is valid.
+ *    passwordRequest = email request password
+ *    logout = 1 = to logout
+ * 
+ * Affects: (this id="????" elements)
+ *   loading  (removed) (used to indicate application is loading)
+ *   loading-mask (hides) (used to hide application when it's building loading)
+ *   
+ * 
+ * Usage: 
+ *    
+ * 
+ * Myapp.login = Roo.Login({
+     url: xxxx,
+   
+     realm : 'Myapp', 
+     
+     
+     method : 'POST',
+     
+     
+     * 
+ })
+ * 
+ * 
+ * 
+ **/
+Roo.Login = function(cfg)
+{
+    this.addEvents({
+        'refreshed' : true
+    });
+    
+    Roo.apply(this,cfg);
+    
+    Roo.onReady(function() {
+        this.onLoad();
+    }, this);
+    // call parent..
+    
+   
+    Roo.Login.superclass.constructor.call(this, this);
+    //this.addxtype(this.items[0]);
+    
+    
+}
+
+
+Roo.extend(Roo.Login, Roo.LayoutDialog, {
+    
+    /**
+     * @cfg {String} method
+     * Method used to query for login details.
+     */
+    
+    method : 'POST',
+    /**
+     * @cfg {String} url
+     * URL to query login data. - eg. baseURL + '/Login.php'
+     */
+    url : '',
+    
+    /**
+     * @property user
+     * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
+     * @type {Object} 
+     */
+    user : false,
+    /**
+     * @property checkFails
+     * Number of times we have attempted to get authentication check, and failed.
+     * @type {Number} 
+     */
+    checkFails : 0,
+      /**
+     * @property intervalID
+     * The window interval that does the constant login checking.
+     * @type {Number} 
+     */
+    intervalID : 0,
+    
+    
+    onLoad : function() // called on page load...
+    {
+        // load 
+         
+        if (Roo.get('loading')) { // clear any loading indicator..
+            Roo.get('loading').remove();
+        }
+        
+        //this.switchLang('en'); // set the language to english..
+       
+        this.check({
+            success:  function(response, opts)  {  // check successfull...
+            
+                var res = this.processResponse(response);
+                this.checkFails =0;
+                if (!res.success) { // error!
+                    this.checkFails = 5;
+                    //console.log('call failure');
+                    return this.failure(response,opts);
+                }
+                
+                if (!res.data.id) { // id=0 == login failure.
+                    return this.show();
+                }
+                
+                              
+                        //console.log(success);
+                this.fillAuth(res.data);   
+                this.checkFails =0;
+                Roo.XComponent.build();
+            },
+            failure : this.show
+        });
+        
+    }, 
+    
+    
+    check: function(cfg) // called every so often to refresh cookie etc..
+    {
+        if (cfg.again) { // could be undefined..
+            this.checkFails++;
+        } else {
+            this.checkFails = 0;
+        }
+        var _this = this;
+        if (this.sending) {
+            if ( this.checkFails > 4) {
+                Roo.MessageBox.alert("Error",  
+                    "Error getting authentication status. - try reloading, or wait a while", function() {
+                        _this.sending = false;
+                    }); 
+                return;
+            }
+            cfg.again = true;
+            _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
+            return;
+        }
+        this.sending = true;
+        
+        Roo.Ajax.request({  
+            url: this.url,
+            params: {
+                getAuthUser: true
+            },  
+            method: this.method,
+            success:  cfg.success || this.success,
+            failure : cfg.failure || this.failure,
+            scope : this,
+            callCfg : cfg
+              
+        });  
+    }, 
+    
+    
+    logout: function()
+    {
+        window.onbeforeunload = function() { }; // false does not work for IE..
+        this.user = false;
+        var _this = this;
+        
+        Roo.Ajax.request({  
+            url: this.url,
+            params: {
+                logout: 1
+            },  
+            method: 'GET',
+            failure : function() {
+                Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
+                    document.location = document.location.toString() + '?ts=' + Math.random();
+                });
+                
+            },
+            success : function() {
+                _this.user = false;
+                this.checkFails =0;
+                // fixme..
+                document.location = document.location.toString() + '?ts=' + Math.random();
+            }
+              
+              
+        }); 
+    },
+    
+    processResponse : function (response)
+    {
+        var res = '';
+        try {
+            res = Roo.decode(response.responseText);
+            // oops...
+            if (typeof(res) != 'object') {
+                res = { success : false, errorMsg : res, errors : true };
+            }
+            if (typeof(res.success) == 'undefined') {
+                res.success = false;
+            }
+            
+        } catch(e) {
+            res = { success : false,  errorMsg : response.responseText, errors : true };
+        }
+        return res;
+    },
+    
+    success : function(response, opts)  // check successfull...
+    {  
+        this.sending = false;
+        var res = this.processResponse(response);
+        if (!res.success) {
+            return this.failure(response, opts);
+        }
+        if (!res.data || !res.data.id) {
+            return this.failure(response,opts);
+        }
+        //console.log(res);
+        this.fillAuth(res.data);
+        
+        this.checkFails =0;
+        
+    },
+    
+    
+    failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
+    {
+        this.authUser = -1;
+        this.sending = false;
+        var res = this.processResponse(response);
+        //console.log(res);
+        if ( this.checkFails > 2) {
+        
+            Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
+                "Error getting authentication status. - try reloading"); 
+            return;
+        }
+        opts.callCfg.again = true;
+        this.check.defer(1000, this, [ opts.callCfg ]);
+        return;  
+    },
+    
+    
+    
+    fillAuth: function(au) {
+        this.startAuthCheck();
+        this.authUserId = au.id;
+        this.authUser = au;
+        this.lastChecked = new Date();
+        this.fireEvent('refreshed', au);
+        //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
+        //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
+        au.lang = au.lang || 'en';
+        //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
+        Roo.state.Manager.set( this.realm + 'lang' , au.lang);
+        this.switchLang(au.lang );
+        
+     
+        // open system... - -on setyp..
+        if (this.authUserId  < 0) {
+            Roo.MessageBox.alert("Warning", 
+                "This is an open system - please set up a admin user with a password.");  
+        }
+         
+        //Pman.onload(); // which should do nothing if it's a re-auth result...
+        
+             
+    },
+    
+    startAuthCheck : function() // starter for timeout checking..
+    {
+        if (this.intervalID) { // timer already in place...
+            return false;
+        }
+        var _this = this;
+        this.intervalID =  window.setInterval(function() {
+              _this.check(false);
+            }, 120000); // every 120 secs = 2mins..
+        
+        
+    },
+         
+    
+    switchLang : function (lang) 
+    {
+        _T = typeof(_T) == 'undefined' ? false : _T;
+          if (!_T || !lang.length) {
+            return;
+        }
+        
+        if (!_T && lang != 'en') {
+            Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
+            return;
+        }
+        
+        if (typeof(_T.en) == 'undefined') {
+            _T.en = {};
+            Roo.apply(_T.en, _T);
+        }
+        
+        if (typeof(_T[lang]) == 'undefined') {
+            Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
+            return;
+        }
+        
+        
+        Roo.apply(_T, _T[lang]);
+        // just need to set the text values for everything...
+        var _this = this;
+        /* this will not work ...
+        if (this.form) { 
+            
+               
+            function formLabel(name, val) {
+                _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
+            }
+            
+            formLabel('password', "Password"+':');
+            formLabel('username', "Email Address"+':');
+            formLabel('lang', "Language"+':');
+            this.dialog.setTitle("Login");
+            this.dialog.buttons[0].setText("Forgot Password");
+            this.dialog.buttons[1].setText("Login");
+        }
+        */
+        
+        
+    },
+    
+    
+    title: "Login",
+    modal: true,
+    width:  350,
+    //height: 230,
+    height: 180,
+    shadow: true,
+    minWidth:200,
+    minHeight:180,
+    //proxyDrag: true,
+    closable: false,
+    draggable: false,
+    collapsible: false,
+    resizable: false,
+    center: {  // needed??
+        autoScroll:false,
+        titlebar: false,
+       // tabPosition: 'top',
+        hideTabs: true,
+        closeOnTab: true,
+        alwaysShowTabs: false
+    } ,
+    listeners : {
+        
+        show  : function(dlg)
+        {
+            //console.log(this);
+            this.form = this.layout.getRegion('center').activePanel.form;
+            this.form.dialog = dlg;
+            this.buttons[0].form = this.form;
+            this.buttons[0].dialog = dlg;
+            this.buttons[1].form = this.form;
+            this.buttons[1].dialog = dlg;
+           
+           //this.resizeToLogo.defer(1000,this);
+            // this is all related to resizing for logos..
+            //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
+           //// if (!sz) {
+             //   this.resizeToLogo.defer(1000,this);
+             //   return;
+           // }
+            //var w = Ext.lib.Dom.getViewWidth() - 100;
+            //var h = Ext.lib.Dom.getViewHeight() - 100;
+            //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
+            //this.center();
+            if (this.disabled) {
+                this.hide();
+                return;
+            }
+            
+            if (this.user.id < 0) { // used for inital setup situations.
+                return;
+            }
+            
+            if (this.intervalID) {
+                // remove the timer
+                window.clearInterval(this.intervalID);
+                this.intervalID = false;
+            }
+            
+            
+            if (Roo.get('loading')) {
+                Roo.get('loading').remove();
+            }
+            if (Roo.get('loading-mask')) {
+                Roo.get('loading-mask').hide();
+            }
+            
+            //incomming._node = tnode;
+            this.form.reset();
+            //this.dialog.modal = !modal;
+            //this.dialog.show();
+            this.el.unmask(); 
+            
+            
+            this.form.setValues({
+                'username' : Roo.state.Manager.get(this.realm + '.username', ''),
+                'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
+            });
+            
+            this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
+            if (this.form.findField('username').getValue().length > 0 ){
+                this.form.findField('password').focus();
+            } else {
+               this.form.findField('username').focus();
+            }
+    
+        }
+    },
+    items : [
+         {
+       
+            xtype : 'ContentPanel',
+            xns : Roo,
+            region: 'center',
+            fitToFrame : true,
+            
+            items : [
+    
+                {
+               
+                    xtype : 'Form',
+                    xns : Roo.form,
+                    labelWidth: 100,
+                    style : 'margin: 10px;',
+                    
+                    listeners : {
+                        actionfailed : function(f, act) {
+                            // form can return { errors: .... }
+                                
+                            //act.result.errors // invalid form element list...
+                            //act.result.errorMsg// invalid form element list...
+                            
+                            this.dialog.el.unmask();
+                            Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
+                                        "Login failed - communication error - try again.");
+                                      
+                        },
+                        actioncomplete: function(re, act) {
+                             
+                            Roo.state.Manager.set(
+                                this.dialog.realm + '.username',  
+                                    this.findField('username').getValue()
+                            );
+                            Roo.state.Manager.set(
+                                this.dialog.realm + '.lang',  
+                                this.findField('lang').getValue() 
+                            );
+                            
+                            this.dialog.fillAuth(act.result.data);
+                              
+                            this.dialog.hide();
+                            
+                            if (Roo.get('loading-mask')) {
+                                Roo.get('loading-mask').show();
+                            }
+                            Roo.XComponent.build();
+                            
+                             
+                            
+                        }
+                    },
+                    items : [
+                        {
+                            xtype : 'TextField',
+                            xns : Roo.form,
+                            fieldLabel: "Email Address",
+                            name: 'username',
+                            width:200,
+                            autoCreate : {tag: "input", type: "text", size: "20"}
+                        },
+                        {
+                            xtype : 'TextField',
+                            xns : Roo.form,
+                            fieldLabel: "Password",
+                            inputType: 'password',
+                            name: 'password',
+                            width:200,
+                            autoCreate : {tag: "input", type: "text", size: "20"},
+                            listeners : {
+                                specialkey : function(e,ev) {
+                                    if (ev.keyCode == 13) {
+                                        this.form.dialog.el.mask("Logging in");
+                                        this.form.doAction('submit', {
+                                            url: this.form.dialog.url,
+                                            method: this.form.dialog.method
+                                        });
+                                    }
+                                }
+                            }  
+                        },
+                        {
+                            xtype : 'ComboBox',
+                            xns : Roo.form,
+                            fieldLabel: "Language",
+                            name : 'langdisp',
+                            store: {
+                                xtype : 'SimpleStore',
+                                fields: ['lang', 'ldisp'],
+                                data : [
+                                    [ 'en', 'English' ],
+                                    [ 'zh_HK' , '\u7E41\u4E2D' ],
+                                    [ 'zh_CN', '\u7C21\u4E2D' ]
+                                ]
+                            },
+                            
+                            valueField : 'lang',
+                            hiddenName:  'lang',
+                            width: 200,
+                            displayField:'ldisp',
+                            typeAhead: false,
+                            editable: false,
+                            mode: 'local',
+                            triggerAction: 'all',
+                            emptyText:'Select a Language...',
+                            selectOnFocus:true,
+                            listeners : {
+                                select :  function(cb, rec, ix) {
+                                    this.form.switchLang(rec.data.lang);
+                                }
+                            }
+                        
+                        }
+                    ]
+                }
+                  
+                
+            ]
+        }
+    ],
+    buttons : [
+        {
+            xtype : 'Button',
+            xns : 'Roo',
+            text : "Forgot Password",
+            listeners : {
+                click : function() {
+                    //console.log(this);
+                    var n = this.form.findField('username').getValue();
+                    if (!n.length) {
+                        Roo.MessageBox.alert("Error", "Fill in your email address");
+                        return;
+                    }
+                    Roo.Ajax.request({
+                        url: this.dialog.url,
+                        params: {
+                            passwordRequest: n
+                        },
+                        method: this.dialog.method,
+                        success:  function(response, opts)  {  // check successfull...
+                        
+                            var res = this.dialog.processResponse(response);
+                            if (!res.success) { // error!
+                               Roo.MessageBox.alert("Error" ,
+                                    res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
+                               return;
+                            }
+                            Roo.MessageBox.alert("Notice" ,
+                                "Please check you email for the Password Reset message");
+                        },
+                        failure : function() {
+                            Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
+                        }
+                        
+                    });
+                }
+            }
+        },
+        {
+            xtype : 'Button',
+            xns : 'Roo',
+            text : "Login",
+            listeners : {
+                
+                click : function () {
+                        
+                    this.dialog.el.mask("Logging in");
+                    this.form.doAction('submit', {
+                            url: this.dialog.url,
+                            method: this.dialog.method
+                    });
+                }
+            }
+        }
+    ]
+  
+  
+})
+
+
+   
\ No newline at end of file